PHP读取隐藏文件怎么设置_PHP目录遍历过滤规则说明【操作】

5次阅读

php opendir() 默认不跳过隐藏文件,实际行为取决于系统 readdir();常见“读不到”是因代码主动过滤,正确做法是先读全量再按业务规则过滤,而非简单判断首字符是否为点。

PHP读取隐藏文件怎么设置_PHP目录遍历过滤规则说明【操作】

PHP opendir() 默认不读隐藏文件,得手动处理

PHP 的 opendir() 本身不跳过隐藏文件(即以 . 开头的文件),但它底层依赖系统 readdir() 行为 —— 在 linux/macos 上,readdir() 会返回 ...,也会返回其他隐藏项(如 .env.gitignore);windows 则基本不生成“隐藏文件”概念,但若文件被标记为 SYSTEM/HIDDEN 属性,scandir() 可能漏掉,opendir() + readdir() 却可能读到(取决于 PHP 版本和 SAPI)。所以「读不到」往往不是函数限制,而是你代码里主动过滤了。

常见错误现象:scandir($dir) 返回结果里没有 .htaccess,但用 shell ls -a 能看到 —— 很大概率是你写了类似 array_filter($files, fn($f) => $f[0] !== '.') 这种粗暴过滤。

  • 不要用字符串首字符判断是否隐藏:有些合法文件名就是 .123..config,且 ... 是特殊目录项,必须单独处理,不能一棍子打死
  • 真正安全的做法是:先用 readdir() 拿全量,再用 is_file() / is_dir() + 显式白名单或业务规则过滤
  • 如果目标只是「读取所有非 . 和 .. 的条目」,用 array_diff(scandir($dir), ['.', '..']) 更稳,它不依赖首字符

用 glob() 读隐藏文件要加 GLOB_BRACE 和显式模式

glob() 默认忽略以 . 开头的文件,这是它的硬编码行为(源于 libc glob 的 GLOB_PERIOD 默认未启用),不是 bug,是设计如此。想让它吐出 .env,必须主动打开开关并写明模式。

使用场景:快速枚举配置类隐藏文件,比如加载项目根目录下的 .env.php-version 等,又不想写完整循环逻辑。

立即学习PHP免费学习笔记(深入)”;

  • 正确写法:glob('.*', GLOB_BRACE | GLOB_NOSORT) —— 注意 GLOB_BRACE 是必须的,否则 .* 会被当字面量,不触发通配
  • 但这个会包含 ...,记得用 array_diff() 剔除:array_diff(glob('.*', GLOB_BRACE), ['.', '..'])
  • 性能影响:开启 GLOB_BRACE 会让 glob 多一次解析,但对小目录几乎无感;不过 glob('.*') 在大目录下比 scandir() 慢,因为它是按模式匹配而非直接读目录项

DirectoryIterator 默认跳过 . 和 ..,但隐藏文件照常出现

DirectoryIterator 是面向对象方式遍历目录,它内部自动跳过 ...(无需手动判断),但对其他隐藏文件(如 .git.DS_Store)完全不设防 —— 它们会正常出现在迭代中。

容易踩的坑:以为用了 DirectoryIterator 就“安全”了,结果线上误删了 .env,只因没做业务层过滤。

  • 检查是否为隐藏文件,别用 $file->getFilename()[0] === '.',改用正则:preg_match('/^./', $file->getFilename())(更准,避免空字符串或 UTF-8 首字节问题)
  • 如果需要跨平台兼容(尤其 Windows),建议结合 FileInfo 判断属性:$file->isDot() 只认 ./..,而隐藏属性需用 exec('attrib ' . escapeshellarg($file->getPathname()))(不推荐)或放弃 —— PHP 本身不提供跨平台隐藏属性 API
  • 注意 DirectoryIterator 实例化时若目录不存在或权限不足,会直接抛 UnexpectedValueException,必须 try/catch,不能只靠 is_dir() 预检

apache/nginx 的目录遍历防护和 PHP 无关

用户常混淆:为什么浏览器访问 /uploads/ 看不到 .htaccess,但 PHP 脚本却能读到?因为 Web 服务器(Apache/Nginx)默认禁止 http 层列出或下载以 . 开头的文件,这是服务器配置层面的规则,和 PHP 的文件系统函数完全隔离。

典型错误认知:把 open_basedirdisable_functions 当成隐藏文件过滤器 —— 它们管的是「能不能读」,不是「要不要列出来」。

  • Apache 中 .htaccess 生效靠的是 AccessFileName .htaccessAllowOverride,它阻止的是外部请求,不影响 fopen('.htaccess', 'r')
  • Nginx 默认用 location ~ /. { deny all; } 拦截,但 PHP-FPM 进程仍可读取这些文件 —— 这正是 laravel.env 放在 webroot 外的原因,而非指望 Nginx “藏住”它
  • 真正该做的:敏感隐藏文件(如 .env)绝不放在 Web 可访问路径下;必须放时,靠 Web 服务器配置 deny,再加 PHP 层校验(如 if (str_starts_with($path, '.')) die('Forbidden');)双保险

隐藏文件不是黑盒,PHP 能读到,说明它就在文件系统里明摆着。关键不在“怎么藏”,而在“谁该看”和“看了之后怎么用”。

text=ZqhQzanResources