首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 目錄遞迴遍歷:glob() 與 scandir() 兩種實作比較

     編輯:PHP 相關知識

需求

遞迴列出指定目錄下的**所有檔案與子目錄**完整路徑。

方法一:glob() + 遞迴函式

<?php
function listFilesGlob(string $path): array {
    $result = [];
    // glob() 不支援遞迴 ** 模式 (PHP 核心未實作),需自行遞迴
    foreach (glob($path . '/*') as $item) {
        $realPath = realpath($item); // 解析真實路徑
        if ($realPath === false) continue; // 忽略無法讀取
        
        $result[] = $realPath;
        
        if (is_dir($realPath)) {
            // 遞迴處理子目錄
            $result = array_merge($result, listFilesGlob($realPath));
        }
    }
    return $result;
}

// 使用
$files = listFilesGlob('/var/www/html');
foreach ($files as $f) echo $f . "<br>";
?>

方法二:scandir() + 遞迴函式

<?php
function listFilesScandir(string $path): array {
    $result = [];
    $entries = scandir($path);
    if ($entries === false) return $result; // 目錄不存在或無權限
    
    foreach ($entries as $entry) {
        if ($entry === '.' || $entry === '..') continue;
        
        $fullPath = $path . DIRECTORY_SEPARATOR . $entry;
        $realPath = realpath($fullPath);
        if ($realPath === false) continue;
        
        $result[] = $realPath;
        
        if (is_dir($realPath)) {
            $result = array_merge($result, listFilesScandir($realPath));
        }
    }
    return $result;
}
?>

方法三:現代最佳實踐 - RecursiveDirectoryIterator (推薦)

<?php
function listFilesSPL(string $path): array {
    $result = [];
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    
    foreach ($iterator as $fileinfo) {
        $result[] = $fileinfo->getPathname();
    }
    return $result;
}

// 記憶體友善版 (Generator,PHP 5.5+)
function yieldFilesSPL(string $path): Generator {
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS)
    );
    foreach ($iterator as $fileinfo) {
        yield $fileinfo->getPathname();
    }
}

// 使用 Generator 逐個處理,不佔用大量記憶體
foreach (yieldFilesSPL('/var/www/html') as $file) {
    // 處理單一檔案
    echo $file . "<br>";
}
?>

三種方法比較

特性glob() 遞迴scandir() 遞迴RecursiveDirectoryIterator (SPL)
程式碼複雜度中中**低** (內建遞迴)
效能 (大量檔案)較慢 (多次 glob 呼叫、陣列合併)中等**最佳** (C 層級迭代、可用 Generator)
記憶體使用高 (遞迴回傳大陣列)高**低** (Generator 逐個產出)
錯誤處理需自行檢查 false需自行檢查 false拋出 Exception (可 try-catch)
檔案資訊存取需額外 stat需額外 stat**內建** (getSize, getMTime 等)
排序控制預設排序預設排序可配合 IteratorIterator 自訂
Windows 路徑相容良好良好良好

glob() 旗標補充

<?php
// GLOB_BRACE: {a,b,c} 擴展
$files = glob('/var/log/{*.log,*.txt}', GLOB_BRACE);

// GLOB_NOSORT: 不排序 (大量檔案較快)
$files = glob('/var/log/*', GLOB_NOSORT);

// GLOB_ONLYDIR: 只列目錄
$dirs = glob('/var/www/*', GLOB_ONLYDIR);

// GLOB_MARK: 目錄結尾加 /
$dirs = glob('/var/www/*', GLOB_MARK);
?>

scandir() 排序參數

<?php
// scandir($dir, $sort_order, $context)
// $sort_order: 0 升序 (預設), 1 降序, SCANDIR_SORT_NONE 不排序 (PHP 5.4+)
$files = scandir('/var/log', SCANDIR_SORT_NONE); // 最快
?>

常見陷阱與修正

  1. 陣列合併效能:遞迴函式中 array_merge 會重建陣列,大目錄極慢。解法:傳遞參考陣列 &$result 或使用 Generator。
  2. 符號連結循環:RecursiveDirectoryIterator::FOLLOW_SYMLINKS 可能導致無限循環。預設 SKIP_DOTS 不跟隨連結,較安全。
  3. 權限錯誤:scandir/glob 遇到無權限目錄會回傳 false/觸發 Warning。SPL 會拋出 UnexpectedValueException,需 try-catch。
  4. 路徑分隔符:使用 DIRECTORY_SEPARATOR 或 / 確保跨平台。

修正版參考傳遞遞迴 (非 Generator)

<?php
function listFilesScandirRef(string $path, array &$result = []): void {
    $entries = @scandir($path);
    if ($entries === false) return;
    
    foreach ($entries as $entry) {
        if ($entry === '.' || $entry === '..') continue;
        $fullPath = $path . DIRECTORY_SEPARATOR . $entry;
        $realPath = realpath($fullPath);
        if ($realPath === false) continue;
        
        $result[] = $realPath;
        if (is_dir($realPath)) {
            listFilesScandirRef($realPath, $result);
        }
    }
}

$allFiles = [];
listFilesScandirRef('/var/www', $allFiles);
// $allFiles 現在包含所有路徑
?>

總結

  • **新專案首選 RecursiveDirectoryIterator` + `Generator`**:效能最好、記憶體友善、物件導向、功能最豐富。
  • 簡單腳本、相容舊版 PHP:scandir() + 參考傳遞遞迴。
  • 需模式匹配 (如 *.php):glob() 方便,但遞迴需自行實作。
  • 永遠處理 .、..、符號連結、權限錯誤。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved