PHP严重漏洞-PHP 8.3 升级 8.5:imagick 编译失败与四个坑

AI 摘要997笔记AI

站点从 PHP 8.3 切到 8.5,改 nginx 配置里的 socket 即可完成,回滚也只需十秒,真正的风险在扩展兼容。imagick 3.8.1 因头文件迁到 Zend 目录编译失败,改一行 include 即可通过;还需注意 CLI 与 FPM 各读一套 ini、extension 必须写在全局区、extension_dir 看似注释实则有默认值。建议依赖第三方扩展的站点先确认兼容性再升级。

此内容由AI根据文章内容自动生成。

站点从 PHP 8.3 换到 8.5,本来以为是个十分钟的活——宝塔里两个版本都装好了,改一行配置就能切。结果光是给一个 imagick 扩展编译通过,就折腾了半个多小时。

这篇把过程和一个晚上踩到的坑都记下来。如果你也打算升 8.5,先看完再动手会省不少时间。

这篇适合谁

用宝塔面板、已经装了多个 PHP 版本、想切到最新版的人。文中提到的坑在 8.4 上大部分也存在,因为 PHP 8.4 之后扩展生态的兼容性变化是连续的。

一、先说结论:8.5 值不值得升

PHP 8.5 是当前最新的稳定版,性能比 8.3 有提升,语法上也有新东西。但升级之前要清楚一件事:扩展生态永远滞后于 PHP 本体。

PHP 官方发布新版之后,PECL 上的扩展通常要一到两个小版本才跟得上。我这次遇到的就是典型的例子——imagick 的最新版 3.8.1 在 8.5 上直接编译失败。

所以判断标准很简单:你的站点依赖哪些扩展?如果只是 mysqli、gd、curl 这类内置扩展,放心升;如果需要 imagick、redis、swoole 这类第三方扩展,先确认它们在 8.5 上能编译。

二、切换本身很简单

PHP 版本切换只需改动站点配置里的 socket 文件名
宝塔的站点配置里只写了 PHP 的 socket 路径,改一行即可切换

宝塔把每个 PHP 版本跑成独立的 FPM 进程,各自一个 socket:

# 各版本的 socket
/tmp/php-cgi-74.sock
/tmp/php-cgi-83.sock
/tmp/php-cgi-85.sock

站点用哪个版本,取决于 nginx 配置里引用哪个 socket:

# /www/server/panel/vhost/nginx/站点.conf
# 切换前(8.3)
fastcgi_pass unix:/tmp/php-cgi-83.sock;

# 切换后(8.5)
fastcgi_pass unix:/tmp/php-cgi-85.sock;

改完 nginx -t 校验、nginx -s reload,就切过去了。不需要重装、不需要迁移数据、站点静态文件一个字都不动。

回滚同理,把 85 改回 83 再 reload,十秒钟的事。所以真正的风险不在切换,而在切换之后扩展能不能用。

三、痛点一:扩展生态没跟上

imagick 在 PHP 8.5 上编译失败,改一行 include 后通过
头文件从 ext/standard 搬到了 Zend 目录,报错信息只说不存在,不说搬哪去了

切到 8.5 之后,站点健康检查提示「缺少推荐模块 imagick」。按常规流程编译,第一步就挂了:

cd /root
curl -sL -o imagick.tgz "https://pecl.php.net/get/imagick-3.8.0.tgz"
tar xzf imagick.tgz && cd imagick-3.8.0
/www/server/php/85/bin/phpize
./configure --with-php-config=/www/server/php/85/bin/php-config
make -j2

报错:

imagick.c:28:10: fatal error: ext/standard/php_smart_string.h:
No such file or directory

这个文件在 PHP 8.5 里被挪走了。查一下头文件目录就知道:

ls /www/server/php/85/include/php/ext/standard/ | grep smart
# 没有任何输出

ls /www/server/php/85/include/php/Zend/ | grep smart
# zend_smart_string.h
# zend_smart_str.h

也就是说 smart_str 这套 API 从 ext/standard 整体迁到了 Zend/。改一行就行:

sed -i 's|#include "ext/standard/php_smart_string.h"|#include "Zend/zend_smart_string.h"|' imagick.c
make clean && make -j2 && make install

编译通过,装上之后验证:

/www/server/php/85/bin/php -r '
  $i = new Imagick();
  echo $i->getVersion()["versionString"], " 支持格式数: ", count($i->queryFormats());
'

输出 ImageMagick 7.1.1-43 Q16,支持两万多种格式,说明扩展真的能用了。

提醒

这种「改一行就能过」算是运气好的。有些扩展要改的 API 不止一处,改完还得自己验证功能。如果你不想碰源码,等官方更新是更稳的选择——通常一两个月就有兼容版。

四、痛点二:CLI 和 FPM 读两套 ini

宝塔的 PHP 命令行与 FPM 分别读取 php-cli.ini 和 php.ini
两处都要加 extension,否则会出现「网站有、命令行没有」的怪现象

扩展编译好、也加进了 php.ini,网站确实生效了。但用命令行跑一个 PHP 脚本,报 Class "Imagick" not found。

原因是宝塔把配置拆成了两份:

/www/server/php/85/bin/php --ini
# Loaded Configuration File: /www/server/php/85/etc/php-cli.ini

命令行读 php-cli.ini,FPM 读 php.ini。只改一个,就会有一边用不上。

这件事影响不小,因为很多后台任务走的是命令行:wp-cli、定时脚本、图片批处理、队列消费。这些都会读 php-cli.ini。

正确做法是两处都加:

for f in /www/server/php/85/etc/php.ini /www/server/php/85/etc/php-cli.ini; do
  grep -q "extension = imagick.so" $f || sed -i "1i extension = imagick.so" $f
done
/etc/init.d/php-fpm-85 restart

五、痛点三:extension 必须写在全局区

这个坑我踩得比较蠢。第一次是把 extension = imagick.so 直接追加到 php.ini 末尾,结果重启后 php -m 里没有 imagick,但也没有任何报错。

原因是 php.ini 末尾已经进入了 [opcache] 之类的分段,写在分段里的 extension 会被忽略。

正确写法是插到第一个 [section] 之前,也就是全局区:

INI=/www/server/php/85/etc/php.ini
FIRST=$(grep -nE "^[[a-zA-Z]" $INI | head -1 | cut -d: -f1)
sed -i "${FIRST}i extension = imagick.so" $INI

判断有没有生效最快的办法不是看 ini 文件,而是看模块列表:

php -m | grep -i '^imagick$'
# 有输出才算真加载

六、痛点四:extension_dir 是注释状态

顺手记一个容易迷惑的点。php.ini 里能看到这两行:

;extension_dir = "./"
;extension_dir = "ext"

都是注释状态,看起来像没配置。但实际运行时的 extension_dir 是有值的——它来自编译时的默认路径:

php -i | grep '^extension_dir'
# /www/server/php/85/lib/php/extensions/no-debug-non-zts-20250925

所以不要因为看到注释就去取消注释,那反而会把路径改错。要确认扩展放哪,直接看这个输出就行。

七、验证与回滚

验证分三层,缺一层都可能留下隐患:

# 1. 命令行有没有加载
/www/server/php/85/bin/php -m | grep -i imagick

# 2. FPM 有没有加载(用一个临时文件走真实请求)
cat > /www/wwwroot/站点/_t.php <<'PHP'
<?php echo class_exists('Imagick') ? "FPM_OK" : "FPM_MISSING";
PHP
curl -sk -H "Host: www.example.com" https://127.0.0.1/_t.php
rm -f /www/wwwroot/站点/_t.php

# 3. 站点首页与后台是否正常
curl -sk -o /dev/null -w "%{http_code}n" -H "Host: www.example.com" https://127.0.0.1/
curl -sk -o /dev/null -w "%{http_code}n" -H "Host: www.example.com" https://127.0.0.1/wp-login.php

第 2 步最容易漏。只跑 php -m 验证的是命令行,不是网站;只访问首页,又看不出扩展有没有加载。用一个临时 PHP 文件走一次真实请求,两个问题一起解决。

回滚就是把 socket 名字改回去:

sed -i 's#php-cgi-85.sock#php-cgi-83.sock#' /www/server/panel/vhost/nginx/站点.conf
nginx -t && nginx -s reload

旧版本一直装着、FPM 也一直在跑,所以回滚不需要重启服务,改配置 reload 就生效。

八、我的建议

三种情况,三种选择:

你的情况 建议
只用 mysqli、gd、curl 等内置扩展 放心升,几乎没有兼容问题
需要 imagick、redis、swoole 等第三方扩展 先查扩展的兼容性,做好打补丁的准备
站点跑着电商、论坛这类重业务 先在测试环境跑一周,别直接切生产

最后一点个人体会:PHP 8.5 的收益主要体现在长期支持和新语法,性能提升在普通博客上看不出来。如果你的站点现在跑 8.3 很稳,其实没有必须升级的理由——8.3 的安全支持到 2027 年底,时间还够。

真正值得升的场景是:你需要 8.5 的新特性,或者某个依赖库开始要求更高的 PHP 版本。除此之外,稳定比新更重要。

上一篇 EdgeOne 全球加速实战:美国访问从 1.2 秒到 0.5 秒