PHP处理多点文件名必须用pathinfo精准拆解:先用PATHINFO_FILENAME和PATHINFO_EXTENSION分离主体与扩展名,再对主体安全替换,最后拼接并用basename和realpath校验路径合法性。

PHP中处理含多个点的文件名(如config.v2.alpha.json、archive.tar.gz)时,直接用str_replace或正则全局替换会误伤扩展名,用explode取最后一段又可能错判多级扩展,必须用pathinfo精准拆解再分步操作。
先分离路径、文件名主体与扩展名
调用pathinfo()并传入PATHINFO_FILENAME和PATHINFO_EXTENSION两个标志位,分别提取不含路径也不含扩展名的主名称、以及真实扩展名:
$file = '/var/log/app.error.v1.log';<br>$name = pathinfo($file, PATHINFO_FILENAME); // 返回 'app.error.v1'<br>$ext = pathinfo($file, PATHINFO_EXTENSION); // 返回 'log'
这一步不能省——【pathinfo()是唯一能正确识别多级扩展(如.tar.gz)边界的原生函数】,而basename($file, '.' . $ext)在文件名本身含点时行为不可靠。
立即学习“PHP免费学习笔记(深入)”;
对文件名主体做安全替换
只在$pathinfo['filename']上执行替换操作,绝不要碰完整字符串:
方法一:用str_replace批量替换固定字符串$new_name = str_replace('.v1', '.v2', $name); // app.error.v2
方法二:用preg_replace支持更灵活的模式匹配$new_name = preg_replace('/\.v\d+/', '.v3', $name); // app.error.v3
注意:如果原始文件名是user..config.json(含连续点),$name会是user..config,此时需额外用rtrim($name, '.')清理尾部点号,否则拼接后变成user..config.json这种非法名称。
拼回扩展名并校验合法性
第一步:检查扩展名是否存在,避免生成newname.这类空扩展文件:
① 若$ext非空,拼接$new_name . '.' . $ext;
② 若$ext为空(如README),直接用$new_name;
③ 拼接后用basename()再次过滤,剥离可能混入的路径字符:
$safe_filename = basename($new_name . (isset($ext) && $ext !== '' ? '.' . $ext : ''));
这一步必须做——【用户输入若含../或%2e%2e%2f,basename()能强制截断路径遍历】,否则rename()可能写入系统敏感目录。
构造完整目标路径并重命名
用pathinfo($file, PATHINFO_DIRNAME)获取源文件所在目录,再拼接安全文件名:
$dir = pathinfo($file, PATHINFO_DIRNAME); // '/var/log'<br>$target = $dir . '/' . $safe_filename; // '/var/log/app.error.v2.log'
最后调用rename()前,务必用realpath()验证源路径和目标路径是否真实存在且在授权范围内:
if (realpath($file) && realpath($dir) === dirname(realpath($file))) {<br> rename($file, $target);<br>}



















