• No results found

The Standard PHP Library

In document ZEND PHP 5 Certification (Page 181-190)

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 aforeach()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 im- plement the functionality required to perform certain operations. By far, the largest number of patterns exposed by SPL are iterators; they allow, among other things:

• Simple Iteration • Seekable Iteration • Recursive Iteration • Filtered Iteration

Accessing Objects as Arrays

TheArrayAccessinterface 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"); }

164Elements of Object-oriented Design

$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 con- trol over one of PHP’s most powerful (and most useful) data types. Used properly,

ArrayAccessis a great tool for building applications that encapsulate complex be- haviours in a data type that everyone is used to.

Simple Iteration

TheIteratorinterface is the simplest of the iterator family, providing simple itera- tion 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 themyDataprivate property in the exact same wayforeach()works on a standard Array.

Seekable Iterators

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

166Elements of Object-oriented Design 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" ) ) ) );

<h1>Company: Acme Anvil Co.</h1> <h2>Department: Human Resources</h2> <ul> <li>Tom</li> <li>Dick</li> <li>Harry</li> </ul> <h2>Department: Accounting</h2> <ul> <li>Zoe</li> <li>Duncan</li> <li>Jack</li> <li>Jane</li> </ul>

By extending RecursiveIteratorIterator, we can define thebeginChildren() and

endChildren() methods so that our class can output the start and end <ul> tags

without any of the complexities normally associated with recursion (such as, for ex- ample, keeping track of multiple nested levels of nesting). The example shown be- low defines two classes, our customRecursiveIteratorIteratorand a very simple

RecursiveArrayObject:

class Company_Iterator extends RecursiveIteratorIterator { function beginChildren()

{

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

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

echo "<ul>" . PHP_EOL; }

}

function endChildren() {

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

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

echo "</ul>" . PHP_EOL; }

} }

class RecursiveArrayObject extends ArrayObject { function getIterator() {

168Elements of Object-oriented Design

} }

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 "<h1>Company: $item</h1>" . PHP_EOL;

break;

case 2:

echo "<h2>Department: $item</h2>" . PHP_EOL;

break;

default:

echo "<li>$item</li>" . PHP_EOL; }

}

Filtering Iterators

TheFilterIteratorclass 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);

} 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; }

Theaccept()method simply determines whether any given element should be al- lowed in the iteration; note thatFilterIteratoralready implements all of the meth- ods ofArrayAccess, so that, effectively, from the outside our class can still be used as an array.

This example outputs only the odd numbers stored in the array:

1 3 5 7 9

Summary

Object Oriented programming, coupled with Design Patterns—including those pro- vided by SPL—is the key to re-usable and highly modular code.

The more forethought you give your design, the more likely you are to be able to re-use at least some of it, later, saving time and effort in the future—not to mention that proper design techniques also make code easier to maintain and extend.

Bringing this experience to the table is what makes you truly versatile; as we men- tioned, design patterns are, after all, largely language- and problem-independent.

In document ZEND PHP 5 Certification (Page 181-190)