必须用__dir__替代./和../相对路径,因其返回当前文件所在目录的绝对路径,不受getcwd()变化影响;拼接路径须用directory_separator确保跨平台兼容;用户输入路径需realpath()解析并校验是否在白名单根目录内。

用 __DIR__ 替代 ./ 和 ../
相对路径 ./config.php 或 ../vendor/autoload.php 在不同入口文件调用时,会因 getcwd() 变化而解析失败——比如 CLI 下执行和 Web 请求下工作目录完全不同。PHP 8.0 不再容忍这种脆弱写法。
必须改用 __DIR__:它永远指向当前文件所在目录的绝对路径,与调用位置无关。
常见错误现象:require '../config.php' 在 admin/user/edit.php 中报 Failed opening required;但换成 require __DIR__ . '/../config.php' 就稳定了。
统一拼接斜杠:用 DIRECTORY_SEPARATOR 而非硬写 / 或 \
Windows 用反斜杠 \,Linux/macOS 用正斜杠 /。手动写死 'uploads/' . $name 在 Windows 上可能变成 uploads\file.jpg,某些函数(如 is_file())虽能兼容,但日志、调试输出或传给外部命令时容易出错。
正确做法是始终用 DIRECTORY_SEPARATOR 拼接:$path = __DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $filename;
注意:dirname() 和 basename() 返回值已自动适配系统,无需额外处理;但自己拼字符串时,DIRECTORY_SEPARATOR 是唯一安全选择。
路径净化必须调用 realpath() + 范围校验
用户输入或配置项里带 ../../etc/passwd 这类路径,直接拼接会导致越权读取。仅靠 basename() 剥离文件名远远不够——它不处理多级上溯。
关键三步:
- 先用 realpath($input) 解析成绝对路径(返回 false 表示非法路径)
- 再用 str_starts_with($resolved, $allowed_root) 判断是否落在白名单根目录内
- 最后确认目标是文件还是目录:is_file($resolved) 或 is_dir($resolved)
例如允许读取 /var/www/app/storage/ 下的文件,则 $allowed_root = '/var/www/app/storage/';,且必须以斜杠结尾,避免 /var/www/app/storage_hacked 被误判通过。
ThinkPHP 8.0 中路径常量定义要早于任何逻辑
TP8 要求所有自定义 PHP 文件第一行必须是 declare(strict_types=1);,这意味着你不能在 config.php 里先写常量再写声明——顺序错了就报 Parse Error。
推荐结构:declare(strict_types=1);define('APP_ROOT', dirname(__DIR__));define('RUNTIME_PATH', APP_ROOT . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR);
这样定义的 RUNTIME_PATH 可被模型、中间件、命令行脚本一致引用,不会因执行入口不同而错位到 public/runtime/ 或 vendor/runtime/ 等错误位置。
跨操作系统路径处理最易被忽略的点,是把“能跑通”当成“安全可靠”。realpath() 返回 false 时不加判断就继续往下走,或者用 <strong>DIR</strong> 却忘了它只解决“当前文件”,不解决“用户传入路径”的合法性——这两处一旦漏掉,轻则功能异常,重则路径遍历。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











