| Bug #50161 | foreach needs to be fixed | ||||
|---|---|---|---|---|---|
| Submitted: | 12 Nov 2009 8:45pm UTC | Modified: | 13 Nov 2009 12:12am UTC | ||
| From: | marc at perkel dot com | Assigned to: | |||
| Status: | Bogus | Category: | Scripting Engine problem | ||
| Version: | 5.2.11 | OS: | Linux | ||
[12 Nov 2009 9:36pm UTC] jani@php.net
Already reported several times, already decided to be the correct behavior which is also documented.
[12 Nov 2009 9:44pm UTC] marc at perkel dot com
If it's been reported several times then you aren't listening. It is a bug. This is why open source has a bad name because people don't fix what is obviously a bug.
[12 Nov 2009 10:56pm UTC] rasmus@php.net
Arbitrarily deleting a reference would break a lot of code. What you are looking for a block-scope variables. We do not have those in PHP.
[12 Nov 2009 11:06pm UTC] marc at perkel dot com
Give me an example of code it would break if you deleted the reference
at the beginning of a foreach loop. I'm not suggesting that it be
deleted at the end. And foreach will delete a variable if it is already
set to a value. For example:
$y = "some test";
foreach ($myarray as $y) {
print "$y\n";
}
In this case $y is overridden. So it is inconsistent not to override a
reference at the beginning of foreach.
There are other cases where references are deleted. If you do:
unset($x);
It unsets $x - not what $x is pointing to.
The point is - the results of the example I posts here makes PHP
laughable out here in the real world. I think it's a bad idea for PHP to
fail the laugh test.
[12 Nov 2009 11:55pm UTC] rasmus@php.net
Note that you are treating references as if they are pointers in your
argument. They are not pointers. They are entries in the symbol table
that reference other entries in the symbol table. So when you do
unset($x) you are removing that symbol table entry. And in your
non-reference example:
$y = "some test";
foreach ($myarray as $y) {
print "$y\n";
}
Here $y is a symbol table entry referencing a string containing "some
test". On the first iteration you essentially do:
$y = $myarray[0]; // Not necessarily 0, just the 1st element
So now the storage associated with $y is overwritten by the value from
$myarray. If $y is associated with some other storage through a
reference, that storage will be changed.
Now let's say you do this:
$myarray = array("Test");
$a = "A string";
$y = &$a;
foreach ($myarray as $y) {
print "$y\n";
}
Here $y is associated with the same storage as $a through a reference so
when the first iteration does:
$y = $myarray[0];
The only place that "Test" string can go is into the storage associated
with $y. There is no other place for it to go. It is clean and
consistent. And this is the example of what would break if foreach
magically broke the reference. Never mind the nightmare of
inconsistencies for other types of loops, like a
while(list($k,$v)=each($myarray)) { } loop. Do we then break the $k and
$v references in a list() call if it happens to be called in the context
of a while loop? Or is a while-each loop now suddenly very different
from a foreach loop?
I think you just have to take our word for it, even if you don't agree,
that it is correct as it is even though it can trip people up. The only
clean way to fix this would be to introduce block-scoped variables, but
that is well beyond the scope of this bug report.
[13 Nov 2009 12:12am UTC] marc at perkel dot com
Block scope variables is NOT the only solution. In this case if the AS variable were unset at the beginning of the loop it would fix the problem.

Description: ------------ When using foreach and looping through an array the second time if the index variable isn't unset the results are that the referenced variables is used as the index rather than the named variable. The issue can be solved if when the foreach is set up that it does an unset on the variable passed as the "as" variable. PHP should be changed to unset the parameter passed as the index into the array. Reproduce code: --------------- $myarray = array("one","two","three","four"); foreach ($myarray as &$x) { $x = "$x -"; print "$x\n"; } print "\n"; foreach ($myarray as $x) { print "$x\n"; } Expected result: ---------------- one - two - three - four - one - two - three - four - Actual result: -------------- one - two - three - four - one - two - three - three -