專案中常需遞歸遍歷目錄(如掃描模板、清理快取、建立檔案索引)。PHP 提供多種目錄讀取 API,本文實測五種主流寫法並附效能基準測試,幫助選型。
<?php
function scanGlob(string $path, array &$result): void {
foreach (glob($path . '/*') as $file) {
if (is_dir($file)) {
scanGlob($file, $result);
} else {
$result[] = realpath($file);
}
}
}
?>
<?php
function scanDirRead(string $path, array &$result): void {
$dir = dir($path);
while (($file = $dir->read()) !== false) {
if ($file === '.' || $file === '..') continue;
$full = realpath($path . '/' . $file);
$result[] = $full;
if (is_dir($full)) scanDirRead($full, $result);
}
$dir->close();
}
?>
<?php
function scanOpenDir(string $path, array &$result): void {
$handle = opendir($path);
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') continue;
$full = realpath($path . '/' . $file);
$result[] = $full;
if (is_dir($full)) scanOpenDir($full, $result);
}
closedir($handle);
}
?>
<?php
function scanScandir(string $path, array &$result): void {
foreach (scandir($path) as $file) {
if ($file === '.' || $file === '..') continue;
$full = realpath($path . '/' . $file);
$result[] = $full;
if (is_dir($full)) scanScandir($full, $result);
}
}
?>
<?php
function scanSPL(string $path, array &$result): void {
$iterator = new DirectoryIterator($path);
foreach ($iterator as $fileInfo) {
if ($fileInfo->isDot()) continue;
$full = $fileInfo->getRealPath();
$result[] = $full;
if ($fileInfo->isDir()) scanSPL($full, $result);
}
}
?>
測試環境:PHP 7.4,SSD,約 5 萬檔案專案目錄。
| 方法 | 耗時(秒) | 記憶體峰值 | 評價 |
|---|---|---|---|
| glob() | 0.164 | 低 | 最快,模式匹配靈活 |
| opendir/readdir | 0.233 | 低 | 經典穩定,資源需手動 close |
| scandir() | 0.240 | 中 | 一次讀全陣列,大目錄記憶體較高 |
| dir()/read() | 0.243 | 低 | OO 封裝,較 opendir 略慢 |
| SPL DirectoryIterator | 0.330 | 中 | 功能最豐富(權限、時間、類型),但效能最差 |
glob() 或 opendir/readdir。SPL DirectoryIterator 或 FilesystemIterator(可過濾 . / ..)。scandir() 一次載入陣列,改用迭代器或 glob() 分批。RecursiveDirectoryIterator + RecursiveIteratorIterator 一行搞定:<?php
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS));
foreach ($files as $file) {
// $file 是 SplFileInfo 物件
echo $file->getRealPath() . PHP_EOL;
}
?>
realpath() 會解析符號連結並快取,大量呼叫可能觸發快取滿;可用 clearstatcache()。Permission denied,建議 try-catch 或 @ 抑制。根據業務場景選對工具,效能差異可達 2 倍以上。