iterator_count
Counts the elements in an iterator or generator by traversing it to the end.
iterator_count(Traversable|array $iterator): int- Added in PHP 5.1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$iterator | Traversable|array | The iterator, generator or array being counted. |
Return value
intThe number of elements in the iterator.
Errors
Throws a TypeError if the argument is neither Traversable nor (as of PHP 8.2) an array, and an Exception if a Generator that has already been consumed is passed.
Changelog
- PHP 8.2.0The iterator parameter was widened from Traversable to Traversable|array, so plain arrays may now be passed.
iterator_count() has no shortcut: it rewinds the iterator, then walks it to the end and counts the steps. That single fact explains every surprise people hit with it.
What trips people up
It leaves the iterator exhausted. Afterwards the pointer sits past the last element, so a manual while ($it->valid()) loop over the same object sees nothing. A rewindable iterator such as ArrayIterator recovers on its own — a later foreach rewinds it again — and counting a clone leaves the original position untouched. Because the count starts from a rewind, a partially consumed ArrayIterator still reports its full length, not the number of remaining elements. For a generator there is no recovery at all: counting consumes it, any later traversal throws, and passing an already-started generator throws because it cannot be rewound.
It also ignores Countable. An object implementing both Countable and Traversable will have its cheap count() method skipped in favour of a full traversal, so use count($obj) when the object offers it. Since PHP 8.2 a plain array is accepted too, though count() is the correct call there.
When to reach for it
- Counting rows from a
Traversableyou are about to discard anyway, such as a validation pass over a directory listing. - Asserting in tests that a generator produced the expected number of items.
If you need both the count and the values, call iterator_to_array() once and count() the result — that traverses once instead of twice.
Example
When to use it
- Asserting in a test that a generator emitted the expected number of items.
- Counting matching entries from a FilterIterator you are about to throw away.
- Reporting how many records a data source produced when the records themselves are not needed.
Frequently asked questions
What is the difference between count and iterator_count?
count() works on arrays and Countable objects and is O(1) for arrays; iterator_count() walks a Traversable element by element. iterator_count() ignores Countable even when the object implements it.
Does iterator_count reset the iterator?
It rewinds the iterator before counting and leaves it sitting at the end. For a rewindable iterator such as ArrayIterator that is harmless, because a following foreach rewinds it again; count a clone if you need the current position preserved. A generator is a different story: counting consumes it for good, and any later traversal throws.
Can I count a generator without consuming it?
No. Generators cannot be rewound, so counting always consumes them. Call iterator_to_array() once and count the resulting array if you need the values as well.
More examples
Counting a generator consumes it
The count is 3, and iterator_count() consumed the generator to get it. Re-traversing it then throws "Cannot traverse an already closed generator" — it does not silently yield nothing. Calling the generator function again and using iterator_to_array() gives you both the count and the values from a single traversal.
<?php
function rows(): Generator {
yield 'a'; yield 'b'; yield 'c';
}
$g = rows();
echo iterator_count($g), "\n";
// The generator is now finished, and a finished generator cannot be
// re-traversed — it throws rather than yielding nothing.
try {
foreach ($g as $row) { echo $row; }
} catch (Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
$values = iterator_to_array(rows(), false);
echo count($values), ' -> ', implode(',', $values), "\n";Countable objects are not consulted
iterator_count() prints "traversing" because it walks every element, while count() prints "counting" and uses the cheap Countable method. When an object offers count(), prefer it.
<?php
class Bag implements IteratorAggregate, Countable {
private array $items = [1, 2, 3, 4];
public function getIterator(): Iterator { echo "traversing\n"; return new ArrayIterator($this->items); }
public function count(): int { echo "counting\n"; return count($this->items); }
}
$b = new Bag();
echo iterator_count($b), "\n";
echo count($b), "\n";
Discussion