
本文详解如何使用原生 PHP 函数(opendir/readdir/closedir)实现真正的递归目录遍历,生成带层级关系的嵌套数组结构,解决子目录内容未被递归解析的常见逻辑错误。
本文详解如何使用原生 php 函数(`opendir`/`readdir`/`closedir`)实现真正的递归目录遍历,生成带层级关系的嵌套数组结构,解决子目录内容未被递归解析的常见逻辑错误。
在开发文件索引系统(如轻量级 h5ai 替代方案)时,常需将目录结构转化为嵌套数组:每个子目录自身为一个关联数组键,其值为递归生成的子树;普通文件则作为索引数组元素存入。由于课程限制不能使用 RecursiveDirectoryIterator 等 SPL 类,必须基于底层函数手动实现递归逻辑——而原问题中“子目录为空数组”的根本原因在于参数传递方式错误导致递归结果未被正确合并回父级数组。
关键修正点在于:避免通过引用参数传递子数组,改用函数返回值显式拼接。原代码中 $this->getFiles($newPath, $parent[$entry]); 将 $parent[$entry](即空数组)传入,虽在递归内部修改了该数组,但由于 PHP 数组默认按值传递(即使传入的是子数组引用,其作用域也仅限于当前调用栈),递归返回后 $parent[$entry] 并未获得子目录的实际内容。正确做法是让 getFiles() 始终返回完整子树,并直接赋值给 $entries[$entry]。
以下是优化后的完整实现:
<?php
class H5AI {
public function __construct(string $path) {
if (!is_dir($path)) {
throw new InvalidArgumentException("Path '{$path}' is not a valid directory.");
}
print_r($this->getFiles($path));
}
public function getFiles(string $directory): array {
$handle = opendir($directory);
if ($handle === false) {
throw new RuntimeException("Cannot open directory: {$directory}");
}
$entries = [];
while (($entry = readdir($handle)) !== false) {
$path = $directory . DIRECTORY_SEPARATOR . $entry;
// 跳过隐藏文件(以 . 开头)及特殊目录
if (str_starts_with($entry, '.')) {
continue;
}
if (is_file($path)) {
$entries[] = $entry;
} elseif (is_dir($path)) {
// 排除标准导航目录和系统目录
if (in_array($entry, ['.', '..', '$RECYCLE.BIN', 'System Volume Information'], true)) {
continue;
}
// 递归获取子目录结构,并直接赋值到当前层级
$entries[$entry] = $this->getFiles($path);
}
}
closedir($handle);
return $entries;
}
}
// 使用示例(命令行运行)
// php index.php "./test_dir"
if (isset($argv[1])) {
new H5AI($argv[1]);
}✅ 核心改进说明:
立即学习“PHP免费学习笔记(深入)”;
- getFiles() 统一返回 array 类型,消除副作用,确保每次递归调用的结果都能被父级准确捕获;
- 移除了冗余的私有属性 $_tree 和 $_path,避免状态管理混乱;
- 添加基础异常处理(路径校验、目录打开失败),提升健壮性;
- 使用 str_starts_with()(PHP 8.0+)或 substr($entry, 0, 1) === '.' 兼容旧版本过滤隐藏项;
- 显式跳过 Windows 系统目录(如 $RECYCLE.BIN),可根据实际需求扩展排除列表。
⚠️ 注意事项:
- 若需支持 PHP
- 深层嵌套目录可能触发 PHP 默认的 max_execution_time 或内存限制,生产环境建议增加递归深度控制或使用迭代替代递归;
- 此实现不处理符号链接(symlink),如需支持,可添加 is_link($path) 判断并决定是否跟随。
该方案简洁、可读性强,完全满足教学项目对“纯手工递归”的要求,同时具备良好的扩展性和错误防御能力。











