Php clone array
Php clone array
I ran into the same problem of an array of objects inside of an object that I wanted to clone all pointing to the same objects. However, I agreed that serializing the data was not the answer. It was relatively simple, really:
Note, that I was working with a multi-dimensional array and I was not using the Key=>Value pair system, but basically, the point is that if you use foreach, you need to specify that the copied data is to be accessed by reference.
class Foo
<
private $bar = 1 ;
public function get ()
<
$x = clone $this ;
return $x -> bar ;
>
>
// will NOT throw exception.
// Foo::$bar property is visible internally even if called as external on the clone
print (new Foo )-> get ();
It should go without saying that if you have circular references, where a property of object A refers to object B while a property of B refers to A (or more indirect loops than that), then you’ll be glad that clone does NOT automatically make a deep copy!
class Foo
<
var $that ;
function __clone ()
<
$this -> that = clone $this -> that ;
>
$a = new Foo ;
$b = new Foo ;
$a -> that = $b ;
$b -> that = $a ;
$c = clone $a ;
echo ‘What happened?’ ;
var_dump ( $c );
It’s possible to know how many clones have been created of a object. I’m think that is correct:
public static $howManyClones = 0 ;
public function __clone () <
++static:: $howManyClones ;
>
public static function howManyClones () <
return static:: $howManyClones ;
>
public function __destruct () <
—static:: $howManyClones ;
>
>
$b = clone $a ;
$c = clone $b ;
$d = clone $c ;
echo ‘Clones:’ . Classe :: howManyClones () . PHP_EOL ;
echo ‘Clones:’ . Classe :: howManyClones () . PHP_EOL ;
Here are some cloning and reference gotchas we came up against at Last.fm.
1. PHP treats variables as either ‘values types’ or ‘reference types’, where the difference is supposed to be transparent. Object cloning is one of the few times when it can make a big difference. I know of no programmatic way to tell if a variable is intrinsically a value or reference type. There IS however a non-programmatic ways to tell if an object property is value or reference type:
$a = new A ;
$a -> p = ‘Hello’ ; // $a->p is a value type
var_dump ( $a );
/*
object(A)#1 (1) <
[«p»]=>
string(5) «Hello» // $ref =& $a -> p ; // note that this CONVERTS $a->p into a reference type!!
var_dump ( $a );
/*
object(A)#1 (1) <
[«p»]=>
&string(5) «Hello» // ?>
2. unsetting all-but-one of the references will convert the remaining reference back into a value. Continuing from the previous example:
unset( $ref );
var_dump ( $a );
?>
I interpret this as the reference-count jumping from 2 straight to 0. However.
2. It IS possible to create a reference with a reference count of 1 — i.e. to convert an property from value type to reference type, without any extra references. All you have to do is declare that it refers to itself. This is HIGHLY idiosyncratic, but nevertheless it works. This leads to the observation that although the manual states that ‘Any properties that are references to other variables, will remain references,’ this is not strictly true. Any variables that are references, even to *themselves* (not necessarily to other variables), will be copied by reference rather than by value.
Here’s an example to demonstrate:
class ByRef
<
var $prop ;
function __construct () < $this ->prop =& $this -> prop ; >
>
$a = new ByVal ;
$a -> prop = 1 ;
$b = clone $a ;
$b -> prop = 2 ; // $a->prop remains at 1
$a = new ByRef ;
$a -> prop = 1 ;
$b = clone $a ;
$b -> prop = 2 ; // $a->prop is now 2
How to clone an array of objects in PHP?
I have an array of objects. I know that objects get assigned by «reference» and arrays by «value». But when I assign the array, each element of the array is referencing the object, so when I modify an object in either array the changes are reflected in the other.
Is there a simple way to clone an array, or must I loop through it to clone each object?
14 Answers 14
References to the same objects already get copied when you copy the array. But it sounds like you want to shallow-copy deep-copy the objects being referenced in the first array when you create the second array, so you get two arrays of distinct but similar objects.
The most intuitive way I can come up with right now is a loop; there may be simpler or more elegant solutions out there:
You need to clone objects to avoid having references to the same object.
As suggested by AndreKR, using array_map() is the best way to go if you already know that your array contains objects:
I opted for clone as well. Cloning an array does not work (you could consider some arrayaccess implementation to do so for you), so as for the array clone with array_map:
Array clone with serialize and unserialize
If your objects support serialisation, you can even sort of deep shallow copy/clone with a tour into their sleeping state and back:
However, that can be a bit adventurous.
You need to loop it (possibly using a function like array_map() for that), there is no PHP function to automatically perform a deep copy of an array.
I’ve done it like this:
The function arg copies the array without cloning the objects, then each nested object is cloned. So it won’t work if the algorithm is not used inside a function.
Note this function clone the array recursively. You can use array_walk instead of array_walk_recursive if you do not want this to happen.
Here is my best practice on an array of objects and cloning. Usually it is a good idea, to have a Collection class for each class of objects (or interface), which are used in an array. With the magic function __clone cloning becomes a formalized routine:
To clone your array, use it as Collection and then clone it:
One step further, you should add a clone method to your class and each sub-class, too. This is important for deep cloning, or you might have unintended side effects:
An important note on using ArrayObject is, that you cannot use is_array() any longer. So be aware of this on refactoring your code.
but it is expensive, I prefer Sebastien version (array_map)
Objects are passed by pointed by default and are not always easy to clone especially as they may have circular references. You would be better suited with a different choice of data structures.
For those providing solutions to shallow copy the easier way is this:
For deep copies I do not recommend this solution:
This is for a deep copy. It only supports a subset of PHP types and will swap objects to array or arrays to objects which might not be what you want as well as potentially corrupting binary values and so on.
If you make a manual recursive function for deep copies the memory usage will be much less afterwards for scalar values and keys so using json or any serializer an impact beyond its point of execution.
It may be better to use unserialize(serialize($a)) for deep copies if performance is not a concern which has wider support for things such as objects though I would not be surprised if it breaks for circular references and several other unusual things.
array_merge_recursive or array_walk_recursive can also be used for arrays.
You can easily create your own recursive function that uses is_object and is_array to choose the appropriate means of copying.
Как клонировать массив объектов в PHP?
У меня есть массив объектов. Я знаю, что объекты присваиваются «reference» и массивами «value». Но когда я назначаю массив, каждый элемент массива ссылается на объект, поэтому, когда я изменяю объект в любом из массивов, изменения отражаются в другом.
Есть ли простой способ клонировать массив, или я должен прокручивать его, чтобы клонировать каждый объект?
Ссылки на те же объекты уже копируются при копировании массива. Но похоже, что вы хотите, чтобы вы создавали второй массив, чтобы получить мелкие копии, копировать объекты, на которые ссылаются в первом массиве, поэтому вы получаете два массива различных, но похожих объектов.
Самый интуитивный способ, который я могу придумать, – это цикл; там могут быть более простые или более элегантные решения:
Вам нужно клонировать объекты, чтобы не иметь ссылок на один и тот же объект.
Как было предложено AndreKR, использование array_map () – лучший способ, если вы уже знаете, что ваш массив содержит объекты:
Я также выбрал клон. Клонирование массива не работает (вы можете рассмотреть возможность реализации arrayaccess для вас), так как для клонирования массива с массивом array_map :
Клонирование массива с сериализацией и несериализацией
Если ваши объекты поддерживают сериализацию, вы можете даже сортировать глубокую мелкую копию / клон с экскурсией в их спящее состояние и обратно:
Однако это может быть немного авантюрным.
Вам нужно зацикливать его (возможно, используя для этого такую функцию, как array_map() ), нет функции PHP для автоматической обработки глубокой копии массива.
Я сделал это вот так:
Функция arg копирует массив без клонирования объектов, затем каждый вложенный объект клонируется. Поэтому он не будет работать, если алгоритм не используется внутри функции.
Обратите внимание, что эта функция клонирует массив рекурсивно. Если вы не хотите, чтобы это произошло, вы можете использовать array_walk вместо array_walk_recursive .
но это дорого, я предпочитаю версию Sebastien (array_map)
Объекты передаются по указанию по умолчанию и не всегда легко клонировать, поскольку они могут иметь круговые ссылки. Вы бы лучше подошли с другим выбором структур данных.
Для тех, кто предоставляет решения для мелкой копии, проще:
Для глубоких копий я не рекомендую это решение:
$ nuarr = json_decode (json_encode ($ array));
Это для глубокой копии. Он поддерживает только подмножество типов PHP и будет заменять объекты на массив или массивы на объекты, которые могут быть не такими, какие вы хотите, а также потенциально разлагаете двоичные значения и т. Д.
Если вы сделаете ручную рекурсивную функцию для глубоких копий, использование памяти будет значительно меньше для скалярных значений и ключей, поэтому использование json или любого сериализатора будет зависеть от его точки исполнения.
Возможно, лучше использовать unserialize (serialize ($ a)) для глубоких копий, если производительность не является проблемой, которая имеет более широкую поддержку таких вещей, как объекты, хотя я не удивлюсь, если она сломается для круговых ссылок и нескольких других необычных вещей.
array_merge_recursive или array_walk_recursive также могут использоваться для массивов.
Вы можете легко создать свою собственную рекурсивную функцию, которая использует is_object и is_array, чтобы выбрать подходящие средства копирования.
Вот моя лучшая практика в массиве объектов и клонировании. Обычно это хорошая идея – иметь класс Collection для каждого класса объектов (или интерфейса), которые используются в массиве. С помощью магической функции клонирование __clone становится формализованной процедурой:
Чтобы клонировать ваш массив, используйте его как Collection и затем клонируйте его:
Еще один шаг, вы должны добавить клон-метод и для своего класса, и для каждого подкласса. Это важно для глубокого клонирования, или у вас могут быть непреднамеренные побочные эффекты:
Важное замечание об использовании ArrayObject заключается в том, что вы больше не можете использовать is_array() . Поэтому имейте это в виду при реорганизации вашего кода.
Как клонировать массив объектов в PHP?
У меня есть массив объектов. Я знаю, что объекты присваиваются «reference» и массивами «value». Но когда я назначаю массив, каждый элемент массива ссылается на объект, поэтому, когда я изменяю объект в любом из массивов, изменения отражаются в другом.
Есть ли простой способ клонирования массива, или я должен прокручивать его для клонирования каждого объекта?
object arrays clone php
10 ответов
39 Решение BoltClock [2011-06-21 03:03:00]
Ссылки на те же объекты уже копируются при копировании массива. Но похоже, что вы хотите, чтобы мелкая копия глубоко копировала объекты, на которые ссылаются в первом массиве при создании второго массива, поэтому вы получаете два массива различных, но похожих объектов.
Самый интуитивный способ, который я могу придумать, — это цикл; там могут быть более простые или более элегантные решения:
70 erani [2012-07-17 16:39:00]
Вам нужно клонировать объекты, чтобы не иметь ссылок на один и тот же объект.
Как было предложено AndreKR, использование array_map() — лучший способ, если вы уже знаете, что ваш массив содержит объекты:
5 hakre [2011-06-21 03:09:00]
Я также выбрал клон. Клонирование массива не работает (вы могли бы рассмотреть некоторую реализацию arrayaccess, чтобы сделать это для вас), так как для клонирования массива с array_map:
Клон массива с сериализацией и несериализацией
Если ваши объекты поддерживают сериализацию, вы можете даже сортировать глубокую мелкую копию/клон с экскурсией в их состояние сна и обратно:
Однако это может быть немного авантюрным.
2 AndreKR [2011-06-21 03:03:00]
Вам нужно зацикливать его (возможно, используя для этого такую функцию, как array_map() ), нет никакой функции PHP для автоматической обработки глубокой копии массива.
2 nuKs [2014-03-11 10:07:00]
Я сделал это вот так:
Функция arg копирует массив без клонирования объектов, то каждый вложенный объект клонируется. Поэтому он не будет работать, если алгоритм не используется внутри функции.
Обратите внимание, что эта функция клонирует массив рекурсивно. Вы можете использовать array_walk вместо array_walk_recursive , если вы этого не хотите.
но это дорого, я предпочитаю версию Sebastien (array_map)
0 jgmjgm [2015-10-26 14:58:00]
Объекты передаются по указанию по умолчанию и не всегда легко клонировать, поскольку они могут иметь круглые ссылки. Вы бы лучше подошли с другим выбором структур данных.
Для тех, кто предоставляет решения для мелкой копии, проще:
Для глубоких копий я не рекомендую это решение:
$nuarr = json_decode (json_encode ($ array));
Это для глубокой копии. Он поддерживает только подмножество типов PHP и будет обменивать объекты на массив или массивы на объекты, которые могут быть не такими, какие вы хотите, а также потенциально повреждаете двоичные значения и т.д.
Если вы сделаете ручную рекурсивную функцию для глубоких копий, использование памяти будет намного меньше для скалярных значений и ключей, поэтому использование json или любого сериализатора будет зависеть от его исполнения.
Лучше использовать unserialize (serialize ($ a)) для глубоких копий, если производительность не является проблемой, которая имеет более широкую поддержку для таких вещей, как объекты, хотя я не удивлюсь, если она сломается для круговых ссылок и нескольких других необычные вещи.
array_merge_recursive или array_walk_recursive также могут использоваться для массивов.
Вы можете легко создать свою собственную рекурсивную функцию, которая использует is_object и is_array для выбора соответствующих средств копирования.
Вот моя лучшая практика в массиве объектов и клонировании. Обычно это хорошая идея — иметь класс Collection для каждого класса объектов (или интерфейса), которые используются в массиве. С магической функцией __clone клонирование становится формализованной процедурой:
Чтобы клонировать ваш массив, используйте его как Collection и затем клонируйте его:
Еще один шаг, вы должны добавить клон-метод и для своего класса, и для каждого подкласса. Это важно для глубокого клонирования, или у вас могут быть непреднамеренные побочные эффекты:
Важным примечанием к использованию ArrayObject является то, что вы больше не можете использовать is_array() . Поэтому имейте это в виду при реорганизации кода.