PHP 7.4 本身不提供“自动归类”能力,所有逻辑必须手动编码实现;最可靠做法是按扩展名分类,用 pathinfo() 提取、strtolower() 统一大小写、scandir() 安全遍历,再配合 mkdir(…, 0755, true) 和 rename() 移动文件,但跨分区会静默失败,需主动拦截校验。

PHP 7.4 本身不提供“自动归类”能力,所有逻辑必须手动编码实现;最可靠、最常用的做法是按扩展名分类,用 pathinfo() 提取、strtolower() 统一大小写、用 scandir() 安全遍历,再配合 mkdir(..., 0755, true) 和 rename() 完成移动——但跨分区会静默失败,这点必须拦截。
为什么不能用 glob() 或 endswith() 做扩展名判断
常见错误是写 glob("*.jpg") 然后对每个结果单独处理:它只能筛一种类型,无法做多路分发;更糟的是,有人用 $file . ".pdf" 或 endswith(".PDF") 判断,这在 Linux 下会漏掉 REPORT.PDF,在 Windows 下又可能误判 archive.tar.gz(实际扩展名是 gz,不是 tar.gz)。
-
pathinfo($file, PATHINFO_EXTENSION)才能正确解析data.tar.gz→gz,image.JPG→JPG - 必须立刻转小写:
strtolower(pathinfo($file, PATHINFO_EXTENSION)),否则匹配表失效 - 空扩展名(如
README)会返回空字符串,得提前定义规则:归入others还是跳过?
怎么安全遍历并过滤目录项
直接 opendir() + readdir() 容易陷入子目录循环或权限中断;scandir() 更可控,但默认包含 . 和 ..,且不区分文件/目录。
- 先用
$items = scandir($source)获取全部条目 - 显式过滤:
if (in_array($item, ['.', '..'])) continue; - 再用
is_file($full_path)排除子目录干扰(除非你明确要递归处理) - 避免在循环里反复调用
filemtime()或filesize()—— 归类阶段不需要元数据
移动前必须校验的 4 个关键点
rename() 返回 false 时不抛异常,只静默失败。常见原因:目标目录不存在、同名文件已存在、跨文件系统、权限不足——这些都得主动检查。
立即学习“PHP免费学习笔记(深入)”;
- 用
mkdir($target_dir, 0755, true)创建多级目录,true参数不可省略 - 拼路径用
dirname(__FILE__) . '/output/' . $category . '/' . basename($file),别手拼斜杠 - 同名文件策略要硬编码:覆盖(危险)、重命名(如
report (2).pdf)、跳过、或记录到日志文件 - 跨分区移动会失败:用
stat($source)['dev'] !== stat($target_dir)['dev']判断(Linux/macOS),Windows 用pathinfo($source, PATHINFO_DRIVE)对比驱动器号;若跨盘,改用copy()+unlink()并逐个检查返回值
规则映射表怎么设计才可维护
把分类规则写死在 if-else 里,后续加个 .epub 就得改逻辑;用关联数组定义映射,既清晰又易扩展。
- 示例结构:
$rules = ['documents' => ['pdf','docx','txt'], 'images' => ['jpg','png','webp']] - 遍历时用
array_key_exists($ext, array_flip($rules[$cat]))查找,或更高效地预先反转为$ext_map = ['pdf'=>'documents', 'jpg'=>'images'] - 未匹配扩展名统一归入
others目录,但要记录日志:哪些扩展名被忽略,方便后续补规则 - 中文文件名不用额外处理——PHP 7.4 默认 UTF-8,
basename()和rename()在 Linux 下原生支持,Windows 需确认系统 locale 是 UTF-8 兼容(如 CP65001)
真正卡住人的从来不是代码写不出来,而是空扩展名怎么归、软链接要不要跟随、.env 这类隐藏文件是否纳入、以及跨分区移动失败后没提示导致文件“消失”——这些边界情况不写进条件分支里,脚本就只是半成品。
文章来自机圈观察员网,发布者:,转载请注明出处:https://www.jqgcy.com/xinjizixun/124174.html