php 8.3 完善只读属性(支持全访问级别、修复继承与构造函数边界问题),8.4 新增只读类并彻底堵住反射、克隆、子类重定义等绕过漏洞,两者定位不同、不可替代。

PHP 8.3 和 8.4 都强化了只读机制,但定位和能力完全不同:8.3 是完善 只读属性(readonly 属性),而 8.4 新增了 只读类(readonly class)这一更高阶的不可变原语。
只读属性:8.3 修边界,8.4 加锁更严
PHP 8.1 引入只读属性,8.3 进一步支持所有访问级别(private readonly、protected readonly 等),并修复了继承和构造函数中的若干边界问题。但此时仍存在绕过可能:
- 反射(
ReflectionProperty::setValue())在部分场景下仍可修改只读属性 - 克隆对象不会报错,只读属性被复制后可能被间接篡改
- 子类虽不能重写只读属性,但早期版本对“覆盖”行为校验不够严格
PHP 8.4 彻底堵住这些缺口:
- 反射强制禁止写入,尝试即抛出
Error(无法try/catch) -
clone $obj直接失败,抛出Error,杜绝克隆导致的状态泄露 - 编译期就拒绝子类重定义父类只读属性——不是运行时报错,而是加载类时就失败
只读类:8.4 独有,整实例冻结
这是 PHP 8.4 全新引入的语言特性,和只读属性无关,也不是它的“升级版”:
-
readonly class Config { public string $host; }—— 整个对象实例化完成后,所有属性(包括动态添加的)一律不可变 - 不支持普通构造函数,必须用构造函数属性提升:
public function __construct(public string $host) {} - 即使通过
__set()、反射或序列化反序列化,都会触发Error - 适合纯数据载体:API 响应 DTO、配置类、领域事件对象
继承规则:从宽松到强制封装
只读属性在继承中的行为也明显收紧:
- PHP 8.3 允许子类“继承”只读属性,但若尝试显式重声明(哪怕加
readonly),会报错;不过某些边缘情况未完全拦截 - PHP 8.4 明确禁止任何重定义,包括接口中定义的只读成员被实现类覆盖,全部在编译期报错
- 子类构造函数中不能再直接赋值父类只读属性,必须通过
parent::__construct()初始化
使用建议:按场景选,别混用
判断依据不是“哪个更新”,而是“你要锁什么”:
- 只需确保几个关键字段不变 → 用
public readonly string $id(8.3 起已够用) - 需要整个对象生命周期内状态绝对不可变 → 必须用
readonly class(仅 8.4 支持) - 正在升级项目:先升到 8.3 并清理掉反射写只读、克隆只读对象等旧习惯,再平滑过渡到 8.4 的只读类
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











