php 8.2 中 readonly 属性核心能力不变但约束更严格:禁止动态添加或反射修改;禁止在 #[\allowdynamicproperties] 类中声明;与 __get/__set 共存时失去保护;不保证对象深不可变。

PHP 8.1 的 readonly 属性升级到 PHP 8.2 后,核心能力没变,但使用边界更清晰,也暴露了几个关键限制——这些不是 bug,而是语言设计上为保障不可变语义刻意设定的硬性约束。
只读属性本身不能被动态添加或反射修改
PHP 8.1 引入的 public readonly string $name,在 8.2 中行为一致:只能在构造函数中赋值一次,之后任何直接赋值、ReflectionProperty::setValue()、或 __set() 都会触发致命错误。PHP 8.2 进一步强化了底层机制,即使通过扩展或 ZVAL 操作绕过常规语法,也会在运行时校验失败。
只读属性不能出现在动态属性启用的类中
如果类用了 #[\AllowDynamicProperties],则**不允许声明任何 readonly 属性**。因为动态属性依赖哈希表存储未声明字段,而只读属性必须严格绑定到预分配的 properties_table 槽位——两者内存模型冲突。PHP 8.2 在解析阶段就报 ParseError,不等到实例化。
只读属性无法与 __get/__set 共存
只要类定义了魔术方法 __get 或 __set,哪怕没实际调用,整个类就**自动失去只读属性的运行时保护**。原因在于:PHP 认为存在这些方法意味着开发者意图支持动态访问逻辑,与只读语义矛盾。此时 readonly 声明虽不报错,但赋值不会被拦截(相当于退化为普通属性)。
只读属性不保证对象整体不可变
这是最容易忽略的一点:readonly 只锁定属性变量本身的引用/值,**不递归冻结其指向的对象内容**。例如:
public readonly array $items; 允许 $obj->items[] = 'new';→
public readonly DateTime $createdAt; 允许 $obj->createdAt->modify('+1 day');
要真正实现深不可变,需配合不可变类型(如 DateTimeImmutable)或手动封装访问器。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











