php 中对象变量存储的是对象标识符(handle),而非实际数据,因此赋值操作不会复制对象,而是共享同一实例;若需独立副本,必须显式使用 `clone` 或其他深拷贝策略。
在 PHP 中,对象不是按值传递的——这是与标量类型(如 int、string)最根本的区别。当你执行 $newlist[5] = $ref;(其中 $ref 是一个对象),你并非创建了该对象的新副本,而是让 $newlist[5] 指向内存中同一个对象实例。这意味着:对 $newlist[5] 的任何修改(如调用 SetTest(5))会直接反映在原始对象上(例如 $listOfTest[4]),从而导致意外的状态污染。
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() 方法,手动克隆子对象,否则仍会共享引用:
public function __clone()
{
if ($this->nestedObject instanceof \stdClass) {
$this->nestedObject = clone $this->nestedObject;
}
}避免误用引用赋值(&$var):示例答案中出现的 $ref = &$listOfTest[4]; 并不能解决问题,反而加剧混淆——它使 $ref 成为 $listOfTest[4] 的引用别名,后续任何对 $ref 的修改等同于直接操作原数组元素,完全违背隔离意图。
大规模场景优化建议:
考虑序列化/反序列化实现深拷贝(unserialize(serialize($obj))),但注意性能与兼容性(要求类可序列化且无资源句柄);总之,PHP 的对象引用语义是明确且一致的设计,关键在于开发者需主动识别并管理对象生命周期。永远不要假设对象赋值 = 复制;需要副本时,显式 clone 是最清晰、最可控的选择。