scandir()递归死循环最常因未过滤'.','..'、符号链接形成环路或权限异常导致重复路径触发;需用realpath()规范路径、in_array()记录已访问路径并检查scandir()返回值防错。

PHP递归遍历文件系统时,死循环最常由符号链接(symlink)形成环路、未过滤 . 和 ..、或权限异常导致的隐式重复路径触发——不是代码写错了,而是文件系统本身“骗”了你。
为什么 scandir() + 递归会陷入死循环
直接用 scandir() 遍历目录时,若目标目录包含指向父级或自身的符号链接(比如 /var/www/html -> /var/www),is_dir($path) 仍返回 true,函数就会反复钻进同一路径。另外,漏掉跳过 . 和 .. 会让函数在当前目录和上层目录之间来回调用自身。
-
scandir()不区分真实目录与符号链接,is_dir()对 symlink 也返回true - 没检查
$item === '.' || $item === '..'→ 递归调用scanDirectory($path . '/.')或scanDirectory($path . '/..') - 遇到权限不足的子目录时,
scandir()返回false,但若没做判空处理,后续逻辑可能误用旧路径继续递归
必须加的三道防护:路径规范化 + 真实目录判断 + 已访问路径记录
光靠跳过 . 和 .. 不够,得从源头切断环路。关键不是“避免进入”,而是“确认这是新路径”。
- 用
realpath($fullPath)获取绝对真实路径,再用is_dir()判断——这能绕过 symlink 伪装 - 维护一个
static $visited = []数组,每次进入前检查in_array($realPath, $visited),命中则return - 对
scandir()结果做空值检查:if ($items === false) return;,防止因权限问题导致后续逻辑错乱
限制深度并捕获异常比硬扛更实际
即使加了防护,深层嵌套目录(如 Docker overlayfs、某些备份工具生成的结构)仍可能耗尽栈空间。PHP 默认递归限制约 100 层,但实际建议主动掐断。
立即学习“PHP免费学习笔记(深入)”;
- 递归函数签名加上
$depth = 0参数,每次调用传$depth + 1 - 开头加判断:
if ($depth > 32) { trigger_error("Recursion depth exceeded", E_USER_WARNING); return; } - 配合
set_time_limit(30)防超时,尤其在 Web 环境下——别等它自己崩
真正容易被忽略的是:符号链接可能跨文件系统,realpath() 在某些挂载点下失效;此时需改用 lstat() + FILEINFO_SYMLINK 主动识别并跳过。安全的遍历从来不是“写对递归”,而是“预判文件系统怎么耍花招”。



















