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:

MethodRole
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";
}
Why this is worth it: nothing outside 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.

Up next: Best Practices wraps up the entire track with a practical checklist for choosing the right pattern in real projects.