
php 中对象变量存储的是对象标识符(handle),而非实际数据,因此赋值操作不会复制对象,而是共享同一实例;若需独立副本,必须显式使用 `clone` 或其他深拷贝策略。
在 PHP 中,对象不是按值传递的——这是与标量类型(如 int、string)最根本的区别。当你执行 $newlist[5] = $ref;(其中 $ref 是一个对象),你并非创建了该对象的新副本,而是让 $newlist[5] 指向内存中同一个对象实例。这意味着:对 $newlist[5] 的任何修改(如调用 SetTest(5))会直接反映在原始对象上(例如 $listOfTest[4]),从而导致意外的状态污染。
为什么 clone 是标准解法?
clone 关键字会触发对象的浅拷贝(shallow copy):它创建一个新对象实例,并复制所有非引用型属性值。对于仅含基本类型属性(如本例中的 private int $m_test)的类,clone 完全满足需求:
function getNewList(TestBase $ref): array
{
$newlist = [
3 => clone $ref, // 新实例,独立于原对象
5 => clone $ref // 另一个新实例
];
$newlist[3]->SetTest(3);
$newlist[5]->SetTest(5);
return $newlist;
}✅ 此时 $listOfTest[4] 不再被修改,输出仍为 2、4、6。
注意事项与进阶实践
-
__clone() 魔术方法:若对象包含其他对象属性(如嵌套对象、资源或动态生成的依赖),需在类中定义 __clone() 方法,手动克隆子对象,否则仍会共享引用:
立即学习“PHP免费学习笔记(深入)”;
public function __clone() { if ($this->nestedObject instanceof \stdClass) { $this->nestedObject = clone $this->nestedObject; } } 避免误用引用赋值(&$var):示例答案中出现的 $ref = &$listOfTest[4]; 并不能解决问题,反而加剧混淆——它使 $ref 成为 $listOfTest[4] 的引用别名,后续任何对 $ref 的修改等同于直接操作原数组元素,完全违背隔离意图。
-
大规模场景优化建议:
- 使用工厂模式统一创建对象,避免临时赋值陷阱;
- 对复杂对象结构,考虑序列化/反序列化实现深拷贝(unserialize(serialize($obj))),但注意性能与兼容性(要求类可序列化且无资源句柄);
- 在框架或领域模型中,采用不可变对象(Immutable Objects)设计,通过 withXxx() 方法返回新实例,从源头规避共享状态。
总之,PHP 的对象引用语义是明确且一致的设计,关键在于开发者需主动识别并管理对象生命周期。永远不要假设对象赋值 = 复制;需要副本时,显式 clone 是最清晰、最可控的选择。











