web-dev-qa-db-fra.com

Comment cloner un tableau d'objets en PHP?

J'ai un tableau d'objets. Je sais que les objets sont affectés par "référence" et les tableaux par "valeur". Mais lorsque j'assigne le tableau, chaque élément du tableau fait référence à l'objet. Par conséquent, lorsque je modifie un objet dans l'un des tableaux, les modifications sont répercutées dans l'autre.

Existe-t-il un moyen simple de cloner un tableau ou dois-je le parcourir pour cloner chaque objet?

53
DisgruntledGoat

Les références aux mêmes objets sont déjà copiées lorsque vous copiez le tableau. Mais on dirait que tu veux copie superficielle Copiez en profondeur les objets référencés dans le premier tableau lorsque vous créez le second tableau, de sorte que vous obtenez deux tableaux d'objets distincts mais similaires.

La façon la plus intuitive que je puisse trouver maintenant est une boucle; il peut y avoir des solutions plus simples ou plus élégantes:

$new = array();

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

Vous devez cloner des objets pour éviter d'avoir des références au même objet.

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

Comme suggéré par AndreKR, utiliser array_map () est la meilleure solution si vous savez déjà que votre tableau contient des objets:

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

J'ai aussi opté pour le clone. Le clonage d'un tableau ne fonctionne pas (vous pouvez envisager une implémentation de arrayaccess pour le faire pour vous), donc pour le clone de tableau avec 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);

Cloner un tableau avec serialize et unserialize

Si vos objets prennent en charge la sérialisation, vous pouvez même trier copie/clone superficielle profonde avec un tour dans leur état de veille et vice-versa:

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

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

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

Cependant, cela peut être un peu aventureux.

5
hakre

Je l'ai fait comme ça:

function array_clone($array) {
    array_walk_recursive($array, function(&$value) {
        if(is_object($value)) {
            $value = clone $value;
        }
    });
    return $array;
}

La fonction arg copie le tableau sans cloner les objets, puis chaque objet imbriqué est cloné. Donc, cela ne fonctionnera pas si l'algorithme n'est pas utilisé dans une fonction.

Notez que cette fonction clone le tableau de manière récursive. Vous pouvez utiliser array_walk au lieu de array_walk_recursive si vous ne voulez pas que cela se produise.

2
nuKs

Voici ma meilleure pratique sur un tableau d'objets et le clonage. Généralement, il est judicieux d’avoir une classe Collection pour chaque classe d’objets (ou d’interface) utilisés dans un tableau. Avec la fonction magique, le clonage __clone devient une routine formalisée:

class Collection extends ArrayObject
{
     public function __clone()
     {
        foreach ($this as $key => $property) {
            $this[$key] = clone $property;
        }
     }
}

Pour cloner votre tableau, utilisez-le comme Collection puis clonez-le:

$arrayObject = new Collection($myArray);
$clonedArrayObject = clone $arrayObject;

Un peu plus loin, vous devez également ajouter une méthode de clonage à votre classe et à chaque sous-classe. Ceci est important pour le clonage en profondeur, ou vous pourriez avoir des effets secondaires non souhaités:

class MyClass
{
     public function __clone()
     {
        $this->propertyContainingObject = clone $this->propertyContainingObject;
     }
}

Une remarque importante sur l’utilisation de ArrayObject est que vous ne pouvez plus utiliser is_array(). Soyez donc conscient de cela lors de la refactorisation de votre code.

2
Trendfischer

Vous devez le boucler (en utilisant éventuellement une fonction telle que array_map()), il n’existe pas de fonction PHP pour effectuer automatiquement une copie complète d’un tableau.

2
AndreKR

Pour PHP 5 et les versions ultérieures, vous pouvez utiliser ArrayObject cunstructur pour cloner un tableau comme suit:

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

Si vous avez tableau multidimensionnel ou un tableau composé de les deux objets et d'autres valeurs, vous pouvez utiliser cette méthode:

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

de cette bibliothèque .

0
Minwork

Les objets sont passés par pointu par défaut et ne sont pas toujours faciles à cloner d'autant plus qu'ils peuvent avoir des références circulaires. Vous seriez mieux adapté à un choix différent de structures de données.

Pour ceux qui proposent des solutions de copie superficielle, le moyen le plus simple est le suivant:

 $b = (array)$a;

Pour les copies profondes, je ne recommande pas cette solution:

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

Ceci est pour une copie en profondeur. Il ne prend en charge qu'un sous-ensemble de types PHP et permutera les objets en un tableau ou des tableaux en objets qui ne sont peut-être pas ce que vous voulez, ainsi qu'une corruption potentielle des valeurs binaires, etc.

Si vous créez une fonction manuelle récursive pour les copies profondes, l’utilisation de la mémoire sera beaucoup moins importante par la suite pour les valeurs scalaires et les clés, de sorte que l’utilisation de json ou d’un sérialiseur aura un impact au-delà de son point d’exécution.

Il peut être préférable d’utiliser unserialize (sérialiser ($ a)) pour les copies complètes si la performance n’est pas une préoccupation qui supporte plus largement des choses telles que des objets, même si je ne serais pas surpris qu’elle se casse pour des références circulaires et plusieurs autres choses inhabituelles.

array_merge_recursive ou array_walk_recursive peuvent également être utilisés pour les tableaux.

Vous pouvez facilement créer votre propre fonction récursive qui utilise is_object et is_array pour choisir le moyen de copie approprié.

0
jgmjgm

ou aussi

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

mais c'est cher, je préfère la version de Sébastien (array_map)

0
Daniele Cruciani