The Standard PHP Library (SPL)

This article was first published on in Zend Developer Zone.

As its name implies, the goal of the Standard PHP Library-or SPL, for short-is to provide a standard library of interfaces that allows developers to take full advantage of object-oriented programming in PHP 5. This library of interfaces creates a standard API for certain kinds of built-in functionality, allowing your classes to interact with the PHP engine in a much more seamless manner. The functionality it provides includes, for example, the ability to define how your objects will react when iterated over with foreach, advanced array access, file and directory access, and advanced SimpleXML object handling. The largest chunk of functionality that the SPL provides comes in the form of iterators.

Iterators

If you’ve ever spent any time looking at the SPL documentation, you’ll notice that many of the classes listed are iterators of some type or another. From ArrayIterator to NoRewindIterator to even IteratorIterator, the SPL appears to be largely focused on the iterator design pattern. So, with all this emphasis on the importance of iterators to the SPL, it may be helpful to take a look at exactly what are iterators and why they are important and beneficial to object-oriented programming.

According to Wikipedia, “an iterator is an object which allows a programmer to traverse through all the elements of a collection, regardless of its specific implementation”1. It goes on to further describe the iterator design pattern, saying, “the iterator pattern is a design pattern in which iterators are used to access the elements of an aggregate object sequentially without exposing its underlying representation”2. The basic gist here is that an iterator allows sequential traversal through the data of an object.

Consider a typical array:

$party = array(
    'Frodo Baggins',
    'Samwise Gamgee',
    'Meriadoc Brandybuck',
    'Peregrin Took',
    'Gandalf',
    'Aragorn',
    'Boromir',
    'Legolas',
    'Gimli',
);

In PHP, it is possible to iterate over an array with the while, do-while, for, and foreach constructs. For example, we could iterate over the $party array like this:

foreach ($party as $member) {
    echo $member . "\n";
}

Yet, what happens when working with an object that has many data properties and you need to easily access those properties and the property names? This is impossible in PHP 4, but, as you can see in Example 1, PHP 5 provides iteration of the public properties of any object by default.

Example 1. Object iteration in PHP 5
<?php
class PartyMember
{
public $name;
public $birthday;
public function __construct($name, $birthday)
{
$this->name = $name;
$this->birthday = $birthday;
}
}
$member = new PartyMember('Frodo Baggins', '2968-09-22');
foreach ($member as $property => $value) {
echo "{$property} --> {$value}\n";
}
?>

This is a very basic example of how iterators work in PHP 5. So far, we’ve done nothing special to the object to facilitate the iteration of its properties. It just works. However, the problem with this simple approach is that, in order for iteration of an object to work, all data that needs to be available through iteration must be located in separate properties, and those properties must be public. This is not often the case in object-oriented programming because we do not always want to allow public access to properties. Instead, we use “getter” and “setter” methods to access the data, and that data may come from a database or elsewhere, so it may not always be available on individual and separate properties. This is where the advanced functionality of the SPL comes into play, as Example 2 illustrates.

Note how the PartyMember class is different in Example 2. This time, the class implements Iterator, there is only one property, the property is private, the constructor does something different this time (data retrieval from a database), and there are several new method names. The very simple class from Example 1 has been replaced with something much more complex. However, don’t let the additional methods and the implemented interface fool you, this is still a very simple example, and the resulting output is relatively the same. However, there is significant increase in power and flexibility gained in this approach: you can override the default foreach behavior on your objects to provide richer functionality.

Example 2. Object iteration with the Iterator interface
<?php
class PartyMember implements Iterator
{
private $dbRow;
public function __construct($memberId)
{
// Get a row from the database with $memberId;
// store to $dbRow
}
public function getName()
{
return $this->dbRow['name'];
}
public function current()
{
return current($this->dbRow);
}
public function key()
{
return key($this->dbRow);
}
public function next()
{
next($this->dbRow);
}
public function rewind()
{
reset($this->dbRow);
}
public function valid()
{
return (current($this->dbRow) !== FALSE);
}
}
$member = new PartyMember(1);
foreach ($member as $key => $value) {
echo "{$key} --> {$value}\n";
}
?>

This is the beauty of the SPL: interfaces allow you to change the default behavior of standard functionality. If I had failed to implement the Iterator interface in Example 2, then PHP would not have used the current(), key(), next(), rewind(), or valid() methods of the object to perform the iteration. Instead, it would have assumed the default behavior and would have tried to iterate over the object’s public properties, of which there are none in this case. Implementing the interface tells PHP to use your methods to override the default functionality.

Interfaces

Earlier, I mentioned that the SPL dictates a standardized way of implementing functionality by defining built-in interfaces. Interfaces are similar to abstract classes in that they define functionality of child classes. However, interfaces do not define how the children should process data. Rather, the interface defines only, well, the interface by which the children must accept or return data. All processing is up to the child, but the important thing for the child to follow is the definition of the interface. That is, the interface provides all of its children with a common language so that anything else that talks to these children and knows the language of the interface will be able to effectively communicate with the children.

Thus, when you use one of the SPL interfaces, the PHP engine can use your objects in special ways that were otherwise impossible before implementing the interface because your objects did not “speak the language” of the interface. PHP understands the common “language” of the SPL interfaces, and, so when you implement them, PHP can do some amazing things with your classes. For example, if your class implements the Countable interface, then PHP knows to call the count() method of your class when anyone uses the PHP count() function on an object of your class.

In order to best understand the SPL, let’s take a look at some of the base interfaces that comprise it and see what kind of functionality they define and how you can make use of that functionality. In keeping with the theme of this article, I’ll focus only on those interfaces related to iterators.

Iterator

The Iterator interface provides for basic iterator functionality. As seen earlier, Iterator can be used for objects that can be iterated internally or it can be used for external iterators such as those created by using the getIterator() method of any class that implements the IteratorAggregate interface. Iterator defines methods for traversing an object. I’ve already mentioned them in passing, but for the sake of thoroughness, they are: current(), key(), next(), rewind(), and valid(). Implementing this interface and defining the behavior of each of these methods is enough to get an iterator working, but for the most part, typical objects will not contain both data and the iteration behavior. Thus, it may be necessary to abstract out the iteration methods and, instead, have your data class implement IteratorAggregate.

IteratorAggregate

As mentioned in the previous section, it is not always practical or advisable to define the behavior for your iterator inside a data class that represents a model of a type of data. For one, models in the real world don’t necessarily exhibit traversable behavior on their own. Instead, something else performs the iteration for them. The SPL provides such a means for this type of abstraction through the use of the IteratorAggregate interface. Example 3 shows how to use this interface by taking the PartyMember class from Example 2 and abstracting out the iteration behavior to a separate PartyMemberIterator class. Now, the PartyMember class worries only about the data, while the PartyMemberIterator handles the traversal of PartyMember data. To do this, simply define a getIterator() method that returns an iterator.

Example 3. Using an IteratorAggregate
<?php
class PartyMember implements IteratorAggregate
{
// define the class as usual with properties,
// a constructor, getter/setter methods, etc.
public function getIterator()
{
return new PartyMemberIterator($this);
}
}
class PartyMemberIterator implements Iterator
{
public function __construct(PartyMember $member)
{
// Store $member locally for iteration
}
// Implement current(), key(), next(), rewind()
// and valid() to iterate over data in $member
}
$member = new PartyMember(1);
foreach ($member->getIterator() as $key => $value) {
echo "{$key} --> {$value}\n";
}
?>

RecursiveIterator

RecursiveIterator defines the interface for recursion through an object with multiple levels of data. Think about multidimensional arrays and how foreach handles them. Rather than traverse each dimension of the array, it stays only at the top level. The same can be said of the standard iterator. Implementing RecursiveIterator, however, allows the iteration to dig deep into the tree and iterate through it all.

This interface itself extends Iterator, so it inherits the standard current(), key(), next(), rewind(), and valid() methods, but it also defines the getChildren() and hasChildren() methods, which an iterator must implement in order for recursion to take place. The getChildren() method must return an object that implements RecursiveIterator.

SeekableIterator

Normal iterators must start at the beginning of the sequence of data and progress to the end. There is no way for them to begin at a specified place in the data. Enter the SeekableIterator.

The SeekableIterator interface, like the RecursiveIterator, simply extends Iterator to provide more functionality. So, while a SeekableIterator has the same methods as an Iterator, it also has the seek() method, which accepts the index of the position to seek. If the position does not exist, it should throw an OutOfBoundsException. Example 4 illustrates how the PartyMemberIterator could be modified as a seekable iterator.

Example 4. Using the SeekableIterator interface
<?php
class PartyMemberIterator implements SeekableIterator
{
public function __construct(PartyMember $member)
{
// Store $member locally for iteration
}
public function seek($index)
{
$this->rewind();
$position = 0;
while ($position < $index && $this->valid()) {
$this->next();
$position++;
}
if (!$this->valid()) {
throw new OutOfBoundsException('Invalid position');
}
}
// Implement current(), key(), next(), rewind()
// and valid() to iterate over data in $member
}
?>

Iterators for All Occassions

While the SPL defines interfaces for creating your own iterators, it also provides some handy built-in classes that can be used to solve standard programming problems. There are many of these built in to SPL, and I won’t take the time to go over them all, though I will mention a few that are especially useful.

ArrayIterator

As an internal class, ArrayIterator implements SeekableIterator, ArrayAccess, and Countable . This, in itself, gives this class a lot of power, but it goes a little further to implement several of its own methods, including: append(), various sorting methods (i.e. asort(), ksort(), etc.), and getArrayCopy(). Finally, the functionality defined by this class allows you unset and modify values while iterating through arrays and objects.

We can use the $party array mentioned earlier to create an ArrayIterator object and manage the array data through the object:

$partyIterator = new ArrayIterator($party);
$partyIterator->asort();

echo count($partyIterator) . "\n";

$partyIterator->seek(4);
echo $partyIterator->current();

DirectoryIterator

The built-in DirectoryIterator class provides an easy way to create a directory iterator and traverse a directory tree, complete with the ability to get information about each file or directory in the tree through the use of the SplFileInfo class, of which the DirectoryIterator returns objects for each file in the directory. All of this is handled in a purely object-oriented manner with no need to call PHP functions to get information about the files or to dig deep into the directory. Previously, such a task required a fair amount of procedural code that we, the programmers, usually put into custom, userland functions for reuse throughout our applications. Now, all such operations are built into the SPL, as Example 5 demonstrates.

As mentioned earlier, the SPL also includes recursive iterators, and, in this case, the built-in RecursiveDirectoryIterator can be utilized to traverse an entire directory tree by getting the children of each directory and iterating through them, as well.

Example 5. Iterating through a directory
<?php
$path = new DirectoryIterator('/path/to/dir');
foreach ($path as $file) {
echo $file->getFilename() . "\t";
echo $file->getSize() . "\t";
echo $file->getOwner() . "\t";
echo $file->getMTime() . "\n";
}
?>

FilterIterator

If you’ll notice the output when you run Example 5, you’ll see that DirectoryIterator loops through all items in the path, including the dot paths for the current (.) and parent (..) directories. We could use an if statement within the foreach loop that checks $file->isDot(), but ultimately, the object still contains these items, and that may not be the desired result. Thus, the SPL introduces the notion of filter iterators that filter out unwanted data so that the object contains only the data that is relevant to our purposes. We do this by defining a class that extends the abstract class FilterIterator. Example 6 extends Example 5 to show how this works.

The FilterIterator processes data by checking the implementation of its abstract accept() method to determine whether the current element passes the filter. In Example 6, the accept() method returns TRUE (acceptable) if the current item is not a dot path. Otherwise, it returns FALSE (not acceptable).

Example 6. Using FilterIterator to extract dot paths from DirectoryIterator
<?php
class DirectoryFilterDots extends FilterIterator
{
public function __construct($path)
{
parent::__construct(new DirectoryIterator($path));
}
public function accept()
{
return !$this->getInnerIterator()->isDot();
}
}
$path = new DirectoryFilterDots('/path/to/dir');
foreach ($path as $file) {
// process each $file
}
?>

LimitIterator

Finally, the last iterator I’d like to focus on is the LimitIterator. This iterator provides functionality for returning limited results from an iterator. In its simplest form, you can pass an iterator object to it (such as a DirectoryIterator object, as Example 7 illustrates) and include an offset and the number of items to return. In Example 7, we tell the LimitIterator to start at index 2 (the third element) and limit the results to three items. This iterator could be extended and combined with the FilterIterator to provide a powerful mechanism for iterating through database recordsets, limiting and filtering out specific records, for example.

Example 7. Limiting results with LimitIterator
<?php
$path = new DirectoryFilterDots('/path/to/dir');
foreach (new LimitIterator($path, 2, 3) as $file) {
// process each $file
}
?>

More SPL Goodies

Finally, the SPL includes many more features than I can cover in depth in a single article, but I would like to take some time to mention a few that are especially important and helpful. These include standard exceptions, advanced array handling, and advanced file and directory access.

Exceptions

PHP 5 introduced exceptions, and exceptions have greatly improved the way we handle errors and failure in applications. Yet, it’s not enough to catch only the base Exception because, in doing this, it’s impossible to dictate multiple alternative behaviors for an application depending on the type of failure. Thus, creating different types of exceptions based on the type of failure you want to catch and handle is preferable. In general, these custom exceptions extend the base Exception and do not need to implement any other functionality. The importance of using them is that they are independently catchable, as Example 8 demonstrates.

Example 8. Catching different types of exceptions
<?php
try {
// operations that may throw various exceptions
} catch (MyException $e) {
// perform an alternative operation
} catch (MyOtherException $e) {
// perform a different alternative operation
} catch (Exception $e) {
// Catching the base exception, perform
// a default operation, since all else failed
}
?>

The SPL includes many built-in exceptions that are ready to use and throw in classes, functions, and scripts and are also thrown by many of the built-in SPL classes. Table 1 lists a handful of these built-in exceptions and their purposes. Use these exceptions when creating classes so that your applications can intelligently handle exceptions and take alternative actions depending on the circumstance of the exception.

Table 1. Built-in SPL exceptions
Exception Purpose
BadMethodCallException The method call was illegal.
InvalidArgumentException The arguments passed were invalid.
LengthException The parameter exceeds the allowed length (used for strings, arrays, file size, etc.).
LogicException Generic error occured in program logic.
OutOfBoundsException An illegal index was requested.
UnexpectedValueException An unexpected value was received (i.e. as the result of a returned value from a method call).

Array Handling

I’ve already mentioned the ArrayIterator class provided by the SPL, but the SPL provides even more advanced array handling through the ArrayAccess interface and ArrayObject class. The ArrayObject class implements ArrayAccess, so I will focus on the functionality of ArrayObject.

The ArrayObject class is a powerful tool for iterating through and accessing arrays. Its practical uses are infinite; use it as a wrapper for all arrays: simple arrays, arrays of database records, arrays of objects, etc. Consider the listing in Example 9. Continuing the PartyMember examples from the earlier listings, I fetch data from a database, creating an array of PartyMember objects, from which I then create an ArrayObject object. While this example shows simple iteration with a foreach loop (which I could do when it was a normal array), using ArrayObject provides access to tools that are not accessible to non-object arrays without the use of procedural code and functions. For example, with the ArrayObject, it is possible to sort the array, modify elements or append new ones, and iterate through the results.

Example 9. Using ArrayObject for database results
<?php
$db = new PDO($dsn, $dbUsername, $dbPassword);
$stmt = $db->query('SELECT * FROM party_member');
$result = $stmt->fetchAll(PDO::FETCH_CLASS, 'PartyMember');
$partyCollection = new ArrayObject($result);
foreach ($partyCollection as $member) {
echo $member->getName() . "\n";
}
?>

File and Directory Access

Finally, the SPL offers advanced file and directory access, giving programmers a purely object- oriented approach to dealing with the file system. I’ve already mentioned DirectoryIterator and how to traverse directory trees, but the SPL offers much more power, providing a means to access files, get file and directory details, and modify the contents of files and directories through the use of the SplFileObject class. Example 10 extends Example 6 to write to files as it traverses a directory.

Example 10. Using SplFileObject to write to files
<?php
$path = new DirectoryFilterDots('/path/to/dir');
foreach ($path as $file) {
$file->openFile('w')->fwrite('foo');
}
?>

Re-iteration

As I conclude this article, I’d like to reiterate (pun intended) a few points. The Standard PHP Library is a built-in library providing a standard way of solving common programming problems. It does so by defining standard interfaces as solutions to these problems. It is up to us to define how classes and applications implement the behavior and logic occurring when using the interfaces.

Most of the interfaces in the SPL define a multitude of different types of iterators. This slew of iterators, I feel, is part of the reason why many people overlook the SPL. Perhaps they find iterators confusing; perhaps they do not see the practicality of using them in applications. However, I hope that you now have a greater understanding of the SPL, what it includes, and why it is so beneficial and helpful to solving common, everyday problems in object-oriented applications.

Further Reading

  1. “Iterator.” Wikipedia, The Free Encyclopedia. 28 May 2007, 18:59 UTC. Wikimedia Foundation, Inc. 30 May 2007 <http://en.wikipedia.org/w/index.php?title=Iterator&oldid=134134876>.

  2. “Iterator pattern.” Wikipedia, The Free Encyclopedia. 10 May 2007, 09:24 UTC. Wikimedia Foundation, Inc. 30 May 2007 <http://en.wikipedia.org/w/index.php?title=Iterator_pattern&oldid=129779432>.