Iterator
The Iterator pattern provides a standard way to walk through the elements of a collection one at a time, without exposing how that collection actually stores its data internally. The consumer of the collection just asks for "the next item" over and over — it never needs to know whether the data lives in an array, a linked list, or something fetched lazily from a database.
PHP's Built-In Iterator Interface
PHP bakes this pattern directly into the language as the Iterator interface. Implement its
five methods on a class, and instances of that class can be dropped straight into a foreach
loop as if they were a plain array:
| Method | Role |
|---|---|
current() | Returns the value at the current internal position |
key() | Returns the key at the current internal position |
next() | Moves the internal position forward by one |
valid() | Returns whether the current position is still within range |
rewind() | Resets the internal position back to the start (called once, before the loop begins) |
A foreach loop over an object implementing Iterator calls these methods in a
fixed sequence: rewind() once, then valid(), current(), and
key() before each iteration's body runs, and next() after each iteration —
repeating until valid() returns false.
A Realistic Worked Example: A Book Collection
Here's a BookCollection class that wraps a private array of books but still supports
foreach, because it implements all five Iterator methods itself:
<?php
class Book {
public function __construct(
public string $title,
public string $author
) {}
}
class BookCollection implements Iterator {
private array $books = [];
private int $position = 0;
public function add(Book $book): void {
$this->books[] = $book;
}
public function current(): Book {
return $this->books[$this->position];
}
public function key(): int {
return $this->position;
}
public function next(): void {
$this->position++;
}
public function valid(): bool {
return isset($this->books[$this->position]);
}
public function rewind(): void {
$this->position = 0;
}
}
// Usage — BookCollection just works in a foreach, no getBooks() needed
$library = new BookCollection();
$library->add(new Book('Clean Code', 'Robert C. Martin'));
$library->add(new Book('Refactoring', 'Martin Fowler'));
foreach ($library as $book) {
echo $book->title . ' by ' . $book->author . "\n";
}
BookCollection ever touches the private
$books array directly. The internal storage could later change to a different data
structure entirely, and every piece of code that does foreach ($library as $book) would
keep working without a single change.
The Simpler Everyday Choice: IteratorAggregate
Writing out all five Iterator methods by hand makes sense when you need full control over
the iteration order or logic. Most of the time, though, a collection just wraps a plain PHP array with
nothing special about how it's walked through — and for that, PHP offers a much shorter path: the
IteratorAggregate interface. It only requires one method, getIterator(),
which returns any object that is itself iterable — commonly PHP's built-in ArrayIterator:
<?php
class BookCollection implements IteratorAggregate {
private array $books = [];
public function add(Book $book): void {
$this->books[] = $book;
}
// One method instead of five — ArrayIterator does all the iteration work
public function getIterator(): ArrayIterator {
return new ArrayIterator($this->books);
}
}
// Usage is identical to the full Iterator version above
foreach ($library as $book) {
echo $book->title;
}
As a rule of thumb: reach for IteratorAggregate first, since it's far less code for the
common case. Only implement Iterator directly when the collection needs custom iteration
logic that ArrayIterator can't provide — skipping items, iterating lazily over a database
cursor, or moving through the collection in a non-sequential order.