641

Is there a function to make a copy of a PHP array to another?

I have been burned a few times trying to copy PHP arrays. I want to copy an array defined inside an object to a global outside it.

vfclists
  • 19,193
  • 21
  • 73
  • 92
  • really late, but in my Environment I tested this (and it worked): 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"] = "changed value #$i"; } } $a2["key-sub-array"] = array("changed sub-array 1", "changed sub-array 2"); var_dump($a1); var_dump($a2); var_dump($a3); The trick is, to do not pass the array as a reference into the function ;-) – Sven Nov 14 '16 at 16:05
  • 2
    @Sven is there a reason this is a comment rather than an answer? I can't make heads or tails of it. – ggorlen Feb 24 '22 at 05:29

19 Answers19

1081

In PHP, all variables except objects are assigned by the mechanism called copy-on-write, while objects are assigned by reference. Which means that for the arrays with scalar values simply $b = $a already will give you a copy:

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

Will yield:

array(0) {
}

Whereas with objects,

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

Yields:

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

An edge case when array elements could be objects that need to be cloned as well, is explained in another answer

You could get confused by intricacies such as ArrayObject, which is an object that acts exactly like an array. Being an object however, it has reference semantics.

Edit: @AndrewLarsson raises a point in the comments below. PHP has a special feature called "references". They are somewhat similar to pointers in languages like C/C++, but not quite the same. If your array contains references, then while the array itself is passed by copy, the references will still resolve to the original target. That's of course usually the desired behaviour, but I thought it was worth mentioning.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
troelskn
  • 115,121
  • 27
  • 131
  • 155
  • 144
    You didn't answer the question. You only explained the problem. Which, for the OP, is most likely what he was looking for. However, for me (and others, too), coming here almost four years later with a similar problem, I still don't have a good way to clone an array without modifying the original array (that includes internal pointers as well). I suppose it's time for me to ask my own question. – Andrew Larsson Jul 18 '13 at 00:30
  • 40
    @AndrewLarsson But PHP does that by default - That's the gist of it. References are not resolved though, so if you need that, you will have to recursively traverse the array and build a new one - Likewise, if the source array contains objects, and you want those cloned, you will have to do so manually. Keep in mind also that references in PHP are *not* the same as pointers in C. Without knowing anything about your case, may I suggest that it's strange to have an array of references in the first case, especially if you don't intent to treat them as references? What's the use case? – troelskn Jul 18 '13 at 07:25
  • 2
    @troelskn I added an answer to this question with a solution to my problem: http://stackoverflow.com/a/17729234/1134804 – Andrew Larsson Jul 18 '13 at 16:37
  • 3
    But what about when it is not desired behavior? The question asks how to make a *deep* copy. It is obviously not desired. Your answer is no better than: `$copy = $original;`. Which doesn't work if the array elements are references. – doug65536 Sep 01 '13 at 02:48
  • 11
    As always `php` presents us with the **least expected result**, because this solution **does not always work**. `$a=array(); $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];` prints `array0` while `$a=$GLOBALS; $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];` prints `array1`. Apparently some arrays are copied by reference. – Tino Feb 28 '14 at 22:36
  • @troelskn Coming back to this question, I see that you misunderstood what I meant by "internal pointers". I was referring to the internal array pointer that is moved by calling [`next()`](http://php.net/next) and similar functions. Perhaps now you see why I had to make my own answer to this question (and you are correct, it was sort of a special case, but I see it having use to many others coming here). – Andrew Larsson Mar 05 '14 at 02:43
  • Ah - Yes, that's true. I very rarely use this way of iterating an array in php. I'd usually iterate using `foreach` or `for`. Except for micro optimisations, I can't think of a use case where it would be better to use the `next`, `current` etc. constructs? – troelskn Mar 05 '14 at 09:33
  • @troelskn I don't use the `next()` function either, but I often need to use a `while(list($key, $value) = each($array))` approach to iterate over an array, which does move the internal array pointer after each iteration (and you have to manually `reset()` before you start and when you're done). My answer is due to me needing to clone the array I was currently iterating over, so I needed to make sure I didn't touch the internal array pointer (because `foreach` moves the internal pointer, I couldn't use that to clone it). Maybe I'm not making sense, but hopefully you see why I did what I did. – Andrew Larsson Apr 16 '14 at 22:02
  • @AndrewLarsson No, you do make sense. Not sure what the performance implications might be (and that's rather specific to the context if it's a problem), but one solution could be to make a copy of the array. That way, you won't share a global array pointer between different iterations. – troelskn Apr 17 '14 at 11:00
  • As this is the most up-voted answer here (and for good reason!), please clarify at the beginning (for PHP beginners) with something simple like: "In short: Copy an array in PHP with `$a_copy = $a;`". Until then, @Reinis I.'s answer gets my upvote. – rinogo Mar 26 '15 at 22:38
  • In current PHP, arrays are treaded like objects. I.e. `$a=[foo=>bar];$b=$a;$b[foo]=42;echo$a[foo];` will print `bar`; but `$a=[foo=>bar];$b=$a;$b->foo=42;echo$a[foo];` will print `42`. I.e.: assigning an array variable to another variable will not create a copy instantly (for performance reasons); only if you modify the values - but not if you use the object syntax. – Titus Aug 26 '16 at 20:33
  • 1
    @Titus Not true. See [link](https://3v4l.org/tg133) – Jens Moser Aug 06 '20 at 19:59
  • 1
    @doug65536 - re *"The question asks how to make a deep copy."* I disagree. The question fails to clarify whether it is describing a deep or shallow copy. In my experience the *default* expectation is that we are discussing a *shallow* copy, unless it is explicitly stated that a *deep* copy is desired. Reason: A deep copy is a complex concept: There may be recursive references. There may be references to very large networks of objects - potentially exhausting memory. And some nested objects may be difficult, very expensive, or even impossible [due to held resources] to correctly clone. – ToolmakerSteve Dec 02 '20 at 00:24
251

PHP will copy the array by default. References in PHP have to be explicit.

$a = array(1,2);
$b = $a; // $b will be a different array
$c = &$a; // $c will be a reference to $a
slikts
  • 8,020
  • 1
  • 27
  • 47
  • To use the reference might be important if the array is huge. I'm not sure but I assume it should lead to less memory consumption and better performance (no need to copy the whole array in memory). – robsch Nov 23 '16 at 07:11
  • 26
    @robsch -- at the level of program logic, the array is copied. But in memory, it won't actually be copied until it's modified -- because PHP uses copy-on-write semantics for all types. http://stackoverflow.com/questions/11074970/will-copy-on-write-prevent-data-duplication-on-arrays – Jessica Knight Apr 06 '17 at 00:29
  • @MightyPork what do you mean? I've tried this ```$a = array(array('a', 'b'), 2, 3); $b = $a; $b[0][1] = 'c'; var_dump($a); ``` and $a doesn't change – Thịnh Phạm Aug 02 '20 at 03:30
  • @ThịnhPhạm i don't know, it was 3 years ago – MightyPork Aug 04 '20 at 09:14
  • @ThịnhPhạm I think he meant what he wrote, including the 'copy-on-write'. To clarify, your code won't actually copy $a to $b until you actually change a $b element . Until that time, b will actually references a.(That's a very short time in this example, I realize.) so yes, you can both be correct. – Dennis Apr 15 '23 at 15:24
57

If you have an array that contains objects, you need to make a copy of that array without touching its internal pointer, and you need all the objects to be cloned (so that you're not modifying the originals when you make changes to the copied array), use this.

The trick to not touching the array's internal pointer is to make sure you're working with a copy of the array, and not the original array (or a reference to it), so using a function parameter will get the job done (thus, this is a function that takes in an array).

Note that you will still need to implement __clone() on your objects if you'd like their properties to also be cloned.

This function works for any type of array (including mixed type).

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
  • 860
  • 1
  • 18
  • 23
  • 1
    Keep in mind that this is a bit of a special case. Also, note that this will only clone the first level references. If you have a deep array, you won't get the deeper nodes cloned, if they are references. Might not be an issue in your case, but just keep it in mind. – troelskn Jul 19 '13 at 08:04
  • 5
    @troelskn I fixed it by adding some recursion. This function would now work on any type of array, including mixed types. It also works just as well for simple arrays, so it's not localized anymore. It's basically a universal array cloning machine. You'd still need to define of the __clone() function in your objects if they're deep, but that's beyond the "scope" of this function (sorry for the bad pun). – Andrew Larsson Jul 19 '13 at 16:46
  • 2
    I strongly believe this is the actual answer to this question, The only way I've seen to actually deep copy an array that contains objects. – Patrick Mar 13 '15 at 17:29
  • It doesn't iterate object properties which may have other arrays and referenced objects. – ya.teck Jan 07 '17 at 14:54
  • @ya.teck Correct, you still need to implement the `__clone()` function on your objects. See the [PHP documentation on cloning](http://php.net/manual/en/language.oop5.cloning.php) for more information on that. The big reason why my `array_clone` function doesn't do it for you is one of the same reasons PHP doesn't do it for you: because there is no efficient, effective, feasible way to protect against circular references. – Andrew Larsson Jan 07 '17 at 19:27
  • @andrew-larsson, __clone() wont be invoked magically for objects that are stored in inside of cloned object or array. – ya.teck Jan 08 '17 at 05:37
  • 1
    @ya.teck Correct, that is why it is up to the developer to implement the `__clone()` function (because only the developer knows what needs to be cloned). If `A` stores `B`, you need to implement the `__clone()` function on `A`. And inside of `A`'s `__clone()` function, you will need to make sure that you clone `B`. Here's an example that shows how and why: http://sandbox.onlinephpfunctions.com/code/ebac24f320a458f0be660df4e80e79b9dd488a80 – Andrew Larsson Jan 08 '17 at 08:17
  • In my case the target is a huge multilevel structure meaning there is object `C` inside `B`, object `D` inside `C`, etc. This means I have to implement `__clone()` for all objects stored on `ABC` level. Furthermore since those objects are of vendor classes I have to override them all and instantiate with this extended classes to implement correct clone behavior. That's practically impossible in real application. – ya.teck Jan 08 '17 at 08:25
  • @ya.teck You could take what I already have here and go one step further and use the `ReflectionClass` to get all properties of each object and clone them as well. I'm not going to stop you from doing that, but I would caution that there is probably a better way to solve your problem if you're having to clone objects that deep. Without knowing much about your situation, perhaps a dependency injection container would be something to look into. Just giving you some ideas to run with. – Andrew Larsson Jan 08 '17 at 08:49
  • @ya.teck DeepCopy provides the behavior you're looking for. It looks like it'll work out of the box for your situation, and if you do end up needing more control, it appears to have a comprehensive amount of configuration so you can handle complex scenarios. – Andrew Larsson Jan 08 '17 at 19:22
  • Maybe was brilliant in 2017, but it's not working on PHP 7 anymore. Just use the call_user_func with a constant instead. – John Jan 28 '19 at 00:58
  • 1
    @John Yes, they changed the behavior of `__FUNCTION__` when used in a anonymous function. I'll update my answer to just use normal recursion. – Andrew Larsson Jan 31 '19 at 22:56
  • You could also store the value of `__FUNCTION__` in a variable on the first line of `array_clone`, so you don't have to remember to update the name of the function in two places if you're planning on putting it somewhere more long-lived. See my edits for an example on how you would do that. – Andrew Larsson Feb 01 '19 at 17:23
  • 1
    In practice, it is often wise to parameterize such a function with `max_depth`, to avoid accidental infinite recursions, or recursions that clone very large networks of objects. At each recursion, decrement max_depth. `function array_clone($array, $max_depth) ... ? $max_depth <= 1 ? $element : array_clone($element, $max_depth-1) ...` Also, an "industrial strength" implementation would detect recursion, and correctly convert a recursive reference to a reference to the clone. Deep copy should be considered a rare, specialized, and potentially dangerous operation. – ToolmakerSteve Dec 02 '20 at 00:49
  • @ToolmakerSteve All great points. I suggest that anyone with an industrial need for this solution should pull up ChatGPT and ask it to add those features for you. Regarding it being rare/specialized/dangerous, there is rarely a "proper" need for this solution - totally agree. But perfect is the enemy of good; sometimes it's much simpler to just `Ctrl+C` on a StackOverflow answer than it is to redesign your app to avoid such a need (which is why I had to come up with this answer in the first place - I knew that adding a deep clone to my problem was much much cheaper than refactoring). – Andrew Larsson Jan 11 '23 at 22:52
34

When you do

$array_x = $array_y;

PHP copies the array, so I'm not sure how you would have gotten burned. For your case,

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

should work fine.

In order to get burned, I would think you'd either have to have been using references or expecting objects inside the arrays to be cloned.

chaos
  • 122,029
  • 33
  • 303
  • 309
29

simple and makes deep copy breaking all links

$new=unserialize(serialize($old));
  • 10
    Generally it works fine however in some cases it may throw an exception because not all variables are serializable (for instance closures and database connections). – ya.teck Jan 07 '17 at 09:53
  • Another thing to note is that object references can be restored if a class implements __wakeup magic method. – ya.teck Jan 07 '17 at 14:36
  • Thanks, finally something that really works, not the other bollock answers having a lot of upvotes, they surely didn't deal with array of objects as is specified in question where number of elements in array might change, but definitelly not the references to the objects inside them – FantomX1 Apr 08 '18 at 14:15
28

I like array_replace (or array_replace_recursive).

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

It works like Object.assign from 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';

will result in

// 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
  • 5,566
  • 3
  • 19
  • 35
  • 3
    What about [`array_slice($arr, 0)`](http://php.net/manual/en/function.array-slice.php) or when you don't care about keys, [`array_values($arr)`](http://php.net/manual/en/function.array-values.php)? I'm thinking they might be faster than searching in an array. Also, in javascript, it's quite popular to use `Array.slice()` to clone arrays. – Christian Jul 29 '18 at 00:55
  • In JS we have [Object](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Object) for key-value-pairs and [Array](https://mzl.la/1kML43T). PHP does not make this difference. For PHP arrays with numbered indexes, `array_slice` and all the other methods mentioned here work very well. But if you want to merge several key-value-pairs (as it is also possible with JS-Objects via `Object.assign` or the [spread-syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals)), `array_replace` can be more useful. – Putzi San Jul 31 '18 at 07:34
  • 1
    @Christian thank you for the suggestion of ```array_values()``` which worked perfectly for my use-case. – bigsee Jan 31 '19 at 09:40
24

array_merge() is a function in which you can copy one array to another in PHP.

David Cain
  • 16,484
  • 14
  • 65
  • 75
Kshitiz Saxena
  • 249
  • 2
  • 2
  • 6
    yes, but keys will be modified, quote: *Values in the input array with numeric keys will be renumbered with incrementing keys starting from zero in the result array.* – zamnuts Nov 21 '13 at 18:55
  • 1
    @zamnuts for maintaining keys: [`$a_c = array_combine(array_keys($a), array_values($a))`](http://php.net/manual/en/function.array-combine.php). – CPHPython Sep 19 '18 at 09:12
13

If you have only basic types in your array you can do this:

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

You won't need to update the references manually
I know it won't work for everyone, but it worked for me

chrmcpn
  • 574
  • 6
  • 10
  • 8
    +1 this is a really bad thing to do, but is technically correct and clever. If I saw this in code I would face palm but I can't help but like it. – Reactgular Apr 02 '16 at 03:03
9

I know this as long time ago, but this worked for me..

$copied_array = array_slice($original_array,0,count($original_array));
bestprogrammerintheworld
  • 5,417
  • 7
  • 43
  • 72
8

Safest and cheapest way I found is:

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

This has also the benefit to reindex the array.

This will not work as expected on associative array (hash), but neither most of previous answer.

Hussard
  • 656
  • 7
  • 14
4

Since this wasn't covered in any of the answers and is now available in PHP 5.3 (assumed Original Post was using 5.2).

In order to maintain an array structure and change its values I prefer to use array_replace or array_replace_recursive depending on my use case.

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

Here is an example using array_replace and array_replace_recursive demonstrating it being able to maintain the indexed order and capable of removing a reference.

http://ideone.com/SzlBUZ

The code below is written using the short array syntax available since PHP 5.4 which replaces array() with []. http://php.net/manual/en/language.types.array.php

Works on either offset indexed and name indexed arrays

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

Output:

["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"
  }
Will B.
  • 17,883
  • 4
  • 67
  • 69
3

Creates a copy of the 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

kheengz
  • 840
  • 12
  • 10
1

This is the way I am copying my arrays in Php:

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

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

This outputs:

Array
(
[0] => aa
[1] => bb
[2] => 3
)
Baykal
  • 569
  • 2
  • 10
  • 15
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
  • 37
  • 2
1

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

Just to post one more solution ;)

Kim
  • 1,757
  • 1
  • 17
  • 32
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;
    }
}
-1

Define this:

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

Copy $_ARRAY to $_ARRAY2 :

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

In php array, you need to just assign them to other variable to get copy of that array. But first you need to make sure about it's type, whether it is array or arrayObject or stdObject.

For Simple php array :

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

$b = $a;

var_dump($b);

output:

array:1 [
  "data" => 10
]
MAULIK MODI
  • 592
  • 6
  • 10
-1
foreach($a as $key => $val) $b[$key] = $val ;

Preserves both key and values. Array 'a' is an exact copy of array 'b'

Krish PG
  • 105
  • 9