Starting with PHP5 almost any PHP installation contained SPL (Standard PHP Library) extension – with few exceptions, when hosters intentionally disabled it. With PHP 5.3 out, this extension is considered to be within PHP core, and as such it’s not possible to disable it anymore. This in fact is a good thing, as SPL really deserves to be the core component.
In an attempt to shed some light on and to draw attention to SPL, I plan to post several articles discussing various parts of this extension. I will start with SPL Interfaces so that you can grasp immediately the usefulness of the SPL.
Comprehending SPL interfaces presupposes that you know standard interfaces that come build-in with PHP5. So, I wrote preliminary article discussing them – I consider it to be a prerequisite for good understanding of the current material.
Covered in this article:
- The Countable Interface
- The OuterIterator Interface
- The SeekableIterator Interface
- The RecursiveIterator Interface
- The SplObserver and SplSubject Interfaces
Countable interface is quite simple: classes implementing it can be used with the count() function.
Countable {
/* Methods */
abstract public int count ( void )
}
As you see, all it takes to comply to interface’s contract is implementation of single method, namely count():
/**
* Dummy "contained" class
*/
class Item {}
/**
* Dummy "container" class implementing Countable
*/
class Items implements Countable
{
protected $items = array();
public function add(Item $item)
{
$this->items[] = $item;
return $this;
}
public function count()
{
return count($this->items);
}
}
$items = new Items();
$items->add(new Item())
->add(new Item())
->add(new Item());
echo count($items); // outputs 3
// you can use sizeof() as well - since it's an alias for count()
echo sizeof($items);
I am not sure why php manual’s respective section doesn’t contain explanation of this interface, my wild guess – doc.team hasn’t time to fill it (hopefully, yet). Anyway, here is what OuterIterator is all about: OuterIterator serves as wrapper for another inner iterator (which is generally passed into constructor of implementing class). It is similar to IteratorAggregate, but while the former is extending Traversable, OuterIterator extends Iterator – so implementing class not only contains accessor method for contained iterator, but could be iterated itself as well
I know, how it sounds, no need to panic though – everything would be clear in a moment. Let’s first look into synopsis:
OuterIterator extends Iterator {
/* Methods */
abstract public Iterator getInnerIterator();
/* Inherited methods */
abstract public mixed current( void )
abstract public scalar key( void )
abstract public void next( void )
abstract public void rewind( void )
abstract public boolean valid( void )
}
So, implementing object must be traversable and contain reference to some inner iterator. There’s great pre-build FilterIterator, which is an abstract class, allowing to create filter iterators (iterators that filter out some unwanted values). FilterIterator implements OuterIterator – you pass the iterator to be filtered, and then you can traverse the FilterIterator for filtered values. Let’s build our own version of FilterIterator:
class OddsFilter implements OuterIterator
{
protected $innerIterator;
public function __construct($it)
{
$this->innerIterator = $it;
}
/**
* whether the current element of the iterator is acceptable
*/
public function accept()
{
return (bool)($this->current() % 2); // false on evens, true on odds
}
/**
* OuterIterator Methods
*/
public function getInnerIterator()
{
return $this->innerIterator;
}
/**
* Inherited Methods
*/
public function current()
{
return $this->innerIterator->current();
}
public function key()
{
return $this->innerIterator->key();
}
public function next()
{
$this->innerIterator->next();
$this->fetch();
}
public function rewind()
{
$this->innerIterator->rewind();
$this->fetch();
}
public function valid()
{
return $this->innerIterator->valid();
}
/**
* Move forward until the condition defined in accept() is met
*/
protected function fetch()
{
while($this->innerIterator->valid()) {
if($this->accept()) {
return;
}
$this->innerIterator->next();
}
}
}
$iterator = new ArrayIterator(array(1, 2, 3, 4, 5));
$filteredIterator = new OddsFilter($iterator);
print_r(iterator_to_array($filteredIterator));
Time to take a closer look at code. OddsFilter is an iterator that filters out even numbers and returns odd numbers only. It accepts instance of Iterator in its constructor, here we used ArrayIterator (very simple iterator, allowing us to traverse arrays and objects). So, on line 70, we created $iterator variable from sample array, and passed this iterator into OddsFilter. OddsFilter mainly delegates calls to contained/inner iterator with a prominent exception of next() and rewind() methods, they call additional method fetch():
protected function fetch()
{
while($this->innerIterator->valid()) {
if($this->accept()) {
return;
}
$this->innerIterator->next();
}
}
Here, again, we eventually call inner iterator’s next() method, but we might call it several times, untill acceptable value is found.
You might wonder what iterator_to_array() function does, simple – it copies iterator into array, so that resultant output of our code would be:
Array
(
[0] => 1
[2] => 3
[4] => 5
)
Of course, instead of using iterator_to_array(), I could have also traversed $filteredIterator with foreach loop – after all OddsFilter is an iterator itself.
To sum up, OuterIterator is helpful when we have some input iterator (which serves as inner iterator), that should be somehow manipulated (filtered in our case), and results should also be traversable (which is true, as OuterIterator implements Iterator interface itself).
Please note that extending FilterIterator class is far superior way of building filters – all you need is to implement abstract accept() method to get very same results (all traversal logic is done within FilterIterator itself).
Note: Thanks to P18X for spotting the bug in original code – fixed now.
The SeekableIterator Interface
This interface is build on Iterator and adds support for quick navigating (seeking) of a certain positions. Everything is done via seek() method added with this interface:
SeekableIterator extends Iterator {
/* Methods */
abstract public void seek( int $position )
/* Inherited methods */
abstract public mixed Iterator::current( void )
abstract public scalar Iterator::key( void )
abstract public void Iterator::next( void )
abstract public void Iterator::rewind( void )
abstract public boolean Iterator::valid( void )
}
Within seek() method internal iterator position should be set to requested value, if position not found or applicable OutOfBoundsException should be thrown.
Here is sample iterator implementing the SeekableIterator:
class Item
{
protected $title = null;
public function __construct($title)
{
$this->title = $title;
}
public function __toString()
{
return $this->title;
}
}
class Items implements SeekableIterator
{
/**
* List of contained items
* @var array
*/
protected $items = array();
/**
* Current iterator position
* @var int
*/
protected $pos = 0;
public function add(Item $item)
{
$this->items[] = $item;
return $this;
}
/**
* SeekableIterator methods
*/
public function seek($pos)
{
$this->pos = $pos;
if(!$this->valid()) { // position is not seekable!
throw new OutOfBoundsException('Item at position ' . $pos . ' not found..');
}
}
/**
* Iterator methods
*/
public function current()
{
return $this->items[$this->pos];
}
public function key()
{
return $this->pos;
}
public function next()
{
$this->pos++;
}
public function rewind()
{
$this->pos = 0;
}
public function valid()
{
return isset($this->items[$this->pos]);
}
}
$items = new Items();
$items->add(new Item('milk'))
->add(new Item('butter'))
->add(new Item('bread'));
try {
$items->seek(2);
printf('%d - %s ', $items->key(), $items->current()); // 2 - bread
$items->seek(3); // OutOfBoundsException should be thrown
// Item at position 3 not found..
} catch (OutOfBoundsException $e) {
die($e->getMessage());
}
As you can see, I based this example on Iterator’s example from preliminary article. Indeed, in order to make our iterator more flexible (next() method is cool but locating items in any order other than sequential is impossible), all we have to do is to add seek() method that does the job.
The RecursiveIterator Interface
Before we get into details, let’s give somewhat formal definition of interface.
RecursiveIterator extends Iterator interface, so it is an iterator itself. What is special about this iterator is the fact that items it contains can be also traversed i.e. we can create iterators for them. Consider multi-dimensional array:
$numbers = array(
array(1, 2),
array(3, 4, 5, array(6, 7)),
8, 9
);
This array has 4 elements in total. Whilst first two are arrays, last two (8, 9) are scalars. Imagine that we wanted to print all numbers in that $numbers array – obviously the recursive function is the most elegant solution (which allows us to have as much depth as necessary). Here, we can traverse first two elements of the array, as they are arrays themselves, even more – the second element has not only scalars in it but yet another array to be traversed:
array(3, 4, 5, array(6, 7))
RecursiveIterator interface was designed to solve exactly this kinds of problems. If you have a complex structure with multiple levels of data, this interface helps you to recursively traverse all contained elements. Thus in PHP manual definition of this interface goes as following:
Classes implementing RecursiveIterator can be used to iterate over iterators.
Indeed, if any contained entry can be traversed (i.e. we can attach iterator to it), RecursiveIterator provides an interface to do so.
Here is the synopsis:
RecursiveIterator extends Iterator {
// Returns an iterator for the current entry
public RecursiveIterator getChildren( void )
// whether an iterator could be created for the current entry
public bool hasChildren( void )
/* Inherited methods */
abstract public mixed Iterator::current ( void )
abstract public scalar Iterator::key ( void )
abstract public void Iterator::next ( void )
abstract public void Iterator::rewind ( void )
abstract public boolean Iterator::valid ( void )
}
As you see two new methods has been added.
Please note, these methods apply to the current entry, not the base iterator itself. So, hasChildren() should check whether current entry is an array or object, and as such is traversable, thus allowing to create iterator for it. By the same token, getChildren() returns iterator for the current entry’s contained items. Somewhat obvious, but when I first read the documentation, it took me some time to figure this out.
In order to iterate over children items, we have to be sure that entry is traversable – hasChildren() method is exactly for this. Once we know, that current entry is traversable, we should be able to obtain iterator to do so – getChildren() returns such an iterator (its type must be RecursiveIterator – what we are dealing with, on all levels of recursion, is always one type of interface).
It’s time to show you some code! First of all NumberIterator I have written to “flatten” arrays, so that despite the dimensions, I can have a list of all elements:
class NumbersIterator implements RecursiveIterator
{
/**
* List of numbers. Array may be multi-dimensional.
* @var array
*/
public $numbers = array();
/**
* Current iterator's position
* @var int
*/
protected $pos = 0;
/**
* Initiates iterator
* @param array $numbers
*/
public function __construct($numbers = array())
{
$this->numbers = $numbers;
}
/**
* RecursiveIterator methods
*/
/**
* Returns current entry's iterator
* @return NumbersIterator
*/
public function getChildren()
{
return new NumbersIterator($this->current());
}
/**
* Returns true if iterator could be obtained for the current entry.
* @return boolean
*/
public function hasChildren()
{
return is_array($this->current());
}
/**
* Iterator methods
*/
public function current()
{
return $this->numbers[$this->pos];
}
public function key()
{
return $this->pos;
}
public function next()
{
$this->pos++;
}
public function rewind()
{
$this->pos = 0;
}
public function valid()
{
return isset($this->numbers[$this->pos]);
}
}
Now we need a recursive function, which would help us traversing:
function displayNumbers(NumbersIterator $iterator) {
while($iterator->valid()) {
if($iterator->hasChildren()) {
displayNumbers($iterator->getChildren());
} else {
echo $iterator->current() . '';
}
$iterator->next();
}
}
Finally, let’s traverse the array I have shown you at the beginning of this section:
$numbers = array(
array(1, 2),
array(3, 4, 5, array(6, 7)),
8, 9
);
$iterator = new NumbersIterator($numbers);
displayNumbers($iterator);
Everything should be pretty self-explanatory – scalars get printed “as is”, if current iteratable entry is an array (i.e. hasChildren() returns true), I traverse all its children, by recursively calling displayNumbers().
This section was one of the hardest to explain, so sorry for lengthy code and explanations. If you still feel lost, please, let me know via comments – I would try to clarify and improve current section.
The SplObserver and SplSubject Interfaces
I would discuss both interfaces in a single section, as they describe well known Observer design pattern.
Again, I am not sure why PHP Manual’s respective section does not provide any info regarding this interfaces, I believe they are quite important, so here is what they all about:
I would not go into any details regarding observer pattern – as there better places (here for eg) that provide explanation and PHP implementations of the pattern. In a nutshell, the pattern allows you to define one-to-many relationship between so called Subject and Observer(s), and once the state of the Subject changes, all bind Observer(s) get notified. If you know Java (read-only is enough), Head First Design Patterns is really mind-friendly intro (I don’t suggest GoF’s book, because if you are able to get through it, there’s 0 possibility you haven’t tried it yet
)
Now, back to interfaces.
SplSubject
SplSubject {
public void attach(SplObserver $observer);
public void detach(SplObserver $observer);
public void notify();
}
Interface is pretty simple: you should be able to attach observing objects, detach them, and, once state of the subject is changed, notify observers. Here is sample implementation:
class Subject implements SplSubject
{
/**
* List of attached observers
* @var array
*/
protected $observers = array();
protected $title = null;
/**
* Update subject's title - observers notified!
* @param string $title
*/
public function setTitle($title)
{
$this->title = $title;
$this->notify(); // all observers get notified of change
}
public function getTitle()
{
return $this->title;
}
/**
* SplSubject methods
*/
public function attach(SplObserver $observer)
{
$this->observers[] = $observer;
}
public function detach(SplObserver $observer)
{
// I cannot use array_search output in IF block directly,
// as 0 might be returned (if what I am detaching is the first observer)
// so I must make sure that $key is not false (taking type into account)
$key = array_search($observer, $this->observers, true);
if( !($key === false) ) {
unset($this->observers[$key]);
}
}
public function notify()
{
foreach($this->observers as $observer) {
$observer->update($this);
}
}
}
SplObserver is even more simple, all you have to do is to provide update() method:
SplObserver {
public void update(SplSubject $subject);
}
Implementation of simple observer class:
class Observer implements SplObserver
{
public function update(SplSubject $subject)
{
echo 'Subject updated! New title: ' . $subject->getTitle();
}
}
Now, let’s use our Subject and Observer together:
$subject = new Subject(); // create subject
$observer = new Observer(); // create observer
$subject->attach($observer); // attach observer
$subject->setTitle('Hi, there!'); // Subject updated! New title: Hi, there!
Well, SPL implementation of the observer pattern is not the best I’ve seen – since it tightly couples the subject and observer (for example, our observer is aware that subject has getTitle() method). However, for most needs it is good enough.
Conclusion
I hope you enjoyed at least some parts of this article, and hopefully it gives you better understanding of the SPL’s interfaces.




Read Later Pile – Exploring Spl Interfaces…
In an attempt to shed some light on and to draw attention to SPL, I plan to post several articles discussing various parts of this extension. I will start with SPL Interfaces so that you can grasp immediately the usefulness of the SPL….
I’m eagerly waiting for your next SPL article, hope you publish them soon.
There are very few resources about SPL and I think most of the developers are not aware of it. Do you know why?
Thank you for this article, it is one of the most comprehensive articles about SPL interfaces I found.
One little bug I found in your implementation of OuterIterator:
the filter does not take into consideration the first element, if you start your array with 6, it will show 6, 3, 5.
To help you fix this, you can take a look at the FilterIterator class source code:
http://www.php.net/~helly/php/ext/spl/filteriterator_8inc-source.html#l00026
Excellent article — it’s difficult to find such a comprehensive yet concrete treatment of SPL.
Is it possible / practical to use the RecursiveIterator interface to “explode” a flat array, as well as flatten an exploded array? Or, is there another tool in the SPL suite designed to accomplish this?
[...] This article serves as preliminary for SPL Interfaces article to be published later [...]
Regarding SplObserver/SplSubject… I presume you wanted to say “for example, our observer is *NOT* aware that subject has getTitle() method”. NOT is missed.
Nice article tho!
@Dmitri
Well, actually no “NOT” is missing, as I wanted to point out that Observer actually has knowledge about Subject (update method requires you to pass subject).
Still, when revisiting this now I suppose that this coupling is avoidable, and as such – issue of the example code, not SplSubject/SplObserver per se.
So thanks for pointing this out.