8

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
Popcorn
  • 5,188
  • 12
  • 54
  • 87
  • I'm not 100% sure but what i'm think that `$c = 4;` defines the integer 4 to the variable. So it doesn't hold the pointer to `$b[2]` anymore. So it make sense that `$b[2]` doesn't contain '4'. – S.Pols Oct 02 '14 at 21:04
  • possible duplicate of [PHP Reference in array key](http://stackoverflow.com/questions/26098982/php-reference-in-array-key) – Elias Van Ootegem Oct 06 '14 at 15:34

1 Answers1

2

What you created $a it was a simple variable. But when you created $b, by default, PHP copied the variable. So $b is now totally separate from $a, just like it was in your first example.

Then you set $c equal to the reference to $b[2]. So they are both pointing at the same memory address. Update one and it updates the other. The problem is you think that $a should be updated as well, but it shouldn't be because $b is its own variable. Consider what happens when we change $b to a reference to $a

$a = array(1, 2, 3);  
$b = &$a;
$c = &$b[2];
$c = 4;
print_r($a); // prints (1, 2, 4)
print_r($b); // prints (1, 2, 4)

This works like you describe because $b and $a reference the same thing (technically $b is now a symbol pointing to $a)

If you want to dive even deeper into the subject here's an excellent article that covers it in depth. http://webandphp.com/how-php-manages-variables

Machavity
  • 30,841
  • 27
  • 92
  • 100
  • Thanks! That's a really useful article. I've updated my question with what's going on under the hood. Hopefully I got it right this time. – Popcorn Oct 03 '14 at 01:23
  • Not 100% accurate: `$c = &$b[2];` is a special case: referencing array values actually makes the array value a reference, too – Elias Van Ootegem Oct 06 '14 at 15:36