如何在PHP中克隆一个对象数组?
我有一个对象的数组。 我知道对象是由“引用”和数组由“值”分配的。 但是,当我分配数组时,数组的每个元素都引用该对象,所以当我修改任一数组中的对象时,这些更改都会反映在另一个数组中。
有一个简单的方法来克隆一个数组,或者我必须通过循环克隆每个对象?
复制数组时,对相同对象的引用已经被复制。 但是当你创build第二个数组的时候,这听起来像是你想浅拷贝被第一个数组引用的对象的深度拷贝,所以你得到了两个不同但相似的对象数组。
我现在最直观的方法是循环; 那里可能有更简单或更优雅的解决scheme:
$new = array(); foreach ($old as $k => $v) { $new[$k] = clone $v; }
$array = array_merge(array(), $myArray);
您需要克隆对象以避免引用相同的对象。
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所build议的那样,如果您已经知道数组包含对象,那么使用array_map()是最好的方法:
$clone = array_map(function ($object) { return clone $object; }, $array);
我也select了克隆。 克隆一个数组是不行的(你可以考虑一些arrayaccess实现为你做),以便array_map的数组克隆 :
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);
但是,这可能有点冒险。
你需要循环它(可能使用像array_map()
这样的函数),没有PHP函数自动执行数组的深层副本。
我已经这样做了:
function array_clone($array) { array_walk_recursive($array, function(&$value) { if(is_object($value)) { $value = clone $value; } }); return $array; }
函数arg拷贝数组而不克隆对象,然后克隆每个嵌套对象。 所以如果algorithm不在函数内部使用,它将不起作用。
注意这个函数recursion地克隆数组。 如果你不希望发生这种情况,你可以使用array_walk
而不是array_walk_recursive
。
或者也是
$nuarr = json_decode(json_encode($array));
但价格昂贵,我更喜欢Sebastien版本(array_map)
对象通过默认指针传递,并不总是很容易克隆,尤其是因为它们可能有循环引用。 你会更适合于不同的数据结构select。
对于那些提供浅拷贝解决scheme的更简单的方法是这样的:
$b = (array)$a;
对于深拷贝,我不build议这个解决scheme:
$ nuarr = json_decode(json_encode($ array));
这是一个深层复制。 它只支持PHPtypes的一个子集,并将对象交换到数组或数组中,这些对象可能不是您想要的,也可能会破坏二进制值等等。
如果你为深拷贝做一个手动的recursion函数,那么标量值和关键字的内存使用量就会less得多,所以使用json或者任何序列化器的影响超出了它的执行点。
如果性能不是更广泛地支持诸如对象之类的事情,那么使用反序列化(serialize($ a))可能会更好,但是如果它打破循环引用和其他一些不寻常的事情,我不会感到惊讶。
array_merge_recursive或array_walk_recursive也可以用于数组。
您可以轻松创build自己的recursion函数,使用is_object和is_array来select适当的复制方法。
这是我在对象和克隆arrays上的最佳做法。 通常,对于在数组中使用的每个对象(或接口)类都有一个Collection类是一个好主意。 随着__clone
的魔术function成为一个forms化的例程:
class Collection extends ArrayObject { public function __clone() { foreach ($this as $key => $property) { $this[$key] = clone $property; } } }
要克隆你的数组,把它作为Collection使用,然后克隆它:
$arrayObject = new Collection($myArray); $clonedArrayObject = clone $arrayObject;
更进一步,您应该为您的类和每个子类添加一个克隆方法。 这对深度克隆很重要,否则可能会产生意想不到的副作用:
class MyClass { public function __clone() { $this->propertyContainingObject = clone $this->propertyContainingObject; } }
关于使用ArrayObject的一个重要的注意事项是,你不能再使用is_array()
。 所以要重构你的代码。
这个例子对我来说也是类似的情况:
$new_array = (array) clone (object)$old_array;