Iterator Pattern in PHP | Using Design Patterns in 2023

Last Updated on

CraftyTechie is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

Using the Iterator Design Pattern in PHP

The iterator design pattern in PHP is a behavioral design pattern and as its name suggests it deals with the traversal of aggregates or more specifically traversable data structures like arrays, linked lists, hashmaps, or trees. This article explores a real life example where the iterator patterns shine.

Iterator Pattern in PHP
Iterator Pattern in PHP

Don’t worry if it seems intimidating for now. You will definitely love the idea of this design pattern as we go through the real life example up next.

Definition | Iterator Design Pattern in PHP

“Iterator pattern is a behavioral design pattern that lets us iterate over elements of an aggregate without exposing its underlying representation.”

Iterator Pattern
Iterator Pattern

The objective of this pattern is to encapsulate the underlying data structure and its traversal logic.

FoodNet | A Real-World Example

Let’s say we are working on an application that aggregates eateries’ menus so that customers can access them in our application rather than visiting their applications and websites. That’s a simple but excellent idea, especially if someone’s new to a town and doesn’t know much about what eateries are around.

CafeInn & LuxDine has signed up!

So, our first partners, CafeInn and LuxDine, offer breakfast and dinner menus, respectively.

Menu
Menu

Looks impressive, and another sigh of relief is that they use a similar implementation for every menu item.

Iterator Pattern in PHP
MenuItem Class

But things get bumpy when it comes to their menu implementation. CafeInn uses a LinkedList, and LuxDine uses an array. Neither is interested in reimplementing their menu, though.

The CafeInn menu

CafeInn uses a  linked list in its menu implementation.

CafeInn Menu
CafeInn Menu

The LuxDine menu

LuxDine uses an array in its implementation. Their fixed array length of six means they can only have six menu items. That’s inflexible, but it is what it is, they have been using this implementation, and we can’t change it.

Iterator Pattern in PHP
LuxDine Menu

Problem 🙁

So what’s wrong with having these different implementations? We can use these classes in our Client module to print their menus. That’s going to be simple, right? We can get the menu items using getMenuItems(), right?

Yes, we can definitely go about it, and that will work but thinking from a design perspective, it will be inflexible and non-maintainable. We’re planning to expand to more eateries, and the application will expand rapidly. So, will our application facilitate this change process? Let’s analyze the Client code and then point out why it may not be the right thing to do.

Client Class
Client Class

Programming to concrete implementations

So, an issue or an obstacle to change is that we depend on concrete classes or data structures, whichever way you see it, but the pain point is programming to implementations. A rule of thumb for a flexible and expandable design is programming to interface, not implementation.

And the Client module is a good example of why this is a problem. We are using two loops, two different methods, to do the same set of operations essentially: 

1. Traverse the data structure

2. Print the menu item’s name and price.

And that’s because we are trying to consume the concrete implementations. Each of the concrete classes returns its internally used data structure, and the difference in how both are traversed is why we have two methods for doing the same operation.

Had we been programming to an interface that hides all this detail, we won’t have had the need to create two methods to print these menu items.

Redundant code

So, as we have already seen, two methods, two pieces of code with almost the same theme, that’s unnecessarily repeating yourself, and it is not considered a good practice. What if we add a new restaurant that uses a Hashmap for menu items? We will add another method and another loop because Hashmap is different from either data structure used.

And this will always continue as long as we add more and more menus with different data structures. That fiercely increases redundancy, compromises code quality, and ultimately creates a hassle when the change requests kick in.

Open-closed principle

A module should be closed to modification and open to extension and that’s less likely to occur if the modules are tightly coupled. We need sufficient abstractions or interfaces to encapsulate the low-level implementations and let the interface carry out the high-level job.

And that’s lacking in our design currently. Suppose we want to add another eatery, we have already seen that we will be adding a lot of similar code in the Client module. Similarly, if a partner eatery backs off then we need to go to the Client code and make deletions manually.

Alot of manual work and a lot of changes to the existing code is akin to opening all sorts of ways for bugs to creep in and that’s a maintenance nightmare and a sign that the design is absurdly inflexible.

Low cohesion

Low cohesion means that a class is doing alot of general work or more than what it is supposed to be. It is generally recommended to follow the Single Responsibility Principle to increase cohesion. The Client module in our case is supposed to print menus without having to worry about how to iterate a data structure. 

But see what other things it does. It is exposed to concrete data structures and it has to take care of the implementation level details. This traversal or iteration responsibility is somewhat not a concern of the Client module.

Solution: Use the Iterator Software Design Pattern 🙂

Encapsulate what changes, a golden principle to remember. So, what is varying? Data structures, right? But what changes when data structure changes? Think about it.

Traversal or Iteration, right? LinkedList and array have different interfaces and methods for iterations.

Iterator Pattern in PHP
Iterating a linked list & array

Encapsulate iterations

So, what varies? The answer is iterations or more precisely the way we iterate and which falls back to data structures. Anyways, let’s leave all these details where it belongs and come up with something that hides these so that the Client is no more concerned with what data structures are used or how to iterate them. 

Client module should use the Iterator to print menus regardless of knowing or depending on their internals.

The Iterator
The Iterator

Iterator Interface

So, we have agreed upon the idea of having an Iterator to encapsulate the traversal implementations, owing to the type of data structures. 

Iterator Pattern in PHP
Iterator Class

Concrete iterators

Now, the concrete iterators would be CafeInnMenuIterator and LuxDineMenuIterator

Concrete Iterator Classes
Concrete Iterator Classes

Each iterator implements hasNext() and next() according to their data structures and the Iterator hides these because the Client module will now polymorphically use the Iterator class, meaning that it will call these two methods without worrying about the concrete classes.

LuxDineMenuIterator

Let’s peek through one of the concrete iterators: LuxDineMenuIterator.

Iterator Pattern in PHP
LuxDineMenuIterator Class

Modifying the Menus with the iterator software pattern

The next step is to return the iterator instead of an array and linked list from the LuxDIneMenu and CafeInnMenu classes. Let’s have a look at LuxDineMenu class as we have already seen its iterator code.

Iterator Pattern in PHP
LuxDineMenu Class with createIterator()

Fixing up the Client module

We have come so far to add flexibility to our design and one final piece of the puzzle is to rework the Client module which was tightly bound to the concrete classes and its internals. So, let’s have a look at that. 

Client Class
Client Class

That’s awesome! The Client now doesn’t care if there is an array, linked list, or a hashmap. It can use an Iterator to get the job done. 

On top of that, we can also add a Menu interface for both menus to add more abstraction to the Client code so it can use Menu classes instead of concrete LuxDineMenu and CafeInnMenu classes.

Benefits of the Iterator Design Pattern

  • The Client module is loosely coupled with the menu classes.
  • We can polymorphically handle the iterations of the menu data structures.
  • The application is flexible, open to extension, and closed to modification. We can add more menus and the application will work fine as long as the interfaces are in place.
  • The Client module is more cohesive and thus easier to debug and maintain.

Complete Architecture | Iterator Design Pattern in PHP

Iterator Pattern in PHP
Iterator Pattern in PHP

Iterator Design Pattern in PHP

Object Oriented OO designs are more obvious in strongly types languages like Java and C++. Besides, arrays in PHP are sort of hybrid because they can behave like a linked list and a hash map and may be seen as an array (Though not fixed length).

However, for demo purposes, we will use PHP array and SplFIxedArray to reproduce the example using the iterator design pattern in PHP.

Code | Iterator Design Pattern in PHP

<?php
class MenuItem {
 
   private $name;
   private $price;
 
   function __construct($name, $price) {
       $this->name  = $name;
       $this->price = $price;
   }
 
   function getName() {
       return $this->name;
   }
 
   function getPrice() {
       return $this->price;
   }
}
 
interface IIterator {
 
   function hasNext();
   function next();
}
 
class CafeInnMenuIterator implements IIterator {
 
   private $menuItems;
 
   function __construct($menuItems) {
       $this->menuItems = $menuItems;
   }
 
   function next() {
       return array_pop($this->menuItems);
   }
   function hasNext() {
       if(count($this->menuItems) <= 0) return false;
       return true;
   }
}
 
 
class LuxDineMenuIterator implements IIterator {
 
   private $menuItems;
   private $offset = 0;
 
   function __construct($menuItems) {
       $this->menuItems = $menuItems;
   }
 
   function next() {
       $item = $this->menuItems[$this->offset];
       $this->offset ++;
       return $item;
   }
   function hasNext() {
       if($this->offset >= $this->menuItems->count()) return false;
       return true;
   }
}
 
interface Menu {
   function createIterator();
}
 
class CafeInnMenu implements Menu {
 
   private $menuItems;
 
   function __construct() {
       $this->menuItems = [];
 
       array_push($this->menuItems, new MenuItem('Esspersso', 5));
       array_push($this->menuItems, new MenuItem('Hot Mocha', 6));
       array_push($this->menuItems, new MenuItem('Cappuccino', 4.5));
       array_push($this->menuItems, new MenuItem('Signature Sandwich', 8));
       array_push($this->menuItems, new MenuItem('Special Salad', 5));
   }
 
   function createIterator() {
       return new CafeInnMenuIterator($this->menuItems);
   }
}
 
class LuxDineMenu implements Menu {
 
   private $MAX_SIZE = 6;
   private $menuItems;
 
   function __construct() {
       $this->menuItems = new SplFixedArray($this->MAX_SIZE);
 
       $this->menuItems[0] = new MenuItem('Turkey Wrap', 6.5);
       $this->menuItems[1] = new MenuItem('The Original Burger', 8);
       $this->menuItems[2] = new MenuItem('Smoked Fish', 25);
       $this->menuItems[3] = new MenuItem('Sour Chicken', 25);
       $this->menuItems[4] = new MenuItem('Nachos', 3.5);
       $this->menuItems[5] = new MenuItem('Huge Steak', 15);
      
   }
 
   function createIterator() {
       return new LuxDineMenuIterator($this->menuItems);
   }
}
 
class Client {
  
   private $luxDineMenu;
   private $cafeInnMenu;
 
   function __construct($luxDineMenu, $cafeInnMenu) {
       $this->luxDineMenu = $luxDineMenu;
       $this->cafeInnMenu = $cafeInnMenu;
   }
 
   function printMenu($iterator) {
 
       while($iterator->hasNext()) {
           $item = $iterator->next();
           echo('Item Name: '.$item->getName().PHP_EOL);
           echo('Item Price: '.'$'.$item->getPrice().PHP_EOL);
       }
   }
 
   function printFullMenu() {
 
       $cafeInnMenuIterator = $this->cafeInnMenu->createIterator();
       $luxDineMenuIterator = $this->luxDineMenu->createIterator();
      
       echo('Breakfast Menu'.PHP_EOL);
       $this->printMenu($cafeInnMenuIterator);
       echo('Dinner Menu'.PHP_EOL);
       $this->printMenu($luxDineMenuIterator);
 
   }
}
 
function main() {
 
   $client = new Client(new LuxDineMenu(), new CafeInnMenu());
 
   $client->printFullMenu();
}
 
main();
?>

Output | Iterator Design Pattern in PHP

Breakfast Menu
Item Name: Special Salad
Item Price: $5
Item Name: Signature Sandwich
Item Price: $8
Item Name: Cappuccino
Item Price: $4.5
Item Name: Hot Mocha
Item Price: $6
Item Name: Esspersso
Item Price: $5
Dinner Menu
Item Name: Turkey Wrap
Item Price: $6.5
Item Name: The Original Burger
Item Price: $8
Item Name: Smoked Fish
Item Price: $25
Item Name: Sour Chicken
Item Price: $25
Item Name: Nachos
Item Price: $3.5
Item Name: Huge Steak
Item Price: $15

Pros and Cons of the Iterator Pattern in PHP

ProsCons
Satisfies the open-closed principleCan be overkill if the application uses a simple data structure
Increases Client module’s cohesionIntroduces a bunch of classes just to encapsulate the iterations
Hides implementational details and lets the client traverse the aggregate without worrying about its concrete type
You can add complex traversal logic to the Iterator

Where is the Iterator Pattern Used?

  • When you have varying data structures under the hood and you want to encapsulate them.
  • Use this pattern to reduce code redundancy related to aggregate traversal.
  • Use this pattern to make your code adaptive to dynamically assigned iterables.

Frequently Asked Questions on the Iterator Design Pattern

What is the iterator pattern?

The iterator design pattern is a behavioral design pattern that lets us iterate over elements of an aggregate without exposing its underlying representation.

What does the iterator pattern do?

It helps in encapsulating details about the type of data structure (iterable) and its traversal logic by abstracting the traversal logic in an Iterator class.

What is an iterator in PHP?

PHP Iterator is an interface in PHP that implements Traversable and it is based on the same idea of an Iterator class as we have seen in this article. It is PHP native interface and we can implement it. This Iterator class is useful and helps us avoid reinventing the wheel by declaring our custom Iterator interface.

Using the Iterator Pattern in PHP Today

Phew! That was a lot of ground to cover. Here’s a recap of what we have seen in the iterator pattern. This pattern is a behavioral pattern that tends to provide abstraction when there are many different traversable data structures.

A prime example is an application that we have included as an example in this article. The application integrates two menus of a cafe and a restaurant. Both these use different data structures for their menu items. The cafe used a linked list and the restaurant used an array.

The problem with this discrepancy was that the Client module was strictly bound to the concrete implementations and representations of these concrete classes and their traversable aggregates. 

Part of the solution was to encapsulate the traversal logic and allow the Client to use an interface polymorphically so that it is independent of what type of data structure is being used behind the scenes.

To implement this solution, we used an Iterator interface with two concrete implementations, one for each class. These iterators dealt with the traversal logic and exposed a couple of methods for its consumer class.

As a result, the Client class used Iterator now instead of implementing traversal logic for every concrete traversable. After applying the pattern, we were able to make the application more flexible and expandable while removing code redundancy and coupling.

So, that’s all about it. We hope you are now well aware of this design pattern and can implement it whenever possible.

See you in another design pattern article. Stay tuned at FuelingPHP.

Books on Design Patterns

Want to learn more about Design Patterns? There are many great resources online. We recommend the following books for your collection as they both can teach you the theoretical and the application of using design patterns in your day-to-day programming. Feel free to use the following Amazon affiliate links if you’d like to purchase them and a way to support our efforts.

Design Patterns: Elements of Reusable Object-Oriented Software

Design Patterns: Elements of Reusable Object-Oriented Software book

This is the book that started it all. I believe that every programmer should have a referenced copy to this book at some point in their career. There have been many updates and excellent newer content through the years, but this is a classic. It still stands the test of time and is just as relevant for today as it was in the original printing in the 90s.

Check it out on amazon

Learning PHP Design Patterns

This is an excellent book to go beyond the theory and apply it to writing good PHP code. Learning PHP Design Patterns is published by the popular O’Reilly media company. O’Reilly consistently publishes some of the most useful reference material related to software development. They are known to provide materials that thoroughly cover a topic in a way that is simple to understand. I recommend this book to every PHP developer.

Check it out on amazon

Want to see our full review of books on design patterns? Read our huge review article on over 15 design pattern books.

Design Patterns in PHP Learning Series.

This article is part of our series of design patterns in PHP. We are going through all of the patterns and showing how they can help you build better applications. Browse through our full list of patterns below.

Did you find this article helpful?

Join the best weekly newsletter where I deliver content on building better web applications. I curate the best tips, strategies, news & resources to help you develop highly-scalable and results-driven applications.

Build Better Web Apps

I hope you're enjoying this article.

Get the best content on building better web apps delivered to you.