跳转到主内容
极星编程网:以代码为星,赴技术山海!

为什么 PHP 8.3 的克隆机制更完善_学习 readonly 属性的深度拷贝

PHP 8.3 允许在 __clone() 中对本类声明的 readonly 属性赋值一次,需显式直接赋值;嵌套 readonly 对象须逐层实现 __clone(),且不可对继承的 readonly 属性操作。 PHP 8.3 中
__clone()
能改
readonly
属性,但仅限一次 PHP 8.3 之前,
readonly
属性一旦在构造函数中赋值,就彻底不可变;哪怕在
__clone()
里尝试重设,也会直接报
Fatal error: Cannot modify readonly property
。8.3 放开了这个限制——只允许在
__clone()
方法体内对每个
readonly
属性**赋值一次**,且必须是该方法的直接执行路径(不能套在条件分支或函数调用里隐式触发)。 这是唯一被允许绕过
readonly
冻结的场景,其他任何地方(包括
__wakeup()
、反射
setValue()
、序列化后手动修改)仍会失败 赋值必须显式写出,例如
$this->createdAt = new DateTime();
,不能通过引用传递或数组解构间接修改 如果属性本身是对象(如
public readonly PHP $php;
),你得自己
clone
它,否则只是浅拷贝引用 深拷贝只读对象时,
__clone()
必须手动处理嵌套只读结构 PHP 不会自动递归克隆只读属性里的只读子对象。比如一个
readonly class Post
包含
public readonly Author $author
,而
Author
本身也是
readonly
类,那么你在
Post::__clone()
里只写
$this->author = clone $this->author;
是不够的——因为
Author::__clone()
同样需要存在并正确处理它自己的只读属性。 缺少子类的
__clone()
方法会导致
clone $this->author
失败,抛出
Fatal error: Cannot modify readonly property
若子类未定义
__clone()
,
clone
操作会跳过只读属性初始化,导致新对象的只读属性保持未初始化状态(
NULL
或默认值),后续访问可能触发
TypeError
推荐做法:所有参与深拷贝链路的只读类,都显式声明
__clone()
,哪怕只是空实现(用于占位),再逐层委托 用
ReflectionProperty::isReadOnly()
检查是否真能被克隆修改 不是所有标了
readonly
的属性都能在
__clone()
里改——只有**类自身声明的属性**才适用该规则。如果属性来自父类(哪怕父类也是
readonly
类),子类的
__clone()
无法触碰它,反射会返回
true
,但运行时仍报错。 PHP 8.5.5 PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。 下载 用
$prop->isReadOnly()
可以提前判断属性是否具备“可被
__clone()
初始化”的资格,但它不区分“本类声明”和“继承而来” 要确认能否安全赋值,还需结合
$prop->getDeclaringClass() === $reflectedClass
框架或通用克隆工具里,建议先检查声明类,再决定是否尝试赋值,避免无谓的致命错误
#[\Override]
和
__clone()
一起用时容易忽略的兼容性问题 如果你给
__clone()
加上
#[\Override]
属性,PHP 会强制检查父类是否存在同名方法。但
__clone()
是魔术方法,父类通常不会显式定义它——这意味着加了
#[\Override]
反而会让代码直接崩溃,报
Fatal error: ... has #[\Override] attribute, but no matching parent method exists
。 立即学习 “ PHP免费学习笔记(深入) ”;
__clone()
本质是“拦截型”魔术方法,不是为覆盖设计的,加
#[\Override]
没有意义,且破坏兼容性 只有当你明确继承了一个定义了
__clone()
的父类(极少见),才考虑加该属性;否则一律不要加 混淆点:有人误以为加了
#[\Override]
就能“启用”克隆时修改只读属性,其实完全无关——该能力由 PHP 8.3 运行时硬编码支持,不依赖任何属性 实际写
__clone()
时,最易被忽略的是嵌套只读对象的初始化顺序和所有权转移。你以为 clone 了一次就万事大吉,但漏掉一层,整个深拷贝链就断在中间。

相关文章