PHP 8.0 中 finfo_open() 默认 MIME 检测更严格,需显式指定 magic 数据库路径、禁用 $_FILES'file' 校验、改用 finfo_buffer() + 扩展名双校验,并确保 fileinfo 扩展 ABI 兼容及 upload_tmp_dir 权限正常。

PHP 8.0 中 finfo_open() 默认 MIME 检测行为变更
升级后用 finfo_open(FILEINFO_MIME_TYPE) 校验上传文件类型返回空或不准确,不是代码写错了,而是 PHP 8.0 改变了 finfo 扩展对二进制数据的默认解析策略:它现在更严格地依赖文件头(magic bytes),且对损坏/截断文件直接返回 false 或空字符串,而 PHP 7.4 会尝试“宽容匹配”。
常见现象:finfo_file($finfo, $_FILES['file']['tmp_name']) 返回空、application/octet-stream,或与预期 MIME 不符(如 JPG 返回 image/jpeg 却被判定为不合法)。
- 必须显式指定 magic database 路径,否则
finfo_open()可能加载失败或退化为弱模式:finfo_open(FILEINFO_MIME_TYPE, '/usr/share/misc/magic')(路径需根据系统确认,可用locate magic或php --ri fileinfo查看) - 不要直接传
$_FILES['file']['tmp_name']给finfo_file()—— 若临时文件已被移动或权限受限,finfo_file()会静默失败。应先用is_readable()确认可读,或改用finfo_buffer(file_get_contents($tmp), FILEINFO_MIME_TYPE)(仅限小文件) - PHP 8.0 对
finfo的错误报告更敏感:若 magic db 加载失败,finfo_open()返回false,但旧代码常忽略该返回值,导致后续调用finfo_file()报 Warning 并中断执行
$_FILES['file']['type'] 不再可信,且 PHP 8.0 不再填充它
很多老项目依赖 $_FILES['file']['type'](即浏览器提交的 Content-Type)做校验,升级后发现该字段总是空或不可靠 —— 这不是 bug,是 PHP 8.0 主动弱化了对客户端伪造 MIME 的信任:它不再尝试解析或补全该字段,且 Web 服务器(如 Nginx)在转发时也可能剥离该头。
关键事实:$_FILES['file']['type'] 由浏览器生成,完全不可信;PHP 7.4 尚会做简单映射(如 .jpg → image/jpeg),PHP 8.0 已移除该逻辑。
立即学习“PHP免费学习笔记(深入)”;
- 立即停用所有基于
$_FILES['file']['type']的判断,例如if ($_FILES['file']['type'] === 'image/png') - 校验必须基于服务端真实内容:
finfo_buffer()+ 文件扩展名白名单双校验(如pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)必须在['png', 'jpg', 'jpeg']中,且finfo_buffer()返回对应 MIME) - 注意:PHP 8.0 下
getimagesize()对非图像文件返回false更严格,不能当作 MIME 替代方案
扩展 ABI 不匹配导致 finfo 初始化失败
如果你是从 PHP 7.4 环境复制 fileinfo.so 到 PHP 8.0,或宝塔/AMPPS 等面板未重装扩展,finfo_open() 可能返回 false 且无明确报错 —— 这是典型的 ABI 不兼容:PHP 8.0 的 ABI ID 是 20200930,而 7.4 是 20190902,扩展无法注册内部类。
- 运行
php -m | grep fileinfo,若输出为空,说明扩展未加载;若输出存在但finfo_open()失败,大概率是 ABI 错配 - 正确做法:卸载旧扩展,用对应 PHP 8.0 的包管理器重装,例如 Ubuntu 上执行
sudo apt install php8.0-fileinfo,宝塔用户需进「软件商店 → PHP 8.0 → 安装扩展」勾选 fileinfo - 验证是否生效:
php --ri fileinfo应显示 "fileinfo support => enabled" 和 "libmagic => ..." 行
临时目录权限 + upload_tmp_dir 配置失效连锁反应
PHP 8.0 对上传临时文件的完整性检查更严格。若 upload_tmp_dir 目录不可写、磁盘满、或 SELinux/AppArmor 限制,不仅 move_uploaded_file() 失败,finfo_file() 也会因读不到完整临时文件而返回空 —— 这容易误判为“类型校验失效”,实则是底层上传已失败。
- 第一步查
$_FILES['file']['error']:若为UPLOAD_ERR_CANT_WRITE (7)或UPLOAD_ERR_NO_TMP_DIR (6),说明问题不在校验逻辑,而在环境 - 确认
upload_tmp_dir值(php -i | grep upload_tmp_dir),检查该路径是否存在、www-data(或对应用户)是否有读写权限、磁盘剩余空间是否充足 - PHP 8.0 下,即使
upload_tmp_dir未显式设置,它也更倾向使用系统默认临时目录(如/tmp),而该目录可能被清理工具定期清空或受安全策略限制
最易被忽略的是:类型校验失效往往不是单一环节问题,而是 finfo 扩展未正确加载 → 导致校验跳过 → 开发者误以为“逻辑没跑”,实际连第一步都没执行。务必从 php -m 和 php --ri fileinfo 开始验证,而不是直接改业务代码。



















