PHP 文件系统操作:大文件处理 / 目录遍历 / 权限管理

wbcm
wbcm 见习用户见习用户
发布于 2026-10-02 20:15 ·5 浏览 ·2 回复
本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-681.html
转载请注明出处,版权归原作者所有。

全部回复 2

yipeng
yipeng 正式会员正式会员认证极客认证极客 1楼 2026-10-02 20:23

接着帖子里第三步往下补:目录遍历优先用 SPL 迭代器,权限管理的核心不是 chmod 而是「禁执行」。

目录遍历的三种正确姿势,别混用:

  • 单层、要完整条目 → `scandir()`,务必跳过 `.` 和 `..`;
  • 递归整棵树 → `RecursiveDirectoryIterator` + `RecursiveIteratorIterator`,比手写递归稳,能顺手用 `SKIP_DOTS` 和 `FilesystemIterator::UNIX_PATHS`;
  • 只需要匹配特定后缀(比如清临时文件)→ `glob()` 或 `glob('**/*', GLOB_BRACE)` 足够。

手写递归最大的坑是符号链接死循环——目录里有个链回上层的 symlink,递归直接跑飞。用 `is_link()` 判断,或者 SPL 那边加 `RecursiveIteratorIterator::CHILD_FIRST` 配合自己过滤。另外任何路径都先过 `realpath()` 校验,防止 `../../etc` 这类穿越。

权限管理部分,记住一句:上传目录要「能写」但绝不能「能被当脚本跑」。目录 755、文件 644,所有者是 PHP-FPM 运行用户(`www`/`www-data`),永远不要 777。更关键的是在 Web 服务器层直接断掉执行:

  • Nginx:`location ~ ^/uploads/.\.(php|phtml|phar)$ { deny all; }`,或者干脆给 uploads 单独一个 location 只做静态文件;
  • Apache:在该目录放 `.htaccess` 写 `php_flag engine off`(或 `RemoveHandler .php`)。

光靠上传白名单挡不住 .htaccess 被覆盖、或解析漏洞这类情况,所以「白名单 + 服务器层禁执行 + 目录不可解析」是三件套,缺一不可。

常见坑:`file_get_contents` 读大文件爆 `memory_limit` 时,报错信息常常是「Allowed memory size exhausted」,但真正元凶经常是 `readfile` 前的 `ob_start` 缓冲没清;下载大文件前那句 `while (ob_get_level()) ob_end_clean();` 别忘了。

zero
zero 见习用户见习用户 #333 2楼 2026-10-02 20:32
yipeng:接着帖子里第三步往下补:**目录遍历优先用 SPL 迭代器,权限管理的核心不是 chmod 而是「禁执行」**。 **目录遍历的三种正确姿势**,别混用: -…

补充得很到位,尤其「禁执行比 chmod 更关键」这一点,很多人只盯着 777 却忘了服务器解析这一层。我顺着再加三个实际踩过的细节。

realpath 校验要防前缀绕过。 `realpath()` 对不存在的文件返回 false,所以必须在 fopen 之前校验,别在校验完再拼路径。而且不能简单 `strpos($real, $base) === 0`——`/var/www/uploads-evil` 会误判通过。正确做法是 base 先 `realpath()` 再用 `DIRECTORY_SEPARATOR` 结尾做前缀比较。

删目录树时 symlink 要先当文件处理。 `RecursiveDirectoryIterator` 默认不跟随链接(没加 `FOLLOW_SYMLINKS`),但 `isDir()` 对指向目录的 symlink 会返回 true,于是 `rmdir` 报错或删错。配合 `CHILD_FIRST` 时加一层:是链接就 `unlink`,别再往下走。

chmod 别写十进制。 `chmod($f, 0644)` 对,`chmod($f, 644)` 等于八进制 1204,权限直接乱掉;另外 PHP 进程的 umask 会削掉新建文件的位,需要固定权限就 `chmod` 显式设置,别指望 `fopen` 时给的 mode。

放回本社区的场景:Clara 的上传白名单和图片二次校验是 PHP 层兜底,服务器层禁执行就是第二道锁,两边都在才稳。真遇到上传失败,先看后台「系统设置→上传」里"允许的扩展名"和"附件允许的扩展名"这两个独立字段,再看 PHP 的 `upload_max_filesize`,最后确认 uploads 可写——页面会直接提示是哪一类。