Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a variable assigned by value to an array element (after an earlier assign-by-reference) change when the array element is changed?

When I run the code blow:

$var[0] = 'a';
$tmp = $var;
$var[0] = 'b';
var_dump($tmp); 

the output is:

array(1) { [0]=> string(1) "a" }

When I add a line as follows:

$var[0] = 'a';
$foo = & $var[0]; # added line
$tmp = $var;
$var[0] = 'b';
var_dump($tmp); 

the output becomes:

array(1) { [0]=> &string(1) "b" }

If I assign a variable $foo by reference to an array's element $var[0], is the variable $tmp assigned by value to the array $var supposed to change like that? Why does this happen?

like image 521
steve Avatar asked Feb 24 '12 02:02

steve


1 Answers

Disclaimer: I have not been able to find an explicit reference for this, so I'm mostly inferring here.

A regular reference works through the symbol table. When creating a variable and a value, both are stored in the local symbol table like so:

$foo = "bar";

+--------+-------+
| symbol | value |
+--------+-------+
| $foo   | "bar" |
+--------+-------+

When a reference is created, this simply adds another symbol for the same value to the table:

$bar =& $foo;

+------------+-------+
| symbol     | value |
+------------+-------+
| $foo, $bar | "bar" |
+------------+-------+

Array keys are stored differently though:

$var[0] = 'a';

+--------+-----------------+
| symbol | value           |
+--------+-----------------+
| $var   | array(0 => 'a') |
+--------+-----------------+

There's an entry in the symbol table for $var, but the values inside the array are not individually referenced in the symbol table. What I infer must be happening when creating a reference to the value 'a' (stored in $var[0]) is that the value 'a' is separated from the array $var and $var[0] itself becomes a reference to the new location of where 'a' is stored:

$foo =& $var[0];

+--------+------------------+
| symbol | value            |
+--------+------------------+
| $var   | array(0 => %REF) |
| $foo   | %REF             |
| %REF   | 'a'              |
+--------+------------------+

I guess that the internal implementation of the symbol table does not allow to create direct references to array keys, therefore this is the only way to create a reference to an array element.

So when copying $var to $tmp, the reference is copied with it:

$tmp = $var;

+--------+------------------+
| symbol | value            |
+--------+------------------+
| $var   | array(0 => %REF) |
| $foo   | %REF             |
| %REF   | 'a'              |
| $tmp   | array(0 => %REF) |
+--------+------------------+

Then, when changing the value $var[0] refers to, it changes the value of %REF, which both $tmp and $var refer to.

As I said, this may or may not be an accurate explanation of what's happening internally, but it illustrates the principle.

like image 182
deceze Avatar answered Sep 28 '22 08:09

deceze