Existe-t-il une fonction pour faire une copie d'un tableau PHP vers un autre?


530

Existe-t-il une fonction pour faire une copie d'un tableau PHP vers un autre?

J'ai été brûlé plusieurs fois en essayant de copier des tableaux PHP. Je veux copier un tableau défini à l'intérieur d'un objet vers un global à l'extérieur.


vraiment tard, mais dans mon environnement, j'ai testé ceci (et cela a fonctionné): function arrayCopy (array $ a) {return $ a; } $ a1 = array (); for ($ i = 0; $ i <3; $ i ++) {$ a1 ["key- $ i"] = "value # $ i"; } $ a1 ["clé-sous-tableau"] = tableau (1, 2, 3, 4); $ a2 = $ a1; $ a3 = arrayCopy ($ a1); for ($ i = 0; $ i <3; $ i ++) {if (! is_array ($ a2 ["key- $ i"])) {$ a2 ["key- $ i"] = "valeur modifiée # $ je"; }} $ a2 ["clé-sous-tableau"] = tableau ("sous-tableau 1 modifié", "sous-tableau 2 modifié"); var_dump ($ a1); var_dump ($ a2); var_dump ($ a3); L'astuce est de ne pas passer le tableau comme référence dans la fonction ;-)
Sven

Réponses:


927

En PHP, les tableaux sont attribués par copie, tandis que les objets sont attribués par référence. Cela signifie que:

$a = array();
$b = $a;
$b['foo'] = 42;
var_dump($a);

Donnera:

array(0) {
}

Tandis que:

$a = new StdClass();
$b = $a;
$b->foo = 42;
var_dump($a);

Rendements:

object(stdClass)#1 (1) {
  ["foo"]=>
  int(42)
}

Vous pourriez être confus par des subtilités telles que ArrayObject, qui est un objet qui agit exactement comme un tableau. Cependant, étant un objet, il a une sémantique de référence.

Edit: @AndrewLarsson soulève un point dans les commentaires ci-dessous. PHP a une fonctionnalité spéciale appelée "références". Ils sont quelque peu similaires aux pointeurs dans des langages comme C / C ++, mais pas tout à fait les mêmes. Si votre tableau contient des références, alors que le tableau lui-même est transmis par copie, les références seront toujours résolues vers la cible d'origine. C'est bien sûr généralement le comportement souhaité, mais j'ai pensé qu'il valait la peine d'être mentionné.


104
Vous n'avez pas répondu à la question. Vous avez seulement expliqué le problème. Ce qui, pour l'OP, est très probablement ce qu'il cherchait. Cependant, pour moi (et d'autres aussi), venant ici près de quatre ans plus tard avec un problème similaire, je n'ai toujours pas de bon moyen de cloner un tableau sans modifier le tableau d'origine (qui inclut également des pointeurs internes). Je suppose qu'il est temps pour moi de poser ma propre question.
Andrew Larsson

28
@AndrewLarsson Mais PHP le fait par défaut - c'est l'essentiel. Les références ne sont pas résolues cependant, donc si vous en avez besoin, vous devrez parcourir récursivement le tableau et en créer un nouveau - De même, si le tableau source contient des objets et que vous souhaitez les cloner, vous devrez le faire manuellement. Gardez également à l'esprit que les références en PHP ne sont pas les mêmes que les pointeurs en C. Sans rien savoir de votre cas, puis-je suggérer qu'il est étrange d'avoir un tableau de références dans le premier cas, surtout si vous n'avez pas l'intention de traiter les comme références? Quel est le cas d'utilisation?
troelskn

1
@troelskn J'ai ajouté une réponse à cette question avec une solution à mon problème: stackoverflow.com/a/17729234/1134804
Andrew Larsson

3
Mais qu'en est-il quand ce n'est pas un comportement souhaité? La question demande comment faire une profonde copie. Ce n'est évidemment pas souhaité. Votre réponse est pas mieux que: $copy = $original;. Ce qui ne fonctionne pas si les éléments du tableau sont des références.
doug65536

8
Comme toujours, phpnous présente le résultat le moins attendu , car cette solution ne fonctionne pas toujours . $a=array(); $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];imprime array0pendant les $a=$GLOBALS; $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];impressions array1. Apparemment, certains tableaux sont copiés par référence.
Tino

186

PHP copiera le tableau par défaut. Les références en PHP doivent être explicites.

$a = array(1,2);
$b = $a; // $b will be a different array
$c = &$a; // $c will be a reference to $a

L'utilisation de la référence peut être importante si le tableau est énorme. Je ne suis pas sûr, mais je suppose que cela devrait entraîner une consommation de mémoire inférieure et de meilleures performances (pas besoin de copier l'ensemble du tableau en mémoire).
robsch

11
@robsch - au niveau de la logique du programme, le tableau est copié. Mais en mémoire, il ne sera pas copié tant qu'il ne sera pas modifié - car PHP utilise la sémantique de copie sur écriture pour tous les types. stackoverflow.com/questions/11074970/…
Jessica Knight

@CoreyKnight Bon à savoir. Merci pour ça.
robsch

4
notez que ce n'est pas vrai pour les tableaux imbriqués, ce sont des références et donc vous vous retrouvez avec un désordre cassé
MightyPork

45

Si vous avez un tableau qui contient des objets, vous devez faire une copie de ce tableau sans toucher son pointeur interne, et vous avez besoin de tous les objets à cloner (afin de ne pas modifier les originaux lorsque vous apportez des modifications au copié tableau), utilisez-le.

L'astuce pour ne pas toucher le pointeur interne du tableau est de vous assurer que vous travaillez avec une copie du tableau, et non avec le tableau d'origine (ou une référence à celui-ci), donc l'utilisation d'un paramètre de fonction fera le travail (donc, c'est une fonction qui prend un tableau).

Notez que vous devrez toujours implémenter __clone () sur vos objets si vous souhaitez que leurs propriétés soient également clonées.

Cette fonction fonctionne pour tout type de tableau (y compris les types mixtes).

function array_clone($array) {
    return array_map(function($element) {
        return ((is_array($element))
            ? array_clone($element)
            : ((is_object($element))
                ? clone $element
                : $element
            )
        );
    }, $array);
}

1
Gardez à l'esprit qu'il s'agit d'un cas particulier. Notez également que cela ne clonera que les références de premier niveau. Si vous avez un tableau profond, vous ne clonerez pas les nœuds plus profonds, s'ils sont des références. Cela pourrait ne pas être un problème dans votre cas, mais gardez-le à l'esprit.
troelskn

4
@troelskn Je l'ai corrigé en ajoutant une récursivité. Cette fonction fonctionnerait désormais sur n'importe quel type de tableau, y compris les types mixtes. Il fonctionne également aussi bien pour les tableaux simples, il n'est donc plus localisé. Il s'agit essentiellement d'une machine de clonage de baie universelle. Vous auriez encore besoin de définir la fonction __clone () dans vos objets s'ils sont profonds, mais cela dépasse la "portée" de cette fonction (désolé pour le mauvais jeu de mots).
Andrew Larsson

2
Je crois fermement que c'est la vraie réponse à cette question, la seule façon que j'ai vue de copier en profondeur un tableau contenant des objets.
Patrick

Il n'itère pas les propriétés des objets qui peuvent avoir d'autres tableaux et objets référencés.
ya.teck

6
Cette utilisation de __FUNCTION__est géniale.
zessx

29

Quand tu fais

$array_x = $array_y;

PHP copie le tableau, donc je ne sais pas comment vous vous seriez brûlé. Pour votre cas,

global $foo;
$foo = $obj->bar;

devrait bien fonctionner.

Pour être brûlé, je pense que vous auriez dû utiliser des références ou vous attendre à ce que les objets à l'intérieur des tableaux soient clonés.


12
+1 pour ceci: "ou s'attendre à ce que des objets à l'intérieur des tableaux soient clonés"
Melsi


18

simple et rend la copie profonde brisant tous les liens

$new=unserialize(serialize($old));

4
En général, cela fonctionne bien, mais dans certains cas, il peut lever une exception car toutes les variables ne sont pas sérialisables (par exemple les fermetures et les connexions à la base de données).
ya.teck

Une autre chose à noter est que les références aux objets peuvent être restaurées si une classe implémente la méthode magique __wakeup.
ya.teck

Merci, enfin quelque chose qui fonctionne vraiment, pas les autres réponses de bollock ayant beaucoup de votes positifs, ils n'ont sûrement pas traité le tableau d'objets comme spécifié en question où le nombre d'éléments dans le tableau peut changer, mais certainement pas les références à la objets à l'intérieur
FentomX1

12

J'aime array_replace(ou array_replace_recursive).

$cloned = array_replace([], $YOUR_ARRAY);

Cela fonctionne comme à Object.assignpartir de JavaScript.

$original = [ 'foo' => 'bar', 'fiz' => 'baz' ];

$cloned = array_replace([], $original);
$clonedWithReassignment = array_replace([], $original, ['foo' => 'changed']);
$clonedWithNewValues = array_replace([], $original, ['add' => 'new']);

$original['new'] = 'val';

aura pour résultat

// original: 
{"foo":"bar","fiz":"baz","new":"val"}
// cloned:   
{"foo":"bar","fiz":"baz"}
// cloned with reassignment:
{"foo":"changed","fiz":"baz"}
// cloned with new values:
{"foo":"bar","fiz":"baz","add":"new"}

1
Qu'en est-il array_slice($arr, 0)ou quand vous ne vous souciez pas des clés array_values($arr),? Je pense qu'ils pourraient être plus rapides que de rechercher dans un tableau. En outre, en javascript, il est très populaire d'utiliser Array.slice()pour cloner des tableaux.
Christian

Dans JS, nous avons Object pour les paires clé-valeur et Array . PHP ne fait pas cette différence. Pour les tableaux PHP avec des index numérotés, array_sliceet toutes les autres méthodes mentionnées ici fonctionnent très bien. Mais si vous souhaitez fusionner plusieurs paires clé-valeur (comme cela est également possible avec JS-Objects via Object.assignou la syntaxe étalée ), cela array_replacepeut être plus utile.
Putzi San

@Christian merci pour la suggestion array_values()qui a parfaitement fonctionné pour mon cas d'utilisation.
bigsee

11

Si vous n'avez que des types de base dans votre tableau, vous pouvez le faire:

$copy = json_decode( json_encode($array), true);

Vous n'aurez pas besoin de mettre à jour les références manuellement
Je sais que cela ne fonctionnera pas pour tout le monde, mais cela a fonctionné pour moi


4
+1 c'est vraiment une mauvaise chose à faire, mais c'est techniquement correct et intelligent. Si je voyais cela dans le code, je ferais face à Palm mais je ne peux pas m'empêcher de l'aimer.
Reactgular

4

Comme cela n'a été couvert dans aucune des réponses et est maintenant disponible en PHP 5.3 (en supposant que Original Post utilisait 5.2).

Afin de maintenir une structure de tableau et de changer ses valeurs, je préfère utiliser array_replaceou en array_replace_recursivefonction de mon cas d'utilisation.

http://php.net/manual/en/function.array-replace.php

Voici un exemple d'utilisation array_replaceet de array_replace_recursivedémonstration de sa capacité à maintenir l'ordre indexé et à supprimer une référence.

http://ideone.com/SzlBUZ

Le code ci-dessous est écrit en utilisant la syntaxe de tableau court disponible depuis PHP 5.4 qui remplace array()par []. http://php.net/manual/en/language.types.array.php

Fonctionne sur les tableaux indexés et indexés par nom

$o1 = new stdClass;
$a = 'd';
//This is the base array or the initial structure
$o1->ar1 = ['a', 'b', ['ca', 'cb']];
$o1->ar1[3] = & $a; //set 3rd offset to reference $a

//direct copy (not passed by reference)
$o1->ar2 = $o1->ar1; //alternatively array_replace($o1->ar1, []);
$o1->ar1[0] = 'z'; //set offset 0 of ar1 = z do not change ar2
$o1->ar1[3] = 'e'; //$a = e (changes value of 3rd offset to e in ar1 and ar2)

//copy and remove reference to 3rd offset of ar1 and change 2nd offset to a new array
$o1->ar3 = array_replace($o1->ar1, [2 => ['aa'], 3 => 'd']);

//maintain original array of the 2nd offset in ar1 and change the value at offset 0
//also remove reference of the 2nd offset
//note: offset 3 and 2 are transposed
$o1->ar4 = array_replace_recursive($o1->ar1, [3 => 'f', 2 => ['bb']]);

var_dump($o1);

Production:

["ar1"]=>
  array(4) {
    [0]=>
    string(1) "z"
    [1]=>
    string(1) "b"
    [2]=>
    array(2) {
      [0]=>
      string(2) "ca"
      [1]=>
      string(2) "cb"
    }
    [3]=>
    &string(1) "e"
  }
  ["ar2"]=>
  array(4) {
    [0]=>
    string(1) "a"
    [1]=>
    string(1) "b"
    [2]=>
    array(2) {
      [0]=>
      string(2) "ca"
      [1]=>
      string(2) "cb"
    }
    [3]=>
    &string(1) "e"
  }
  ["ar3"]=>
  array(4) {
    [0]=>
    string(1) "z"
    [1]=>
    string(1) "b"
    [2]=>
    array(1) {
      [0]=>
      string(2) "aa"
    }
    [3]=>
    string(1) "d"
  }
  ["ar4"]=>
  array(4) {
    [0]=>
    string(1) "z"
    [1]=>
    string(1) "b"
    [2]=>
    array(2) {
      [0]=>
      string(2) "bb"
      [1]=>
      string(2) "cb"
    }
    [3]=>
    string(1) "f"
  }

3

Je le sais depuis longtemps, mais cela a fonctionné pour moi ..

$copied_array = array_slice($original_array,0,count($original_array));

2

Voici comment je copie mes tableaux en Php:

function equal_array($arr){
  $ArrayObject = new ArrayObject($arr);
  return $ArrayObject->getArrayCopy();  
}

$test = array("aa","bb",3);
$test2 = equal_array($test);
print_r($test2);

Cela produit:

Array
(
[0] => aa
[1] => bb
[2] => 3
)

2
Pourquoi ne pas simplement dire $test2 = $test;? Quel problème ArrayObjectrésout ici?
Nate

1
<?php
function arrayCopy( array $array ) {
        $result = array();
        foreach( $array as $key => $val ) {
            if( is_array( $val ) ) {
                $result[$key] = arrayCopy( $val );
            } elseif ( is_object( $val ) ) {
                $result[$key] = clone $val;
            } else {
                $result[$key] = $val;
            }
        }
        return $result;
}
?>

1

Le moyen le plus sûr et le moins cher que j'ai trouvé est:

<?php 
$b = array_values($a);

Cela a également l'avantage de réindexer le tableau.

Cela ne fonctionnera pas comme prévu sur le tableau associatif (hachage), mais pas la plupart des réponses précédentes.


1

Crée une copie de l'ArrayObject

<?php
// Array of available fruits
$fruits = array("lemons" => 1, "oranges" => 4, "bananas" => 5, "apples" => 10);

$fruitsArrayObject = new ArrayObject($fruits);
$fruitsArrayObject['pears'] = 4;

// create a copy of the array
$copy = $fruitsArrayObject->getArrayCopy();
print_r($copy);

?>

depuis https://www.php.net/manual/en/arrayobject.getarraycopy.php


0

Définissez ceci:

$copy = create_function('$a', 'return $a;');

Copiez $ _ARRAY dans $ _ARRAY2:

$_ARRAY2 = array_map($copy, $_ARRAY);

0

Dans le tableau php, vous devez simplement les affecter à une autre variable pour obtenir une copie de ce tableau. Mais vous devez d'abord vous assurer de son type, qu'il s'agisse de array, arrayObject ou stdObject.

Pour un tableau php simple:

$a = array(
'data' => 10
);

$b = $a;

var_dump($b);

output:

array:1 [
  "data" => 10
]

0
private function cloneObject($mixed)
{
    switch (true) {
        case is_object($mixed):
            return clone $mixed;
        case is_array($mixed):
            return array_map(array($this, __FUNCTION__), $mixed);
        default:
            return $mixed;
    }
}

0

$arr_one_copy = array_combine(array_keys($arr_one), $arr_one);

Juste pour poster une autre solution;)


-1
foreach($a as $key => $val) $b[$key] = $val ;

Préserve à la fois la clé et les valeurs. Le tableau «a» est une copie exacte du tableau «b»

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.