recursivedirectoryiterator 比 scandir 更可靠,能正确处理符号链接、权限错误、循环软链及跨文件系统边界;需 try/catch 捕获 unexpectedvalueexception,仅对 isfile() 为 true 的项调用 getsize(),避免 filesize() 对目录返回 false 导致计算错误。

用 recursiveDirectoryIterator 遍历比 scandir 更可靠
直接递归读取子目录时,scandir 容易漏掉符号链接、权限不足的子目录,或在遇到循环软链时卡死。recursiveDirectoryIterator 内置跳过 . 和 ..,还能配合 RecursiveIteratorIterator 控制遍历深度,更贴近真实文件系统行为。
- 必须用
try/catch包裹迭代过程,否则权限错误(如Permission denied)会直接抛出UnexpectedValueException - 避免用
is_dir()+scandir()手动递归——它不处理挂载点跨文件系统边界,也难控制并发深度 - 若需排除特定路径(如
node_modules),在迭代中检查$file->getRealPath()比字符串匹配更安全
filesize() 对目录返回 false,别直接累加
PHP 的 filesize() 对目录始终返回 false(不是 0),如果没判断就直接加到总和里,结果会变成 0 或触发 notice。必须显式跳过目录项,只对 $file->isFile() 为 true 的条目调用 filesize()。
- 注意:某些 Linux 系统上,即使有读权限,
stat()系统调用也可能因 NFS 或容器挂载失败,导致filesize()返回false;此时应记录警告而非中断 - 不要用
file_exists()前置判断——它不保证后续filesize()成功,反而多一次系统调用 - 大文件(>2GB)在 32 位 PHP 中可能返回负值,建议用
$file->getSize()替代filesize(),它内部做了平台适配
计算超大目录时内存和超时要手动干预
遍历数万文件时,RecursiveDirectoryIterator 默认会把整个迭代器树留在内存里,加上 PHP 默认 max_execution_time=30,很容易超时或 OOM。不能靠增加 memory_limit 硬扛。
- 用
set_time_limit(0)解除执行时间限制,但得搭配ignore_user_abort(true)防止用户关页面中断 - 改用
Generator分块 yield 文件大小,而不是一次性收集所有SplFileInfo对象 - 对单个文件调用
clearstatcache(true, $path)没必要;但若目录结构频繁变动,可在循环外调用clearstatcache()避免缓存脏数据
Windows 下获取大小可能比 Linux 慢一倍
Windows 的 NTFS 不像 ext4 那样把目录大小存在 inode 里,每次 filesize() 或 getSize() 都要走完整路径解析 + 权限检查 + 属性读取,尤其在有大量小文件或启用了杀毒软件实时扫描时,延迟明显。
立即学习“PHP免费学习笔记(深入)”;
- 若只是粗略估算,可先用
shell_exec('dir /s /a-d ' . escapeshellarg($path) . ' 2>&1')提取最后一行数字(仅限 CLI 场景) - 不要在 Web 请求中调用
exec类函数——容易被禁用,且输出格式不稳定 - 考虑用
opcache.enable_cli=1加速脚本自身加载,但这对 I/O 瓶颈无帮助
.git 或突然挂起的网络驱动器 —— 这类情况没法靠“优化循环”解决,得靠提前设好超时和降级逻辑。











