it-swarm.com.ru

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

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

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

53
DisgruntledGoat

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

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

$new = array();

foreach ($old as $k => $v) {
    $new[$k] = clone $v;
}
43
BoltClock
$array = array_merge(array(), $myArray);
66
erani

Вы должны клонировать объекты, чтобы избежать ссылок на один и тот же объект.

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;
}
18
Daniel Teichman

Как рекомендует AndreKR, использование array_map () - лучший способ, если вы уже знаете, что ваш массив содержит объекты:

$clone = array_map(function ($object) { return clone $object; }, $array);
13
Sébastien Fauvel

Я также выбрал клон. Клонирование массива не работает (вы могли бы сделать это для некоторой реализации arrayaccess), например, для array clone с 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);

Массив клонов с сериализацией и десериализацией

Если ваши объекты поддерживают сериализацию, вы можете даже сортировать deep shallow copy/clone с переходом в их спящее состояние и обратно:

$f = new foo('moo');
$a = array($f);

$b = unserialize(serialize($a));

$b[0]->store='bar';
var_dump($a, $b);

Тем не менее, это может быть немного приключений.

5
hakre

Я сделал это так:

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

2
nuKs

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

2
Trendfischer

Вы должны зациклить его (возможно, для этого используйте функцию, подобную array_map()), нет функции PHP для автоматического выполнения глубокой копии массива.

2
AndreKR

Для PHP 5 и выше можно использовать ArrayObject cunstructur для клонирования массива, как показано ниже:

$myArray = array(1, 2, 3);
$clonedArray = new ArrayObject($myArray);
0
Samer Abu Gahgah

Если у вас есть многомерный массив или массив, состоящий из обоих объектов и других значений, вы можете использовать этот метод:

$cloned = Arr::clone($array);

из этой библиотеки .

0
Minwork

По умолчанию объекты передаются по указанным и их не всегда легко клонировать, тем более что они могут иметь циклические ссылки. Вы бы лучше подходили с другим выбором структур данных.

Для тех, кто предоставляет решения для мелкого копирования, более простой способ:

 $b = (array)$a;

Для глубоких копий я не рекомендую это решение:

$ nuarr = json_decode (json_encode ($ array));

Это для глубокой копии. Он поддерживает только подмножество типов PHP и будет заменять объекты массивом или массивами на объекты, которые могут не соответствовать вашим требованиям, а также потенциально портить двоичные значения и т.д.

Если вы сделаете ручную рекурсивную функцию для глубоких копий, впоследствии использование скалярных значений и ключей будет намного меньше, так что использование json или любого сериализатора окажет влияние за пределы его точки выполнения.

Может быть лучше использовать unserialize (serialize ($ a)) для глубоких копий, если производительность не является проблемой, которая имеет более широкую поддержку таких вещей, как объекты, хотя я не удивлюсь, если она сломается для циклических ссылок и некоторых других необычных вещей.

array_merge_recursive или array_walk_recursive также могут использоваться для массивов.

Вы можете легко создать свою собственную рекурсивную функцию, которая использует is_object и is_array для выбора подходящих средств копирования.

0
jgmjgm

или также

$nuarr = json_decode(json_encode($array));

но это дорого, я предпочитаю версию Sebastien (array_map)

0
Daniele Cruciani