按这篇的步骤走一遍,你就能把跑在 PHP 8.2 上的站点平滑迁到 8.3/8.4,并且提前发现那些"升级后白屏"的雷点。
PHP 的大版本升级对现代项目来说不算惊险,但 8.4 有几个改动会直接命中老代码:隐式可空参数被弃用、$GLOBALS 使用进一步收紧、部分 mysqli 过程化函数被移除。下面按顺序来。
第一步:先备份,再看清现在跑的是什么
动手前先打包整站文件和数据库,这一步没有例外。
php -v
php -m > /tmp/ext-before.txt
记下当前的 PHP 版本和扩展清单。像 Clara BBS 这类无框架纯 PHP 系统,依赖面通常集中在 mysqli/pdo_mysql、mbstring、gd、fileinfo、curl、openssl、json,把你实际用到的扩展记全。
注意:扩展比版本更容易出事。8.4 换编译环境后 gd、redis 这类扩展如果没跟着装,页面会直接 500 而不是报语法错。
第二步:全站语法 lint,先把语法层面的坑扫掉
用新版本的 PHP 二进制做一次全量语法检查,不要用旧版本:
find . -name "*.php" -not -path "./vendor/*" -print0 \
| xargs -0 -n1 -P8 /www/server/php/84/bin/php -l
只输出 No syntax errors detected 就继续;出现 Fatal error 或 Deprecated 的,逐条改。
顺手把运行时的弃用警告也抓出来:
/www/server/php/84/bin/php -d error_reporting=E_ALL \
-d display_errors=1 -d log_errors=1 -d error_log=/tmp/php84.log -r 'echo "ok\n";'
然后访问测试站点,把所有流量都跑一遍,再翻 /tmp/php84.log。
第三步:重点改 8.4 的隐式可空参数
这是升级到 8.4 最常见的失败点。以前这么写是合法的:
function format(string $name = null) { ... }
8.4 起会被标记弃用,正确写法是显式声明:
function format(?string $name = null) { ... }
批量找出来:
grep -rnE '\b(int|float|string|bool|array|callable|iterable|object|[A-Z][A-Za-z0-9_]*)\s+\$[A-Za-z_][A-Za-z0-9_]*\s*=\s*null' --include="*.php" .
结果里凡是没有 ? 前缀的,都补上。
注意:?Foo $x = null 和 Foo|null $x = null 都行,但不要漏掉引用参数和接口实现——父类方法签名改了,子类没改会报签名不兼容。
第四步:清掉 8.3/8.4 的其他高频雷区
mysqli_ping()、mysqli_kill() 等过程化函数在 8.4 被移除,改成 $mysqli->ping() 这类对象方法。
get_class() / get_parent_class() 不带参数调用,8.3 起弃用,改成 $obj::class。
- 动态取类常量请用
Foo::{$name},不要再拼字符串后 constant()。
$GLOBALS 整体赋值、$GLOBALS 引用传递都会被拒,改成显式传参。
- 8.3 起
json_validate() 可用,替代 json_decode() === null 的判空写法,省内存。
新特性可以慢慢用,但别在升级当口一起上:
- 8.3:类型化类常量、
#[\Override] 属性(标了它,父类方法改名会直接报错,很适合当重构护栏)、mb_str_pad()。
- 8.4:属性钩子、非对称可见性
public private(set)、array_find() / array_any() / array_all()、new Foo()->bar() 免括号。
注意:属性钩子、非对称可见性是语法级新东西,插件生态和 IDE 未必都跟上。像 Clara BBS 这种带插件钩子体系的系统,主体用没事,但第三方插件文件建议先在测试站跑一遍再上生产。
第五步:切换版本并回归验证
面板用户直接在「网站 → 站点设置 → PHP 版本」切到 8.4,然后在「软件商店」确认 8.4 的扩展装齐。命令行用户改完配置后:
/www/server/php/84/sbin/php-fpm -t
systemctl reload php-fpm-84
如果是命令行/常驻进程,别忘了 reload 或重启对应服务。
切完之后按这份清单过一遍:注册登录、发帖回帖带图上传、后台登录、计划任务手动触发一次、缓存开关一次(Redis 和文件各试一遍)、附件下载、支付/余额类操作。跑通即算迁移完成。
注意:Clara BBS 这类"保存即生效、无需清编译缓存"的系统,切版本后如果首页空白而其他页正常,八成是 opcache 里还挂着旧的字节码,重启 php-fpm 即可。
小结
- 先备份,再对比扩展清单,扩展缺失比语法错误更常见。
- 用新版本 PHP 二进制做全量
php -l,别用旧的。
- 8.4 最大的坑是隐式可空参数,全部改成
?type $x = null。
- 过程化 mysqli 函数、无参
get_class()、$GLOBALS 滥用要逐个清理。
- 新特性(属性钩子、
#[\Override]、array_find)等升级稳定后再引入,不要和版本切换同时做。
- 最后一定要做功能回归,而不是只看首页能不能打开。