Skip to content

\iter\isEmpty() can indirectly cause unexpected consumption from iterators in rare cases #97

@athrawes

Description

@athrawes

I've come across a rare case where the behavior of \iter\isEmpty() can indirectly cause unexpected consumption from iterators in normal use cases.

Specifically, in the case where isEmpty is provided an object of \IteratorAggregate, the object's getIterator() method is called. Any further uses of this object may call getIterator() a second time, so if this method is not a pure function then the first item(s?) we might expect from the iterator could be lost to the void.

I've put together what I believe is a minimal reproduction case here:
https://gist.github.com/athrawes/e451484fdb4d0646f9aa03c9e47b566a

Basically, if the item passed to isEmpty looks like this:

class MyClass implements \IteratorAggregate {
    public function getIterator(): \Traversable {
        // some logic which consumes some resource
        // or otherwise mutates global mutable state
        while ($item = array_pop($someGlobalState)) { yield $item; }
    }
}

then the following does not behave as expected:

$iterable = new MyClass();
\iter\isEmpty($iterable); // <- initially appears to behave as expected, items have not technically been consumed yet
foreach ($iterable as $item) { ... } // <- missing item(s?) from the front as we got a 'new' iterable with said items missing

Then interacting with that item in the future with any foreach constructs or any methods in this library will be missing some item(s?) from the front of the iterable.

I'm not sure if this is really a bug in this library per se, but it is rather unexpected - especially as the docblock on isEmpty claims to not consume items from iterators. Would this simply be a matter of warning users about this edge case, or is there some way to handle this here?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions