Iterable objects are a powerful tool that can help you manage your data more efficiently. In this article, we will show you how to create iterable objects in PHP. First, let’s take a look at the basics of iterable objects: An iterable object is an object that can be used to return a sequence of values. It can be created using the iterator() function and passed in a list or array as its first argument. The second argument is the type of iterator to use, which can be either a for() or an exec() function. Once you have created an iterable object, it is easy to use it: You can use the iterator() function to return values from the object: $iter = new Iterator(); $iter->next(); // returns the next value in the sequence $iter->hasNext(); // returns true if there is a next value in the sequence $iter->previous(); // returns false if there are no previous values in the sequence $iter->size(); // returns the number of values in the sequence. You can also use it to access individual values from within the object: $iterator = new Iterator(array( ’name’ => ‘John’, ‘age’ => 25 )); $iterator->name; // Returns “John” $iterator->age; // Returns 25
PHP allows you to create iterable objects. These can be used within loops instead of scalar arrays. Iterables are commonly used as object collections. They allow you to typehint that object while retaining support for looping.
Simple Iteration
To iterate over an array in PHP, you use a foreach loop:
This example would emit 1 2 3.
You can also iterate over an object:
This example would emit bar.
The Collection Problem
For basic classes with public properties, a plain foreach works well. Let’s now consider another class:
This class represents a collection of UserDomain instances. As PHP doesn’t support typed arrays, classes like this are necessary when you want to typehint an array which can only hold one type of value. Collections also help you create utility methods, like containsWithAdmin, that facilitate natural interaction with array items.
Unfortunately, trying to iterate over this collection won’t give the desired results. Ideally, iterating should operate on the $items array, not the class itself.
Implementing Iterator
Natural iteration can be added using the Iterator interface. By implementing Iterator, you can define PHP’s behaviour when instances of your class are used with foreach.
Iterator has five methods which you’ll need to implement:
current() : mixed – Get the item at the current position in the iteration. key() : scalar – Get the key at the current position in the iteration. next() : void – Move to the next position in the iteration. rewind() : void – Rewind the position to the start of the iteration. valid() : bool – Get whether the current position has a value.
These methods might be confusing at first. Implementing them is straightforward though – you’re specifying what to do at each stage of a foreach execution.
Each time your object is used with foreach, rewind() will be called. The valid() method is called next, informing PHP whether there’s a value at the current position. If there is, current() and key() are called to get the value and key at that position. Finally, the next() method is called to advance the position pointer. The loop returns to calling valid() to see if there’s another item available.
Here’s a typical implementation of Iterator:
Here’s what would happen when iterating DemoIterator:
Your iterator needs to maintain a record of the loop position, check whether there’s an element at the current loop position (via valid()) and return the key and value at the current position.
PHP won’t try to access the key or value when the loop position is invalid. Returning false from valid() immediately terminates the foreach loop. Typically, this will be when you get to the end of the array.
IteratorAggregate
Writing iterators quickly gets repetitive. Most follow the exact recipe shown above. IteratorAggregate is an interface which helps you quickly create iterable objects.
Implement the getIterator() method and return a Traversable (the base interface of Iterator). It will be used as the iterator when your object is used with foreach. This is usually the easiest way to add iteration to a collection class:
When working with collections, three lines of code are usually all you need to setup iteration! An ArrayIterator is returned as the Traversable. This is a class which automatically creates an Iterator out of an array. You can now iterate over the logical values within your object, instead of the object’s direct properties.
Using Prebuilt PHP Iterators
You’ll need to write your own iterators if you have complex logic. It’s rarely necessary to start from scratch as PHP ships with several advanced iterators provided by SPL.
The built-in classes include DirectoryIterator, FilesystemIterator, GlobIterator and various recursive iterators. Here’s some of the most useful generic iterators.
LimitIterator
The LimitIterator lets you iterate over a subset of an array. You don’t need to splice the array first or manually keep track of position inside your foreach.
This example would emit a b. Note that LimitIterator accepts another Iterator, not an array. The example uses ArrayIterator, the built-in class which constructs an Iterator from an array.
InfiniteIterator
InfiniteIterator never terminates the loop, so you’ll need to break from it manually. Otherwise, the iterator automatically wraps back to the start of the array when it reaches the end.
This iterator is particularly useful when working with time-based values. Here’s an easy way of building a three-year calendar, which also uses the LimitIterator described above:
This emits three years’ worth of months.
FilterIterator
FilterIterator is an abstract class which you must extend. Implement the accept method to filter out unwanted values which should be skipped during iteration.
This example would emit 1 4 3.
The array values which are higher than 5 are filtered out and do not appear in the foreach.
The “iterable” type
Sometimes you might write a generic function that uses a foreach loop but knows nothing about the values it’ll be iterating over. One example is an abstract error handler which simply dumps the values it receives.
You can typehint iterable in these scenarios. This is a pseudo-type which will accept either an array or any object implementing Traversable:
The iterable type is so vague that you should think carefully before using it. Nonetheless, it can be useful as a last-resort type if you need to guarantee an input value will work with foreach.
Conclusion
Making use of iterators helps you write clean code that’s more modular. You can move methods that act on arrays of objects into dedicated collection classes. These can then be typehinted individually while remaining fully compatible with foreach.
Most of the time, adding iteration support can be achieved by implementing IteratorAggregate and returning an ArrayIterator configured with the items in your collection. PHP’s other iterator types, which tend to go unused by developers, can greatly simplify more specific loops. They offer complex logical behaviours without requiring manual pointer tracking in your foreach statement.