Existe uma função para fazer uma cópia de uma matriz PHP para outra?


530

Existe uma função para fazer uma cópia de uma matriz PHP para outra?

Fui queimado algumas vezes tentando copiar matrizes PHP. Eu quero copiar uma matriz definida dentro de um objeto para um global fora dele.


muito tarde, mas no meu ambiente eu testei isso (e funcionou): function arrayCopy (array $ a) {return $ a; } $ a1 = array (); for ($ i = 0; $ i <3; $ i ++) {$ a1 ["key- $ i"] = "valor # $ i"; } $ a1 ["key-sub-array"] = matriz (1, 2, 3, 4); $ a2 = $ a1; $ a3 = arrayCopy ($ a1); for ($ i = 0; $ i <3; $ i ++) {if (! is_array ($ a2 ["chave- $ i"])) {$ a2 ["chave- $ i"] = "valor alterado # $ Eu"; }} $ a2 ["sub-matriz-chave"] = matriz ("sub-matriz alterada 1", "sub-matriz alterada 2"); var_dump ($ a1); var_dump ($ a2); var_dump ($ a3); O truque é, para não passar a matriz como uma referência para a função ;-)
Sven

Respostas:


927

No PHP, matrizes são atribuídas por cópia, enquanto objetos são atribuídos por referência. Isso significa que:

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

Produzirá:

array(0) {
}

Enquanto que:

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

Rendimentos:

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

Você pode ficar confuso com complexidades como ArrayObject, que é um objeto que age exatamente como uma matriz. Sendo um objeto, no entanto, possui semântica de referência.

Edit: @AndrewLarsson levanta um ponto nos comentários abaixo. O PHP possui um recurso especial chamado "referências". Eles são um pouco semelhantes aos ponteiros em linguagens como C / C ++, mas não são exatamente iguais. Se sua matriz contiver referências, enquanto a própria matriz for passada por cópia, as referências ainda serão resolvidas para o destino original. É claro que esse é geralmente o comportamento desejado, mas achei que valia a pena mencionar.


104
Você não respondeu à pergunta. Você só explicou o problema. O qual, para o OP, é provavelmente o que ele estava procurando. No entanto, para mim (e para outros também), chegando aqui quase quatro anos depois com um problema semelhante, ainda não tenho uma boa maneira de clonar uma matriz sem modificar a matriz original (que também inclui indicadores internos). Suponho que é hora de fazer minha própria pergunta.
Andrew Larsson

28
@AndrewLarsson Mas o PHP faz isso por padrão - Essa é a essência. Porém, as referências não são resolvidas; portanto, se você precisar disso, terá que percorrer recursivamente a matriz e criar uma nova. Lembre-se também de que as referências no PHP não são as mesmas que as indicadas no C. Sem saber nada sobre o seu caso, posso sugerir que é estranho ter uma matriz de referências no primeiro caso, especialmente se você não pretende tratar eles como referências? Qual é o caso de uso?
troelskn

1
@troelskn eu adicionei uma resposta a esta pergunta com uma solução para o meu problema: stackoverflow.com/a/17729234/1134804
Andrew Larsson

3
Mas e quando não é o comportamento desejado? A pergunta pergunta como fazer uma cópia profunda . Obviamente não é desejado. A sua resposta não é melhor do que é: $copy = $original;. O que não funciona se os elementos da matriz são referências.
Doug65536

8
Como sempre, phpapresenta o resultado menos esperado , porque essa solução nem sempre funciona . $a=array(); $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];imprime array0enquanto $a=$GLOBALS; $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];imprime array1. Aparentemente, algumas matrizes são copiadas por referência.
Tino

186

O PHP copiará a matriz por padrão. Referências em PHP devem ser explícitas.

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

Usar a referência pode ser importante se a matriz for enorme. Não tenho certeza, mas presumo que deva levar a menos consumo de memória e melhor desempenho (não é necessário copiar toda a matriz na memória).
robsch

11
@robsch - no nível da lógica do programa, o array é copiado. Mas na memória, ele não será copiado até que seja modificado - porque o PHP usa semântica de cópia na gravação para todos os tipos. stackoverflow.com/questions/11074970/...
Jessica Cavaleiro

@CoreyKnight É bom saber. Obrigado por isso.
robsch

4
Note que isso não é verdade para matrizes aninhadas, são referências e assim você acaba com uma bagunça quebrado
MightyPork

45

Se você possui uma matriz que contém objetos, precisa fazer uma cópia dessa matriz sem tocar no ponteiro interno e precisa que todos os objetos sejam clonados (para não modificar os originais ao fazer alterações nas cópias copiadas). array), use isso.

O truque para não tocar no ponteiro interno da matriz é garantir que você esteja trabalhando com uma cópia da matriz, e não com a matriz original (ou uma referência a ela), portanto, o uso de um parâmetro de função fará o trabalho (portanto, esta é uma função que recebe uma matriz).

Observe que você ainda precisará implementar __clone () em seus objetos se desejar que suas propriedades também sejam clonadas.

Esta função funciona para qualquer tipo de matriz (incluindo tipo misto).

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

1
Lembre-se de que este é um caso especial. Além disso, observe que isso clonará apenas as referências de primeiro nível. Se você possui uma matriz profunda, não clonará os nós mais profundos, se forem referências. Pode não ser um problema no seu caso, mas lembre-se disso.
troelskn

4
@troelskn Corrigi-o adicionando alguma recursão. Esta função agora funcionaria em qualquer tipo de matriz, incluindo tipos mistos. Também funciona da mesma forma para matrizes simples, portanto não está mais localizado. É basicamente uma máquina de clonagem de matriz universal. Você ainda precisa definir a função __clone () em seus objetos, se eles forem profundos, mas isso está além do "escopo" dessa função (desculpe o trocadilho).
Andrew Larsson

2
Acredito firmemente que essa é a resposta real a essa pergunta: a única maneira que vi de copiar profundamente uma matriz que contém objetos.
Patrick

Não itera as propriedades do objeto que podem ter outras matrizes e objetos referenciados.
usar o seguinte comando

6
Este uso de __FUNCTION__é brilhante.
Zessx 22/03/19

29

Quando você faz

$array_x = $array_y;

O PHP copia a matriz, então não tenho certeza de como você se queimaria. Para o seu caso,

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

deve funcionar bem.

Para se queimar, acho que você teria que usar referências ou esperar que objetos dentro das matrizes fossem clonados.


12
+1 para isso: "ou esperando objetos no interior das matrizes para ser clonado"
Melsi


18

simples e faz cópias profundas quebrando todos os links

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

4
Geralmente funciona bem, porém, em alguns casos, pode gerar uma exceção, porque nem todas as variáveis ​​são serializáveis ​​(por exemplo, fechamentos e conexões com o banco de dados).
usar o seguinte comando

Outro ponto a ser observado é que as referências a objetos podem ser restauradas se uma classe implementar o método mágico __wakeup.
usar o seguinte comando

Obrigado, finalmente, algo que realmente funciona, não sendo que as outras respostas vazias tenham muitas votações, elas certamente não lidaram com a matriz de objetos, como é especificado na pergunta em que o número de elementos na matriz pode mudar, mas definitivamente não são as referências ao objetos dentro deles
FentomX1

12

Eu gosto array_replace(ou array_replace_recursive).

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

Funciona como Object.assignno 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';

vai resultar em

// 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
E array_slice($arr, 0)quando você não se importa com chaves array_values($arr)? Eu estou pensando que eles podem ser mais rápidos do que pesquisar em uma matriz. Além disso, em javascript, é bastante popular usar Array.slice()para clonar matrizes.
Christian

Em JS, temos Objeto para pares de valores-chave e Matriz . PHP não faz essa diferença. Para matrizes PHP com índices numerados, array_slicee todos os outros métodos mencionados aqui funcionam muito bem. Mas se você deseja mesclar vários pares de valores-chave (como também é possível com JS-Objects via Object.assignou a sintaxe de propagação ), array_replacepode ser mais útil.
Putzi San

@ Christian obrigado pela sugestão de array_values()que funcionou perfeitamente para o meu caso de uso.
bigsee 31/01/19

11

Se você tiver apenas tipos básicos em sua matriz, poderá fazer o seguinte:

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

Você não precisará atualizar as referências manualmente
. Sei que não funcionará para todos, mas funcionou para mim


4
Marcar com +1 é uma coisa muito ruim de se fazer, mas é tecnicamente correto e inteligente. Se eu visse isso no código, enfrentaria a palma da mão, mas não posso deixar de gostar.
Reactgular

4

Como isso não foi abordado em nenhuma das respostas e agora está disponível no PHP 5.3 (assumindo que o Post Original estava usando o 5.2).

Para manter uma estrutura de matriz e alterar seus valores, prefiro usar array_replaceouarray_replace_recursive dependendo do meu caso de uso.

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

Aqui está um exemplo usando array_replaceearray_replace_recursive demonstrando que é capaz de manter a ordem indexada e capaz de remover uma referência.

http://ideone.com/SzlBUZ

O código abaixo é escrito usando a sintaxe de matriz curta disponível desde o PHP 5.4 que substitui array()por []. http://php.net/manual/en/language.types.array.php

Funciona em matrizes indexadas offset e indexadas por nome

$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);

Resultado:

["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

Eu sei disso há muito tempo, mas isso funcionou para mim ..

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

2

É assim que estou copiando minhas matrizes no Php:

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

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

Isso gera:

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

2
Por que não apenas dizer $test2 = $test;? Que problema está ArrayObjectresolvendo aqui?
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

A maneira mais segura e barata que encontrei é:

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

Isso também tem o benefício de reindexar a matriz.

Isso não funcionará como esperado no array associativo (hash), mas nem na maioria das respostas anteriores.


1

Cria uma cópia do 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);

?>

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


0

Defina isso:

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

Copie $ _ARRAY para $ _ARRAY2:

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

0

No array php, você precisa apenas atribuí-los a outra variável para obter uma cópia desse array. Mas primeiro você precisa ter certeza do seu tipo, seja array ou arrayObject ou stdObject.

Para array php simples:

$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);

Apenas para postar mais uma solução;)


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

Preserva a chave e os valores. A matriz 'a' é uma cópia exata da matriz 'b'

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.