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.

vfclists
la source
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é.

troelskn
la source
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
slikts
la source
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);
}
Andrew Larsson
la source
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.

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

array_merge() est une fonction dans laquelle vous pouvez copier un tableau dans un autre en PHP.

Kshitiz Saxena
la source
4
oui, mais les clés seront modifiées, citation: Les valeurs dans le tableau d'entrée avec les touches numériques seront renumérotées avec des clés d'incrémentation à partir de zéro dans le tableau de résultats.
zamnuts
@zamnuts pour maintenir les clés: $a_c = array_combine(array_keys($a), array_values($a)).
CPHPython
18

simple et rend la copie profonde brisant tous les liens

$new=unserialize(serialize($old));
Matthew Cornelisse
la source
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"}
Putzi San
la source
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

chrmcpn
la source
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"
  }
fyrye
la source
3

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

$copied_array = array_slice($original_array,0,count($original_array));
bestprogrammerintheworld
la source
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
)
Baykal
la source
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;
}
?>
umair
la source
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.

Hussard
la source
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

kheengz
la source
0

Définissez ceci:

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

Copiez $ _ARRAY dans $ _ARRAY2:

$_ARRAY2 = array_map($copy, $_ARRAY);
Oscar Gardiazabal
la source
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
]
MAULIK MODI
la source
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;
    }
}
Artemiy StagnantIce Alexeew
la source
0

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

Juste pour poster une autre solution;)

Kim
la source
-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»

Krish PG
la source