Как клонировать массив объектов в PHP?


У меня есть массив объектов. Я знаю, что объекты назначаются по " ссылке "и массивы по"значению". Но когда я назначаю массив, каждый элемент массива ссылается на объект, поэтому, когда я изменяю объект в любом массиве, изменения отражаются в другом.

есть ли простой способ клонировать массив, или я должен перебирать его, чтобы клонировать каждый объект?

12 53

12 ответов:

ссылки на те же объекты уже копируются при копировании массива. Но это звучит так, как будто вы хотите мелкий-копия глубокое копирование объектов, на которые ссылаются в первом массиве при создании второго массива, так что вы получаете два массива различных, но похожих объектов.

самый интуитивный способ, который я могу придумать прямо сейчас, - это цикл; там могут быть более простые или более элегантные решения:

$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 Если вы не хотите, чтобы это произошло.

вот моя лучшая практика по массиву объектов и клонированию. Как правило, это хорошая идея, чтобы иметь класс коллекции для каждого класса объектов (или интерфейс), которые используются в массив. С волшебной функцией __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);

С библиотеке.