Is this a bug in PHP?
<?php
$items = array('apple', 'banana', 'carrot');
print_r($items);
foreach ($items as &$item) { }
print_r($items);
foreach ($items as $item) { }
print_r($items);
?>
// Output:
Array
(
[0] => apple
[1] => banana
[2] => carrot
)
Array
(
[0] => apple
[1] => banana
[2] => carrot
)
Array
(
[0] => apple
[1] => banana
[2] => banana
)
Two bananas in the last set?! Not what I expected.
Richard
Richard K Miller schreef:
Is this a bug in PHP?
No<?php
$items = array('apple', 'banana', 'carrot');
print_r($items);
foreach ($items as &$item) { }
$item is now a reference to the last element of $items.
print_r($items);
foreach ($items as $item) { }
And here, the foreach loop writes to $item for each element: thus
assiging that value to the last element of $items.
print_r($items);
?>// Output:
Array
(
[0] => apple
[1] => banana
[2] => carrot
)
Array
(
[0] => apple
[1] => banana
[2] => carrot
)
Array
(
[0] => apple
[1] => banana
[2] => banana
)Two bananas in the last set?! Not what I expected.
You can fix your bug by using 'unset($item);' after the second foreach-loop.
-- Jille
Richard
Hi Jill,
<?php
$items = array('apple', 'banana', 'carrot');
print_r($items);
foreach ($items as &$item) { }
$item is now a reference to the last element of $items.
print_r($items);
foreach ($items as $item) { }
And here, the foreach loop writes to $item for each element: thus
assiging that value to the last element of $items.
The last element of $items is 'carrot'. Why does it print apple,
banana, banana?
It sounds like you're saying that $item should be left with the value
'carrot', which makes sense, but why would the original array be
modified? (These are empty foreach loops.)
print_r($items);
?>// Output:
Array
(
[0] => apple
[1] => banana
[2] => carrot
)
Array
(
[0] => apple
[1] => banana
[2] => carrot
)
Array
(
[0] => apple
[1] => banana
[2] => banana
)Two bananas in the last set?! Not what I expected.
You can fix your bug by using 'unset($item);' after the second
foreach-loop.
Richard, enumerator exhausted, repeat your sub-sequence again you will get it
e.g pointer and pointed
Best Regards
On Wed, Oct 21, 2009 at 7:01 PM, Richard K Miller
richardkmiller@gmail.com wrote:
Hi Jill,
<?php
$items = array('apple', 'banana', 'carrot');
print_r($items);
foreach ($items as &$item) { }$item is now a reference to the last element of $items.
print_r($items);
foreach ($items as $item) { }And here, the foreach loop writes to $item for each element: thus
assiging that value to the last element of $items.The last element of $items is 'carrot'. Why does it print apple, banana,
banana?It sounds like you're saying that $item should be left with the value
'carrot', which makes sense, but why would the original array be modified?
(These are empty foreach loops.)print_r($items);
?>// Output:
Array
(
[0] => apple
[1] => banana
[2] => carrot
)
Array
(
[0] => apple
[1] => banana
[2] => carrot
)
Array
(
[0] => apple
[1] => banana
[2] => banana
)Two bananas in the last set?! Not what I expected.
You can fix your bug by using 'unset($item);' after the second
foreach-loop.
I don't follow. Is this really the intended behavior? It seems quite
unintuitive that the original array would be modified by empty loops.
Suppose I create an include file that loops through $_GET variables
with references:
foreach ($_GET as &$get) { /* empty loop */ }
Subsequent foreach() loops on $_GET would cause corruption. Seems
dangerous.
When would this behavior be desired?
!intuitive && !desired == bug ?
Richard
Richard, enumerator exhausted, repeat your sub-sequence again you
will get it
e.g pointer and pointed
<?php
$items = array('apple', 'banana', 'carrot');
print_r($items);
foreach ($items as &$item) { }
print_r($items);
foreach ($items as $item) { }
print_r($items);
I don't follow. Is this really the intended behavior? It seems quite
unintuitive that the original array would be modified by empty
loops.
It is intended behaviour. Consider your code; at the end of this loop:
$items = array('apple', 'banana', 'carrot');
print_r($items);
foreach ($items as &$item) { }
$item will contain a reference to the last item of $items. Therefore,
when you start the next loop:
foreach ($items as $item) { }
$item will receive each of the values stored in $items, in sequence.
But $item is a reference to the last item in $items, so whatever you
write into it will also be written into the last item of the array.
Thus, the loop will cause these values to be written there:
- On the first iteration, "apple"
- On the second iteration, "banana"
- On the third iteration, "banana" (because you are taking the third
item in the array, which you just overwrote)
When would this behavior be desired?
Probably never, but that does not make it counterintuitive—the
interpreter is behaving correctly based on the data it is handling and
the instructions you are giving to it. The correct solution, as
someone has already recommended, is to unset the iterator if you want
to reuse it.
Cheers,
Marco.
Is this a bug in PHP?
Search the bug DB before asking such questions.
http://bugs.php.net/bug.php?id=29992
http://bugs.php.net/bug.php?id=39307
http://bugs.php.net/bug.php?id=40065
http://bugs.php.net/bug.php?id=40654
http://bugs.php.net/bug.php?id=41603
http://bugs.php.net/bug.php?id=43501
http://bugs.php.net/bug.php?id=43806
http://bugs.php.net/bug.php?id=43988
http://bugs.php.net/bug.php?id=47388
--
Wbr,
Antony Dovgal
http://pinba.org - realtime statistics for PHP
Is this a bug in PHP?
Search the bug DB before asking such questions.
http://bugs.php.net/bug.php?id=29992
http://bugs.php.net/bug.php?id=39307
http://bugs.php.net/bug.php?id=40065
http://bugs.php.net/bug.php?id=40654
http://bugs.php.net/bug.php?id=41603
http://bugs.php.net/bug.php?id=43501
http://bugs.php.net/bug.php?id=43806
http://bugs.php.net/bug.php?id=43988
http://bugs.php.net/bug.php?id=47388
Antony,
I've seen some of those, though not all, but thought I'd bring it up
for discussion. I've appreciated the thoughtful responses, for example
from Marco Tabini, to help explain this. It still doesn't quite make
sense to me.
Doesn't it seem alarming that there are so many bug reports on this
issue, even if from an internals perspective it makes sense to you?
Richard
Richard K Miller wrote:
Is this a bug in PHP?
Search the bug DB before asking such questions.
http://bugs.php.net/bug.php?id=29992
http://bugs.php.net/bug.php?id=39307
http://bugs.php.net/bug.php?id=40065
http://bugs.php.net/bug.php?id=40654
http://bugs.php.net/bug.php?id=41603
http://bugs.php.net/bug.php?id=43501
http://bugs.php.net/bug.php?id=43806
http://bugs.php.net/bug.php?id=43988
http://bugs.php.net/bug.php?id=47388Antony,
I've seen some of those, though not all, but thought I'd bring it up for
discussion. I've appreciated the thoughtful responses, for example from
Marco Tabini, to help explain this. It still doesn't quite make sense to
me.Doesn't it seem alarming that there are so many bug reports on this
issue, even if from an internals perspective it makes sense to you?
It is one of these things that make perfect sense when you think about
it a little bit. Yes, it catches some people, just like strpos()
returning character position 0 on a first-char match catches some
people. There is no way to fix things like these without completely
breaking things. If we had strpos not use 0-based string positions or
if we arbitrarily broke references after loops, all sorts of other
things would start breaking and no longer make sense.
-Rasmus
It is one of these things that make perfect sense when you think about
it a little bit. Yes, it catches some people, just likestrpos()
returning character position 0 on a first-char match catches some
people. There is no way to fix things like these without completely
breaking things. If we had strpos not use 0-based string positions or
if we arbitrarily broke references after loops, all sorts of other
things would start breaking and no longer make sense.-Rasmus
Rasmus,
Thanks for your reply. With respect, this seems to be in a different
class from matching strings with character position 0, IMO.
What about unsetting the iterator before foreach() looping begins? The
iterator is altered anyway, so would this prevent it from being
"attached" to previous foreach() iterations?
Best,
Richard
Richard K Miller wrote:
It is one of these things that make perfect sense when you think about
it a little bit. Yes, it catches some people, just likestrpos()
returning character position 0 on a first-char match catches some
people. There is no way to fix things like these without completely
breaking things. If we had strpos not use 0-based string positions or
if we arbitrarily broke references after loops, all sorts of other
things would start breaking and no longer make sense.-Rasmus
Rasmus,
Thanks for your reply. With respect, this seems to be in a different
class from matching strings with character position 0, IMO.What about unsetting the iterator before foreach() looping begins? The
iterator is altered anyway, so would this prevent it from being
"attached" to previous foreach() iterations?
It would be arbitrarily breaking an explicit reference. I know I have
code lying around that relies on multiple loops cleaning up a big
complicated multi-level array. I do ugly things with references into
that array and it would completely break if PHP magically deleted my
references whether they are in the iterator or elsewhere.
This has been this way for 6+ years and it is well documented on the
http://php.net/foreach page. (see the big red warning box)
-Rasmus
It would be arbitrarily breaking an explicit reference. I know I have
code lying around that relies on multiple loops cleaning up a big
complicated multi-level array. I do ugly things with references into
that array and it would completely break if PHP magically deleted my
references whether they are in the iterator or elsewhere.This has been this way for 6+ years and it is well documented on the
http://php.net/foreach page. (see the big red warning box)-Rasmus
Fair enough. Thanks for examining it again with me.
Best,
Richard