The Standard PHP Library (SPL)

      3 Comments on The Standard PHP Library (SPL)

The Standard PHP Library (SPL)

PHP 5 Adoption Series

Ben Ramsey


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


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


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


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


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


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


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


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 preferrable. 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


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


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


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.

Ben Ramsey is a Software Architect for Schematic and a leader in the PHP community. He blogs about PHP and technology at http://benramsey.com/.


[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.