# bug when using foreach with references?

- Thread: https://externals.io/message/45862
- Original archive: https://news-web.php.net/php.internals/45862
- Started by Richard K Miller on 2009-10-22T01:37:02+00:00
- Messages: 12
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #45862 — Richard K Miller — 2009-10-22T01:37:02+00:00

URL: https://externals.io/message/45862#45862

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

---

## #45863 — Jille Timmermans — 2009-10-22T01:40:27+00:00

URL: https://externals.io/message/45862#45863 · In reply to: #45862

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

---

## #45864 — Richard K Miller — 2009-10-22T02:01:40+00:00

URL: https://externals.io/message/45862#45864 · In reply to: #45863

Hi Jill,

> > […]
> $item is now a reference to the last element of $items.
> > […]
> 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.)

> > […]
> You can fix your bug by using 'unset($item);' after the second  
> foreach-loop.

---

## #45865 — mm w — 2009-10-22T02:19:13+00:00

URL: https://externals.io/message/45862#45865 · In reply to: #45864

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,
>
> > […]
>
> 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.)
>
> > […]

---

## #45866 — Richard K Miller — 2009-10-22T02:40:08+00:00

URL: https://externals.io/message/45862#45866 · In reply to: #45865

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
>

> > […]

---

## #45867 — Marco Tabini — 2009-10-22T02:47:41+00:00

URL: https://externals.io/message/45862#45867 · In reply to: #45866

> 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:

> > […]


$item will contain a reference to the last item of $items. Therefore,  
when you start the next loop:

> > […]


$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.

---

## #45868 — Antony Dovgal — 2009-10-22T07:42:33+00:00

URL: https://externals.io/message/45862#45868 · In reply to: #45862

> 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

---

## #45869 — Richard K Miller — 2009-10-22T15:28:33+00:00

URL: https://externals.io/message/45862#45869 · In reply to: #45868

> 
> > […]
>
> 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

---

## #45870 — Rasmus Lerdorf — 2009-10-22T15:33:53+00:00

URL: https://externals.io/message/45862#45870 · In reply to: #45869

Richard K Miller wrote:
> 
> 
> > […]
> 
> 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?

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

---

## #45871 — Richard K Miller — 2009-10-22T16:49:12+00:00

URL: https://externals.io/message/45862#45871 · In reply to: #45870

> 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

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

---

## #45873 — Rasmus Lerdorf — 2009-10-22T17:06:26+00:00

URL: https://externals.io/message/45862#45873 · In reply to: #45871

Richard K Miller wrote:
> > […]
> 
> 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

---

## #45874 — Richard K Miller — 2009-10-22T17:14:50+00:00

URL: https://externals.io/message/45862#45874 · In reply to: #45873

> 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
