使用 scandir() 遍历目录时须过滤 . 和 ..,建议用 array_diff(scandir($dir), ['.', '..']);重命名前需检查目标存在性并处理编码、跨文件系统等边界问题。

用 scandir() 遍历目录时,别忘了过滤 . 和 ..
PHP 的 scandir() 会把当前目录 . 和父目录 .. 也列进来,直接遍历重命名会导致 Warning: rename(): Cannot rename ./. to ./xxx 这类错误,甚至误删当前目录。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 始终用
array_diff(scandir($dir), ['.', '..'])剔除这两个条目 - 如果要处理子目录,需额外判断
is_dir(),否则对文件调用is_dir()没问题,但对.或..调用可能触发 warning(尤其在某些 open_basedir 环境下) - 路径拼接务必用
dirname(__FILE__) . '/' . $filename或realpath($dir . '/' . $filename),避免相对路径歧义
重命名前必须检查目标文件是否存在 —— rename() 不覆盖
rename() 在目标路径已存在时直接失败,返回 false,不会像 shell 的 mv -f 那样强制覆盖。线上批量操作一旦卡住,容易漏改或中断。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 用
file_exists($new_path)判断,存在则先unlink($new_path)(注意权限) - 如果想保留原文件备份,可先
copy($old_path, $backup_path)再重命名,避免单点失败丢数据 - Windows 下大小写不敏感,
foo.txt和FOO.TXT视为同一文件,rename()可能静默失败,建议统一转小写再比对
中文文件名重命名失败?优先确认 mb_internal_encoding() 和系统编码
PHP 默认使用 ASCII 处理字符串,遇到 UTF-8 中文路径时,rename() 可能报 Operation not permitted 或静默失败,尤其在 Linux + ext4 文件系统上。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 执行前加
mb_internal_encoding('UTF-8'),确保内部编码一致 - 用
iconv('GBK', 'UTF-8//IGNORE', $filename)转换(仅限 Windows 本地开发环境旧编码场景) - 更稳妥的做法:所有文件名在 PHP 中只用原始字节流处理(即不 decode),
rename($old_path, $new_path)直接传入原始$_SERVER['PATH_INFO']类似来源的二进制字符串,避免多层编码转换
批量重命名中途出错怎么办?加事务感逻辑和日志记录
PHP 没有文件系统事务,rename() 是原子操作,但整个批量流程不是。某次失败后,部分文件已改名、部分未动,很难回滚。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 提前生成完整映射表:
$map = [$old => $new, ...],逐个尝试,失败时记录到error_log("fail: $old -> $new") - 用
opendir()+readdir()替代scandir(),边读边处理,内存更可控(大目录下scandir()会一次性加载全部文件名) - 关键业务中,改名前先
touch($new_path . '.pending'),成功后再unlink(),靠临时标记判断是否完成过
最麻烦的其实是跨文件系统移动(比如从 /tmp 到 /home),这时 rename() 会退化为 copy + unlink,失败概率高、耗时长,且无法原子保证——这种场景得拆成两步手动处理,不能图省事全塞一个循环里。











