I'm reading this article about PHP variable references: http://derickrethans.nl/talks/phparch-php-variables-article.pdf and wanted to check if my understanding is correct, regarding when new variable containers get created.
For non-arrays, variable containers get created whenever you assign a variable that is not pointing to a container with is_ref set.
Ex 1 (let {..} be a container):
$a = 1; // "a" => {integer, 1, is_ref = 0, ref_count = 1}
$b = $a; // "a", "b" => {integer, 1, is_ref = 0, ref_count = 2}
$b = 2; // "a" => {integer, 1, is_ref = 0, ref_count = 1}
// "b" => {integer, 2, is_ref = 0, ref_count = 1}
Ex 2:
$a = 1; // "a" => {integer, 1, is_ref = 0, ref_count = 1}
$b = &$a; // "a", "b" => {integer, 1, is_ref = 1, ref_count = 2}
$b = 2; // "a", "b" => {integer, 2, is_ref = 1, ref_count = 2}
How does it work for arrays? It doesn't look like the same thing applies. For example,
$a = array(1, 2, 3);
$b = $a;
$c = &$b[2];
$c = 4;
print_r($a); // prints (1, 2, 3) instead of (1, 2, 4)
print_r($b); // prints (1, 2, 4)
My expectation:
$a and $b points to the same container. Within this container, we have 3 numeric_keys "0", "1", "2" that point to containers for integers 1, 2, and 3 respectively.
When we do $c = &$b[2]
, we update the container containing integer 3:
- is_ref = 0 becomes is_ref = 1
- ref_count = 1 becomes ref_count = 2.
When we do $c = 4
, we update the container containing integer 3:
- integer 3 becomes integer 4 since is_ref is set
However, something is wrong with my expectation because $a[2] != 4
at the end. I'm trying to figure out why. My best guess is that when we try to reference elements of an array, or properties of an object, the PHP engine first checks the array / object itself to see if is_ref = 1. If it is, everything works according to my expectations. If is_ref = 0, then something else happens, which is what I'm seeing. Can someone fill me in on what that "something else" is?
EDIT Looks like this is what's actually going on. This code should clarify everything!
$a = array(1, 2, 3);
$b = $a;
$c = &$b[2]; // $b points to a new container where $b[0], $b[1] still point to same container as $a[0], $a[1], but $b[2] points to a new container also pointed to by $c
$d = $b; // $d points to $b's container, this means changing $c will also change $d[2]
$d[0] = 5; // The container pointed to by $d[0] is the same as the one pointed to by $a[0] and $b[0]. Since this container has is_ref = 0, $d[0] will now point to a new container
// At this point $a = (1, 2, 3), $b = (1, 2, 3), $c = 3, $d = (5, 2, 3)
$d[2] = 25; // The container pointed to by $d[2] is the same as the one pointed to by $b[2] and $c. Since this container has is_ref = 1, Changing $d[2] will affect both $b[2] and $c.
// At this point $a = (1, 2, 3), $b = (1, 2, 25), $c = 25, $d = (5, 2, 25)
$e = $d[2]; // Since $d[2]'s container has is_ref = 1, $e will point to its own container
$c = 4; // Same idea as $d[2] = 25; except $e won't get affected
// At this point $a = (1, 2, 3), $b = (1, 2, 4), $c = 4, $d = (5, 2, 4), $e = 25
// only way to have $d[2] be different from $b[2] is to make the container's is_ref = 0
unset($b[2]);
unset($c);
$b[2] = $d[2];
$d[2] = 55;
// At this point $a = (1, 2, 3), $b = (1, 2, 4), $d = (5, 2, 25), $e = 25