Archive for the ‘SPL’ tag
Iterarea obiectelor folosind PHP si SPL
Iterator pattern este probabil cel mai popular pattern din SPL. Este un exemplu foarte simplu de a demonstra avantajele unei interfete si SPL.
Motivatie:
Posibilitatea de a itera structuri de tip obiect folosind functii precum foreach(), var_dump(), print_r() etc.
Diagrama:
Structura Iterator:
In SPL se gasesc mai multe interfete si clase pentru iterator.
Structura de baza pentru interfata Iterator:
/** * Interfata Iterator din SPL */ Iterator extends Traversable { /** * Intoarce elementul curent */ abstract public mixed current ( void ) /** * Cheia pentru elementul curent */ abstract public scalar key ( void ) /** * Trece la urmatorul element */ abstract public void next ( void ) /** * Reseteaza iterarea la pozitia initiala */ abstract public void rewind ( void ) /** * Verifica daca pozitia curenta este valida */ abstract public boolean valid ( void ) }
Exemplu 1:
Un obiect iterabil simplu.
/** * Clasa care va genera un obiect interabil */ class Iterabil implements Iterator { /** * Index pentru elementul iterabil */ private $_current = 0; /** * Array cu elementele de iterat */ private $_elements = array(); /** * Constructor * * @param array $elements Elementele de iterat */ public function __construct($elements) { $this->_elements = $elements; } /** * Elementul curent * * @return mixed Elementul curent */ public function current() { return $this->_elements[$this->_current]; } /** * Index curent * * @return integer Index curent */ public function key() { return $this->_current; } /** * Trecerea la index-ul urmator */ public function next() { $this->_current++; } /** * Resetare index */ public function rewind() { $this->_current = 0; } /** * Verifica daca elementul curent este setat * * @return boolean Daca elementu curent este setat */ public function valid() { return isset($this->_elements[$this->_current]); } } // instantiere clasa $obj = new Iterabil(array(1, 2, 3, 4, 5)); // iterare obiect foreach ($obj as $value) { echo $value.PHP_EOL; } // output: // 1 // 2 // 3 // 4 // 5
Exemplu 2:
Un exemplu putin mai complex, o clasa care permite iterarea prin proprietatile publice ale clasei care o extinde. Se folosesc Iterator si Reflection.
/** * Clasa care va genera un obiect cu proprietatile publice iterabile */ class Iterabil implements Iterator { /** * Index pentru elementul iterabil */ private $_current = 0; /** * Array cu elementele de iterat */ private $_elements = array(); /** * Elementul curent * * @return mixed Proprietatea curenta in iteratie */ public function current() { return $this->_elements[$this->_current]->name; } /** * Index curent * * @return integer Index curent */ public function key() { return $this->_current; } /** * Trecerea la index-ul urmator */ public function next() { $this->_current++; } /** * Resetare index si preluare prorietari */ public function rewind() { // rewind se apeleaza prima, // deci aici ar trebui preluate prorprietatile obiectului // se initializeaza obiectul de tip ReflectionClass // cu parametru numele clasei curente $reflection = new ReflectionClass(get_class($this)); // se extrag metodele publice $this->_elements = $reflection->getProperties(ReflectionMethod::IS_PUBLIC); // se seteaza indexul curent $this->_current = 0; } /** * Verifica daca elementul curent este setat * * @return boolean Daca elementu curent este setat */ public function valid() { return isset($this->_elements[$this->_current]); } } /** * O noua clasa care va avea proprietati publice * */ class Testing extends Iterabil { public $proprietate1; public $proprietate2; } // instantiere clasa $obj = new Testing(); // iterare obiect foreach ($obj as $value) { echo $value.PHP_EOL; } // output: // proprietate1 // proprietate2
Iar daca vrem ca exemplu de mai sus sa fie si accesibil ca un array, nu trebuie decat sa mai implementam din SPL ArrayAccess.
Structura ArrayAccess:
ArrayAccess { /** * Verifica daca un offset exista */ abstract public boolean offsetExists ( string $offset ); /** * Intoarce elementul unui offset sau NULL daca nu exista */ abstract public mixed offsetGet ( string $offset ); /** * Seteaza o valoare pentru un offset */ abstract public void offsetSet ( string $offset , string $value ); /** * Dezaloca o valoare pentru un offset */ abstract public void offsetUnset ( string $offset ) }
Exemplu 3:
Un exemplu destul de dificil care are rolul de a evidentia puterea interfetelor din SPL. Obiect iterabil si accesibil ca un array.
Pentru a simplifica logica de acces am folosit chiar functiile pentru parcurgerea unui array (next(), reset()).
/** * Clasa care va genera un obiect interabil */ class Iterabil implements Iterator, ArrayAccess, Countable { /** * Array cu elementele de iterat */ private $_elements = array(); /** * Constructor * * @param array $elements Elementele de iterat */ public function __construct($elements) { $this->_elements = $elements; } /** * Elementul curent * * @return mixed Elementul curent */ public function current() { return current($this->_elements); } /** * Index curent * * @return integer Index curent */ public function key() { return key($this->_elements); } /** * Trecerea la index-ul urmator */ public function next() { next($this->_elements); } /** * Resetare index */ public function rewind() { reset($this->_elements); } /** * Verifica daca elementul curent este setat * * @return boolean Daca elementu curent este setat */ public function valid() { return current($this->_elements)?true:false; } /** * Verifica daca un offset exista * * @param string $offset Element cautat * @return boolean Daca elementul de la offset exista */ public function offsetExists($offset) { return isset($this->_elements[$offset]); } /** * Elementul de la un offset * * @param string $offset Offset array * @return mixed */ public function offsetGet($offset) { return $this->_elements[$offset]; } /** * Setare element in array * * @param string $offset Offset element * @param mixed $value Valoarea pentru elementul din array */ public function offsetSet($offset, $value) { $this->_elements[$offset] = $value; } /** * Dezalocare element in array * * @param string $offset Offset element */ public function offsetUnset($offset) { unset($this->_elements[$offset]); } /** * Numarul de elemente din array-ul curent * * @return integer Nr elemente array */ public function count() { return count($this->_elements); } } // instantiere clasa $obj = new Iterabil(array(1, 2, 3, 4, 5)); echo 'Iterare folosind "for":'.PHP_EOL; // iterare ca printr-un array simplu for($i = 0; $i < count($obj); $i++) { echo $obj[$i].PHP_EOL; } echo 'Element de sters: '.$obj[1].PHP_EOL; unset($obj[1]); echo 'Iterare folosind "foreach":'.PHP_EOL; // iterare array folosind foreach foreach ($obj as $element) { echo $element.PHP_EOL; } // Output: //Iterare folosind "for": //1 //2 //3 //4 //5 //Element de sters: 2 //Iterare folosind "foreach": //1 //3 //4 //5
PHP observer pattern si SPL
Observer pattern se refera la un obiect “subiect” care are asociata o lista de obiecte dependente, numite observatori, pe care le apeleaza automat de fiecare data cand se intampla o actiune.
Un mic exemplu de ce se foloseste:
– sa zicem ca avem o clasa pe care se fac niste modificari:
class Actiune { private $val; function __construrct() { // ceva cod in constructor } function change($val) { $this->val = $val; } }
De fiecare data cand se face modifica $val vrem sa se apeleze o metoda a unui obiect “observator”:
class Actiune { private $val; function __construrct() { // ceva cod in constructor } function change($val, $observator) { $this->val = $val; $observator->update($this); } }
Teoretic nu suna rau, dar cu cat sunt mai multe metode cu atat exista o dependenta mai mare si de fiecare data cand se adauga un obiect nou de tip observator trebuie modificata clasa, avand toate sansele sa rezulte intr-un haos aproape imposibil de portat.
Acum observator pattern arata cam asa:
SPL (Standard PHP Library), care este bine cunoscut pentru iteratorii definiti, vine cu interfetele SplSubject si SplObserver, pentru subiect respectiv observator.
O implementare arata cam asta:
/** * clasa care trebuie urmarita */ class Actiune implements SplSubject { private $observatori = array(); private $val; /** * metoda atasare obiect observator * * @param SplObserver $observator */ function attach(SplObserver $observator) { $this->observatori[] = $observator; } /** * metoda deatasare obiect observator * * @param SplObserver $observator */ function detach(SplObserver $observator) { $observatori = array(); foreach($this->observatori as $observatorul) { if($observatorul != $observator) $observatori[] = $observatorul; } $this->observatori = $observatori; } /** * metoda care notifica obiectele de tip observator */ function notify() { foreach($this->observatori as $observator) { $observator->update($this); } } /** * metoda care face modificarea in clasa * * @param int $val */ function update($val) { echo 'facem update... '; $this->val = $val; $this->notify(); } /** * metoda publica care intoarce statusul obiectului * * @return int */ function getStatus() { return $this->val; } } /** * o clasa observator */ class Observator implements SplObserver { function update(SplSubject $subiect) { echo $subiect->getStatus(); } } // instanta observator $observator = new Observator(); // instanta subiect $subiect = new Actiune(); // atasare observator la subiect $subiect->attach($observator); // update subiect $subiect->update(5);
Ce mi se pare mie ciudat este ca nu exista o documentatie pentru aceste interfetele din SPL. Chiar pe site-ul zend exista un articol PHP Patterns: The Observer Pattern care nu foloseste SPL, iar asta in masura in care exista documentatie pentru namespaces chiar inainte sa apara PHP 5.3.