Есть ли функция, чтобы сделать копию массива PHP в другой?


530

Есть ли функция, чтобы сделать копию массива PHP в другой?

Я был сожжен несколько раз, пытаясь скопировать массивы PHP. Я хочу скопировать массив, определенный внутри объекта, в глобальный за его пределами.


очень поздно, но в моей среде я проверил это (и это сработало): function arrayCopy (array $ a) {return $ a; } $ a1 = array (); for ($ i = 0; $ i <3; $ i ++) {$ a1 ["key- $ i"] = "value # $ i"; } $ a1 ["key-sub-array"] = array (1, 2, 3, 4); $ a2 = $ a1; $ a3 = arrayCopy ($ a1); for ($ i = 0; $ i <3; $ i ++) {if (! is_array ($ a2 ["key- $ i"])) {$ a2 ["key- $ i"] = "измененное значение # $ я"; }} $ a2 ["key-sub-array"] = array ("измененный подмассив 1", "измененный подмассив 2"); var_dump ($ a1); var_dump ($ a2); var_dump ($ a3); Хитрость заключается в том, чтобы не передавать массив в качестве ссылки в функцию ;-)
Свен

Ответы:


927

В PHP массивы назначаются путем копирования, а объекты - по ссылке. Это значит, что:

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

Будет давать:

array(0) {
}

В то время как:

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

Урожайность:

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

Вы можете запутаться в таких сложностях, как ArrayObject, например , объект, который действует точно так же, как массив. Однако, будучи объектом, он имеет ссылочную семантику.

Изменить: @AndrewLarsson поднимает вопрос в комментариях ниже. PHP имеет специальную функцию, называемую «ссылки». Они несколько похожи на указатели в таких языках, как C / C ++, но не совсем так. Если ваш массив содержит ссылки, то, хотя сам массив передается копией, ссылки все равно будут преобразовываться в исходную цель. Это, конечно, обычно желаемое поведение, но я подумал, что стоит упомянуть.


104
Вы не ответили на вопрос. Вы только объяснили проблему. Что для ОП, скорее всего, то, что он искал. Однако для меня (и других тоже), приехав сюда почти четыре года спустя с аналогичной проблемой, у меня все еще нет хорошего способа клонировать массив без изменения исходного массива (который также включает в себя внутренние указатели). Я полагаю, пришло время задать свой вопрос.
Эндрю Ларссон

28
@AndrewLarsson Но PHP делает это по умолчанию - в этом суть. Однако ссылки не разрешаются, поэтому, если вам это нужно, вам придется рекурсивно проходить по массиву и создавать новый - аналогично, если исходный массив содержит объекты и вы хотите, чтобы они были клонированы, вам придется делать это вручную. Помните также, что ссылки в PHP не совпадают с указателями в C. Не зная ничего о вашем случае, могу ли я предположить, что странно иметь массив ссылок в первом случае, особенно если вы не собираетесь рассматривать их как ссылки? Какой вариант использования?
troelskn

1
@troelskn Я добавил ответ на этот вопрос с решением моей проблемы: stackoverflow.com/a/17729234/1134804
Эндрю Ларссон

3
Но как быть, когда это нежелательное поведение? Вопрос в том, как сделать глубокую копию. Это явно не желательно. Нет Ваш ответ не лучше: $copy = $original;. Что не работает, если элементы массива являются ссылками.
doug65536

8
Как всегда, phpдарит нам наименьший ожидаемый результат , потому что это решение не всегда работает . $a=array(); $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];печатает array0пока $a=$GLOBALS; $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];печатает array1. Видимо, некоторые массивы скопированы по ссылке.
Тино

186

PHP будет копировать массив по умолчанию. Ссылки в PHP должны быть явными.

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

Использование ссылки может быть важно, если массив огромен. Я не уверен, но я предполагаю, что это должно привести к меньшему потреблению памяти и лучшей производительности (не нужно копировать весь массив в памяти).
robsch

11
@robsch - на уровне программной логики массив копируется. Но в памяти он фактически не будет скопирован, пока не будет изменен - ​​потому что PHP использует семантику копирования при записи для всех типов. stackoverflow.com/questions/11074970/…
Джессика Найт

@CoreyKnight Полезно знать. Спасибо тебе за это.
Робш

4
обратите внимание, что это не верно для вложенных массивов, они являются ссылками, и поэтому вы заканчиваете с разбитым беспорядком
MightyPork

45

Если у вас есть массив, содержащий объекты, вам нужно сделать копию этого массива, не касаясь его внутреннего указателя, и вам необходимо клонировать все объекты (чтобы не изменять оригиналы при внесении изменений в скопированный объект). массив), используйте это.

Хитрость в том, чтобы не касаться внутреннего указателя массива, заключается в том, чтобы убедиться, что вы работаете с копией массива, а не с исходным массивом (или ссылкой на него), поэтому использование параметра функции позволит выполнить работу (таким образом, это функция, которая принимает массив).

Обратите внимание, что вам все равно нужно реализовать __clone () для ваших объектов, если вы хотите, чтобы их свойства также были клонированы.

Эта функция работает для любого типа массива (включая смешанный тип).

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

1
Имейте в виду, что это немного особый случай. Кроме того, обратите внимание, что это будет клонировать только ссылки первого уровня. Если у вас есть глубокий массив, вы не сможете клонировать более глубокие узлы, если они являются ссылками. Может не быть проблемой в вашем случае, но просто имейте это в виду.
troelskn

4
@troelskn Я исправил это, добавив некоторую рекурсию. Эта функция теперь будет работать с любым типом массива, включая смешанные типы. Это также работает для простых массивов, так что больше не локализуется. Это в основном универсальная машина для клонирования массивов. Вам все еще нужно определить функцию __clone () в ваших объектах, если они глубоки, но это выходит за рамки этой функции (извините за плохой каламбур).
Эндрю Ларссон

2
Я твердо верю, что это реальный ответ на этот вопрос. Единственный способ, которым я видел, - это глубокое копирование массива, содержащего объекты.
Патрик

Он не выполняет итерацию свойств объекта, которые могут иметь другие массивы и ссылочные объекты.
я.тек

6
Это использование __FUNCTION__великолепно.
zessx

29

Когда вы делаете

$array_x = $array_y;

PHP копирует массив, поэтому я не уверен, как бы вы сгорели. Для вашего случая

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

должно работать нормально.

Для того, чтобы сгореть, я думаю, вы должны были использовать ссылки или ожидать клонирования объектов внутри массивов.


12
+1 за это: «или ожидая, что объекты внутри массивов будут клонированы»
Melsi

20

array_merge() это функция, в которой вы можете скопировать один массив в другой в PHP.


4
да, но ключи будут изменены, цитата: Значения во входном массиве с цифровыми ключами будут перенумерованы с увеличивающимися ключами, начинающимися с нуля в результирующем массиве.
Замнут

@zamnuts для сохранения ключей: $a_c = array_combine(array_keys($a), array_values($a)).
CPHPython

18

простой и делает глубокое копирование, ломая все ссылки

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

4
Обычно он работает нормально, однако в некоторых случаях он может генерировать исключение, потому что не все переменные являются сериализуемыми (например, замыкания и соединения с базой данных).
я.тек

Следует также отметить, что ссылки на объекты можно восстановить, если класс реализует магический метод __wakeup.
я.тек

Спасибо, наконец-то, что действительно работает, а не ответы на другие смелые ответы с большим количеством голосов, они, конечно, не имели дело с массивом объектов, как указано в вопросе, где может измениться количество элементов в массиве, но определенно не ссылки на объекты внутри них
FentomX1

12

Мне нравится array_replace(или array_replace_recursive).

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

Это работает как Object.assignиз 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';

приведет к

// 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
Как насчет array_slice($arr, 0)или когда вам нет дела до ключей array_values($arr)? Я думаю, что они могут быть быстрее, чем поиск в массиве. Кроме того, в javascript он довольно популярен Array.slice()для клонирования массивов.
Кристиан

В JS у нас есть Object для пар ключ-значение и Array . PHP не делает эту разницу. Для массивов PHP с нумерованными индексами array_sliceи всех других методов, упомянутых здесь, работают очень хорошо. Но если вы хотите объединить несколько пар ключ-значение (как это также возможно с JS-Objects через Object.assignили с помощью синтаксиса распространения ), это array_replaceможет быть более полезным.
Putzi San

@ Кристиан, спасибо за предложение, array_values()которое отлично сработало для моего варианта использования.
Bigsee

11

Если у вас есть только базовые типы в вашем массиве, вы можете сделать это:

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

Вам не нужно обновлять ссылки вручную,
я знаю, что это не будет работать для всех, но у меня это сработало


4
+1 это действительно плохая вещь, но технически правильная и умная. Если бы я видел это в коде, я столкнулся бы с пальмой, но я не могу не любить это.
Reactgular

4

Поскольку это не было охвачено ни одним из ответов и теперь доступно в PHP 5.3 (предполагается, что Original Post использовал 5.2).

Чтобы поддерживать структуру массива и изменять его значения, я предпочитаю использовать array_replaceили в array_replace_recursiveзависимости от моего варианта использования.

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

Вот пример использования array_replaceи array_replace_recursiveдемонстрации его способности поддерживать индексированный порядок и удаления ссылки.

http://ideone.com/SzlBUZ

Приведенный ниже код написан с использованием синтаксиса короткого массива, доступного начиная с PHP 5.4, который заменяется array()на []. http://php.net/manual/en/language.types.array.php

Работает с массивами со смещением и именами

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

Вывод:

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


2

Вот как я копирую свои массивы в Php:

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

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

Это выводит:

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

2
Почему бы просто не сказать $test2 = $test;? Какую проблему ArrayObjectздесь решаем?
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

Самый безопасный и дешевый способ, который я нашел, это:

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

Это также дает преимущество для переиндексации массива.

Это не будет работать, как ожидается, для ассоциативного массива (хэш), но не для большинства предыдущих ответов.


1

Создает копию 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);

?>

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



0

В массиве php вам нужно просто присвоить их другой переменной, чтобы получить копию этого массива. Но сначала вам нужно убедиться в его типе, будь то массив или arrayObject или stdObject.

Для простого массива php:

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

$b = $a;

var_dump($b);

output:

array:1 [
  "data" => 10
]


0

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

Просто чтобы опубликовать еще одно решение;)


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

Сохраняет как ключ, так и значения. Массив 'a' является точной копией массива 'b'

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.