Как клонировать массив объектов в PHP?
У меня есть массив объектов. Я знаю, что объекты назначаются "ссылкой", а массивы - "значением". Но когда я назначаю массив, каждый элемент массива ссылается на объект, поэтому, когда я изменяю объект в любом массиве, изменения отражаются в другом.
есть ли простой способ клонировать массив, или я должен пройти через него, чтобы клонировать каждый объект?
12 ответов
ссылки на те же объекты уже копируются при копировании массива. Но это звучит, как вы хотите мелкий-копия deep-скопируйте объекты, на которые ссылаются в первом массиве при создании второго массива, чтобы получить два массива различных, но похожих объектов.
самый интуитивный способ, который я могу придумать прямо сейчас, - это цикл; там могут быть более простые или более элегантные решения:
$new = array();
foreach ($old as $k => $v) {
$new[$k] = clone $v;
}
вам нужно клонировать объекты, чтобы избежать ссылок на один и тот же объект.
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);
Я также выбрал клон. Клонирование массива не работает (вы можете рассмотреть некоторую реализацию 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 копирует массив без клонирования объектов, затем каждый вложенный объект клонируется. Поэтому он не будет работать, если алгоритм не используется внутри функции.
обратите внимание, что эта функция клонирует массив рекурсивно. Вы можете использовать array_walk
вместо array_walk_recursive
Если вы не хотите, чтобы это произошло.
вот моя лучшая практика по массиву объектов и клонированию. Обычно это хорошая идея, иметь класс коллекции для каждого класса объектов (или интерфейса), которые используются в массиве. С помощью функции magic __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;
}
}
важным примечанием при использовании ArrayObject является то, что вы не можете использовать is_array()
больше. Поэтому помните об этом при рефакторинге кода.
или
$nuarr = json_decode(json_encode($array));
но это дорого, я предпочитаю версию Себастьяна (array_map)
объекты передаются по умолчанию и не всегда легко клонировать, особенно если они могут иметь циклические ссылки. Вы бы лучше подошли с другим выбором структур данных.
для тех, кто предоставляет решения для неглубокого копирования, более простой способ:
$b = (array)$a;
для глубоких копий я не рекомендую это решение:
$nuarr = json_decode (json_encode ($array));
Это для глубокой копии. Это только поддерживает подмножество типов PHP и обменивает объекты на массивы или массивы на объекты, которые могут быть не тем, что вы хотите, а также потенциально повреждающие двоичные значения и так далее.
Если вы сделаете ручную рекурсивную функцию для глубоких копий, использование памяти будет намного меньше для скалярных значений и ключей, поэтому использование json или любого сериализатора повлияет на его точку выполнения.
возможно, лучше использовать unserialize (serialize ($a)) для глубоких копий, если производительность не проблема, которая имеет более широкую поддержку для таких вещей, как объекты, хотя я не удивлюсь, если она прерывается для круговых ссылок и нескольких других необычных вещей.
array_merge_recursive или array_walk_recursive также могут использоваться для массивов.
вы можете легко создать свою собственную рекурсивную функцию, которая использует is_object и is_array для выбора соответствующих средств копирования.
для PHP 5 и выше можно использовать ArrayObject
cunstructur для клонирования массива следующим образом:
$myArray = array(1, 2, 3);
$clonedArray = new ArrayObject($myArray);
Если у вас многомерный массив или массив, состоящий из оба объекта и других ценностей вы можете использовать этот метод:
$cloned = Arr::clone($array);
С библиотеке.