递归遍历目录时必须过滤 . 和 ..,否则引发无限循环或“maximum function nesting level”错误;推荐用 recursivedirectoryiterator 并启用 skip_dots 标志,配合 try/catch 处理权限异常,且需防范符号链接导致的死循环。

递归函数里忘记过滤 . 和 .. 会导致无限循环或报错
PHP 的 scandir() 返回的数组默认包含 .(当前目录)和 ..(父目录),如果在递归中不显式跳过,is_dir($path . '/' . $file) 会对这两个字符串反复调用自身,轻则卡死,重则触发“maximum function nesting level”错误。
必须在每次遍历前加判断:
if ($file === '.' || $file === '..') continue;- 别用
==,必须用严格等于===,避免字符串类型隐式转换出问题 - 如果业务逻辑需要排除隐藏文件(如
.git、.env),额外加str_starts_with($file, '.')判断
opendir() + readdir() 比 scandir() 更省内存但容易漏关句柄
当目录下文件极多(比如上万)时,scandir() 会一次性把全部文件名加载进内存;而 opendir() 是流式读取,更适合大目录场景。
但要注意:readdir() 返回 false 表示读完或出错,必须用 !== false 判断,否则遇到文件名是 0 或空字符串时会提前退出:
$dh = opendir($dir);
while (($entry = readdir($dh)) !== false) {
if ($entry === '.' || $entry === '..') continue;
$full = $dir . '/' . $entry;
if (is_dir($full)) {
// 递归调用
getSubdirs($full, $result);
}
}
closedir($dh); // 忘关句柄会导致资源泄漏,尤其在长生命周期脚本中
用 RecursiveDirectoryIterator 获取子文件夹更健壮但需注意权限异常
原生迭代器方式不用自己写递归逻辑,也自动跳过 . 和 ..,但默认不跳过不可读子目录——一旦遇到权限不足的子目录(如 /proc 下某些项),会直接抛出 UnexpectedValueException。
正确做法是包一层 try/catch,并启用 RecursiveDirectoryIterator::SKIP_DOTS 标志:
function getSubdirectories($path) {
$result = [];
try {
$iter = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iter as $item) {
if ($item->isDir() && $item->getRealPath() !== $path) {
$result[] = $item->getRealPath();
}
}
} catch (UnexpectedValueException $e) {
// 忽略无权限目录,不中断整个遍历
}
return $result;
}
注意:SELF_FIRST 确保父目录先于其内容被处理;getRealPath() !== $path 排除起始目录本身,只保留子级。
返回结果里混入符号链接可能造成路径重复或死循环
Linux/Unix 下目录可能是符号链接,is_dir() 和 RecursiveDirectoryIterator 默认都会跟随链接。如果链接形成环(比如 A → B → A),递归就会无限深入。
安全做法是:用 lstat() 替代 is_dir() 先检查是否为真实目录(非链接),或在迭代器构造时传入 FilesystemIterator::FOLLOW_SYMLINKS 的反向逻辑(即不跟随):
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS | FilesystemIterator::KEY_AS_PATHNAME)- 再配合
$item->getType() === 'dir'(该方法不跟随链接) - 或者直接用
!is_link($full) && is_dir($full)双重判断
这点在部署到容器或挂载网络存储时特别容易被忽略——看似正常的目录结构,实际底层是链接嵌套。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











