
本文介绍使用 PHP 的 scandir() 与 is_dir() 配合,安全、清晰地遍历指定路径下的所有目录(不含文件),并逐级生成带层级关系的 HTML 链接列表,支持自定义过滤和嵌套结构输出。
本文介绍使用 php 的 `scandir()` 与 `is_dir()` 配合,安全、清晰地遍历指定路径下的所有目录(不含文件),并逐级生成带层级关系的 html 链接列表,支持自定义过滤和嵌套结构输出。
在 Web 开发或文件管理系统中,常需以树形结构展示目录层级(如导航菜单、资源浏览器),仅列出目录(排除所有文件)并为每级目录分配可操作变量,是关键需求。原始代码中混用 glob() 多层嵌套、错误地将文件数组当作路径拼接(如 $files . '/*')、未正确构建父子关系,导致逻辑混乱与输出异常(如误渲染 HTML 文件内容)。
以下是一个健壮、可读性强的解决方案,采用两层 scandir() 扫描 + 显式路径校验,避免 glob() 的路径拼接风险,并支持灵活定制:
<?php define('ROOT_PATH', __DIR__); // 替换为你实际的根目录路径,如 '/var/www/html/uploads'
// 定义需排除的系统/隐藏/默认文件(可根据需要增删)
$excluded = ['.', '..', '.ftpquota', '.htaccess', 'index.html', 'index.php', 'error_log'];
$content = '';
$folders = array_diff(scandir(ROOT_PATH, SCANDIR_SORT_DESCENDING), $excluded);
if (!empty($folders)) {
foreach ($folders as $folder) {
$fullPath = ROOT_PATH . DIRECTORY_SEPARATOR . $folder;
if (is_dir($fullPath)) {
// 主目录项:作为父级容器(可用 <details>/<summary> 或 CSS 层级控制展开)
$content .= '<li class="parent"><a href="./?dir='%20.%20urlencode(%24folder)%20.%20'">' . htmlspecialchars($folder) . '</a></li>';
// 扫描该主目录下的子目录
$subfolders = array_diff(scandir($fullPath, SCANDIR_SORT_DESCENDING), $excluded);
if (!empty($subfolders)) {
foreach ($subfolders as $subfolder) {
$subFullPath = $fullPath . DIRECTORY_SEPARATOR . $subfolder;
if (is_dir($subFullPath)) {
// 子目录项:缩进显示,体现层级关系
$content .= '<li class="child"><a href="./?dir='%20.%20urlencode(%24folder%20.%20'/'%20.%20%24subfolder)%20.%20'">' .
str_repeat(' ', 2) . htmlspecialchars($subfolder) . '</a></li>';
}
}
}
}
}
}
// 最终输出完整列表(可嵌入 <ul> 中)
echo '<ul>' . $content . '</ul>';
?></ul></summary>
✅ 关键优化点说明:
- 使用
DIRECTORY_SEPARATOR替代硬编码/,确保跨平台兼容(Windows/Linux/macOS); -
urlencode()对路径参数编码,防止 URL 中出现空格或特殊字符引发错误; -
htmlspecialchars()输出前转义目录名,杜绝 XSS 风险; -
SCANDIR_SORT_DESCENDING(即1)提升可读性(字母倒序),也可改为SCANDIR_SORT_ASCENDING(0); - 排除列表
$excluded易于维护,避免误列配置文件或入口页; - 每个目录均通过
is_dir($fullPath)二次验证,杜绝符号链接或权限异常导致的误判。
⚠️ 注意事项:
- 若需深度递归(不限两级),应改用
RecursiveDirectoryIterator+RecursiveIteratorIterator,但本场景明确只需“目录 + 其直接子目录”,两层扫描更高效、可控; - 确保 PHP 进程对目标目录具有
read权限,否则scandir()将返回警告; - 生产环境建议添加
error_reporting(0)或日志捕获,避免暴露敏感路径信息。
该方案逻辑清晰、无冗余循环、防御性强,可直接集成至后台管理界面或静态资源导航模块,轻松生成符合预期的层级化目录链接结构。










