Search code examples
phparrayspass-by-reference

Looping in array changes element type


Consider this PHP code snippet which loops in array by value reference:

$arr = [1 ,2 , 3];
var_dump($arr);
echo '<br>';

foreach ($arr as &$val) { // note ampersand sign
   // Anything
}

var_dump($arr);

Now first var_dump() emits

array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) }

But second emits

array(3) { [0]=> int(1) [1]=> int(2) [2]=> &int(3) }

Thus looping in array by reference has changed last value type - from integer to integer reference ! How can it be at all ? Why PHP has decided itself to change element types without developer intention to do so ?


Solution

  • To understand this behaviour, you need to understand a few things about PHP:

    • References in PHP are symmetrical: you don't "create a reference to a variable", you "add the variable to a reference set".
    • A foreach by reference is the same as a series of assignments by reference, one after the other.
    • Array values are treated as variables in their own right, and can have all the same type information, including being part of a reference set.

    So let's "unroll" your loop:

    // create a reference set containing $val and $arr[0]
    $val =& $arr[0];
    // remove $val from the first reference set, 
    // and create a second reference set containing $val and $arr[1]
    $val =& $arr[1];
    // remove $val from the second reference set, 
    // and create a third reference set containing $val and $arr[2]
    $val =& $arr[2];
    

    At this point, $arr[0] and $arr[1] are each in a reference set of size 1, so can be seen as "normal values". However, $arr[2] is still in a reference set with $val.

    What that means is that any change to $arr[2] will be reflected in $val, and any change in $val will be reflected in $arr[2]. That's why var_dump is annotating that item with an &, to show that changing it will also change another variable somewhere else.

    This is why it is a good habit to always run unset($val); after using a foreach-by-reference.