| 方案 | 代码量 | 内存占用 | 性能 | 推荐场景 |
|---|---|---|---|---|
| scandir 递归 + 数组合并 | 少 | 高(全量数组) | 中 | 小目录(<1万文件)、一次性处理 |
| Generator 生成器逐个产出 | 中 | 极低(流式) | 高 | 大目录、管道处理、内存受限 |
| RecursiveDirectoryIterator (SPL) | 少 | 低(迭代器模式) | 高 | 标准库、需过滤/排序/深度控制 |
<?php
function getAllFiles(string $dir): array
{
$files = [];
$entries = scandir($dir);
foreach ($entries as $entry) {
if ($entry === '.' || $entry === '..') continue;
$path = $dir . DIRECTORY_SEPARATOR . $entry;
if (is_dir($path)) {
$files = array_merge($files, getAllFiles($path));
} else {
$files[] = $path;
}
}
return $files;
}
// 使用
$list = getAllFiles(__DIR__ . '/storage');
print_r($list);
缺点:array_merge反复复制数组,大目录下内存峰值 = 所有路径字符串总和 + 索引数组开销,易触发memory_limit。
<?php
function iterFiles(string $dir): \Generator
{
$entries = scandir($dir);
foreach ($entries as $entry) {
if ($entry === '.' || $entry === '..') continue;
$path = $dir . DIRECTORY_SEPARATOR . $entry;
if (is_dir($path)) {
yield from iterFiles($path); // PHP 7+ 委托生成器
} else {
yield $path;
}
}
}
// 使用:逐个处理,内存恒定
foreach (iterFiles('/var/www/uploads') as $file) {
// 处理单文件:上传、压缩、扫描病毒...
processFile($file);
}
// 若必须收集数组(不推荐大目录)
$all = iterator_to_array(iterFiles($dir), false);
生成器实现「惰性求值」,yield from 无缝委托子生成器,代码简洁且内存友好。
<?php
$dir = '/var/www/uploads';
$flags = FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, $flags),
RecursiveIteratorIterator::SELF_FIRST // 先目录后文件,或 CHILD_FIRST
);
// 过滤:仅 .jpg/.png,大小 > 1MB
$filter = new RegexIterator($iterator, '/\.(jpg|png)$/i');
$filter = new CallbackFilterIterator($filter, function ($current) {
return $current->getSize() > 1024 * 1024;
});
foreach ($filter as $path => $fileInfo) {
/** @var SplFileInfo $fileInfo */
echo "{$fileInfo->getPathname()} ({$fileInfo->getSize()} bytes)\n";
}
// 统计总数
$count = iterator_count($filter);
echo "Total: $count\n";
RegexIterator、CallbackFilterIterator、FileTypeFilterIterator 组合无需手写判断。SplFileInfo 提供 getSize()、getMTime()、getExtension()、isReadable() 等方法。RecursiveIteratorIterator::setMaxDepth($n) 限制递归层级。Iterator/Countable,可直接 iterator_to_array、count()、foreach。<?php
// 统一使用 DIRECTORY_SEPARATOR 常量
$path = $dir . DIRECTORY_SEPARATOR . $entry;
// 或实时规范化
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
方案一 scandir+array_merge: 2.8s 峰值内存 180 MB
方案二 Generator: 1.9s 峰值内存 2 MB
方案三 SPL Iterator: 1.7s 峰值内存 3 MB
<?php
// 1. 按修改时间排序(需先收集数组)
$files = iterator_to_array(iterFiles($dir));
usort($files, fn($a, $b) => filemtime($b) <=> filemtime($a));
// 2. 计算目录总大小
$totalSize = 0;
foreach (iterFiles($dir) as $f) $totalSize += filesize($f);
// 3. 查找重复文件(MD5 分组)
$map = [];
foreach (iterFiles($dir) as $f) {
$hash = md5_file($f);
$map[$hash][] = $f;
}
$duplicates = array_filter($map, fn($v) => count($v) > 1);
小目录、一次性脚本:scandir 递归最直观。
大目录、长时间运行、内存敏感:Generator 或 SPL RecursiveDirectoryIterator 二选一,SPL 功能更丰富,Generator 语法更灵活。现代 PHP 项目推荐默认使用 SPL 迭代器,配合过滤器链实现声明式文件遍历。