优先用 pathinfo($filename, PATHINFO_EXTENSION) 获取扩展名,它语义清晰、边界安全、兼容多点扩展和特殊路径;正则易出错,仅在必要时谨慎使用并严格锚定结尾。

用 pathinfo() 比正则更稳,别硬套正则
匹配文件扩展名,pathinfo() 是 PHP 原生、语义清晰、边界安全的首选。正则容易漏掉 .tar.gz、.htaccess(无点)、大小写混杂、路径含查询参数等场景。比如 basename('a/b/c.php?x=1') 返回 c.php?x=1,正则若没处理问号就会错配。
实操建议:
- 优先用
pathinfo($filename, PATHINFO_EXTENSION)—— 它按 PHP 内部规则解析,兼容 Windows 路径分隔符、URL 查询符、多点扩展(如.tar.bz2会返回tar.bz2) - 若必须用正则(如在 Nginx 配置或日志过滤中),确保锚定末尾:
/\.([a-zA-Z0-9]{1,6})$/,但注意它无法识别.htaccess(无点)或file.(末尾点) - 避免写
/\.(\w+)$/——\w包含下划线和中文(取决于 PCRE 版本),且长度不限,可能误吞.php?abc=1中的?abc=1
preg_match() 提取扩展名时,为什么 $matches[1] 经常为空?
常见错误是没加 $ 锚点或忽略大小写干扰。例如 preg_match('/\.([a-z]+)/', 'IMG.JPG', $matches) 不会命中,因为 JPG 是大写。
正确写法要点:
立即学习“PHP免费学习笔记(深入)”;
- 必须加结尾锚定:
preg_match('/\.([a-zA-Z0-9]+)$/i', $filename, $matches)——$防止匹配到中间的点(如archive.tar.gz中的.tar) - 加
i修饰符统一处理大小写 - 检查
preg_match()返回值是否为1,再访问$matches[1];返回0表示未匹配,$matches可能未定义或为空数组 - 不推荐用
.*\.([a-z]+)—— 贪婪匹配可能导致意外截断,比如a.b.c.txt中捕获到c
处理双扩展名(如 .tar.gz)该不该用正则?
PHP 的 pathinfo() 默认只返回最后一个点后的部分(gz),这不是 bug,是设计行为。要获取完整后缀(如 tar.gz),正则反而更直接,但得写对。
可行方案:
- 用
strrpos()找最后一个点位置,再substr()截取:$ext = substr($filename, strrpos($filename, '.') + 1)—— 简单、快、不依赖 PCRE - 若需支持
.tar.gz这类,可用正则:preg_match('/\.([a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9])$/i', $filename, $matches),它允许中间有短横和点,但强制首尾是字母数字 - 别用
/\.[^.]+$/—— 它会把.htaccess当成htaccess(漏掉点),也接受.123..456这种非法格式
在 $_FILES 上传场景中验证扩展名,光靠正则够吗?
不够。用户可伪造文件名(如把 shell.php 改成 image.jpg),正则只校验名字,不校验内容。真实风险在于 MIME 类型欺骗和文件头绕过。
必须组合验证:
- 先用
pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)获取扩展名,转小写后比对白名单(如['jpg','png','pdf']) - 再用
finfo_open(FILEINFO_MIME_TYPE)检查实际二进制类型,例如image/jpeg是否匹配扩展名语义 - 拒绝空扩展名(
file.或noext)和危险扩展(php、phar、htaccess)—— 注意.htaccess本身无扩展名,pathinfo()返回空字符串 - 上传目录禁用执行权限,并用随机重命名(非依赖原文件名)
正则在这里只是第一道门,不是锁。



















