PHP 8.5 升级生存指南:避免凌晨两点回滚的检查清单(一)


引言:升级的诱惑与陷阱


PHP 8.5 带来了令人兴奋的新特性,如管道操作符、改进的 URI 处理、更严格的类型检查等。对于开

发者而言,升级到最新版本意味着能利用这些新功能提升代码质量与开发效率。然而,升级过程并非一

帆风顺,尤其是对于运行中的生产环境。许多团队在升级后遭遇了意想不到的问题,被迫在凌晨两点进

行紧急回滚。本文将提供一个详细的检查清单,帮助你在升级 PHP 8.5 时避免这些陷阱,确保升级过程

平稳且可逆。


一、升级前的准备:定义范围与边界

1.1 明确升级范围


升级常常在范围定义阶段无声失败。你需要明确:


包含内容‌:运行时版本升级(FPM/CLI)、Composer 依赖调整、CI 矩阵更新、生产环境上线策略、升级后验证。

排除内容‌:除非你明确添加,否则不包含重构代码以使用 PHP 8.5 新特性、与 PHP 8.5 无关的主要框架升级、“顺手改”的重写。

1.2 避免“范围蔓延”


升级过程中,很容易被诱惑去“顺便”优化代码或升级其他依赖。这可能导致项目范围失控,增加风险。

坚持最初的升级范围,将额外工作留到后续迭代。


1.3 示例:升级范围文档


创建一个简单的文档,列出升级的具体内容与排除项,确保团队所有成员理解并同意。例如:


包含‌:升级 PHP 从 8.4 到 8.5,更新 Composer 依赖,调整 CI 测试矩阵。

排除‌:不重构代码使用管道操作符,不升级 Laravel 框架。

二、环境检查:生产环境与开发环境的差异

2.1 生产环境 vs. 开发环境


开发环境与生产环境之间的差异是升级失败的主要来源。在开发环境中,一切可能运行良好,但生产环境可能有不同的扩展、系统库或 SAPI 配置。


2.2 关键检查点

扩展与系统库‌:确保生产环境安装了所有必要的扩展(如 intl、gd)。使用 composer show --platform 列出 Composer 看到的平台包,然后使用 composer check-platform-reqs 验证你的真实服务器是否满足已安装包的 PHP/ext 要求。

SAPI 配置‌:检查 PHP 的 SAPI(如 FPM、CLI)配置是否与开发环境一致。使用 php -i | head -n 50 快速查看关键配置。

操作系统差异‌:确保生产环境的操作系统版本与开发环境兼容,避免因系统库版本不同导致的行为差异。

2.3 示例:环境检查脚本


创建一个简单的 PHP 脚本,用于生成环境快照:


php

Copy Code

<?php

$snapshot = [

    'php_version' => PHP_VERSION,

    'php_version_id' => PHP_VERSION_ID,

    'sapi' => PHP_SAPI,

    'os' => PHP_OS_FAMILY . ' ' . php_uname('r'),

    'ini_loaded' => php_ini_loaded_file(),

    'ini_scanned' => php_ini_scanned_files(),

    'extensions' => [],

];


$exts = get_loaded_extensions();

sort($exts);

foreach ($exts as $ext) {

    $snapshot['extensions'][$ext] = ext_version($ext);

}


echo json_encode($snapshot, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL;


三、依赖管理:Composer 与第三方库

3.1 Composer 依赖调整


升级 PHP 版本后,Composer 依赖可能需要调整。使用 composer update --platform php.5 更新平台依赖,确保所有依赖包与 PHP 8.5 兼容。


3.2 第三方库兼容性


许多第三方库可能尚未完全支持 PHP 8.5。检查库的文档或 GitHub 页面,确认其兼容性。如果某个关键库不兼容,考虑寻找替代方案或等待其更新。


3.3 示例:Composer 命令


在生产环境运行以下命令,确保依赖兼容:


bash

Copy Code

composer show --platform

composer check-platform-reqs

composer update --platform php.5


四、废弃功能处理:避免隐藏的陷阱

4.1 PHP 8.5 废弃功能概览


PHP 8.5 废弃了 11 个功能,包括:


__sleep() 和 __wakeup():改用 __serialize() 和 __unserialize()。

非标准类型转换:如 integer、double、boolean、binary 改为标准类型 int、float、bool、string。

输出缓冲处理器:必须返回字符串,不能在处理器中直接输出。

Directory 类:改用 dir(),避免动态属性赋值。

4.2 废弃功能迁移策略

静态分析工具‌:使用 PHPStan、Psalm 或 PhpDepend 扫描代码库,找出废弃用法。

废弃警告开启‌:在开发/测试环境中,将 error_reporting 设为包含 E_DEPRECATED 和 E_WARNING,尽早发现问题。

兼容代码编写‌:为废弃功能编写兼容代码,例如为 __sleep() 和 __wakeup() 提供适配器支持两种格式。

4.3 示例:废弃功能检查脚本


创建一个 PHP 脚本,模拟废弃功能并展示如何迁移:


php

Copy Code

<?php

// 废弃的 __sleep 和 __wakeup

class OldClass {

    public $data;

    

    public function __sleep() {

        return ['data'];

    }

    

    public function __wakeup() {

        // 初始化逻辑

    }

}


// 迁移到 __serialize 和 __unserialize

class NewClass {

    public $data;

    

    public function __serialize(): array {

        return ['data' => $this->data];

    }

    

    public function __unserialize(array $data): void {

        $this->data = $data['data'];

    }

}


五、回滚计划:确保可逆性

5.1 回滚的现实性


回滚不只是“指回旧的 PHP 二进制”。你需要:


保留之前的容器镜像或包。

保留之前的 composer.lock 制品。

知道切换流量和回滚依赖和缓存的方法。

5.2 回滚检查清单

数据库迁移‌:确保数据库迁移是向后兼容的,或明确排除在升级范围外。

缓存清理‌:确保 opcache/APCu 或框架缓存是否需要清理。

回滚文档‌:如果回滚需要三个人和一份 wiki 页面,在压力下它会失败。简化回滚步骤,确保单人可操作。

5.3 示例:回滚脚本


创建一个简单的 bash 脚本,用于回滚 PHP 版本:


bash

Copy Code

#!/bin/bash


# 1. 切换回旧的 PHP 二进制

sudo update-alternatives --set php /usr/bin/php8.4


# 2. 恢复 Composer lock

cp /backup/composer.lock /path/to/project/composer.lock


# 3. 清理缓存

php artisan cache:clear


# 4. 重启服务

sudo systemctl restart php-fpm


六、升级后验证:确保稳定性

6.1 即时检查(第一小时)

扫描错误日志查找新的致命错误/异常。

观察日志量是否突然增长(通常是弃用)。

在生产主机上验证 composer check-platform-reqs。

用合成检查验证核心流程(登录、结账、上传、定时任务端点)。

6.2 首日检查

对比 p95/p99 延迟与基线。

检查内存使用:确保没有内存泄漏或显著增长。

结语:升级的艺术


升级 PHP 8.5 不是简单的版本切换,而是一个需要精心规划、执行和验证的过程。通过遵循本检查清单,你可以避免凌晨两点的紧急回滚,确保升级过程平稳且可逆。记住,升级的艺术在于平衡新特性的诱惑与生产环境的稳定性。