mkdir() 报 Permission denied 最常见原因是 PHP 进程用户对目标路径的直接父目录无写权限;报 No such file or directory 是因未启用递归创建(缺第三个参数 true);权限不符是 umask 位运算导致,应创建后用 chmod() 显式设权。
mkdir() 报 Permission denied:先确认 PHP 进程用户对父目录有写权限
失败最常见原因不是代码写错,而是 PHP 运行用户(如
、
或容器里的
)对目标路径的**直接父目录**没有
权限。比如要创建
,哪怕
已存在,只要它属主是
且权限是
,
就无法在其中新建
子目录。
验证方式很简单:
用
看属主和权限;输出中第二列应为
或你 PHP 进程所属组,第三列需含
(如
中的第二个
不代表可写,要看中间三位:
才表示属主可写)
在 PHP 里用
替代
判断——后者对权限不足的已存在目录也返回
,容易误判
Docker 环境下必须在
的
后加
,不能靠
补救
mkdir() 创建了目录但权限不对:umask 正在悄悄改你的
你写了
,结果
看到的是
(即
),这不是 bug,是系统
在生效。PHP 调用
系统调用时,内核会把传入的
和当前进程的
做位运算:
。
典型表现:
立即学习
“
PHP免费学习笔记(深入)
”;
Linux 默认
是
,所以
→ 实际得
(注意:属组和其他用户的
被拿掉了)
写出来也没用,照样被砍;而且八进制必须写成
,写成
会被当十进制处理(等于
八进制,完全不是你想要的)
不要在生产环境全局
—— 多线程 Web 服务器中它会跨请求污染,且不安全
稳妥做法是创建后立刻
,或只对关键路径这么做
mkdir() 报 No such file or directory:递归参数没开,或路径含非法字符
错误信息是
,说明 PHP 默认只建最后一级,前面所有父目录都得手动存在。这不是权限问题,是调用方式问题。
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
必须显式传第三个参数
:
错:
—— 即使
存在,
缺失就失败
对:
—— 会依次建
→
→
第二个参数(权限)不能省略,尤其 Linux 下不传会受
影响更大;Windows 忽略权限但建议保留以保跨平台
路径含中文或 Unicode 字符时,确保脚本文件保存为 UTF-8 无 BOM,且系统 locale 支持(如
),否则
可能静默失败
重复调用 mkdir() 触发 warning:别用 @ 抑制,要主动防御
多次执行
,第二次就会报
。@ 抑制只是掩盖问题,还会让真正该报错的地方也被吞掉。
正确姿势是前置判断:
用
,不是
—— 后者对同名文件也返回
,逻辑错乱
但注意:
对权限不足的已存在路径也返回
,所以更健壮的写法是:
如果目录结构复杂(如按日期哈希分层),建议用
循环向上检查每一级,或用
配合
构建容错逻辑
真正卡住人的从来不是语法,而是权限归属、umask 掩码、用户上下文这三者交织的现实约束。写完
后多跑一句
看实际属主和权限,比反复改 PHP 代码快得多。
www-datanginxapachewriteuploads/images/2024/06uploadsroot755www-dataimagesls -ld /var/www/html/uploadswww-datawdrwxr-xr-xxrwxis_writable(dirname($path))is_dir()falseDockerfileCOPYRUN chown -R www-data:www-data /var/wwwchmod 7770755mkdir('log', 0755, true)ls -ldrwxr-x---750umaskmkdir(2)modeumaskfinal_mode = mode & ~umaskumask00220755 & ~0022 = 0733drwx-wx-wxr0777077777701411umask(0)chmod($path, 0755)Warning: mkdir(): No such file or directorytruemkdir('a/b/c')aa/bmkdir('a/b/c', 0755, true)aa/ba/b/cumasken_US.UTF-8mkdir()mkdir('cache')Warning: mkdir(): File existsis_dir($path)file_exists($path)trueis_dir()falseif (!is_dir($path) && !is_writable(dirname($path))) { /* 报错 */ }dirname()RecursiveDirectoryIteratormkdirmkdir()ls -ld