php 8.2 新增 readonly class,强制不可变性、禁用动态属性和魔术方法,并要求构造函数无副作用;而 readonly 属性自 8.1 起存在,仅保护单个字段。

readonly 属性在 8.1 就已存在,readonly class 是 8.2 才加的——如果你只需要锁定单个字段,8.1 够用;但想一劳永逸地禁止任何状态变更、杜绝动态属性、明确表达“这个类就是不可变的”,那必须升到 8.2。
只读类(readonly class)不是 readonly 属性的简单叠加
很多人以为把所有属性都标成 readonly 就等价于 readonly class,其实不是:
-
readonly class自动禁用动态属性(哪怕你没加#[AllowDynamicProperties]),而普通类即使所有属性都是readonly,仍可能被意外塞入新字段(PHP 8.2 起会触发 Deprecation 警告) -
readonly class不允许定义__set()或__get(),强制封死所有反射/魔术方法绕过路径;普通类加了readonly属性后,仍可实现这些方法并偷偷改状态 -
readonly class的构造函数必须是public且不能有副作用(比如触发事件、写日志),语言层做了校验;而手写readonly属性的类,没人拦你调用外部服务
动态属性废弃警告从 PHP 8.2 开始生效
如果你的项目还依赖未声明的属性(比如 $user->temp_cache 或 $config->env_override),升级到 8.2 后会立刻看到 Deprecated: Creation of dynamic property ... is deprecated。这不是报错,但 CI/CD 流水线里通常配置了“警告即失败”,会导致构建中断。
解决路径只有两条:
- 补全所有属性声明(推荐),配合
#[AllowDynamicProperties]临时兜底(仅限遗留模块) - 彻底重构为显式属性 + 构造器提升,顺便把
__get/__set替换为更安全的访问器
注意:readonly class 和 #[AllowDynamicProperties] 互斥——加了前者就不能加后者,否则 fatal error。
枚举(enum)在 8.1 和 8.2 行为一致,但 8.2 强化了 BackedEnum 实用性
enum 本身是 PHP 8.1 引入的,基础语法(case Draft;)在两个版本完全兼容。真正区别在于:
- PHP 8.2 允许
BackedEnum的底层值(string或int)直接参与类型推导,比如function handle(Status|string $s)中,Status::Draft会被自动解包为'draft'(需显式声明enum Status: string) -
Status::from('draft')在 8.2 中返回类型更精确(Status而非mixed),IDE 和静态分析工具(如 PHPStan)能更好识别 - 8.2 新增
Status::tryFrom('invalid'),失败时返回null而非抛异常,适合宽松解析场景
别忽略 JIT 和内存管理的实际影响
PHP 8.2 的 JIT 编译器优化和内存碎片控制,对长周期 CLI 任务(如队列消费者、数据同步脚本)效果明显;但对典型 FPM 请求(平均耗时 pcntl_fork 或频繁创建销毁对象(比如实时消息路由),8.2 的垃圾回收改进能显著降低 OOM 风险。
一个容易被忽略的点:readonly class 实例在 GC 中标记为“不可变”,PHP 8.2 会跳过对其内部引用的递归扫描——这意味着,如果它持有大数组或嵌套对象,内存释放更快,但前提是:你真把它当不可变对象用,而不是在构造后偷偷用反射改掉私有属性。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











