php遍历文件系统需据场景选方法:scandir()适合单层但需过滤.和..;递归首选recursivedirectoryiterator+recursiveiteratoriterator;glob()内存开销大且跨平台问题多;opendir()/readdir()省内存但需防句柄泄漏。

PHP里遍历文件系统不是选“最短代码”,而是看场景:要单层还是递归?要过滤扩展名还是按权限跳过?内存是否受限?用错方法可能卡死或漏掉文件。
scandir() 适合单层快速列举,但必须手动处理 . 和 ..
它返回的是包含所有条目的数组,包括 . 和 ..,不加过滤直接传给 is_file() 或拼路径会出错。
- 用
array_filter($files, fn($f) => $f !== '.' && $f !== '..')去掉特殊项 - 排序默认是字典序,
file10.txt会排在file2.txt前面;需要自然排序就加natcasesort($files) - 路径拼接必须用
DIRECTORY_SEPARATOR或realpath(),硬写/在 Windows 下会失效 - 不递归,想进子目录得自己写递归调用,容易栈溢出或无限循环(比如符号链接成环)
RecursiveDirectoryIterator + RecursiveIteratorIterator 是递归首选
这是 PHP 官方推荐的递归方式,比手写递归健壮得多,自动跳过不可读目录(只要加 try/catch),也支持 SKIP_DOTS 标志省去手动过滤。
- 初始化时传
FilesystemIterator::SKIP_DOTS,.和..就不会出现在迭代中 - 用
$file->isFile()判断类型,别用is_file($file->getPathname())—— 后者可能因权限失败而返回 false,误判为非文件 - 遇到
Permission denied会抛UnexpectedValueException,必须用try/catch包裹foreach,否则整个遍历中断 - 如果只需要文件(不含目录),构造
RecursiveIteratorIterator时第三个参数用RecursiveIteratorIterator::LEAVES_ONLY
glob() 看似简洁,但只适合小目录+固定模式
glob() 是一次性加载全部匹配结果到内存的,对含几千个文件的目录,很容易触发 memory_limit 或超时。它也不原生支持深度递归(GLOB_BRACE 或 ** 依赖系统 libc,跨平台行为不一致)。
- 模式里写
$dir . '/*.php'没问题,但写$dir . '/**/*.php'在部分 PHP 版本或系统上会静默失败 - 无法跳过无权限目录,
glob()遇到/proc类路径直接返回空数组,不报错也不提示,容易误以为“没文件” - 返回的是纯路径字符串数组,没有元信息(如修改时间、大小),后续要查得再
stat()一次,IO 开销翻倍 - 不能和
is_readable()结合做前置检查——glob()本身不校验可读性
opendir() + readdir() 适合内存敏感场景,但易漏资源释放
它不预加载全部条目,边读边处理,内存占用稳定,适合遍历超大目录(比如日志归档目录)。但忘了 closedir() 会导致句柄泄漏,尤其在长生命周期脚本(如 CLI 守护进程)中会累积。
- 每次
readdir()返回的是相对名,拼完整路径前先用is_dir()或is_file()判断,避免对./..调用stat() - 循环里不要在
readdir()条件中直接写is_file($path . '/' . $f),万一$f是空字符串或非法字符会出错 - PHP 8.1+ 支持
Dir类替代opendir(),语法更干净:$dir = dir($path); while (($f = $dir->read()) !== false) { ... } $dir->close(); - Windows 下注意路径长度限制(MAX_PATH=260),长路径要用
\?前缀,opendir()不支持,得换RecursiveDirectoryIterator
真正麻烦的不是“怎么写”,而是权限异常、符号链接循环、超长路径、编码不一致(比如 FTP 上传的 UTF-8 文件名在某些 locale 下 scandir() 返回乱码)——这些情况在测试环境常被忽略,上线后才暴露。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











