PHP 8.2只读类不支持常规克隆,因属性构造后即锁定,__clone中赋值会报Fatal error;正确方式是提取数据并用构造函数重建新实例,确保嵌套只读对象链式还原。

PHP 8.2 的 readonly 类对象不支持常规克隆后修改属性,因为所有属性在构造完成后即被锁定,clone 操作无法绕过只读约束。
克隆操作会触发但立即失败
执行 clone $obj 时,若类定义了 __clone() 方法,该方法会被调用;但此时对象已初始化完成,所有属性处于只读状态。任何在 __clone() 中尝试赋值的行为(包括直接写入、数组元素更新、对象属性重设)都会抛出 Fatal error: Cannot modify readonly property。
- 即使属性是 public,也不允许运行时修改
- 反射(ReflectionProperty)、
serialize/unserialize、var_export等手段均无法解除只读限制 - 不存在“临时解除只读”机制,这是语言级硬性保障
只读对象没有“浅拷贝”或“深拷贝”的概念
因为克隆本身不产生有效副本——clone 得到的是一个与原对象共享相同只读属性引用的新对象实例,但你无法通过它构建差异副本。所谓“拷贝”在只读语义下必须是重建,而非复制后修改。
- 原生
clone对只读类仅做对象实例复制,不复制内部可变结构(如嵌套数组、对象) - 若属性含数组或对象,它们仍是原引用,修改其内容可能意外影响原对象(尤其当嵌套对象非只读时)
- 因此,不能依赖
clone实现数据隔离
正确做法:用构造函数重建新实例
要获得逻辑上“独立且等价”的副本,应提取原始对象的可读数据,对嵌套结构做显式深拷贝,再传入新构造函数生成实例。
立即学习“PHP免费学习笔记(深入)”;
- 确保只读类提供访问所有必要字段的途径(如 public 属性、getter 方法)
- 对数组递归
array_map('unserialize', array_map('serialize', $arr))或使用json_decode(json_encode($arr), true)(注意类型丢失) - 对嵌套只读对象,同样调用其构造参数重建,形成链式还原
- 避免用
unserialize(serialize($obj)),它会破坏只读性并可能引发未定义行为
为什么不能靠继承或反射绕过?
只读限制在引擎层强制执行,与访问控制无关。即使是子类、反射或 eval 动态代码,都无法在对象创建后更改只读属性值。
- 只读类可被继承,但子类也必须声明为
readonly,且不能添加可变属性 - 反射设置属性(
$prop->setValue())会直接报错,不进入任何钩子 - 试图用
__set()或魔术方法拦截赋值无效,只读检查早于这些逻辑



















