php 8.3只读属性仅可在声明时、构造函数(含参数提升)或__unserialize()中初始化;构造函数外直接赋值会报错,但反序列化未初始化时允许写入,且支持默认值与参数提升初始化。

PHP 8.3 的只读属性不能在构造函数外直接赋值——这是语言层面的硬性限制,任何尝试(如 $obj->prop = 'new')都会触发 Error: Cannot modify readonly property。
但“不能直接赋值”不等于“完全无法在构造函数外设置值”。实际开发中,有合法且受支持的绕过方式,前提是满足初始化前提(即属性尚未被初始化)。以下是三种官方认可的途径:
-
在
__unserialize()方法中写入
反序列化时,若只读属性尚未初始化,PHP 允许在__unserialize()内直接赋值:class User { public readonly string $name; public readonly int $id; } class UserWithUnserialize extends User { public function __unserialize(array $data): void { // 只有未初始化时才可写入 $this->name = $data['name'] ?? 'Guest'; $this->id = $data['id'] ?? 0; } } $user = unserialize('O:15:"UserWithUnserialize":2:{s:4:"name";s:5:"Alice";s:2:"id";i:1001;}'); -
使用声明时默认值(无需构造函数)
PHP 8.3 支持为只读属性设默认值,对象创建时自动生效,不依赖构造函数逻辑:class Config { public readonly string $env = 'production'; public readonly array $features = ['cache', 'metrics']; } $cfg = new Config(); // $cfg->env 已是 'production',且不可改 -
通过构造函数参数提升(promoted parameters)隐式初始化
虽然发生在构造函数调用时,但语法上“没有显式构造函数体”,属于语言级初始化机制,不是“构造函数外”:class User { public function __construct( public readonly string $name, public readonly int $id ) {} } $u = new User('Bob', 42); // $u->name 在此处完成唯一一次初始化
⚠️ 注意:
-
__set()、动态属性($this->unknown = ...)、反射写入(ReflectionProperty::setValue())均被禁止,会报错; -
static readonly仍不支持(PHP 8.3); - 未初始化就访问只读属性会触发致命错误,必须确保初始化完成再读取。
本质上,PHP 8.3 把“初始化时机”严格限定在类实例化过程的早期阶段:要么声明时、要么构造函数(含提升参数)、要么 __unserialize()。超出这个窗口,赋值即非法。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











