rmdir只能删除空目录,因封装POSIX系统调用且无递归功能;删非空目录需用RecursiveDirectoryIterator配合CHILD_FIRST遍历后逐层删除,或严格过滤后调用系统命令。

PHP rmdir 为什么直接删非空目录会报错
rmdir 函数设计上只接受**空目录**,只要目录里有哪怕一个文件、子目录、隐藏文件(比如 .git 或 .DS_Store),调用就会失败,并抛出 Warning: rmdir(): Directory not empty。这不是 bug,是 POSIX 级别的行为约束——PHP 的 rmdir 直接封装了系统调用,不处理递归逻辑。
常见错误现象:rmdir('/path/to/dir') 返回 false,但没报错(因错误报告级别设置),结果目录还在;或者开了 E_WARNING 就看到满屏警告。
- 别指望
rmdir自带递归开关——它根本没有$recursive参数 - 别先
scandir再挨个unlink却忘了删子目录本身,最后rmdir还是失败 - Windows 下注意路径分隔符,但
rmdir和unlink都支持正斜杠,不用硬切\
手动递归删除:用 RecursiveDirectoryIterator 最稳
这是 PHP 5.3+ 原生推荐方式,能自动跳过 . 和 ..,天然支持符号链接判断,且可捕获异常(比如权限不足)。比手写 scandir + foreach 更少漏掉隐藏文件或循环引用。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 用
RecursiveIteratorIterator包一层,把嵌套结构“拉平”,避免自己写递归函数 - 必须从最深层开始删(
RecursiveIteratorIterator::CHILD_FIRST),否则父目录删不掉 - 对每个
SplFileInfo对象,先用isDir()判断,再决定调用unlink()还是rmdir()
简短示例:
function rrmdir($dir) {
$iter = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iter as $file) {
if ($file->isDir()) {
rmdir($file->getPathname());
} else {
unlink($file->getPathname());
}
}
return rmdir($dir);
}
用 exec 调系统命令的坑要当心
Linux/macOS 下有人图快写 exec("rm -rf $dir"),看似一行解决,但风险集中:
-
$dir如果含空格、换行或*,没过滤就拼进 shell,可能误删其他路径(命令注入) - Windows 不认
rm -rf,得切rd /s /q,跨平台维护成本陡增 - Web 服务器(如 Apache/Nginx)通常禁用
exec类函数,线上环境大概率失败 - 即使成功,也绕过了 PHP 的 open_basedir 限制和用户权限检查,安全策略形同虚设
除非你完全控制运行环境、输入绝对可信、且明确需要删巨量小文件(此时系统命令确实更快),否则别碰。
删之前必须检查的三件事
递归删除不可逆,一旦执行就无法撤回。容易被忽略但实际高频出问题的点:
- 确认
$dir是绝对路径,相对路径在 CLI 和 Web 下行为不一致(比如__DIR__ . '/cache'比'./cache'安全得多) - 检查目标是否为软链接——
is_link($dir)为true时,rmdir会删链接本身,不是链接指向的目录 - 留意 SELinux 或容器环境下的权限模型:即使 PHP 进程有写权限,也可能被策略拦截,此时
rmdir静默失败,需查getlasterror()或系统日志
删生产目录前,至少 echo 一遍路径,肉眼核对;批量操作加 if (false) { ... } 开关保命。



















