You must be surprised/scared after reading title of this article. But I would say yes, foreach loop can create very confusing bug in your program/application if it has not been used in proper way. I will demonstrate the same in this article.
P.S.: This article does to mean to say foreach loop always creates problem, but it would create problem if not used in proper manner.
Let’s move to practical stuff to see this problem in action, please have a look at below code block:
[cc lang=”php”]
$arr = [1, 2, 3]; // PHP 5.4 Only
echo implode(‘,’, $arr), “\n”;
foreach ($arr as $value) {}
echo implode(‘,’, $arr), “\n”;
foreach ($arr as $value) {}
echo implode(‘,’, $arr), “\n”;
[/cc]
[gads]
Above code will give you following output.
[cc lang=”php”]
1,2,3
1,2,3
1,2,3
[/cc]
Upto now foreach seems fine and nothing unusual happens. But now let’s see some unusal things with foreach now, writing same code again with just (very)minor change in it.
[cc lang=”php”]
$arr = [1, 2, 3]; // PHP 5.4 Only
echo implode(‘,’, $arr), “\n”;
foreach ($arr as &$value) {} // Reference
echo implode(‘,’, $arr), “\n”;
foreach ($arr as $value) {} // Value
echo implode(‘,’, $arr), “\n”;
[/cc]
You must have guessed that it will give you same output but infact it is not true. It will give you following output.
[cc lang=”php”]
1,2,3
1,2,3
1,2,2
[/cc]
Now you must be surprised and having question of WHY that happens. Let’s go through each step of above code which shows the real case.
Step 1:
Here array is declared and echoed by imploding array elements. So here nothing changed to array.
Step 2:
Here we have foreach loop, you must have noted [code]&[/code] in front of [code]$value[/code], which means we are passing each element of [code]$arr[/code] with reference instead of just value. So for each iteration we are assigning reference to array element. Once this iteration is completed $value will have reference to last element of [code]$arr[/code] which is [code]$arr[2][/code] and value is [code]3[/code].
Step 3:
When this foreach starts [code]$value[/code] holds [code]3[/code] which is reference to [code]$arr[2][/code]. This loop will create a confusing issue specially when accessing [code]$value[/code] by value instead of reference. We will go through step of step each iteration.
- 1st Pass: This will copies [code]$arr[0][/code] (i.e., “1”) into [code]$value[/code] which is a reference to [code]$arr[2][/code], so [code]$arr[2][/code] now equals [code]1[/code]. So [code]$arr[/code] now contains [code][1, 2, 1][/code].
- 2nd Pass: This will copies [code]$arr[1][/code] (i.e., “2”) into [code]$value[/code] which is a reference to [code]$arr[2][/code], so [code]$arr[2][/code] now equals [code]2[/code]. So [code]$arr[/code] now contains [code][1, 2, 2][/code].
- 3rd Pass: This will copies [code]$arr[2][/code] (which now equals “2”) into [code]$value[/code] which is a reference to [code]$arr[2][/code], so [code]$arr[2][/code] still equals [code]2[/code]. So $arr now contains [code][1, 2, 2][/code].
[gads]
Now you should have clear idea on why does that wierd thing happen with foreach loop. This bug was already reported back in 2004 (https://bugs.php.net/bug.php?id=29992), but this has not been considered as BUG.
Solution
So what can be the resolution for this issue? Its very simple, just remove reference which is assigned to [code]$value[/code], just have a look at below code block.
[cc lang=”php”]
$arr = [1, 2, 3]; // PHP 5.4 Only
echo implode(‘,’, $arr), “\n”;
foreach ($arr as &$value) {} // Reference
echo implode(‘,’, $arr), “\n”;
unset($value); // This will remove reference to last element
foreach ($arr as $value) {} // Value
echo implode(‘,’, $arr), “\n”;
/* Output */
1,2,3
1,2,3
1,2,3
[/cc]
So this was the weird stuff I have found with foreach loop in PHP. Look forward to your comments on this.
more worst case
But In scope of foreach loop your your not printing the $arr and not even replacing values of $arr. So it should not because of foreach.