最典型报错是warning: mkdir(): permission denied或no such file or directory;前者因父目录权限不足或selinux限制,后者因上级目录不存在且未启用递归创建。

mkdir 创建目录失败的常见报错是什么
最典型的是 Warning: mkdir(): Permission denied 或 Warning: mkdir(): No such file or directory。前者说明父路径权限不够或被 SELinux 限制;后者不是“没权限”,而是上级目录根本不存在——mkdir 默认不递归创建,这点和 Linux 命令行里的 mkdir -p 完全不同。
怎么安全地递归创建多级目录
必须显式传入第三个参数 $recursive = true,否则 mkdir('/var/log/app/cache') 在 /var/log/app 不存在时直接失败。
-
mkdir()的第二个参数是权限掩码(如0755),但实际生效受umask影响,建议写成0777 & ~umask()才可靠 - PHP 7.1+ 支持第四个参数
$context,一般用不到;但若路径含 stream wrapper(如s3://),就得配对应 context - 不要依赖
file_exists()再mkdir()—— 竞态条件:两次调用之间目录可能已被其他进程创建,导致mkdir()报E_WARNING
稳妥写法:
if (!is_dir($path) && !mkdir($path, 0755, true)) {
throw new RuntimeException("Failed to create directory: $path");
}
为什么用 dirname() + is_dir() 预检反而更危险
因为 is_dir() 和 mkdir() 是两个独立操作,中间存在时间窗口。并发请求下,A 进程通过 is_dir() 发现目录不存在,正准备 mkdir(),B 进程已抢先建好,A 再执行就会触发警告甚至中断流程(尤其在 strict_types=1 或错误转异常时)。
立即学习“PHP免费学习笔记(深入)”;
- PHP 5.0+ 的
mkdir()返回布尔值,失败不抛异常,所以必须手动判断返回值 - 如果父目录权限为
0750,而当前 PHP 进程用户不属于该目录所属组,即使0777也建不成功 - Windows 下注意路径分隔符:用
DIRECTORY_SEPARATOR或统一用/(PHP 内部会自动转换),别硬写\
替代方案:用 RecursiveDirectoryIterator 还是 scandir()
都不是。这两个是遍历工具,跟创建无关。真要绕开 mkdir(),唯一可行的是调用系统命令 exec('mkdir -p ' . escapeshellarg($path)),但代价是失去跨平台性、增加 shell 注入风险、且无法精确捕获错误类型。
- 坚持用原生
mkdir(),配合clearstatcache()(仅在极少数反复删建测试场景需要) - Web 环境中,确保运行 PHP 的用户(如
www-data)对目标父路径有x权限(进入目录)和w权限(在其中新建) - 容器部署时,提前在 Dockerfile 里
RUN mkdir -p /app/storage/logs && chown www-data:www-data /app/storage/logs,比运行时创建更稳
递归创建这件事本身不复杂,难的是权限链路里任何一个环节卡住——父目录属主、umask、SELinux 策略、容器挂载选项,都可能让 mkdir($path, 0755, true) 静静失败。先查 error_log 里有没有被忽略的 warning,再逐级 ls -ld 看权限,比改代码更快。











