PHP 8.5.7 的 clone with 语法可安全生成 readonly 类的新实例,支持在克隆时覆盖构造函数中已初始化的 public 属性,无需手写 with 方法或修改原对象,但不适用于 private/protected、final 或未初始化的属性。

PHP 8.5.7 的 clone with 语法不是“改属性”,而是“安全生成新实例”——它专为 readonly 类设计,让你跳过手写一堆 withName()、withActive() 方法,直接在克隆时注入新值。
它能做什么:快速创建只读对象的变体
比如你有一个不可变的 DTO:
readonly class User {
public function __construct(
public string $name,
public int $age,
)
{}
原来要改名字,得写方法或新建实例;现在只需一行:
立即学习“PHP免费学习笔记(深入)”;
$newUser = clone ($oldUser, ['name' => 'Alice']);
结果是全新对象,$oldUser 不变,$newUser->name 是 'Alice',其他字段自动继承。
- 只对构造函数中初始化的 public 属性生效(不支持 private/protected)
- 不破坏 readonly 语义——不是“修改原对象”,而是“用新值构造副本”
- 无需类定义
__clone()或额外 with 方法,开箱即用
它不能做什么:别指望绕过语言约束
这个语法不会让你暴力覆盖 final 属性、未初始化的 readonly 字段,或触发 __set() 拦截。
- 如果属性声明为
public readonly string $id;,但构造函数没给它赋值,clone ($obj, ['id' => '123'])会报错 - 类若定义了
__set()并 throw 异常,该语法也会失败 - 它不适用于数组、标量或资源类型——只作用于对象克隆场景
怎么写才不出错:三个关键前提
想让 clone ($obj, [...]) 成功运行,必须同时满足:
- 目标类是 PHP 8.5+ 支持的 readonly class(或普通 class,但 only public 属性可被覆盖)
- 你要覆盖的属性,在构造函数中已被显式初始化(哪怕初始化为 null)
- 该属性不是 final,且没有被
__set()显式封锁写入逻辑
实际用在哪:DTO 转换和请求预处理最典型
常见于 API 层数据流转:
- 接收原始请求数据 → 构建 readonly DTO → clone with 过滤/补全字段 → 传给业务逻辑
- 测试中快速构造边界用例:
clone ($validUser, ['age' => -5])模拟非法输入 - 避免临时变量污染:不用先
$tmp = clone $x;再$tmp->prop = ...;,一步到位



















