rename() 是php移动或重命名文件夹的唯一可靠方法,同文件系统下为原子操作,跨文件系统需手动递归复制再删除,并注意权限、符号链接、open_basedir等限制。

rename() 是唯一可靠的选择
PHP 没有专门的 move_folder() 或 mv_dir() 函数,移动或重命名文件夹只能靠 rename()。它底层调用系统 rename(2) 系统调用,只要源和目标在同一个文件系统内,就是原子操作,不会出现中间态残留;跨文件系统时会退化为“复制 + 删除”,但 PHP 不报错也不提示,这点必须手动校验。
- 成功时返回
true,失败返回false,务必检查返回值 - 目标路径不能已存在(除非是空目录,且某些系统允许覆盖空目录——不可依赖)
- 源路径必须存在且可读,目标父目录必须存在且可写
- Windows 下路径分隔符用反斜杠不影响,但统一用
/更安全
跨文件系统移动需手动实现
当 rename() 返回 false 且 error_get_last()['message'] 包含 "Invalid cross-device link",说明跨了挂载点(如从 /home 移到 /mnt/usb)。此时必须自己递归复制再删除:
// 示例:安全跨设备移动
function move_dir_cross_device($from, $to) {
if (is_dir($from)) {
if (!mkdir($to, 0755, true)) return false;
foreach (scandir($from) as $item) {
if (in_array($item, ['.', '..'])) continue;
$src = $from . '/' . $item;
$dst = $to . '/' . $item;
if (is_dir($src)) {
if (!move_dir_cross_device($src, $dst)) return false;
} else {
if (!copy($src, $dst) || !unlink($src)) return false;
}
}
return rmdir($from);
}
return false;
}
注意:copy() 不保留文件权限和修改时间,如需精确还原,得用 shell_exec('cp -a ...')(仅限 Linux 且需启用 exec)。
权限与符号链接的坑
rename() 对符号链接本身操作,不是它指向的目标。如果 $from 是软链,移动后软链仍有效,但目标路径不变;如果想移动软链指向的内容,得先 readlink() 解析真实路径。
立即学习“PHP免费学习笔记(深入)”;
- Web 服务器用户(如
www-data)可能无权访问源或目标目录,is_writable()要提前验证 - SELinux 或 AppArmor 启用时,即使权限 777 也可能被拦截,需查
audit.log - 移动非空目录到已有同名目录会失败,PHP 不支持
mv -r src dst/这种行为
相对路径和 open_basedir 的限制
传给 rename() 的路径如果是相对路径,会基于当前工作目录(getcwd()),不是脚本所在目录。线上环境常因 chdir() 或 CLI 工作目录不同导致路径错乱。
更隐蔽的是 open_basedir 限制:即使路径存在、权限足够,若不在白名单内,rename() 直接失败且错误信息模糊(常为 "Operation not permitted")。可用 ini_get('open_basedir') 检查范围,或改用绝对路径并确保其落在白名单中。
实际部署时,别只测 rename('/a/b', '/a/c'),一定要覆盖 rename('/var/www/a', '/backup/a') 这类跨分区场景,否则上线就静默失败。











