常見需求:統計項目代碼行數、清理臨時文件、生成站點地圖。本文對比三種實現方式並給出生產環境推薦方案。
<?php
function scanDirRecursive(string $dir, int &$dirCount, int &$fileCount): void {
$handle = opendir($dir);
while (($entry = readdir($handle)) !== false) {
if ($entry === '.' || $entry === '..') continue;
$path = $dir . DIRECTORY_SEPARATOR . $entry;
if (is_dir($path)) {
$dirCount++;
scanDirRecursive($path, $dirCount, $fileCount);
} else {
$fileCount++;
}
}
closedir($handle);
}
$dirs = $files = 0;
scanDirRecursive('/var/www/html', $dirs, $files);
echo "目錄: $dirs, 文件: $files\n";
缺點:深層目錄易棧溢出、無法輕鬆過濾/排序、效率較低(重複 `stat` 系統調用)。
<?php
function scanWithSPL(string $root): array {
$dirCount = $fileCount = 0;
$flags = FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS;
$iter = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root, $flags),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iter as $path => $info) {
if ($info->isDir()) $dirCount++;
else $fileCount++;
}
return ['dirs' => $dirCount, 'files' => $fileCount];
}
print_r(scanWithSPL('/var/www/html'));
優勢:C 層實現極快、自動處理遞歸、支援過濾器(`RegexIterator`)、可獲取 `SplFileInfo` 完整元數據(大小、時間、權限)。
$count = count(new GlobIterator('/var/www/html/*.php', FilesystemIterator::SKIP_DOTS));
// 僅統計根目錄下 .php 文件
$skipDirs = ['vendor', '.git', 'node_modules', 'storage', 'bootstrap/cache'];
$filter = new RegexIterator(
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
'/^((?!' . implode('|', $skipDirs) . ').)*$/'
);
$iter = new RecursiveIteratorIterator($filter, RecursiveIteratorIterator::SELF_FIRST);
| 方法 | 耗時 | 內存峰值 | 備註 |
|---|---|---|---|
| 遞歸函數 | ~1.2s | ~8MB | PHP 函數調用開銷大 |
| SPL 迭代器 | ~0.3s | ~4MB | C 層遍歷,推薦生產用 |
| shell `find` | ~0.15s | ~1MB | 需 `exec` 權限,跨平台差 |