
php 中对象变量赋值不会创建新实例,而是生成新引用;修改副本会同步影响原对象,需用 `clone` 显式深拷贝对象。
在 PHP 中,对象(object)本质上是引用类型——这与数组、字符串等标量类型有根本区别。当你执行 $new_item = $item; 时,并非复制对象数据,而是让 $new_item 指向与 $item 完全相同的内存地址。因此,后续对 $new_item->title 的修改,实际是在操作同一个对象实例,自然也会反映在原 $item 上。
这一点从 var_dump() 的输出编号即可验证:
$item = (object)['id' => 2, 'title' => 'original 2']; $new_item = $item; var_dump($item, $new_item); // 输出中两个对象均标记为 #1 → 同一实例
要真正获得独立副本,必须使用 clone 关键字:
$calendar = [
(object)['id' => 1, 'title' => 'original 1'],
(object)['id' => 2, 'title' => 'original 2'],
(object)['id' => 3, 'title' => 'original 3'],
];
foreach ($calendar as $key => $item) {
if ($item->id === 2) {
$cloned = clone $item; // ✅ 创建全新对象实例(浅拷贝)
$cloned->title = 'new 2'; // ✅ 仅修改副本,不影响原对象
array_splice($calendar, $key, 0, [$cloned]); // 在原位置前插入副本
break; // 避免因数组长度变化导致的遍历异常
}
}
print_r($calendar);✅ 正确输出将包含两个独立对象:
立即学习“PHP免费学习笔记(深入)”;
- 原 [1] => ... 'title' => 'original 2'
- 新插入的 [1] => ... 'title' => 'new 2'
⚠️ 注意事项:
- clone 默认执行浅拷贝:若对象属性本身是对象或资源,其引用关系仍被保留;如需深拷贝,需在类中定义 __clone() 方法手动克隆嵌套对象。
- foreach 中的 $item 是只读副本(非引用),但因其是对象,仍指向原实例——所以直接修改 $item->title 会影响原数组中的对象。这也是为何示例中原始代码已导致原项被意外修改。
- 若需彻底避免副作用,推荐在循环外构造新数组,或使用 array_map() + clone 组合实现函数式风格处理。
总结:PHP 的对象引用语义是设计特性而非 bug,clone 是打破引用链、获得独立对象实例的标准且唯一可靠方式。养成“对象赋值即引用”的意识,能有效规避大量隐蔽的逻辑错误。











