遞迴列出指定目錄下的**所有檔案與子目錄**完整路徑。
<?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>";
?>
<?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;
}
?>
<?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 路徑相容 | 良好 | 良好 | 良好 |
<?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);
?>
<?php
// scandir($dir, $sort_order, $context)
// $sort_order: 0 升序 (預設), 1 降序, SCANDIR_SORT_NONE 不排序 (PHP 5.4+)
$files = scandir('/var/log', SCANDIR_SORT_NONE); // 最快
?>
array_merge 會重建陣列,大目錄極慢。解法:傳遞參考陣列 &$result 或使用 Generator。RecursiveDirectoryIterator::FOLLOW_SYMLINKS 可能導致無限循環。預設 SKIP_DOTS 不跟隨連結,較安全。scandir/glob 遇到無權限目錄會回傳 false/觸發 Warning。SPL 會拋出 UnexpectedValueException,需 try-catch。DIRECTORY_SEPARATOR 或 / 確保跨平台。<?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`**:效能最好、記憶體友善、物件導向、功能最豐富。scandir() + 參考傳遞遞迴。*.php):glob() 方便,但遞迴需自行實作。.、..、符號連結、權限錯誤。