Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does PHP references work under the hood for arrays?

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
like image 866
Popcorn Avatar asked Oct 02 '14 19:10

Popcorn


People also ask

Does PHP pass arrays by reference?

With regards to your first question, the array is passed by reference UNLESS it is modified within the method / function you're calling. If you attempt to modify the array within the method / function, a copy of it is made first, and then only the copy is modified.

How do PHP arrays work?

An array is a special variable that we use to store or hold more than one value in a single variable without having to create more variables to store those values. To create an array in PHP, we use the array function array( ) . By default, an array of any variable starts with the 0 index.

How are PHP arrays implemented?

The PHP array is a chained hash table (lookup of O(c) and O(n) on key collisions) that allows for int and string keys. It uses 2 different hashing algorithms to fit the two types into the same hash key space.

What does => mean in PHP array?

=> is the separator for associative arrays. In the context of that foreach loop, it assigns the key of the array to $user and the value to $pass .


1 Answers

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

like image 109
Machavity Avatar answered Nov 05 '22 17:11

Machavity