The Standard PHP Library

The Standard PHP Library


The Standard PHP Library (SPL) is a great addition to PHP 5. It provides a number of very useful facilities that expose some of PHP’s internal functionality and allow the “userland” developer to write objects that are capable of behaving like arrays, or that transparently implement certain iterative design patterns to PHP’s own core functionality, so that you, for example, use a foreach() construct to loop through an object as if it were an array, or even access its individual elements using the array operator [].


SPL works primarily by providing a number of interfaces that can be used to implement the functionality required to perform certain operations. By far, the largest number of patterns exposed by SPL are iterators; they allow, among other things:

    Array Access to objects
    Simple Iteration
    Seekable Iteration
    Recursive Iteration
    Filtered Iteration


Accessing Objects as Arrays

The ArrayAccess interface can be used to provide a means for your object to expose themselves as pseudo-arrays to PHP:


interface ArrayAccess {

function offsetSet($offset, $value);

function offsetGet($offset);

function offsetUnset($offset);

function offsetExists($offset);

}


This interface provides the basic methods required by PHP to interact with an array:

    offsetSet() sets a value in the array
    offsetGet() retrieves a value from the array
    offsetUnset() removes a value from the array
    offsetExists() determines whether an element exists


As a very quick example, consider the following class, which “emulates” an array that only accepts elements with numeric keys:


class myArray implements ArrayAccess {

protected $array = array();

function offsetSet ($offset, $value) {

if (!is_numeric ($offset)) {

throw new Exception ("Invalid key $offset");

}

$this->array[$offset] = $value;

}

function offsetGet ($offset) {

return $this->array[$offset];

}

function offsetUnset ($offset) {

unset ($this->array[$offset]);

}

function offsetExists ($offset) {

return array_key_exists ($this->array, $offset);

}

}

$obj = new myArray();

$obj[1] = 2; // Works.

$obj[’a’] = 1; // Throws exception.


As you can see, this feature of SPL provides you with an enormous amount of control over one of PHP’s most powerful (and most useful) data types. Used properly, ArrayAccess is a great tool for building applications that encapsulate complex behaviours in a data type that everyone is used to.





Simple Iteration

The Iterator interface is the simplest of the iterator family, providing simple iteration over any single-dimension array. It looks like this:


interface Iterator {}

function current();

function next();

function rewind();

function key();

function valid();

function seek($key);

}


You can see a simple implementation of the interface that allows iteration over a private property containing a simple array:


class myData implements iterator {

private $_myData = array(

"foo",

"bar",

"baz",

"bat");

private $_current = 0;

function current() {

return $this->myData[$this->current];

}

function next() {

$this->current += 1;

}

function rewind() {

$this->current = 0;

}

function key() {

return $this->current;

}

function valid() {

return isset($this->myData[$this->current]);

}

}

$data = new myData();

foreach ($data as $key => $value) {

echo "$key: $value\n";

}


This example will iterate over each of the four elements in the myData private property in the exact same way foreach() works on a standard Array.



Seekable Iterators


The next step up from a standard Iterator is the SeekableIterator, which extends the standard Iterator interface and adds a seek() method to enable the ability to retrieve a specific item from internal data store. Its interface looks like this:


interface SeekableIterator {

function current();

function next();

function rewind();

function key();

function valid();

function seek($index);

}


Recursive Iteration


Recursive Iteration allows looping over multi-dimensional tree-like data structures.

SimpleXML, for example, uses recursive iteration to allow looping through complex XML document trees.


To understand how this works, consider the following complex array:


$company = array(

array("Acme Anvil Co."),

array(array("Human Resources",array("Tom","Dick","Harry")),

array("Accounting",array("Zoe","Duncan","Jack","Jane"))));


Our goal is to print out something like this:



Company: Acme Anvil Co.


Department: Human Resources


Tom

Dick

Harry


Department: Accounting


Zoe

Duncan

Jack

Jane



By extending RecursiveIteratorIterator, we can define the beginChildren() and endChildren() methods so that our class can output the start and end tags without any of the complexities normally associated with recursion (such as, for example, keeping track of multiple nested levels of nesting). The example shown below defines two classes, ou r custom RecursiveIteratorIterator and a very simple.


RecursiveArrayObject:


class Company_Iterator extends RecursiveIteratorIterator {

function beginChildren()

{

if ($this->getDepth() >= 3) {

echo str_repeat("\t", $this->getDepth() - 1);

echo "

    " . PHP_EOL;

}

}

function endChildren()

{

if ($this->getDepth() >= 3) {

echo str_repeat("\t", $this->getDepth() - 1);

echo "

" . PHP_EOL;

}

}

}


class RecursiveArrayObject extends ArrayObject {

function getIterator() {

return new RecursiveArrayIterator($this);

}

}


Then, to produce our desired end result, we simply use this code:


$it = new Company_Iterator(new RecursiveArrayObject($company));

$in_list = false;

foreach ($it as $item) {

echo str_repeat("\t", $it->getDepth());

switch ($it->getDepth()) {

case 1:

echo "
Company: $item

" . PHP_EOL;

break;

case 2:

echo "
Department: $item

" . PHP_EOL;

break;

default:

echo "

$item
" . PHP_EOL;

}

}



Filtering Iterators

The FilterIterator class can be used to filter the items returned by an iteration:


class NumberFilter extends FilterIterator {

const FILTER_EVEN = 1;

const FILTER_ODD = 2;

private $_type;

function__construct($iterator, $odd_or_even = self::FILTER_EVEN)

{

$this->_type = $odd_or_even;

parent::__construct($iterator);

}

function accept()

{

if ($this->_type == self::FILTER_EVEN) {

return ($this->current() % 2 == 0);

Elements of Object-oriented Design ” 169

} else {

return ($this->current() % 2 == 1);

}

}

}

$numbers = new ArrayObject(range(0, 10));

$numbers_it = new ArrayIterator($numbers);

$it = new NumberFilter($numbers_it, NumberFilter::FILTER_ODD);

foreach ($it as $number) {

echo $number . PHP_EOL;

}


The accept() method simply determines whether any given element should be allowed in the iteration; note that FilterIterator already implements al l of the methods of ArrayAccess, so that, effectively, from the outside our class can still be used as an array.

Share this

0 Comment to " The Standard PHP Library "

Post a Comment