最稳方式是pathinfo($filename, PATHINFO_EXTENSION),它专为提取扩展名设计,兼容多级扩展名、跨平台路径,但只返回最后一段(如tar.gz→gz),且需手动转小写比对。

直接用 pathinfo 的 PATHINFO_EXTENSION 最稳
想快速拿到扩展名,pathinfo($filename, PATHINFO_EXTENSION) 是最推荐的方式。它专为这个目的设计,不依赖字符串截取逻辑,能正确处理带多个点的文件名(比如 archive.tar.gz),也兼容 Windows 路径分隔符和末尾斜杠场景。
常见错误是传入空字符串或目录路径(如 /var/www/),这时返回空字符串 —— 不报错但结果不符合预期,得提前校验 is_file($filename) 或至少 basename($filename) 是否含点。
-
pathinfo('a.b.c', PATHINFO_EXTENSION)→c(只取最后一个点后的内容) -
pathinfo('/home/user/file.txt', PATHINFO_EXTENSION)→txt -
pathinfo('image.', PATHINFO_EXTENSION)→''(空字符串,不是.)
用 substr + strrpos 手动截取容易出错
这种写法看似直观,实则边界情况多:文件名无点、以点开头(.gitignore)、只有点(.)、或路径末尾带斜杠时 basename 没套上。
必须先做两步:提取纯文件名(去掉路径),再找最后一个点的位置。漏掉任意一步都可能返回错误结果或警告。
立即学习“PHP免费学习笔记(深入)”;
- ✅ 正确写法:
$ext = substr($filename, strrpos(basename($filename), '.') + 1); - ❌ 错误写法:
substr($filename, strrpos($filename, '.') + 1)(路径里有/a.b/c.txt就崩了) - ⚠️ 注意:
strrpos('file', '.') === false,直接substr会触发警告,得加判断
explode 分割再取最后一段不可靠
用 end(explode('.', $filename)) 看似简洁,但完全忽略路径结构 —— /var/log/error.log 会被切成 ['/var/log/error', 'log'],而 error.log 实际在子目录里,这显然不对。
更糟的是,它把 name.with.dots 和 .hidden 都当成合法扩展名,前者返回 dots(错),后者返回 hidden(错,应为空或 hidden 视需求而定,但 pathinfo 明确定义为“最后一个点之后”)。
-
end(explode('.', 'a.b.c'))→c(碰巧对) -
end(explode('.', '/a/b/c.txt'))→txt(碰巧对,但只是因为路径没点) -
end(explode('.', 'config.'))→''(对);但end(explode('.', '.env'))→env(错)
Windows 下大小写和长扩展名要额外注意
pathinfo 返回的扩展名是原样小写的,PHP 不做标准化。如果业务需要统一小写(比如存数据库比对),得手动 strtolower();反过来,若需保留原始大小写(如某些配置文件区分 .YML 和 .yml),就不能假设它一定是小写。
另外,NTFS 支持长扩展名(如 .tar.gz),但 pathinfo 仍只认最后一个点,这是它的定义行为,不是 bug。真要支持复合扩展名,得自己解析 basename 并查白名单,不能指望内置函数。
-
pathinfo('FILE.TXT', PATHINFO_EXTENSION)→TXT(保持原样) -
pathinfo('data.tar.gz', PATHINFO_EXTENSION)→gz(不是tar.gz) - 判断是否为图片?别只看
pathinfo(..., PATHINFO_EXTENSION) === 'jpg',建议用in_array(strtolower($ext), ['jpg', 'jpeg', 'png'])
pathinfo 给的是规范答案,但业务规则永远在它之外。



















