mkdir()递归创建失败主因是$recursive默认false,需显式设true;php 7.1+权限检查更严格,须检查返回值;建议用symfony filesystem组件替代原生函数。

mkdir() 递归创建失败的常见原因
直接调用 mkdir() 创建多层路径(比如 "uploads/images/2024/06")会报错 Warning: mkdir(): No such file or directory,因为默认不自动创建父级目录。
根本原因是 mkdir() 的第三个参数 $recursive 默认为 false,必须显式设为 true 才启用递归创建。
- PHP 5.0+ 支持
mkdir($path, $mode, true)—— 这是最简方案 - 注意权限掩码
$mode受系统 umask 影响,建议用0755而非0777,否则可能被截断 - Windows 下路径分隔符用
/或\都行,但统一用/更安全(PHP 内部自动兼容)
mkdir(true) 在不同 PHP 版本下的行为差异
PHP 7.1+ 对 mkdir($path, $mode, true) 的权限处理更严格:如果中间某级目录已存在但权限不足(比如只读),函数直接返回 false,不会继续尝试下一级。
而低版本(如 5.6)可能静默跳过该级,导致后续创建失败却不报错 —— 容易埋坑。
立即学习“PHP免费学习笔记(深入)”;
- 务必检查返回值:
if (!mkdir($path, 0755, true)) { throw new RuntimeException("无法创建目录: $path"); } - 若需兼容老环境或精细控制,改用
dirname()+ 循环调用mkdir()(逐级判断是否存在) - 避免在
open_basedir限制目录外操作,否则即使$recursive=true也会失败
用 spl_autoload_register() 或 Composer 自动加载时,别误删 vendor 目录
有些脚本在部署时会「清空旧目录再重建」,如果路径写成 __DIR__ . '/vendor' 并传给递归 mkdir(),可能意外触发 rmdir() + mkdir() 流程,把整个 vendor 删掉。
这不是 mkdir() 的问题,而是逻辑耦合错误 —— 创建目录前必须确认目标路径是否应被覆盖。
- 永远先用
is_dir()判断,而不是无条件mkdir(..., true) - 涉及敏感路径(如
vendor、storage、public/uploads)时,加白名单校验:if (!in_array(realpath($path), $allowed_dirs)) die('非法路径'); - 开发环境和生产环境的
upload_path配置要分离,避免本地测试时误操作线上目录
替代方案:用 Symfony Filesystem 组件更稳妥
原生 mkdir() 没有原子性保证,网络文件系统(NFS)或并发请求下,可能两个进程同时判断目录不存在、又同时创建,触发竞态。
Symfony 的 Filesystem::mkdir() 内部做了重复检测和异常屏蔽,还支持批量路径、可选权限、符号链接等扩展能力。
- 安装:
composer require symfony/filesystem - 使用:
$fs = new SymfonyComponentFilesystemFilesystem(); $fs->mkdir($path, 0755); - 它会自动处理
mkdir() failed on Windows with long paths类错误(通过\?前缀绕过 WinAPI 限制)
mkdir($path, 0755, true) 在某些共享主机上仍会因 open_basedir 或 SELinux 策略失败 —— 这时候得看 error_log 里具体的拒绝原因,而不是反复改参数。










