Design Pattern: Iterator Pattern in PHP

Iterator pattern is used to access items sequentially from an aggregated object. The client does not need to know the underlying details of the iterator, it can just use the iterator easily.

This pattern makes it very easy for the client to traverse through a list of objects, one by one. It can also implement the traversing process in any order (ascending or descending) if required.

This article demonstrates the Iterator pattern implementation in PHP. Check the implementation details and the examples.

Implementation

Here we are discussing the process of iterator pattern implementation. Let’s explore the Iterator step-by-step.

Method #1: Simple Implementation

This method is for demonstration and understanding purposes. In the actual implementation, we will use the predefined iterator interfaces, which is explained in the next method (Method #2).

Follow the steps below to implement Iterator pattern-

  • Define an interface for the iterator. Declare method for checking if items are left, and for getting next item.
  • Create Iterator class and implement the iterator interface. In the constructor accept the list of items.
  • Create a class for handling the list of items. This is the aggregation handler.

Here is a simple example of the implementation-

<?php
// Iterator pattern in PHP

interface IteratorI {
    function hasNext(): bool;
    function next(): mixed;
}

class MyIterator implements IteratorI {
    private int $index = 0;
    private array $items;

    public function __construct(array $items) {
        $this->items = $items;
    }

    public function hasNext(): bool {
        return $this->index < count($this->items);
    }

    public function next(): mixed {
        if ($this->index >= count($this->items)) {
            throw new Exception('No more item');
        }

        $item = $this->items[$this->index];
        $this->index++;

        return $item;
    }
}

class Data {
    private array $items;

    public function __construct(array $items) {
        $this->items = $items;
    }

    public function getIterator(): IteratorI {
        return new MyIterator($this->items);
    }
}

// Demo
$items = [
    'Item One',
    'Item Two',
    'Item Three',
    'Item Four',
    'Item Five',
];

$data = new Data($items);
$iterator = $data->getIterator();

while ($iterator->hasNext()) {
    $currentItem = $iterator->next();

    echo $currentItem . "\n";
}

Output will be as below-

Item One
Item Two
Item Three
Item Four
Item Five

Method #2: Using Built-in Interfaces [Recommended]

We will be using 2 interfaces that are built-in into the PHP implementation. The interfaces are “Iterator” and “IteratorAggregator“. This way we don’t have to define the interface ourselves. As we are using the standard interfaces, other clients can easily identify and use it.

Let’s check the definition of the built-in interfaces first-

// Iterator interface
interface Iterator extends Traversable {
    
    // Returns the current item
    public function current(): mixed;

    // Returns the index of current item
    public function key(): mixed;

    /// Moves the index to the next 
    public function next(): void;

    // Reset the index to the first/begining 
    public function rewind(): void;

    // Check if there is a valid item for the current index
    public function valid(): bool;

}

// Interface for handling an aggregation of iterator
interface IteratorAggregate extends Traversable {

    // Generate and return an iterator
    public function getIterator(): Traversable;

}

Follow the steps below for Iterator pattern implementation using the PHP built-in interfaces-

  • Create an iterator class. Implement interface “Iterator” for the class.
  • Create a class for handling an aggregation of iterators. Implement interface “IteratorAggregate”. Define methods for adding and/or remove items from the list if required.
  • In the client create an object of the aggregration class. Then add some items. Then we can traverse through the items in a loop.

Here is a simple example of the implementation-

<?php
// Iterator pattern implementation in PHP
// with builtin PHP iterator interfaces

class MyIterator implements \Iterator {

    private array $items;
    private int $index = 0;

    public function __construct(array $items) {
        $this->items = $items;
    }

    // Get current item
    function current(): mixed {
        return $this->items[$this->index];
    }

    // Get current index
    function key(): int {
        return $this->index;
    }

	// Move to the next index
	function next(): void {
        $this->index++;
    }

	// Reset index to the first item
	function rewind(): void {
        $this->index = 0;
    }

	// Check if the current index exists or not
	function valid(): bool {
        return isset($this->items[$this->index]);
    }
}

class MyAggregator implements \IteratorAggregate {
    
    private array $items = [];

    // Create a new iterator and return that
	function getIterator(): Iterator {
        return new MyIterator($this->items);
    }

    // Utility method to add item
    // Not required for the IteratorAggregate interface implementation
    public function addItem(mixed $item): void {
        array_push($this->items, $item);
    }

}

// Demo
$iteratorHandler = new MyAggregator();
$iteratorHandler->addItem("Item One");
$iteratorHandler->addItem("Item Two");
$iteratorHandler->addItem("Item Three");
$iteratorHandler->addItem("Item Four");
$iteratorHandler->addItem("Item Five");

foreach ($iteratorHandler->getIterator() as $item) {
    echo $item . "\n";
}

Output will be as below-

Item One
Item Two
Item Three
Item Four
Item Five

Examples

Here are a few examples of Iterator pattern implementation in PHP.

Example #1: Pagination Iterator

Here we are implementing the Iterator pattern to generate and print the pagination pages.

Page Item Class [General]

This is a general class to represent a pagination page. This class is not directly related to the Iterator pattern implementation, but it is used to just represent a page item of the pagination.

  • Create file “Page.php”.
  • Define “Page” class. Define properties for page number, path, etc. Also define the getter and setter for the properties.
<?php
// Page.php

namespace BigBoxCode\DesignPattern\Iterator\Pagination;

class Page {
    private int $pageNumber = 0;
    private ?string $path = null;

    public function getNumber(): int {
        return $this->pageNumber;
    }

    public function setNumber(int $pageNumber): void {
        $this->pageNumber = $pageNumber;
    }

    public function getPath(): string {
        if ($this->path == null) {
            return "/page/" . $this->pageNumber;
        }

        return $this->path;
    }

    public function setPath(string $path): void {
        $this->path = $path;
    }
}

Page Iterator Class

  • Create file “Iterator.php”.
  • Define class “PageIterator”.
  • Implement interface “Iterator” for the class. Define methods “current”, “key”, “next”, “rewind”, “valid” for the interface implementation. There can be other methods for utility and other purposes.
  • Define private properties “$currentPosition” for holding the current position number, and “$pages” for storing the list of the pages.
<?php
// PageIterator.php

namespace BigBoxCode\DesignPattern\Iterator\Pagination;


class PageIterator implements \Iterator {
    private int $currentPosition = 0;

    public function __construct(private array $pages) {
    }

    // Get current page
    function current(): mixed {
        return $this->pages[$this->currentPosition];
    }

    // Get current index
    function key(): int {
        return $this->currentPosition;
    }

    // Move to the next index
    function next(): void {
        $this->currentPosition++;
    }

    // Reset index to the first page
    function rewind(): void {
        $this->currentPosition = 0;
    }

    // Check if the current index exists or not
    function valid(): bool {
        return isset($this->pages[$this->currentPosition]);
    }
}

Page List/Aggregator Class

  • Create file “PageAggregator.php”.
  • Define class “PageAggregator”.
  • Implement interface “IteratorAggregate” for the class. Define method “getIterator” as part of the interface implementation. The “getIterator” method generates a new “PageIterator” object and returns that.
  • Define some utility methods for add and/or removing items from the page list.
<?php
// PageAggregator.php

namespace BigBoxCode\DesignPattern\Iterator\Pagination;


class PageAggregator implements \IteratorAggregate {
    private array $pages = [];

    public function add(Page $page): void {
        $this->pages[$page->getNumber()] = $page;
    }

    public function remove(Page $page): void {
        unset($this->pages[$page->getNumber()]);
    }

    public function getIterator(): \Iterator {
        return new PageIterator($this->pages);
    }
}

Demo

Here we have a utility method “populatePageList” to generate a list of pages. This method is not directly related to the Iterator pattern, but is just a dummy utility method for the demo purpose.

In the client, we have to generate an object of “PageAggregator” and then add items to it.

After that, we can get the iterator using the “getIterator” method. Then we can use the iterator in a loop and travers through the full list of pages.

<?php
// demo.php

require __DIR__ . '/../../vendor/autoload.php';

use BigBoxCode\DesignPattern\Iterator\Pagination\Page;
use BigBoxCode\DesignPattern\Iterator\Pagination\PageAggregator;

// Generate dummy list of pages for demo purpose.
// Not required for the the pattern implementation.
// This is used for testing the implementation.
function populatePageList(): \IteratorAggregate {
    $pageList = new PageAggregator();

    for ($i = 0; $i < 10; $i++) {
        $page = new Page();
        $page->setNumber($i);

        $pageList->add($page);
    }

    return $pageList;
}

// Demo start
$pageList = populatePageList();
$pageIterator = $pageList->getIterator();

echo "Page list:\n\n";

foreach ($pageIterator as $page) {
    echo "Page Number: " . $page->getNumber() . "\n";
    echo "Page Path: " . $page->getPath() . "\n";
}


// Rewind and print pag data again
$pageIterator->rewind();

echo "\n\nPage list after rewinding:\n\n";

while ($pageIterator->valid()) {
    echo "Pagination key: " . $pageIterator->key() . "\n";
    echo "Page Number: " . $pageIterator->current()->getNumber() . "\n";
    echo "Page Path: " . $pageIterator->current()->getPath() . "\n";

    $pageIterator->next();
}

Output

The following output will be generated-

Page list:

Page Number: 0
Page Path: /page/0

Page Number: 1
Page Path: /page/1

Page Number: 2
Page Path: /page/2

Page Number: 3
Page Path: /page/3

Page Number: 4
Page Path: /page/4

Page Number: 5
Page Path: /page/5

Page Number: 6
Page Path: /page/6

Page Number: 7
Page Path: /page/7

Page Number: 8
Page Path: /page/8

Page Number: 9
Page Path: /page/9



Page list after rewinding:

Pagination key: 0
Page Number: 0
Page Path: /page/0

Pagination key: 1
Page Number: 1
Page Path: /page/1

Pagination key: 2
Page Number: 2
Page Path: /page/2
Pagination key: 3
Page Number: 3
Page Path: /page/3

Pagination key: 4
Page Number: 4
Page Path: /page/4

Pagination key: 5
Page Number: 5
Page Path: /page/5

Pagination key: 6
Page Number: 6
Page Path: /page/6

Pagination key: 7
Page Number: 7
Page Path: /page/7

Pagination key: 8
Page Number: 8
Page Path: /page/8

Pagination key: 9
Page Number: 9
Page Path: /page/9

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check the Iterator pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.