php 8.3 中安全深克隆含只读属性的对象必须在 __clone() 内赋值,因仅此方法被允许单次写入只读属性;反射已禁用写入权限,通用克隆器需将逻辑封装进新对象的 __clone() 中。

PHP 8.3 中要安全地深克隆含只读属性的对象,必须绕过只读限制完成属性复制,否则直接赋值会触发 Cannot modify readonly property 致命错误。
理解 __clone 的特殊权限
PHP 8.3 明确允许在 __clone() 魔术方法内部对只读属性进行单次写入——这是语言层唯一豁免构造函数的场景。其他任何地方(包括反射、__unserialize()、普通方法)均不可修改已初始化的只读属性。
这一步是前提:没有实现 __clone(),就无法合法深克隆只读属性。
手动深克隆只读属性的三步法
第一步:在类中声明 __clone() 方法,并显式调用 parent::__clone()(如有父类);
第二步:对每个只读属性,使用 $this->prop = clone $original->prop; 语法重新赋值——仅在此方法体内有效;
第三步:对非只读但需深拷贝的引用类型属性(如数组、对象),同样用 clone 或递归复制,确保引用断裂。
用反射绕过只读限制?不推荐
方法一:尝试用 ReflectionProperty::setValue() 强制写入 —— PHP 8.3 已彻底禁止该操作,调用即抛出 ReadOnlyPropertyError;
方法二:先用 ReflectionProperty::setAccessible(true) 再 setValue —— 无效,setAccessible() 对只读属性无解除作用;
注意:反射在 PHP 8.3 中仅保留 isReadOnly() 和 getValue() 权限,写入通道已被硬性封禁。
自动深克隆工具类方案
创建一个通用克隆器,接收目标对象,通过 ReflectionClass 获取所有属性,对标记为 readonly 的属性,在临时构造的匿名 __clone 上下文中执行赋值;
关键点:该工具必须将克隆逻辑封装进新对象的 __clone() 方法内,而非在工具函数中直接操作原对象;
这一步做完后,调用 clone $obj 即可触发安全深拷贝。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











