本文目录导读:

- 第一阶段:评估与准备(最重要)
- 第二阶段:渐进式升级(低风险路线)
- 第三阶段:具体迁移步骤(以 PHP 5.6 -> 7.4 -> 8.1 为例)
- 第四阶段:工具与自动化
- 第五阶段:生产环境注意事项
- 快速决策表
为PHP低版本项目做平滑升级,核心思路是分步走、先兼容后切换、充分测试,直接跳多个大版本(如5.6直接升8.2)风险极高。
以下是详细的升级路线图和步骤:
第一阶段:评估与准备(最重要)
-
明确当前版本与目标版本
- 当前版本:确认是 PHP 5.3, 5.4, 5.6, 7.0 还是 7.1。
- 目标版本:建议走阶梯路线,不要跳跃。
- 如果当前是 PHP 5.x:先升到 PHP 7.4(有最长支持周期和安全更新)。
- 如果当前是 PHP 7.x:可以尝试直接升到 PHP 8.1 或 2。
- 终极建议:PHP 8.1 是目前在性能、兼容性和新特性之间平衡较好的版本。
-
全面审计代码
- 使用静态分析工具:利用
PHPStan、Psalm、Rector扫描代码。 - 检查废弃函数:逐个检查代码中是否使用了在新版本中移除或废弃的函数(如
mysql_*、each()、ereg()、split()等)。 - 检查语法特性:低版本中的一些 “特性” 在高版本中是致命错误:
- 不能在非对象上调用方法(如
$obj = null; $obj->method();) - 可变变量、可变函数、 的用法变化
- 魔术方法签名必须匹配(
__toString()必须返回string) foreach中引用变量的问题(foreach ($arr as &$val)后$val会保留引用)
- 不能在非对象上调用方法(如
- 使用静态分析工具:利用
-
检查第三方依赖
- Composer:检查
composer.json中所有包的require和require-dev是否支持目标 PHP 版本。 - 框架:Laravel 4.x / 5.x 不能直接用于 PHP 8.x,需要先升级框架本身,ThinkPHP 3.x 基本无法用于 PHP 7.2+。
- 扩展:确认
php.ini中启用的扩展(如Memcached,Redis,pdo_mysql)在新版本是否有对应版本。
- Composer:检查
第二阶段:渐进式升级(低风险路线)
不要直接在线上替换 PHP 版本,用以下方式逐步过渡:
方案 A:双环境并行(推荐)
- 搭建新环境:在另一台服务器或同一台机器的不同端口(如 8080)部署 PHP 8.x。
- 代码兼容性处理:在新环境中运行代码,逐一修复报错和警告。
- 关键操作:使用 PHP 7.x/8.x 过渡层,将
mysql_*函数替换为mysqli_*或 PDO,这些改动在旧版本中也能运行。
- 关键操作:使用 PHP 7.x/8.x 过渡层,将
- 流量切换:
- 先用小流量(如 1% 的用户)通过负载均衡或代理切换到新环境。
- 监控错误日志、接口响应时间、内存占用。
- 逐步放大流量(10% -> 50% -> 100%)。
方案 B:版本平滑忽略(仅限 7.4 升 8.x)
利用 PHP 的 apc 或 opcache 预编译特性,但需要配合 Rector 做自动化代码转换。
- 使用 Rector 自动重构代码,让它同时兼容旧版本和新版本(通过 polyfill 或 shim)。
- 在旧服务器上安装多个 PHP 版本(如 PHP 7.4 和 PHP 8.1),通过
fastcgi_pass或php-fpm的listen端口切换。 - 切换时,只需要重启 php-fpm 或修改 nginx 的配置,无需中断服务。
第三阶段:具体迁移步骤(以 PHP 5.6 -> 7.4 -> 8.1 为例)
第一步:修复 PHP 5.6 到 7.4 的兼容性问题
- *去掉 `mysql_
**:替换为mysqli_*` 或 PDO,这是最大障碍。 - 去掉魔术引号:如果代码依赖
magic_quotes,需要手动处理$_GET/$_POST。 - 修复
each()替换为foreach/key()/current()。 - 修复
__autoload()替换为spl_autoload_register()。 - 修复
list()赋值:list($a, $b) = [1,2];在 7.0 中没问题,但list()接收反向顺序(= [$b, $a])在 7.1 后是错误。 - 测试:在 PHP 7.4 环境下运行所有单元测试和手动回归。
第二步:修复 PHP 7.4 到 8.1 的兼容性问题
- 移除
real()等:real()在 8.0 中移除了,替换为is_float()、filter_var()。 - 修复
get_magic_quotes_gpc():PHP 8.0 中直接返回false,但函数已被移除,需要删除调用。 - 修复
implode()参数顺序:implode($glue, $pieces)和implode($pieces, $glue)两种顺序在 7.4 都支持,8.0 中只支持第一个。 - 处理
match关键字:如果代码里定义了match作为函数名或常量,需要重命名,因为 PHP 8.0 将其作为保留关键字。 - 处理
null/false/true作为函数返回类型:PHP 8.0 中这些类型是合法的,但之前你打印它们可能导致E_WARNING。 - 修复
Fiber/Union Types:如果你的代码使用了低版本的 hacks 来模拟 Union Types,可能需要重写。
第四阶段:工具与自动化
-
Rector:最好的自动升级工具。
# 安装 composer require rector/rector --dev # 创建 rector.php 配置文件 vendor/bin/rector process src/ --set php80 vendor/bin/rector process src/ --set php81
它可以自动重写代码、添加类型声明、替换废弃函数。
-
PHPStan / Psalm:在升级前,将它们配置到最高级别(level 9),识别所有潜在的类型错误、未定义变量、不兼容的返回类型。
-
单元测试:升级前必须保证测试覆盖率至少 80% 以上,没有测试的升级是盲人摸象。
第五阶段:生产环境注意事项
- 压测:PHP 8.x 相比 5.x/7.x 性能提升巨大(JIT),但也要确保数据库、Web 服务器能配合。
- 缓存:升级后,清空 Opcache、APCu、Redis 等所有代码缓存。
- 监控:重点关注
error.log中的E_DEPRECATED和E_WARNING,使用 Sentry 或类似工具捕捉。 - 回滚方案:保留旧版本 PHP 的容器或服务,如果出现问题,能在 1 分钟内切回。
快速决策表
| 当前版本 | 推荐目标版本 | 主要风险点 | 自动化工具帮助 |
|---|---|---|---|
| 3 - 5.6 | 4 | mysql_* 函数、魔术引号、each() |
Rector (php74 set) |
| 0 - 7.1 | 1 | 字符串偏移处理、list() 顺序、real() |
Rector (php80, php81 set) |
| 2 - 7.4 | 2 | implode() 参数顺序、废弃的 utf8_encode/decode |
Rector (php80, php81) |
| 遗留系统 | 1 + 隔离运行 | 大量非标代码、无测试 | 先写测试,再逐个模块替换 |
最终建议: 如果你的项目非常老旧且没有单元测试,不要追求一次升级到 8.x,先升级到 PHP 7.4(稳定、兼容性好),运行半年稳定后,再考虑升级到 8.1。