Skip to content

Instantly share code, notes, and snippets.

@athrawes
Last active October 11, 2023 15:05
Show Gist options
  • Save athrawes/e451484fdb4d0646f9aa03c9e47b566a to your computer and use it in GitHub Desktop.
Save athrawes/e451484fdb4d0646f9aa03c9e47b566a to your computer and use it in GitHub Desktop.
Demonstrates an issue with `\iter\isEmpty()` for objects implementing `\IteratorAggregate` where `getIterator()` is impure
<?php
/**
* Copied directly from https://github.com/nikic/iter/blob/master/src/iter.php#L1090
* on commit d9f88bc
*/
function isEmpty($iterable): bool {
if (\is_array($iterable) || $iterable instanceof \Countable) {
return count($iterable) == 0;
}
if ($iterable instanceof \Iterator) {
return !$iterable->valid();
} else if ($iterable instanceof \IteratorAggregate) {
$inner = $iterable->getIterator();
if ($inner instanceof \Iterator) {
return !$inner->valid();
} else if ($inner !== $iterable) {
return isEmpty($inner);
} else {
throw new \InvalidArgumentException(
'Argument must not be recursively defined');
}
} else {
throw new \InvalidArgumentException(
'Argument must be iterable or implement Countable');
}
}
class MyIter implements \IteratorAggregate
{
/**
* Simulates some stateful generator which cannot be safely restarted
* or rewound
*/
private array $inner;
public function __construct() {
$this->inner = [1, 2, 3, 4, 5];
}
public function getIterator(): \Traversable
{
while (!empty($this->inner)) {
yield array_shift($this->inner);
}
}
}
$gen = new MyIter();
echo "gen isEmpty? " . (isEmpty($gen) ? "yes" : "no") . "\n";
// prints: no
foreach ($gen as $value) {
echo "got $value\n";
}
// prints:
//
// > got 2
// > got 3
// > got 4
// > got 5
//
// instead of expected
//
// > got 1
// > got 2
// > got 3
// > got 4
// > got 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment