On 16 January 2015 at 21:15, Yasuo Ohgaki <yohg...@ohgaki.net> wrote:

> Hi Rowan,
>
> On Sat, Jan 17, 2015 at 1:22 AM, Rowan Collins <rowan.coll...@gmail.com>
> wrote:
>
>> Yasuo Ohgaki wrote on 16/01/2015 08:40:
>>
>>> Hi all,
>>>
>>> Take a look at
>>>
>>> http://3v4l.org/HbVnd
>>>
>>> foreach should not affect internal(zval) array position, but it does.
>>> I'm not sure why foreach works this way, but HHVM behavior is
>>> reasonable. IMHO.
>>>
>>> PHP 7 would be perfect opportunity fix this behavior.
>>> Any comments?
>>>
>>
>> I seem to remember somebody mentioning that the pointer is reused in some
>> situations and not others. I think the reason is efficiency - we have a
>> pointer associated with each array, which is rarely used, so why not borrow
>> it for foreach? (Answer: because it causes the oddity you just spotted. :P)
>>
>
> Right. foreach should not affect internal(zval) position pointer, but it
> does.
>
>
>>
>> For instance, if you put a reset() in the middle of the foreach loop, it
>> doesn't interrupt the progress of the loop, but does cause it to be tracked
>> separately and output "Apple" after the loop finishes:
>> http://3v4l.org/KTCrI
>>
>> There are other weird cases, like nesting two foreach loops over the same
>> variable:
>> - with a true array, the loops are independent (both loops see the full
>> set of values): http://3v4l.org/lJTQG
>> - with a rewindable Traversable, such as SimpleXMLElement, they interact
>> (the inner loop sees all values, but the outer loop doesn't continue after
>> the inner one completes): http://3v4l.org/fs2Jv
>> - with a non-rewindable Traversable, such as a Generator, PHP tries to
>> rewind and gives a fatal error, while HHVM simply uses up the last values
>> of the generator on the inner loop (possibly a bug?):
>> http://3v4l.org/BYOmQ
>>
>> My conclusion: don't try and nest foreach or any related constructs using
>> the same variable, because the behaviour is not guaranteed to be in any way
>> sane.
>>
>
> Nesting foreach is strange, too. There are two issues
>  - array nesting does not work as it seems
>  - generator nesting does not work at all
>
> Besides generator, current behavior is strange.
> It's matter of using external position pointer for arrays, isn't it?
>
> How about make foreach behaves sane manner by PHP 7?
> It's rare that program uses internal position pointer, but
> it's a basic language construct. Basic language construct
> should behave intuitive way. IMHO.
>


My concern is, at what cost? Given how rarely used the internal pointer is,
are we carrying around a chunk of extra memory with every array just on the
off-chance that it will be used, or is there some magic that makes it
zero-width until it's needed?

Reusing it for foreach makes perfect sense if it increases performance for
the majority of cases, and the only way to cover every edge-case like the
one at the top of this thread would be to ban that optimisation outright.
If there's some way of separating things such that the rarely used
constructs take the performance hit, I'm all for it, though.

Alternatively, we could achieve consistency the other way round, by making
foreach() reset the internal pointer even when it *doesn't* use it, and
documenting that fact like we do for certain array functions. Code relying
on the current behaviour when mixing them is probably buggy anyway, because
it is not well-defined, so the BC concern should be low.

As for nesting, I think PHP is doing the right thing for plain arrays and
non-rewindable integrators, because I would expect a for each loop to start
at the beginning, thus have an implicit reset/rewind. The rewindable
behaviour is awkward, though - intuitively, the iteraror needs to be able
to track multiple positions simultaneously, which isn't guaranteed by the
interface design. Maybe an error should be issued if the integrator is
already subject of a foreach loop?

-- 
Rowan Collins
[IMSoP]

Reply via email to