我有一个对象数组。我知道对象是通过“引用”分配的,而数组是通过“值”分配的。但是当我分配数组时,每个数组元素都引用对象,因此当我在数组中修改对象时,另一个数组中的更改也会反映出来。
是否有简单的方法可以克隆一个数组,或者必须循环遍历每个对象以克隆它们?
$array = array_merge(array(), $myArray);
在复制数组时,引用相同对象的引用已经被复制了。但是看起来你想要在创建第二个数组时浅拷贝深拷贝第一个数组中被引用的对象,这样你就可以得到两个具有不同但相似对象的数组。
我目前能想到的最直观的方法是循环;可能还有更简单或更优雅的解决方案:
$new = array();
foreach ($old as $k => $v) {
$new[$k] = clone $v;
}
__clone
函数,则仍然只会进行浅复制。换句话说:如果你要克隆的对象的开发人员做得对,那么它将是一个深层复制。 - kba__clone()
方法来实现深拷贝,并且一直以为clone
的默认行为是浅拷贝。 - BoltClock为了避免引用同一个对象,您需要克隆对象。
function array_copy($arr) {
$newArray = array();
foreach($arr as $key => $value) {
if(is_array($value)) $newArray[$key] = array_copy($value);
else if(is_object($value)) $newArray[$key] = clone $value;
else $newArray[$key] = $value;
}
return $newArray;
}
根据AndreKR的建议,如果您已经知道您的数组包含对象,那么使用array_map()是最佳选择:
$clone = array_map(function ($object) { return clone $object; }, $array);
我也选择克隆。直接克隆数组是行不通的(你可以考虑实现一些数组访问来替代),所以对于array_map中的array clone:
class foo {
public $store;
public function __construct($store) {$this->store=$store;}
}
$f = new foo('moo');
$a = array($f);
$b = array_map(function($o) {return clone $o;}, $a);
$b[0]->store='bar';
var_dump($a, $b);
如果您的对象支持序列化,您甚至可以通过将它们转换为睡眠状态再转回来,实现一种深浅拷贝/克隆:
$f = new foo('moo');
$a = array($f);
$b = unserialize(serialize($a));
$b[0]->store='bar';
var_dump($a, $b);
纯 PHP 7.4及以上版本的解决方案:
$cloned = array_map(fn ($o) => clone $o, $original);
以下是关于对象数组和克隆的最佳实践。通常,为每种对象(或接口)使用一个集合类是一个好主意,这些对象在数组中使用。使用魔术函数__clone
,克隆变成了一种正式的例程:
class Collection extends ArrayObject
{
public function __clone()
{
foreach ($this as $key => $property) {
$this[$key] = clone $property;
}
}
}
要克隆您的数组,请将其用作集合,然后进行克隆:
$arrayObject = new Collection($myArray);
$clonedArrayObject = clone $arrayObject;
更进一步,您应该为您的类及其每个子类添加一个克隆方法。这对于深度克隆非常重要,否则可能会导致意外的副作用:
class MyClass
{
public function __clone()
{
$this->propertyContainingObject = clone $this->propertyContainingObject;
}
}
is_array()
函数。因此,在重构代码时请注意这一点。function array_clone($array) {
array_walk_recursive($array, function(&$value) {
if(is_object($value)) {
$value = clone $value;
}
});
return $array;
}
函数arg会复制数组,但不会克隆对象,而是克隆每个嵌套的对象。因此,如果算法不在函数内使用,则无法正常工作。
请注意,此函数会递归克隆数组。如果您不希望发生这种情况,可以使用array_walk
代替array_walk_recursive
。
你需要循环它 (可能使用像array_map()
这样的函数),没有PHP函数可以自动执行数组的深度复制。
对于 PHP 5 及以上版本,可以使用 ArrayObject
构造函数来克隆数组,如下所示:
$myArray = array(1, 2, 3);
$clonedArray = new ArrayObject($myArray);