Debugging Error- Leaving Dangling Array References After Foreach Loops for PHP Coders
0 70751
Not sure how to utilise PHP's foreach loops? If you want to perform operations on each element of the array that you are iterating through, references in foreach loops can be helpful. Let’s understand this with set of an example –
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
The issue is that this could also result in some negative side effects and consequences if you're not careful. In the example above, $value will continue to be in scope and hold a reference to the final element of the array after the function has been run. Therefore, subsequent operations involving $value can mistakenly change the array's last element.
Here’s an example of the kind of confusing bugs this can lead to:
$array = [1, 2, 3];
echo implode(',', $array), "\n";
foreach ($array as &$value) {} // by reference
echo implode(',', $array), "\n";
foreach ($array as $value) {} // by value (i.e., copy)
echo implode(',', $array), "\n";
The above code will output the following:
1, 2, 3
1, 2, 3
1, 2, 3
Well, it’s definitely not a typing mistake. The last value on the last line is indeed a 2, not a 3.
Let’s understand why?
After going through the first foreach loop, $array remains unchanged but, as explained above, $value is left as a dangling reference to the last element in $array (since that foreach loop accessed $value by reference).
As a result, when we go through the second foreach loop, “weird stuff” appears to happen. Specifically, since $value is now being accessed by value (i.e., by copy), foreach copies each sequential $array element into $value in each step of the loop. As a result, here’s what happens during each step of the second foreach loop:
Pass 1: Copies $array[0] (i.e., “1”) into $value (which is a reference to $array[2]), so $array[2] now equals 1.
So $array now contains [1, 2, 1].
Pass 2: Copies $array[1] (i.e., “2”) into $value (which is a reference to $array[2]), so $array[2] now equals 2.
So $array now contains [1, 2, 2].
Pass 3: Copies $array[2] (which now equals “2”) into $value (which is a reference to $array[2]), so $array[2] still equals 2.
So $array now contains [1, 2, 2].
To still get the benefit of using references in foreach loops without running the risk of these kinds of problems, call unset( )function on the variable, immediately after the foreach loop, to remove the reference;
For Instance
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}
unset($value); // $value no longer references $arr[3]
Hope this short summary helps you further in understanding foreach loops.
Share:
Comments
Waiting for your comments