-
Iterator patter is probably the most popular pattern from SPL. Is a very simple way to demonstrate the advantages of an interface and SPL.
Motivation:
The possibility of iterating object type structures, using functions like foreach(), var_dump(), print_r() etc.Diagram:
Iterator structure:
In SPL there are a lot of interfaces and classes for iteration.
Iterator interface base structure:
1/** 2 * Iterator interface from SPL 3 */ 4Iterator extends Traversable { 5 /** 6 * Returns the current element 7 */ 8 abstract public mixed current ( void ) 9 10 /** 11 * Returns the key of the current element 12 */ 13 abstract public scalar key ( void ) 14 15 /** 16 * Moves to the next element in the array 17 */ 18 abstract public void next ( void ) 19 20 /** 21 * Reset the iteration to the initial position 22 */ 23 abstract public void rewind ( void ) 24 25 /** 26 * Check to see if the current position is valid 27 */ 28 abstract public boolean valid ( void ) 29}
Example 1:
A simple iterator object.
1/** 2 * The class for the iterator object 3 */ 4class Iterabil implements Iterator { 5 6 /** 7 * The index for the iterated element 8 */ 9 private $_current = 0; 10 11 /** 12 * Array with elements to iterate 13 */ 14 private $_elements = array(); 15 16 /** 17 * Constructor 18 * 19 * @param array $elements Elements to iterate 20 */ 21 public function __construct($elements) { 22 $this->_elements = $elements; 23 } 24 25 /** 26 * Current element 27 * 28 * @return mixed Current element 29 */ 30 public function current() { 31 return $this->_elements[$this->_current]; 32 } 33 34 /** 35 * Current index 36 * 37 * @return integer Current index 38 */ 39 public function key() { 40 return $this->_current; 41 } 42 43 /** 44 * Move to the next index 45 */ 46 public function next() { 47 $this->_current++; 48 } 49 50 /** 51 * Reset index 52 */ 53 public function rewind() { 54 $this->_current = 0; 55 } 56 57 /** 58 * Check if the current element is set 59 * 60 * @return boolean If the current element is set 61 */ 62 public function valid() { 63 return isset($this->_elements[$this->_current]); 64 } 65} 66 67// class instance 68$obj = new Iterabil(array(1, 2, 3, 4, 5)); 69 70// iterate object 71foreach ($obj as $value) { 72 echo $value.PHP_EOL; 73} 74 75// output: 76// 1 77// 2 78// 3 79// 4 80// 5
Example 2:
Another example a little more complex, a class that allows to iterate through the public properties of a class which extends it. Iterator and Reflection are used.
1/** 2 * Class which iterates through the public properties of a class which extends it 3 */ 4class Iterabil implements Iterator { 5 6 /** 7 * The index for the iterated element 8 */ 9 private $_current = 0; 10 11 /** 12 * Array with elements to iterate 13 */ 14 private $_elements = array(); 15 16 /** 17 * Current element 18 * 19 * @return mixed Current element 20 */ 21 public function current() { 22 return $this->_elements[$this->_current]->name; 23 } 24 25 /** 26 * Current index 27 * 28 * @return integer Current index 29 */ 30 public function key() { 31 return $this->_current; 32 } 33 34 /** 35 * Move to next index 36 */ 37 public function next() { 38 $this->_current++; 39 } 40 41 /** 42 * Reset index and get the properties 43 */ 44 public function rewind() { 45 // rewind is the first to be called 46 // here the properties list should be obtained 47 // ReflectionClass is initialized 48 // with the current class name as a parameter 49 $reflection = new ReflectionClass(get_class($this)); 50 51 // we get the public properties 52 $this->_elements = $reflection->getProperties(ReflectionMethod::IS_PUBLIC); 53 54 // set the current index 55 $this->_current = 0; 56 } 57 58 /** 59 * Check if the current element is set 60 * 61 * @return boolean If the current element is set 62 */ 63 public function valid() { 64 return isset($this->_elements[$this->_current]); 65 } 66} 67 68/** 69 * A new class with public properties 70 * 71 */ 72class Testing extends Iterabil { 73 public $proprietate1; 74 public $proprietate2; 75} 76 77// class instance 78$obj = new Testing(); 79 80// iterate object 81foreach ($obj as $value) { 82 echo $value.PHP_EOL; 83} 84 85// output: 86// proprietate1 87// proprietate2
And if you what the above example to be accessible as an array you just have to implement ArrayAccess from SPL.
ArrayAccess structure:
1ArrayAccess { 2 /** 3 * Check if the offset exists 4 */ 5 abstract public boolean offsetExists ( string $offset ); 6 7 /** 8 * Returns the element of an offset or NULL if it does not exist 9 */ 10 abstract public mixed offsetGet ( string $offset ); 11 12 /** 13 * Set a value for an offset 14 */ 15 abstract public void offsetSet ( string $offset , string $value ); 16 17 /** 18 * Unset a value for an offset 19 */ 20 abstract public void offsetUnset ( string $offset ) 21}
Example 3:
An even more complicated example which shows the power of interfaces from SPL. Iterator object accessible like an array.
To simplify the array access logic I’ve used the php native functions for iterating an array (next(), reset()).
1/** 2 * The class for the iterator object 3 */ 4class Iterabil implements Iterator, ArrayAccess, Countable { 5 6 /** 7 * Array with elements to iterate 8 */ 9 private $_elements = array(); 10 11 /** 12 * Constructor 13 * 14 * @param array $elements Elementele de iterat 15 */ 16 public function __construct($elements) { 17 $this->_elements = $elements; 18 } 19 20 /** 21 * Current element 22 * 23 * @return mixed Current element 24 */ 25 public function current() { 26 return current($this->_elements); 27 } 28 29 /** 30 * Current index 31 * 32 * @return integer Current index 33 */ 34 public function key() { 35 return key($this->_elements); 36 } 37 38 /** 39 * Move to the next index 40 */ 41 public function next() { 42 next($this->_elements); 43 } 44 45 /** 46 * Reset index 47 */ 48 public function rewind() { 49 reset($this->_elements); 50 } 51 52 /** 53 * Check if the current element is set 54 * 55 * @return boolean If the current element is set 56 */ 57 public function valid() { 58 return current($this->_elements)?true:false; 59 } 60 /** 61 * Check if the offset exists 62 * 63 * @param string $offset Element key 64 * @return boolean If the element is set 65 */ 66 public function offsetExists($offset) { 67 return isset($this->_elements[$offset]); 68 } 69 70 /** 71 * Returns the element of an offset or NULL if it does not exist 72 * 73 * @param string $offset Array offset 74 * @return mixed Element or NULL 75 */ 76 public function offsetGet($offset) { 77 return $this->_elements[$offset]; 78 } 79 80 /** 81 * Set a value for an offset 82 * 83 * @param string $offset Element offset 84 * @param mixed $value Value of the element in the array 85 */ 86 public function offsetSet($offset, $value) { 87 $this->_elements[$offset] = $value; 88 } 89 90 /** 91 * Unset a value for an offset 92 * 93 * @param string $offset Element offset 94 */ 95 public function offsetUnset($offset) { 96 unset($this->_elements[$offset]); 97 } 98 99 /** 100 * Number of elements in the array 101 * 102 * @return integer Number of elements in array 103 */ 104 public function count() { 105 return count($this->_elements); 106 } 107} 108 109// Class instance 110$obj = new Iterabil(array(1, 2, 3, 4, 5)); 111 112echo 'Iteration using "for":'.PHP_EOL; 113 114// iterate the object like a simple array 115for($i = 0; $i < count($obj); $i++) { 116 echo $obj[$i].PHP_EOL; 117} 118 119echo 'Element to delete: '.$obj[1].PHP_EOL; 120 121unset($obj[1]); 122 123echo 'Iteration using "foreach":'.PHP_EOL; 124 125// iterate the object using foreach 126foreach ($obj as $element) { 127 echo $element.PHP_EOL; 128} 129 130// Output: 131//Iteration using "for": 132//1 133//2 134//3 135//4 136//5 137//Element to delete: 2 138//Iteration using "foreach": 139//1 140//3 141//4 142//5
-
Observer pattern refers to a class called “subject” that has a list of dependents, called observers, and notifies them automatically each time an action is taking place.
A small example of why is used:
– let’s say we have a class with does someting:
1class Actiune { 2 private $val; 3 function __construrct() { 4 // someting in the constructor 5 } 6 7 function change($val) { 8 $this->val = $val; 9 } 10}
Each time $val changes we want to call a method of an “observer” object:
1class Actiune { 2 private $val; 3 function __construrct() { 4 // someting in the constructor 5 } 6 7 function change($val, $observator) { 8 $this->val = $val; 9 $observator->update($this); 10 } 11}
Theoretically is not bad, but the more methods there are so does the dependence grows bigger and each time we add a new observer object we must modify the class, with will probably result in chaos, which will be almost impossible to port.
Now, the observator pattern looks something like this:
SPL (Standard PHP Library), which is well known for it’s defined iterators, comes with the interfaces SplSubject and SplObserver, for the subject and respectively the observer.
An implementation looks someting like this:
1/** 2 * the class which must be monitored 3 */ 4class Actiune implements SplSubject { 5 private $observatori = array(); 6 private $val; 7 8 /** 9 * method to attach an observer 10 * 11 * @param SplObserver $observator 12 */ 13 function attach(SplObserver $observator) { 14 $this->observatori[] = $observator; 15 } 16 17 /** 18 * method to detach an observer 19 * 20 * @param SplObserver $observator 21 */ 22 function detach(SplObserver $observator) { 23 $observatori = array(); 24 foreach($this->observatori as $observatorul) { 25 if($observatorul != $observator) $observatori[] = $observatorul; 26 } 27 $this->observatori = $observatori; 28 } 29 30 /** 31 * method that notifies the observer objects 32 */ 33 function notify() { 34 foreach($this->observatori as $observator) { 35 $observator->update($this); 36 } 37 } 38 39 /** 40 * method for makeing changes in the class 41 * 42 * @param int $val 43 */ 44 function update($val) { 45 echo 'updateing...'; 46 $this->val = $val; 47 $this->notify(); 48 } 49 50 /** 51 * public method with the subject's status 52 * 53 * @return int 54 */ 55 function getStatus() { 56 return $this->val; 57 } 58} 59 60/** 61 * and observer class 62 */ 63class Observator implements SplObserver { 64 function update(SplSubject $subiect) { 65 echo $subiect->getStatus(); 66 } 67} 68 69// an observer instance 70$observator = new Observator(); 71 72// an subject instance 73$subiect = new Actiune(); 74 75// attaching an observer to the subject 76$subiect->attach($observator); 77 78// update subject 79$subiect->update(5);
What seems strange is that there isn’t any documentation on this SPL interfaces. Even on the Zend website there is an article PHP Patterns: The Observer Pattern which does not use SPL, but for something like namespaces there was documentation even before PHP 5.3 was out.