PHP Copying Array Elements by Value, Not by Reference

php copying array elements by value, not by reference

You can take advantage of the fact that PHP will dereference the results of a function call.

Here's some example code I whipped up:

$x = 'x';
$y = 'y';
$arr = array(&$x,&$y);
print_r($arr);

echo "<br/>";
$arr2 = $arr;
$arr2[0] = 'zzz';
print_r($arr);
print_r($arr2);

echo "<br/>";
$arr2 = array_flip(array_flip($arr));
$arr2[0] = '123';
print_r($arr);
print_r($arr2);

The results look like this:


Array ( [0] => x [1] => y )
Array ( [0] => zzz [1] => y ) Array ( [0] => zzz [1] => y )
Array ( [0] => zzz [1] => y ) Array ( [0] => 123 [1] => y )

You can see that the results of using array_flip() during the assigment of $arr to $arr2 results in differences in the subsequent changes to $arr2, as the array_flip() calls forces a dereference.

It doesn't seem terribly efficient, but it might work for you if $this->x->getResults() is returning an array:

$data['x'] = array_flip(array_flip($this->x->getResults()));
$data['y'] = $data['x'];

See this (unanswered) thread for another example.

If everything in your returned array is an object however, then the only way to copy an object is to use clone(), and you would have to iterate through $data['x'] and clone each element into $data['y'].

Example:

$data['x'] = $this->x->getResults();
$data['y'] = array();
foreach($data['x'] as $key => $obj) {
$data['y'][$key] = clone $obj;
}

PHP copy array without references

If your issue is solved using the duplicate link in your question's first comment (supported by 4 others at the time I'm writing this). Please delete your question so that SO can reduce duplicate questions / needless bloat.

Otherwise, just declare a static copy of the original array for future use.

$arr = [1,2,3];
$copy=$arr; // preserve the original
print_r($arr); echo"<br>";
$x = &$arr[1];
$arr2 = $arr;
print_r($arr); print_r($arr2); echo"<br>";
$x = 8;
print_r($arr); print_r($copy); echo"<br>";

Are arrays in PHP copied as value or as reference to new variables, and when passed to functions?

For the second part of your question, see the array page of the manual, which states (quoting) :

Array assignment always involves value
copying. Use the reference operator to
copy an array by reference.

And the given example :

<?php
$arr1 = array(2, 3);
$arr2 = $arr1;
$arr2[] = 4; // $arr2 is changed,
// $arr1 is still array(2, 3)

$arr3 = &$arr1;
$arr3[] = 4; // now $arr1 and $arr3 are the same
?>


For the first part, the best way to be sure is to try ;-)

Consider this example of code :

function my_func($a) {
$a[] = 30;
}

$arr = array(10, 20);
my_func($arr);
var_dump($arr);

It'll give this output :

array
0 => int 10
1 => int 20

Which indicates the function has not modified the "outside" array that was passed as a parameter : it's passed as a copy, and not a reference.

If you want it passed by reference, you'll have to modify the function, this way :

function my_func(& $a) {
$a[] = 30;
}

And the output will become :

array
0 => int 10
1 => int 20
2 => int 30

As, this time, the array has been passed "by reference".


Don't hesitate to read the References Explained section of the manual : it should answer some of your questions ;-)

Copy a multi-dimentional array by Value (not by reference) in PHP

if this is your real print_r output, then you have some unwanted chars in your array keys.

Array
(
[formid] => 2
[edu] => Array //<--- $_SESSION['post']['edu']
(
['id'] => Array // <---- $_SESSION['post']['edu']["'id'"]

PHP Array element assignment for readonly - is value copied?

PHP uses copy-on-write. It attempts to avoid physically copying data unless it needs to.

From PHP docs - Introduction to Variables:

PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting.

You can test this easily:

/* memory usage helpers */

$mem_initial = memory_get_usage();
$mem_last = $mem_initial;

$mem_debug = function () use ($mem_initial, &$mem_last) {
$mem_current = memory_get_usage();
$mem_change = $mem_current - $mem_last;
echo 'Memory usage change: ', $mem_change >= 0 ? '+' : '-', $mem_change, " bytes\n";
$mem_last = $mem_current;
};

/* test */

echo "Allocating 10kB string\n";
$string = str_repeat('x', 10000);
$mem_debug();
echo "\n";

echo "Copying string by direct assignment\n";
$string2 = $string;
$mem_debug();
echo "\n";

echo "Modyfing copied string\n";
$string2 .= 'x';
$mem_debug();
echo "\n";

echo "Copying string with a (string) cast\n";
$string3 = (string) $string;
$mem_debug();

Output for PHP 5.x:

Allocating 10kB string
Memory usage change: +10816 bytes

Copying string by direct assignment
Memory usage change: +56 bytes

Modyfing copied string
Memory usage change: +10048 bytes

Copying string with a (string) cast
Memory usage change: +10104 bytes
  • direct assignment doesn't copy the string in memory as expected
  • modifying the copied string does duplicate the string in memory - copy-on-write has happened
  • assigning the string with an additional (string) cast seems to duplicate the string in memory even if it is unchanged

Output for PHP 7.0:

Allocating 10kB string
Memory usage change: +13040 bytes

Copying string by direct assignment
Memory usage change: +0 bytes

Modyfing copied string
Memory usage change: +12288 bytes

Copying string with a (string) cast
Memory usage change: +0 bytes
  • copy-on-write behavior is the same as in the 5.x versions but meaningless (string) casts don't cause the string to be duplicated in memory

Why does Copy On Write not work on PHP Array which has reference element?

$b = &$a;

This is pass by reference and in other words, a new shallow copy of $a. So, any modification in $b will reflect in $a and vice-versa.

Demo: https://3v4l.org/r86j4

$c = $b;

This is pass by value. So, any modification in $c will only reflect in $c.

$a = [1, &$x];

This is the same as the first example. The second location in the array is now a new copy of same $x.

$x = 1;

$a = [1, &$x];

$b = $a;

$c = $b;

$c[1] = 2;

$b = $a and $c = $b above is pass by value. So, this assignment clones a new copy of $a, however, the &$x in the 2nd location is preserved.

That being said, it's never a good practice to assign variables as pass by reference to arrays individual locations(except for educational purposes). You would soon land into debugging issues and code's undefined behaviors.

All in all, if you want to create a new shallow copy, use &, else use a simple assignment for deep copy(More info).

Update:

the 2nd location is preserved because the second element is a reference, so it holds the reference and naturally the modification reflects everywhere. In the below example, it is pass by value.

<?php

$x = 4;

$y = &$x;

$a = [1,2,$y];

$b = $a;
$b[2] = 40;

var_dump($a);
var_dump($b);

Demo: https://3v4l.org/W9PIR

Reference vs. value copy for PHP arrays?

Let's take a closer look at

    while ($colData= $meta->fetch_field()) {
$this->paramArray[]= &$this->assocArray[$colData->name];
}

call_user_func_array(array($stmt,'bind_result'),$this->paramArray);

There you have your reference(s) and that's causing the trouble. If you copy an array that contains a reference the result is still an array containing a reference.

Try

while ($row= $res->fetch()) {
var_dump($row);

and you will see that $row also contains references. All those corresponding elements in paramArray, assocArray and $row actually reference the same value. And thus when you invoke return $this->stmt->fetch() it doesn't only affect paramArray but all those references, all the way down to $rows since $rows[]= $row; still only copies the array but does not "de-reference" the elements.



Related Topics



Leave a reply



Submit