php8中推荐用recursivedirectoryiterator+recursiveiteratoriterator遍历目录,因其自动跳过.和..、支持异常捕获、内存友好;手写递归易因权限、符号链接、未过滤.和..等问题出错;disk_total_space()等函数返回文件系统级数据,不适用于目录大小统计。

PHP8 中用 recursiveDirectoryIterator 遍历目录比手写递归更稳
直接手写递归函数算文件夹大小,在 PHP8 里容易踩坑:比如符号链接循环、权限拒绝导致 scandir() 报错中断、大目录下内存暴涨。官方推荐用 SPL 的 RecursiveDirectoryIterator + RecursiveIteratorIterator 组合,它天然跳过 . 和 ..,支持异常捕获,且迭代器模式内存友好。
关键点:
-
RecursiveDirectoryIterator默认不递进子目录,必须配合RecursiveIteratorIterator才能展开层级 - 需设标志位
FilesystemIterator::SKIP_DOTS,否则会把当前/父目录当普通项处理 - 遇到无权限子目录时,
getChildren()可能抛UnexpectedValueException,必须 try/catch
$size = 0;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/path/to/dir', FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$size += $file->getSize();
}
}
手写递归函数要注意 is_readable() 和 is_dir() 的顺序
如果坚持用 scandir() + 自己递归,最常见错误是先 is_dir() 再 scandir(),但某些路径可能 is_dir() 返回 true 却因权限不足无法读取——这时 scandir() 直接警告并返回 false,整个函数就断了。
正确做法:
- 先用
is_readable($path)判断可读性,不可读就跳过(或记录 warning) - 再用
is_dir($path)确认是否为目录,避免把文件当目录递归 - 对每个子项调用前检查
!in_array($item, ['.', '..']),PHP8 不再隐式过滤
function getDirSize(string $path): int {
if (!is_readable($path)) {
return 0;
}
if (!is_dir($path)) {
return (int) filesize($path);
}
$size = 0;
foreach (scandir($path) as $item) {
if (in_array($item, ['.', '..'])) continue;
$full = $path . DIRECTORY_SEPARATOR . $item;
$size += getDirSize($full);
}
return $size;
}
disk_total_space() 和 disk_free_space() 不能替代目录统计
有人误以为用 disk_total_space() 减去 disk_free_space() 就是当前目录大小,这是错的。这两个函数返回的是**所在文件系统**的总/空闲空间,和目录内容完全无关——哪怕你只统计一个空文件夹,结果也是整个磁盘已用空间。
典型误用场景:
- 想查某个项目目录占了多少 MB,却调用了
disk_total_space(__DIR__) - 在容器或挂载子目录中运行,
__DIR__所在分区和实际目标路径不在同一设备上
务必确认:你要的是「这个路径下所有文件加起来多大」,不是「这台机器硬盘还剩多少」。
大目录下记得设 set_time_limit(0) 和内存限制
PHP 默认执行时间 30 秒,遍历含数万文件的目录很容易超时;而 RecursiveDirectoryIterator 在 PHP8 中虽内存优化,但极端情况仍可能触发 Allowed memory size exhausted。
上线前必须加:
-
set_time_limit(0)—— 放在脚本开头,禁用超时(CLI 模式下有效) -
ini_set('memory_limit', '512M')—— 根据预估文件数调整,别设 -1 - 若用于 Web 请求,建议改用队列或分块统计,避免用户等太久
另外,RecursiveDirectoryIterator 构造时传入的路径必须存在且可读,否则直接抛 UnexpectedValueException,这点比 scandir() 的 false 更严格——少个判断就会 crash。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











