У меня есть массив объектов. Я знаю, что объекты присваиваются «reference» и массивами «value». Но когда я назначаю массив, каждый элемент массива ссылается на объект, поэтому, когда я изменяю объект в любом из массивов, изменения отражаются в другом.
Есть ли простой способ клонировать массив, или я должен прокручивать его, чтобы клонировать каждый объект?
Ссылки на те же объекты уже копируются при копировании массива. Но похоже, что вы хотите, чтобы вы создавали второй массив, чтобы получить мелкие копии, копировать объекты, на которые ссылаются в первом массиве, поэтому вы получаете два массива различных, но похожих объектов.
Самый интуитивный способ, который я могу придумать, – это цикл; там могут быть более простые или более элегантные решения:
$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, использование 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
.
или также
$nuarr = json_decode(json_encode($array));
но это дорого, я предпочитаю версию Sebastien (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, чтобы выбрать подходящие средства копирования.
Вот моя лучшая практика в массиве объектов и клонировании. Обычно это хорошая идея – иметь класс Collection для каждого класса объектов (или интерфейса), которые используются в массиве. С помощью магической функции клонирование __clone
становится формализованной процедурой:
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()
. Поэтому имейте это в виду при реорганизации вашего кода.