紧接 7.4→8.0 和 8.0→8.1,本文聚焦 PHP 8.1 → 8.2 的升级。8.2 发布于 2022 年 12 月,是 PHP 8.x 时代最具里程碑意义的版本——它把 8.0 引入的"新范式"全部"拧紧了螺丝":8.1 只是 Deprecated 警告的动态属性,8.2 默认直接 Error;readonly class 正式引入;first-class callable 从 8.1 实验版变成正式语法。
PHP 官方对 8.2 的定位是"PHP 8 时代的稳定锚点"。从实际生态看,Laravel 10+、Symfony 6+、ThinkPHP 8 全都把 php: "^8.2" 作为最低要求。如果你现在还在跑 8.1 或更早,8.2 是必须升的版本——它不仅是当前主流框架的入场券,也是 8.3/8.4/8.5 后续增量升级的基础。
一、核心差异速查表
| 维度 | PHP 8.1 | PHP 8.2 | 影响程度 |
|---|---|---|---|
| 动态属性 | Deprecated 警告 | 默认 Error(#[\AllowDynamicProperties] 可豁免) |
破坏性关键 |
| readonly class | 只支持属性级 readonly | 类级别声明,所有属性自动 readonly | 工程化 |
| first-class callable | 实验版 | 正式语法 Foo::bar(...) |
可选改进 |
| preg_replace /e | Deprecated | 彻底移除 | 破坏性 |
| utf8_encode | 正常工作 | Deprecated,建议 mb_convert_encoding | 轻量破坏 |
| 性能 | — | JIT 更成熟,整体约 5% 加速 | 性能 |
| 支持状态(2026-10) | ❌ 完全 EOL | ❌ 完全 EOL(2024-12 结束) | 安全风险 |
二、8.2 最重要的三个变化
1. 动态属性从 Deprecated 变 Error —— 8.2 升级最容易炸的点
8.1 只是 Warning,8.2 默认直接抛 Error。stdClass 和 SimpleXMLElement 是两个例外,本身就支持动态属性,不受影响。其他所有普通 class 会被严格检查:
class MyClass {}
$obj = new MyClass();
$obj->temp = 'data';
// PHP 8.1: ⚠️ Deprecated
// PHP 8.2: ❌ Error: Attempt to assign property "temp" on MyClass
// 豁免方案:加 Attribute
#[\AllowDynamicProperties]
class LegacyModel {}
// 但这只是临时救急,长期还是要清理动态属性
Rector 的 php82 规则集可以自动扫出所有写动态属性的位置。升级前跑一遍,把能改的都改掉。
2. readonly class —— 不可变对象终极语法糖
8.1 只能逐属性写 readonly,8.2 允许在类级别声明,所有属性自动 readonly:
// PHP 8.1:逐属性写(7 行样板)
class Point {
public readonly float $x;
public readonly float $y;
public function __construct(float $x, float $y) {
$this->x = $x;
$this->y = $y;
}
}
// PHP 8.2:一个 readonly 搞定(3 行)
readonly class Point {
public function __construct(
public float $x,
public float $y,
) {}
}
// 构造后属性不可修改,clone 也不会重置 readonly 属性
readonly class 配合 DDD 的 Value Object / Value Type 模式是黄金搭档。DTO、Money、Email、Address 这类"创建后就不该变"的对象,编译器帮你做语义检查。
3. first-class callable 正式化
8.1 引入但带 Reserved 标记(实验性质),8.2 正式成为 PHP 一等公民:
// PHP 8.1:
$fn = new Closure::fromCallable([MyClass::class, 'method']);
// PHP 8.2:原生语法
$fn = MyClass::method(...);
// 典型用法:array_map 配合原生 strlen
$result = array_map(strlen(...), ['hello', 'world']);
// 比 array_map(fn($s) => strlen($s), ...) 更简洁
三、破坏性变更清单
8.1→8.2 的破坏点比 7→8 少得多,但比 8.0→8.1 多,主要集中在"收紧之前留口子的宽松行为"。
1. preg_replace /e 修饰符彻底移除
// PHP 8.2 直接 Error
preg_replace('/(\d+)/e', '$1 * 2', 'abc123');
// 改成
preg_replace_callback('/(\d+)/', fn($m) => (int)$m[1] * 2, 'abc123');
// Rector php82 规则集可以自动处理
2. utf8_encode / utf8_decode 废弃
标记 Deprecated(不是 Error),推荐改用 mbstring 或 iconv:
// ❌ Deprecated
$utf8 = utf8_encode($iso88591_str);
// ✅ 改用
$utf8 = mb_convert_encoding($iso88591_str, 'UTF-8', 'ISO-8859-1');
3. 其他小变更
strftime()在 8.1 已 Deprecated,8.2 继续 Deprecated(建议用IntlDateFormatter)- 某些内部函数的默认返回值变化(如
htmlspecialchars的默认 encoding 从 UTF-8 改为 ISO-8859-1) null作为独立类型(function foo(null $x) {})在 8.2 彻底不允许
四、升级实操指南
# Composer 项目
# Step 1: composer.json 改 php constraint
# "php": "^8.2.0"
# "config": { "platform": { "php": "8.2.31" } }
# Step 2: Rector 自动修复
composer require rector/rector --dev
vendor/bin/rector process src --set php82
# Step 3: update 依赖
composer update --with-all-dependencies
# Step 4: 冒烟测试
php artisan optimize:clear # Laravel
php bin/console cache:clear # Symfony
# Windows 官方包:下载 PHP 8.2.x,流程与之前版本一致
# https://windows.php.net/download/
# 源码编译:configure 参数与 8.1 基本相同
wget https://www.php.net/distributions/php-8.2.31.tar.xz
tar xvf php-8.2.31.tar.xz && cd php-8.2.31
./configure --prefix=/usr/local/php82 --enable-fpm --enable-opcache --enable-jit --enable-mysqlnd --enable-mbstring --with-curl --with-zip --with-intl --with-openssl
make -j$(nproc) && sudo make install
五、要不要直接跳 8.3 或 8.4?
在 2026 年这个节点上,8.2 不是终点而是跳板。但 8.2 的动态属性 Error 必须先解决——否则直接升 8.3/8.4 会同时遇到多个破坏性变更。推荐节奏:8.1 → 8.2(解决动态属性 + preg /e)→ 8.4(Property Hooks 是 8.4 才有的重磅特性)。8.3 可以跳过,它的增量特性(类型化常量、Override Attribute)对大多数项目吸引力有限。
参考链接
- PHP 8.2 Release Notes:https://www.php.net/releases/8.2/
- PHP 8.2 Migration Guide:https://www.php.net/manual/en/migration82.php