上个月把服务器的PHP从7.4升级到了8.5,本以为是个简单的版本升级,结果踩了一堆坑。前后折腾了一周才稳定下来。记录一下这次升级过程中遇到的问题和解决方案,给要升级的同学提个醒。

为什么要升级

先说一下为什么要升级。我们用的PHP7.4已经停止官方维护了,安全更新也停了。而且PHP8.x在性能上有明显提升,JIT、命名参数、联合类型、枚举这些新特性也确实好用。

本来计划是先升到8.1,稳定后再往上升。但既然要升,干脆一步到位,直接上8.5。现在想想,这个决定有点冲动了。

坑一:废弃函数一大堆

升级完第一件事,日志里全是Deprecated警告。数了一下,大大小小有二十多处。

1. curl_close() 被废弃

这个最意外。从PHP8.0开始,curl资源就变成了对象,由GC自动回收,curl_close()已经没有实际作用了。到8.5直接标记为废弃。

解决方案:把所有curl_close($ch)改成unset($ch)。虽然不写也没事,但写了更规范。

2. imagedestroy() 被废弃

和curl_close一样的道理,GD资源在PHP8.0之后也变成了对象。这个影响比较大,因为我们图片处理的地方挺多的。

解决方案:封装了一个gd_free()函数,PHP7.x调用imagedestroy,PHP8.x直接跳过。

3. 其他废弃函数

还有一些零散的,比如strftime()、each()、create_function()这些,都是老代码里的。逐个替换成对应的新函数。

坑二:unserialize() 报错

升级后缓存系统频繁报错:

Warning: unserialize(): Error at offset 0 of 1 bytes

查了半天,发现是缓存文件的问题。PHP7.4的时候,有些缓存数据序列化后存文件,读取的时候unserialize。但升级到8.5后,原来的序列化数据格式有细微变化,导致反序列化失败。

解决方案:unserialize的时候加@抑制警告,失败就重新生成缓存。同时把缓存格式改成了JSON,更通用也更安全。

坑三:类型严格化

PHP8.x对类型的要求严格了很多。以前能跑的代码,升级后直接报错。

1. 隐式类型转换变严格

比如把字符串传给int类型的参数,PHP7.4会自动转换,PHP8.x直接报TypeError。我们有几处数据库查询的参数传了字符串类型的数字,全部要改成(int)强转。

2. 算术运算类型检查

PHP8.0开始,对数组、资源做算术运算会抛TypeError。以前有个地方不小心把数组当数字加了,PHP7.4默默给了个结果,PHP8.x直接炸了。

3. 可选参数之前不能有必填参数

这个在PHP8.0就废弃了,8.5直接报错。有几个老函数的参数顺序写得不对,必填参数跟在可选参数后面,全部要调整。

坑四:第三方库不兼容

这个是最头疼的。我们用了一些第三方库,有些已经不维护了,升级PHP后直接报错。

比如一个老的PDF生成库,用了很多PHP4风格的构造函数(和类名同名的方法),PHP8.x已经完全不支持了。找了一圈替代品,花了两天时间替换。

还有一个邮件库,用了preg_replace的/e修饰符,PHP7就已经废弃了,8.5直接移除了。改成preg_replace_callback搞定。

建议:升级前先把所有第三方库列出来,确认每个库都支持目标PHP版本。不支持的,提前找好替代品。

坑五:性能反而下降了

升级完发现,有些接口的响应时间反而变长了。查了半天,发现是OPcache的配置问题。

PHP8.5的OPcache默认配置和7.4不太一样,我们沿用了老的配置,导致缓存命中率很低。重新调整了opcache.memory_consumption、opcache.max_accelerated_files、opcache.validate_timestamps这些参数后,性能才上来。

另外,PHP8.x的JIT默认是关闭的,需要手动开启。对于计算密集型的任务,JIT提升很明显。但我们是Web应用,JIT的提升不大,反而可能增加内存占用,最后没开。

坑六:错误处理变化

PHP8.x把很多以前的Warning改成了Error。比如未定义数组下标,PHP7.4是Notice,PHP8.x直接是Warning,而且默认会显示。

我们有几处代码直接访问可能不存在的数组下标,比如$arr['key'],如果key不存在,PHP7.4默默给个null,PHP8.x会报警告。虽然不影响功能,但日志里全是警告,看着难受。

解决方案:全部改成$arr['key'] ?? '',或者用array_key_exists先判断。

升级建议

总结一下这次升级的经验:

1. 不要跨大版本直接升

从7.4直接升到8.5,跨度太大,问题集中爆发。建议7.4→8.0→8.1→8.2→8.5逐步升级,每个版本稳定运行一段时间再升下一个。

2. 先在测试环境跑通

一定要有测试环境,把所有功能测一遍再上生产。我们就是测试环境没测全,上线后才发现一些边缘功能有问题。

3. 打开所有错误报告

升级前把error_reporting开到E_ALL,把所有警告、通知都修掉。不要带着警告升级,不然升级后全是报错。

4. 准备好回滚方案

万一升级出问题,要能快速回滚。我们是用的多版本PHP共存,出问题直接切回7.4,几分钟就能恢复。

5. 关注官方升级指南

PHP官方的迁移指南写得很详细,每个版本的不兼容变更都列出来了。升级前一定要通读一遍。

写在最后

虽然踩了这么多坑,但升级到PHP8.5还是值得的。性能提升明显,代码质量也因为类型严格化而提高了不少。

技术升级就是这样,过程痛苦,但熬过去之后,系统会更健康。不要因为怕踩坑就不升级,一直用老版本,技术债只会越积越多。

当然,也不要像我一样脑子一热就跨大版本直接升。稳一点,慢一点,比什么都强。