Чи є функція зробити копію масиву PHP до іншого?


529

Чи є функція зробити копію масиву PHP до іншого?

Мене кілька разів спалювали, намагаючись скопіювати масиви PHP. Я хочу скопіювати масив, визначений всередині об'єкта, у глобальний поза ним.


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

Відповіді:


926

У 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
Andrew Larsson

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/…
Jessica Knight

@CoreyKnight Приємно знати. Дякую за це.
robsch

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
Я переконаний, що це фактична відповідь на це питання. Єдиний спосіб, який я бачив, щоб насправді глибоко скопіювати масив, що містить об’єкти.
Патрік

Він не повторює властивості об'єктів, які можуть мати інші масиви та посилання на об'єкти.
ya.teck

6
Це використання __FUNCTION__геніальне.
zessx

29

Коли ви робите

$array_x = $array_y;

PHP копіює масив, тому я не впевнений, як би ви отримали спалення. Для вашого випадку

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

повинні добре працювати.

Для того, щоб отримати опік, я б подумав, що вам або доведеться використовувати посилання або очікувати клонування об'єктів всередині масивів.


12
+1 для цього: "або очікують, що об’єкти всередині масивів будуть клоновані"
Мелсі

20

array_merge() це функція, за допомогою якої можна копіювати один масив в інший в PHP.


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

@zamnuts для збереження ключів: $a_c = array_combine(array_keys($a), array_values($a)).
CPHPython

18

простий і робить глибоку копію, порушуючи всі посилання

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

4
Як правило, він працює добре, проте в деяких випадках він може кинути виняток, оскільки не всі змінні є серіалізаційними (наприклад, закриття та підключення до бази даних).
ya.teck

Ще одна річ, що слід зазначити, що посилання на об'єкти можуть бути відновлені, якщо клас реалізує __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 для пар ключ-значення та масив . PHP не робить цього. Для масивів PHP з нумерованими індексами array_sliceта всі інші методи, згадані тут, працюють дуже добре. Але якщо ви хочете об'єднати кілька пар ключів-значень (як це можливо і з JS-об’єктами через Object.assignабо синтаксисом спред ), array_replaceможе бути кориснішим.
Putzi San

@Christian дякую за пропозицію, array_values()яка чудово працювала для мого використання.
bigsee

11

Якщо у вашому масиві є лише основні типи, ви можете зробити це:

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

Вам не потрібно буде оновлювати посилання вручну.
Я знаю, що це не буде працювати для всіх, але це працювало для мене


4
+1 це дуже погано, але технічно правильно та розумно. Якби я бачив це в коді, я зіткнувся б з долонею, але не можу не сподобатися.
Реакційний

4

Оскільки це не було висвітлено в жодній з відповідей і тепер доступне в PHP 5.3 (припускається, що в Оригінальній публікації використовується 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тут вирішується ?
Нейт

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 вам потрібно просто призначити їх іншій змінній, щоб отримати копію цього масиву. Але спершу вам потрібно переконатися, що це тип, чи це array або 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.