SPL Standard PHP Library Marcus Brger PHP Quebec
SPL Standard PHP Library Marcus Börger PHP Quebec 2006 Marcus Börger SPL - Standard PHP Library
SPL - Standard PHP Library þ Discuss overloadable engine features þ Learn about SPL aka Standard PHP Library Marcus Börger SPL - Standard PHP Library 2
From engine overloading. . . þ Zend engine 2. 0+ allows to overload the following þ by implementing interfaces þ Foreach by implementing Iterator, Iterator. Aggregate þ Array access by implementing Array. Access þ Serializing by implementing Serializable þ by providing magic functions þ Function invocation by method __call() þ Property access by methods __get() and __set() þ Automatic loading of classes by function __autoload() Marcus Börger SPL - Standard PHP Library 3
. . . to SPL It is easy in a complex way - Lukas Smith php conference 2004 þ A collection of standard interfaces and classes Most of which based around engine overloading þ A few helper functions Marcus Börger SPL - Standard PHP Library 4
What is SPL about & what for þ Captures some common patterns þ Advanced Iterators þ Functional programming þ File and directory handling þ Makes __autoload() useable þ Exception hierarchy with documented semantics Marcus Börger SPL - Standard PHP Library 5
What are Iterators þ Iterators are a concept to iterate anything that contains other things. þ Iterators allow to encapsulate algorithms Marcus Börger SPL - Standard PHP Library 6
What are Iterators þ Iterators are a concept to iterate anything that contains other things. Examples: þ þ þ þ Values and Keys in an array. Array. Object, Array. Iterator Text lines in a file Spl. File. Object Files in a directory [Recursive]Directory. Iterator XML Elements or Attributes ext: Simple. XML, DOM Database query results ext: PDO, SQLite, My. SQLi Dates in a calendar range PECL/date (? ) Bits in an image ? Iterators allow to encapsulate algorithms Marcus Börger SPL - Standard PHP Library 7
What are Iterators þ Iterators are a concept to iterate anything that contains other things. Examples: þ þ þ þ Values and Keys in an array. Array. Object, Array. Iterator Text lines in a file Spl. File. Object Files in a directory [Recursive]Directory. Iterator XML Elements or Attributes ext: Simple. XML, DOM Database query results ext: PDO, SQLite, My. SQLi Dates in a calendar range PECL/date (? ) Bits in an image ? Iterators allow to encapsulate algorithms þ Classes and Interfaces provided by SPL: Append. Iterator, Caching. Iterator, Limit. Iterator, Filter. Iterator, Empty. Iterator, Infinite. Iterator, No. Rewind. Iterator, Outer. Iterator, Parent. Iterator, Recursive. Iterator, Seekable. Iterator, Spl. File. Object, . . . Marcus Börger SPL - Standard PHP Library 8
Array vs. Iterator þ An array in PHP þ þ þ $ar = array() can be rewound: reset($ar) is valid unless it's key is NULL: !is_null(key($ar)) have current values: current($ar) have keys: key($ar) can be forwarded: next($ar) Something that is traversable $it = new Iterator; þ may know how to be rewound: $it->rewind() (does not return the element) þ should know if there is a value: $it->valid() þ may have a current value: $it->current() þ may hava a key: $it->key() (may return NULL at any time) þ can forward to its next element: $it->next() Marcus Börger SPL - Standard PHP Library 9
How Iterators work þ Iterators can be used manually <? php $o = new Array. Iterator(array(1, 2, 3)); $o->rewind(); while ($o->valid()) { $key = $o->key(); $val = $o->current(); // some code $o->next(); } ? > þ Iterators can be used implicitly with foreach <? php $o = new Array. Iterator(array(1, 2, 3)); foreach($o as $key => $val) { // some code } ? > Marcus Börger SPL - Standard PHP Library 10
The big difference þ Arrays þ require memory for all elements þ allow to access any element directly þ Iterators þ þ only know one element at a time only require memory for the current element forward access only Access done by method calls þ Containers þ require memory for all elements þ allow to access any element directly þ can create external Iterators or are internal Iterators Marcus Börger SPL - Standard PHP Library 11
The basic concepts þ Iterators can be internal or external also referred to as active or passive þ An internal iterator modifies the object itself þ An external iterator points to another object without modifying it þ PHP always uses external iterators at engine-level þ Iterators may iterate over other iterators Marcus Börger SPL - Standard PHP Library 12
PHP Iterators þ þ þ Anything that can be iterated implements Traversable Objects implementing Traversable can be used in foreach User classes cannot implement Traversable Iterator. Aggregate is for objects that use external iterators Iterator is for internal traversal or external iterators Marcus Börger SPL - Standard PHP Library 13
Implementing Iterators Marcus Börger SPL - Standard PHP Library 14
Overloading Array access þ PHP provides interface Array. Access þ Objects that implement it behave like normal arrays (only in terms of syntax though) þ Array. Access does not allow references (the following is an error) interface function } Marcus Börger Array. Access { &offset. Get($offset); offset. Set($offset, &$value); offset. Exists($offset); offset. Unset($offset); SPL - Standard PHP Library 15
Array and property traversal þ Array. Objectallows external traversal of arrays þ Array. Objectcreates Array. Iteratorinstances þ Multiple Array. Iteratorinstances can reference the same target with different states þ Both implement Seekable. Iteratorwhich allows to 'jump' to any position in the Array directly. Marcus Börger SPL - Standard PHP Library 16
Array and property traversal Marcus Börger SPL - Standard PHP Library 17
Functional programming? þ þ Abstract from the actual data (types) Implement algorithms without knowing the data Example: Sorting requires a container for elements Sorting requires element comparison Containers provide access to elements Sorting and Containers must not know data Marcus Börger SPL - Standard PHP Library 18
An example þ þ Reading a menu definition from an array Writing it to the output Problem Handling of hierarchy Detecting recursion Formatting the output Marcus Börger SPL - Standard PHP Library 19
Recursion with arrays þ A typical solution is to directly call array functions No code reuse possible <? php function recurse_array($ar) { // do something before recursion reset($ar); while (!is_null(key($ar))) { // probably do something with the current element if (is_array(current($ar))) { recurse_array(current($ar)); } // probably do something with the current element // probably only if not recursive next($ar); } // do something after recursion } ? > Marcus Börger SPL - Standard PHP Library 20
Detecting Recursion þ An array is recursive If the current element itself is an Array In other words current() has children This is detectable by is_array() Recursing requires creating a new wrapper instance for the child array þ Recursive. Iterator is the interface to unify Recursion þ Recursive. Iterator handles the recursion þ þ class Recursive. Array. Iterator extends Array. Iterator implements Recursive. Iterator { function has. Children() { return is_array($this->current()); } function get. Children() { return new Recursive. Array. Iterator($this->current()); } } Marcus Börger SPL - Standard PHP Library 21
Debug Session <? php $a = array('1', '2', array('31', '32'), '4'); $o = new Recursive. Array. Iterator($a); $i = new Recursive. Iterator($o); foreach($i as $key => $val) { echo "$key => $valn"; } ? > 0 1 3 => => => 1 2 31 32 4 <? php class Recursive. Array. Iterator implements Recursive. Iterator { protected $ar; function __construct(Array $ar) { $this->ar = $ar; } function rewind() { reset($this->ar); } function valid() { return !is_null(key($this->ar)); } function key() { return key($this->ar); } function current() { return current($this->ar); } function next() { next($this->ar); } function has. Children() { return is_array(current($this->ar)); } function get. Children() { return new Recursive. Array. Iterator($this->current()); } } ? > Marcus Börger SPL - Standard PHP Library 22
Recursive. Iterator Marcus Börger SPL - Standard PHP Library 23
Making Array. Object recursive þ Change class type of Array. Objects Iterator We simply need to change get. Iterator() <? php class Recursive. Array. Object extends Array. Object { function get. Iterator() { return new Recursive. Array. Iterator($this); } } ? > Marcus Börger SPL - Standard PHP Library 24
How does our Menu look? þ þ þ The basic interface is Menu. Item A Menu. Entry is the basic element of class Menu A Menu stores one or more Menu. Item objects A Sub. Menu is a Menu and a Menu. Item A Menu. Iterator shall iterate Menu and Sub. Menu è è è Menu can store Menu. Entry and Sub. Menu can store in a Menu. Entry or Sub. Menu. Item should know whether it has children Menu is a Iterator. Aggregate Menu. Iterator is a Recursive. Iterator Marcus Börger SPL - Standard PHP Library 25
How does our Menu look? þ The general interface for menu entries þ Only talking to entries through this interface ensures the code works no matter what we later add or change interface Menu. Item { /** @return string representation of item (e. g. name/link)*/ function __to. String(); /** @return whether item has children */ function get. Children(); /** @return children of the item if any available */ function has. Children(); /** @return whether item is active or grayed */ function is. Active(); /** @return whether item is visible or should be hidden */ function is. Visible(); } /** @return the name of the entry if any */ function get. Name(); Marcus Börger SPL - Standard PHP Library 26
How does our Menu look? þ We need a storage for the items þ Either extend Recursive. Array. Iterator þ Or use an array an implement Iterator. Aggregate class Menu implements Iterator. Aggregate { public $_ar = array(); // PHP does not support friend function add. Item(Menu. Item $item) { $this->_ar[$item->get. Name()] = $item; return $item; } function get. Iterator() { return new Menu. Iterator($this); } } Marcus Börger SPL - Standard PHP Library 27
How does our Menu look? þ Extend Recursive. Array. Iterator but be typesafe þ Ensure get. Children() returns the correct type þ Elements are non arrays þ Recursion works slightly different þ Override has. Children() to not use is_array() þ Keep existing get. Children() and other iterator methods class Recursive. Array. Iterator Menu. Iterator extends Recursive. Array. Iterator { extends Array. Iterator implements Recursive. Iterator { function __construct(Menu $menu) { function parent: : __construct($menu->_ar); has. Children() { } return is_array($this->current()); function has. Children() { } function returnget. Children() $this->current()->has. Children(); { } if return (empty($ref)) new Recursive. Array. Iterator($this->current()); $this->ref = new Reflection. Class($this); } } return $ref->new. Instance($this->current()); } } protected $ref; } Marcus Börger SPL - Standard PHP Library 28
How does our Menu look? class Menu. Entry implements Menu. Item { protected $name, $link, $active, $visible; function __construct($name, $link = NULL) { $this->name = $name; $this->link = is_numeric($link) ? NULL : $link; $this->active = true; $this->visible = true; } function __to. String() { if (strlen($this->link)) { return '<a href="'. $this->link. '">'. $this>name. '</a>'; } else { return $this->name; } } function has. Children() { return false; } function get. Children() { return NULL; } function is. Active() { return $this->active; } function is. Visible() { return $this->visible; } function get. Name() { return $this->name; } Marcus Börger SPL - Standard PHP Library 32
How does our Menu look? class Sub. Menu extends Menu implements Menu. Item { protected $name, $link, $active, $visible; function __construct($name = NULL, $link = NULL) { $this->name = $name; $this->link = is_numeric($link) ? NULL : $link; $this->active = true; $this->visible = true; } function __to. String() { if (strlen($this->link)) { return '<a href="'. $this->link. '">'. $this>name. '</a>'; } else if (strlen($this->name)) { return $this->name; } else return ''; } function has. Children() { return true; } function get. Children() { return new Menu. Iterator($this); } function is. Active() { return $this->active; } function is. Visible() { return $this->visible; } function get. Name() { return $this->name; } Marcus Börger SPL - Standard PHP Library 33
How to create a menu þ To create a Menu we manually call add. Item() þ We need to keep track of the level in local temp vars <? php $menu = new Menu(); $menu->add. Item(new Menu. Entry('Home')); $sub = new Sub. Menu('Downloads'); $sub->add. Item(new Menu. Entry('')); $menu->add. Item($sub); ? > Marcus Börger SPL - Standard PHP Library 34
Reading a menu from an array þ þ We'd need to foreach the array and do recursion Recursive. Iterator helps with events class Recursive. Iterator { /** @return $this->get. Inner. Iterator()->has. Children()*/ function call. Has. Children() /** @return $this->get. Inner. Iterator()->get. Children()*/ function call. Get. Children() /** Called if recursing into children */ function begin. Children() /** called after last children */ function end. Children() /** called if a new element is available */ function next. Element() //. . . } Marcus Börger SPL - Standard PHP Library 35
Reading a menu from array class Menu. Load. Array extends Recursive. Iterator { protected $sub = array(); function __construct(Menu $menu, Array $def) { $this->sub[0] = $menu; parent: : __construct( new Recursive. Array. Iterator($def, self: : LEAVES_ONLY)); } function call. Get. Children() { $child = parent: : call. Get. Children(); $this->sub[] = end($this->sub)->add. Item(new Sub. Menu()); return $child; Provide some storage for the } menu, its sub menus and their function end. Children() { sub menus. array_pop($this->sub); } function next. Element() { end($this->sub)->add. Item( new Menu. Entry($this->current(), $this->key())); } } $def = array('1', '2', array('31', '32'), '4'); $menu = new Menu(); foreach(new Menu. Load. Array($menu, $def) as $v); Marcus Börger SPL - Standard PHP Library 36
Reading a menu from array class Menu. Load. Array extends Recursive. Iterator { protected $sub = array(); function __construct(Menu $menu, Array $def) { $this->sub[0] = $menu; parent: : __construct( new Recursive. Array. Iterator($def, self: : LEAVES_ONLY)); } function call. Get. Children() { $child = parent: : call. Get. Children(); $this->sub[] = end($this->sub)->add. Item(new Sub. Menu()); return $child; Menu. Load. Array controls the } recursive iteration. . . function end. Children() { array_pop($this->sub); …a recursive structure. } function next. Element() { end($this->sub)->add. Item( new Menu. Entry($this->current(), $this->key())); } } $def = array('1', '2', array('31', '32'), '4'); $menu = new Menu(); foreach(new Menu. Load. Array($menu, $def) as $v); Marcus Börger SPL - Standard PHP Library 37
Reading a menu from array class Menu. Load. Array extends Recursive. Iterator { protected $sub = array(); function __construct(Menu $menu, Array $def) { $this->sub[0] = $menu; parent: : __construct( new Recursive. Array. Iterator($def, self: : LEAVES_ONLY)); } function call. Get. Children() { $child = parent: : call. Get. Children(); $this->sub[] = end($this->sub)->add. Item(new Sub. Menu()); return $child; When recursing we create a new } unnamed Sub. Menu and make it function end. Children() { the new top level element of our array_pop($this->sub); 'level' storage. } function next. Element() { end($this->sub)->add. Item( new Menu. Entry($this->current(), $this->key())); } } $def = array('1', '2', array('31', '32'), '4'); $menu = new Menu(); foreach(new Menu. Load. Array($menu, $def) as $v); Marcus Börger SPL - Standard PHP Library 38
Reading a menu from array class Menu. Load. Array extends Recursive. Iterator { protected $sub = array(); function __construct(Menu $menu, Array $def) { $this->sub[0] = $menu; parent: : __construct( new Recursive. Array. Iterator($def, self: : LEAVES_ONLY)); } function call. Get. Children() { $child = parent: : call. Get. Children(); $this->sub[] = end($this->sub)->add. Item(new Sub. Menu()); return $child; At the end of a sub array in our } case representing a sub menu function end. Children() { when pop that sub menu thus array_pop($this->sub); going to it's parent menu. } function next. Element() { end($this->sub)->add. Item( new Menu. Entry($this->current(), $this->key())); } } $def = array('1', '2', array('31', '32'), '4'); $menu = new Menu(); foreach(new Menu. Load. Array($menu, $def) as $v); Marcus Börger SPL - Standard PHP Library 39
Reading a menu from array class Menu. Load. Array extends Recursive. Iterator { protected $sub = array(); function __construct(Menu $menu, Array $def) { $this->sub[0] = $menu; parent: : __construct( new Recursive. Array. Iterator($def, self: : LEAVES_ONLY)); } function call. Get. Children() { $child = parent: : call. Get. Children(); $this->sub[] = end($this->sub)->add. Item(new Sub. Menu()); return $child; All elements in our definition that } are not sub arrays are meant to function end. Children() { end up as entries so we only array_pop($this->sub); want leaves as elements. } function next. Element() { end($this->sub)->add. Item( new Menu. Entry($this->current(), $this->key())); } } $def = array('1', '2', array('31', '32'), '4'); $menu = new Menu(); foreach(new Menu. Load. Array($menu, $def) as $v); Marcus Börger SPL - Standard PHP Library 40
Reading a menu from array class Menu. Load. Array extends Recursive. Iterator { protected $sub = array(); function __construct(Menu $menu, Array $def) { $this->sub[0] = $menu; parent: : __construct( new Recursive. Array. Iterator($def, self: : LEAVES_ONLY)); } function call. Get. Children() { $child = parent: : call. Get. Children(); $this->sub[] = end($this->sub)->add. Item(new Sub. Menu()); return $child; Now let us use thing to fill in } the menu from the definition in function end. Children() { the array $def. array_pop($this->sub); } function next. Element() { end($this->sub)->add. Item( new Menu. Entry($this->current(), $this->key())); } } $def = array('1', '2', array('31', '32'), '4'); $menu = new Menu(); foreach(new Menu. Load. Array($menu, $def) as $v); Marcus Börger SPL - Standard PHP Library 41
Output HTML þ Problem how to format the output using </ul> Detecting recursion begin/end class Menu. Output extends Recursive. Iterator { function __construct(Menu $menu) { parent: : __construct($menu); } function begin. Children() { // called after childs rewind() is called echo str_repeat(' ', $this>get. Depth()). "<ul>n"; } function end. Children() { // right before child gets destructed echo str_repeat(' ', $this>get. Depth()). "</ul>n"; } } Marcus Börger SPL - Standard PHP Library 42
Output HTML þ þ Problem how to write the output Echo the output within foreach The following works for our Array def <ul> <li>1</li> <li>2</li> <ul> <li>31</li> <li>32</li> </ul> <li>4</li> </ul> class Menu. Output extends Recursive. Iterator { function __construct(Recursive. Iterator $ar) { parent: : __construct($ar); } function begin. Children() { echo str_repeat(' ', $this->get. Depth()). "<ul>n"; } function end. Children() { echo str_repeat(' ', $this->get. Depth()). "</ul>n"; } } $def = array('1', '2', array('31', '32'), '4'); $menu = new Recursive. Array. Iterator($def); $it = new Menu. Output($menu); echo "<ul>n"; // for the intro foreach($it as $m) { echo str_repeat(' ', $it->get. Depth()+1)'<li>', $m, "</li>n"; } echo "</ul>n"; // for the outro Marcus Börger SPL - Standard PHP Library 43
Output HTML þ þ Problem how to write the output Echo the output within foreach The following works for our Menu <ul> <li>1</li> <li>2</li> <ul> <li>31</li> <li>32</li> </ul> <li>4</li> </ul> class Menu. Output extends Recursive. Iterator { function __construct(Menu $ar) { parent: : __construct($ar); } function begin. Children() { echo str_repeat(' ', $this->get. Depth()). "<ul>n"; } function end. Children() { echo str_repeat(' ', $this->get. Depth()). "</ul>n"; } } $def = array('1', '2', array('31', '32'), '4'); $menu = new Menu(); foreach(new Menu. Load. Array($menu, $def) as $v); $it = new Menu. Output($menu); echo "<ul>n"; // for the intro foreach($it as $m) { echo str_repeat(' ', $it->get. Depth()+1)'<li>', $m, "</li>n"; } echo "</ul>n"; // for the outro Marcus Börger SPL - Standard PHP Library 44
Wow - but why? þ Why did we use SPL here? þ More reliability þ Fix one time – no problem in finding all incarnations þ Easier to change something without touching other stuff þ Functional separation þ Code ruse þ Responsability control Marcus Börger SPL - Standard PHP Library 45
Outer. Iterator þ Outer. Iterator is the interface for iterator wrapper þ Allows read access to its inner iterator interface Outer. Iterator extends Iterator { function get. Inner. Iterator(); } Marcus Börger SPL - Standard PHP Library 46
Iterator þ Iterator is an unspecified iterator wrapper class Iterator implements Outer. Iterator { function __construct(Traversable $iter, $classname) { $this->iterator = $iter; } function get. Inner. Iterator() { return $this->iterator; } function valid() {return $this->iterator->valid(); } function key() {return $this->iterator->key(); } function current() {return $this->iterator->current(); } function next() {return $this->iterator->next(); } function rewind() {return $this->iterator->rewind(); } function __call($func, $params) { return call_user_func_array($this->itator, $func), $params); } private $iterator; SPL - Standard PHP Library 47 }Marcus Börger
Filtering Problem Only recurse into active Menu. Item elements Only show visible Menu. Item elements Changes prevent recurse_array from <? php reuse class Menu. Item Menu { function is. Active() // return true if active function is. Visible() // return true if visible } function recurse_array($ar) { // do something before recursion while (!is_null(key($ar))) { if (is_array(current($ar))) (is_array(current($ar)) && { current($ar)->is. Active()) { recurse_array(current($ar)); } if (current($ar)->current()->is. Active()) { // do something } next($ar); } // do something after recursion } ? > Marcus Börger SPL - Standard PHP Library 48
Filtering Solution to filter the incoming data Unaccepted data simply needs to be skipped Do not accept inactive menu elements Using a Filter. Iterator interface Menu. Item { //. . . function is. Active() // return true if active function is. Visible() // return true if visible } Marcus Börger SPL - Standard PHP Library 49
Filter. Iterator þ Filter. Iterator is an abstract Outer. Iterator þ Constructor takes an Iterator (called inner iterator) þ Any iterator operation is executed on the inner iterator þ For every element accept() is called Inside the call current()/key() are valid è All you have to do is implement accept() þ Recursive. Filter. Iterator is also available Marcus Börger SPL - Standard PHP Library 50
Filter. Iterator Marcus Börger SPL - Standard PHP Library 51
Debug Session <? php $a = array(1, 2, 5, 8); $i = new Even. Filter(new My. Iterator($a)); foreach($i as $key => $val) { echo "$key => $valn"; } ? > 1 => 2 3 => 8 <? php class Even. Filter extends Filter. Iterator { function __construct(Iterator $it) { parent: : __construct($it); } function accept() { return $this->current() % 2 == 0; } } class My. Iterator implements Iterator { function __construct($ar) { $this->ar = $ar; } function rewind() { reset($this->ar); } function valid() { return !is_null(key($this->ar)); } function current() { return current($this->ar); } function key() { return key($this->ar); } function next() { next($this->ar); } } ? > Marcus Börger SPL - Standard PHP Library 52
Filtering þ Using a Filter. Iterator <? php class Menu. Filter extends Recursive. Filter. Iterator { function __construct(Menu $m) { parent: : __construct($m); } function accept() { return $this->current()->is. Visible(); } function has. Children() { return $this->current()->has. Children() && $this->current()->is. Active(); } function get. Children() { return new Menu. Filter( $this->current()->get. Children()); } } ? > Marcus Börger SPL - Standard PHP Library 53
Putting it together þ Make Menu. Output operate on Menu. Filter Pass a Menu to the constructor (guarded by type hint) Create a Menu. Filter from the Menu. Filter implements Recursive. Iterator We could also use a special Menu. Filter/Menu proxy We could also have Menu as an interface of Menu. Filter class Menu. Output extends Recursive. Iterator { function __construct(Menu $m) { parent: : __construct(new Menu. Filter($m)); } function begin. Children() { echo "<ul>n"; } function end. Children() { echo "</ul>n"; } } Marcus Börger SPL - Standard PHP Library 54
What now þ þ If your menu structure comes from a database If your menu structure comes from XML You have to change Menu or provide an alternative to Menu. Load. Array Detection of recursion works differently No single change in Menu. Output needed No single change in Menu. Filter needed Marcus Börger SPL - Standard PHP Library 55
Using PDO þ Change Menu to read from database PDO supports Iterator based access PDO can create and read into objects PDO is integrated since PHP 5. 1 <? php $db = new PDO("mysql: //. . . "); $stmt= $db->prepare("SELECT. . . FROM Menu. . . ", "Menu"); foreach($stmt->execute() as $m) { // fetch now returns Menu instances echo $m; // call $m->__to. String() } ? > Marcus Börger SPL - Standard PHP Library 56
Using XML þ Change Menu to inherit from Simple. XMLIterator þ Which is already a Recursive. Iterator þ We need to make it create Menu instances for children class Menu extends Simple. XMLIterator { static function factory($xml) { return simplexml_load_string($xml, 'Menu'); } function is. Active() { return $this['active']; // access attribute } function is. Visible() { return $this['visible']; // access attribute } // get. Children already returns Menu instances } Marcus Börger SPL - Standard PHP Library 57
Speaking of XML þ SPL makes Simple. XML recursion aware þ Use simplexml_load_(file|string) with 2 nd param <? php $xml = simplexml_load_file($argv[1], 'Simple. Xml. Iterator'); foreach(new Recursive. Iterator($xml) as $e) { if (isset($e['href'])) { echo $e['href']. "n"; } } ? > Marcus Börger SPL - Standard PHP Library 58
Speaking of XML þ SPL makes Simple. XML recursion aware þ Use simplexml_load_(file|string) with 2 nd param þ Or Simple. Xml. Iterator direct by constructor <? php $xml = new Simple. Xml. Iterator($argv[1], 0, true); foreach(new Recursive. Iterator($xml) as $e) { if (isset($e['href'])) { echo $e['href']. "n"; } } ? > Marcus Börger SPL - Standard PHP Library 59
Another example þ An Outer. Iterator may not pass data from its Inner. Iterator directly þ Provide a 404 handler that looks for similar pages þ Use Recursive. Directory. Iterator to test all files þ Use Filter. Iterator to skip all files with low similarity þ Sort by similarity -> convert iterated data into an array Marcus Börger SPL - Standard PHP Library 60
Looking for files þ In PHP 4 you would use standard directory funcs function search($path, $search, $limit, &$files) { if ($dir = @opendir($path)) { while (($found = readdir($dir) !== false) { switch(filetype("$path/$found")) { case 'file': if (($s = similariry($search, $found)) >= $limit) { $files["$path/$found"] = $s; } break; case 'dir': if ($found != '. ' && $found != '. . ') { search("$path/$found", $search, $limit, $files); } break; } } closedir($dir); } Marcus Börger SPL - Standard PHP Library 61
Looking for files þ PHP 5 offers Recursive. Directory. Iterator class Find. Similar extends Filter. Iterator { protected $search, $limit, $key; function __construct($root, $search, $limit) { parent: : __construct( new Recursive. Iterator( new Recursive. Directory. Iterator($root))); $this->search = $search; $this->limit = min(max(0, $limit), 100); // percentage } function current() { return similarity($this->search, $this->current()); } function key() { return $this->get. Sub. Pathname(); // $root stripped out } function accept() { return $this->is. File() && this->current()>=$this>limit; } } Marcus Börger SPL - Standard PHP Library 62
Looking for files þ Filtering the Recursive. Directory. Iterator class Find. Similar extends Filter. Iterator { protected $search, $limit, $key; function __construct($root, $search, $limit) { parent: : __construct( new Recursive. Iterator( new Recursive. Directory. Iterator($root))); $this->search = $search; $this->limit = min(max(0, $limit), 100); // percentage } function current() { return similarity($this->search, $this->current()); } function key() { return $this->get. Sub. Pathname(); // $root stripped out } function accept() { return $this->is. File() && this->current()>=$this>limit; } } Marcus Börger SPL - Standard PHP Library 63
Error 404. php þ Displaying alternatives in an error 404 handler <html> <head><title>File not found</title></head> <body> <? php if (array_key_exists('missing', $_REQUEST)) { $missing = urldecode($_REQUEST['missing']); url_split($missing, $protocol, $host, $path, $ext, $query); $it = new Find. Similar($path); $files = iterator_to_array($it, $missing, 35); asort($files); foreach($files as $file => $similarity) { echo "<a href='". $file. "'>"; echo $file. " [". $similarity. "%]</a><br/>"; } if (!count($files)) { echo "No alternatives were foundn"; } } ? > </body> </html> Marcus Börger SPL - Standard PHP Library 64
Error 404. php þ Sorting requires iterator to array conversion <html> <head><title>File not found</title></head> <body> <? php if (array_key_exists('missing', $_REQUEST)) { $missing = urldecode($_REQUEST['missing']); url_split($missing, $protocol, $host, $path, $ext, $query); $it = new Find. Similar($path); $files = iterator_to_array($it, $missing, 35); asort($files); foreach($files as $file => $similarity) { echo "<a href='". $file. "'>"; echo $file. " [". $similarity. "%]</a><br/>"; } if (!count($files)) { echo "No alternatives were foundn"; } } ? > </body> </html> Marcus Börger SPL - Standard PHP Library 65
More Iterators pliezzze Marcus Börger SPL - Standard PHP Library 66
Limiting iterators þ Limit. Iteratorallows to limit the returned values Compareable to LIMITof some SQL dialects þ You can specify the start offset þ You can specify the number of returned values þ When the inner Iterator is a Seekable. Iteratorthen method seek will be used. Otherwise seek operation will be manually. Marcus Börger SPL - Standard PHP Library 67
Limiting iterators Marcus Börger SPL - Standard PHP Library 68
Limits of the Limit. Iterator þ Here using Limi. Iterator != limited use <html> <head><title>File not found</title></head> <body> <? php if (array_key_exists('missing', $_REQUEST)) { $missing = urldecode($_REQUEST['missing']); url_split($missing, $protocol, $host, $path, $ext, $query); $it = new Find. Similar($path); $it = new Limit. Iterator($it, 10); $files = iterator_to_array($it, $missing, 35); asort($files); foreach($files as $file => $similarity) { echo "<a href='". $file. "'>"; echo $file. " [". $similarity. "%]</a><br/>"; } if (!count($files)) { echo "No alternatives were foundn"; } } ? > </body> </html> Marcus Börger SPL - Standard PHP Library 69
Appending Iterators þ Append. Iteratorallows to concatenate Iterators Compareable to SQL clause UNION þ Uses a private Array. Iteratorto store Iterators þ Append. Iterator: : append($it ) þ allows to append iterators þ does not call rewind() þ if $this is invalid $this will move to appended iterator Marcus Börger SPL - Standard PHP Library 70
Appending Iterators Marcus Börger SPL - Standard PHP Library 71
Getting rid of rewind þ No. Rewind. Iterator allows to omit rewind calls Especially helpful when appending with þ Array. Object: : append () þ Array. Iterator: : append () þ Append. Iterator: : append () if your code would otherwise force a rewind() Also helpfull when skipping a head part of iteration Marcus Börger SPL - Standard PHP Library 72
Getting rid of rewind() Marcus Börger SPL - Standard PHP Library 73
Limit and no rewind þ Example: Show the n-th set of filtered data $input = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); $len = 2; $set = 1; class Even. Filter extends Filter. Iterator { function accept() { return $this->current() % 2 == 0; } } $ar = new Even. Filter(new Array. Iterator($input)); $ar->rewind(); $ar = new No. Rewind. Iterator($ar); while(--$set >= 0) { foreach(new Limit. Iterator($ar, 0, $len) as $v) ; } foreach(new Limit. Iterator($ar, 0, $len) as $v) { echo "$vn"; } Marcus Börger SPL - Standard PHP Library 74
Limit and no rewind þ Provide Input data and a filter $input = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); $len = 2; $set = 1; class Even. Filter extends Filter. Iterator { function accept() { return $this->current() % 2 == 0; } } $ar = new Even. Filter(new Array. Iterator($input)); $ar->rewind(); $ar = new No. Rewind. Iterator($ar); while(--$set >= 0) { foreach(new Limit. Iterator($ar, 0, $len) as $v) ; } foreach(new Limit. Iterator($ar, 0, $len) as $v) { echo "$vn"; } Marcus Börger SPL - Standard PHP Library 75
Limit and no rewind þ Must rewind before wrapping in No. Rewind. Iterator $input = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); $len = 2; $set = 1; class Even. Filter extends Filter. Iterator { function accept() { return $this->current() % 2 == 0; } } $ar = new Even. Filter(new Array. Iterator($input)); $ar->rewind(); $ar = new No. Rewind. Iterator($ar); while(--$set >= 0) { foreach(new Limit. Iterator($ar, 0, $len) as $v) ; } foreach(new Limit. Iterator($ar, 0, $len) as $v) { echo "$vn"; } Marcus Börger SPL - Standard PHP Library 76
Limit and no rewind þ Skip top n-1 sets $input = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); $len = 2; $set = 1; class Even. Filter extends Filter. Iterator { function accept() { return $this->current() % 2 == 0; } } $ar = new Even. Filter(new Array. Iterator($input)); $ar->rewind(); $ar = new No. Rewind. Iterator($ar); while(--$set >= 0) { foreach(new Limit. Iterator($ar, 0, $len) as $v) ; } foreach(new Limit. Iterator($ar, 0, $len) as $v) { echo "$vn"; } Marcus Börger SPL - Standard PHP Library 77
Limit and no rewind þ Showing/Using remaining data (n-th set) $input = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); $len = 2; $set = 1; class Even. Filter extends Filter. Iterator { function accept() { return $this->current() % 2 == 0; } } $ar = new Even. Filter(new Array. Iterator($input)); $ar->rewind(); $ar = new No. Rewind. Iterator($ar); while(--$set >= 0) { foreach(new Limit. Iterator($ar, 0, $len) as $v) ; } foreach(new Limit. Iterator($ar, 0, $len) as $v) { echo "$vn"; } Marcus Börger SPL - Standard PHP Library 78
Vacuity & Infinity Sometimes it is helpful to have þ Empty. Iteratoras a placeholder for no data þ Infinite. Iterator to endlessly repeat data in an iterator Marcus Börger SPL - Standard PHP Library 79
Vacuity & Infinity Marcus Börger SPL - Standard PHP Library 80
has. Next ? þ Caching. Iteratorcaches the current element þ This allows to know whether one more value exists þ Recursive. Caching. Iterator does this recursively þ This allows to draw tree graphics marcus@frodo /usr/src/php-cvs $ php ext/spl/examples/tree. php ext/spl |-CVS |-examples | |-CVS | -tests | -CVS -tests -CVS Marcus Börger SPL - Standard PHP Library 81
has. Next ? Marcus Börger SPL - Standard PHP Library 82
Parents only Marcus Börger SPL - Standard PHP Library 83
Conclusion so far þ Iterators require a new way of programming þ Iterators allow to implement algorithms abstracted from data þ Iterators promote code reuse þ Some things are already in SPL þ Filtering þ Handling recursion þ Limiting Marcus Börger SPL - Standard PHP Library 84
Files & Directories Marcus Börger SPL - Standard PHP Library 85
File and directory handling þ Spl. File. Info is the filesystem information base class þ get. ATime, get. CTime, get. MTime, is. Dir, is. File, is. Link þ get. Filename, get. Pathname þ get. Perms, get. Owner, get. INode, get. Type þ get. File. Info, get. Path. Info þ open. File Marcus Börger SPL - Standard PHP Library 86
File and directory handling class Spl. File. Info { private $fname; function __construct($file_name) { $this->fname = $file_name; } function get. Filename() {return basename($this->fname); } function get. Path() {return dirname($this->fname); function get. Pathname() {return $this->fname; } function __to. String(){return $this->get. Pathname(); } function is. Dir() {return function is. File() {return function is. Link() {return function get. ATime() {return function get. CTime() {return function get. MTime() {return function get. Size() {return // more file functions Marcus Börger } is_dir($this->fname); } is_file($this->fname); } is_link($this->fname); } file. ATime($this->fname); } file. CTime($this->fname); } file. MTime($this->fname); } fsize($this->fname); } SPL - Standard PHP Library 87
File and directory handling class Spl. File. Info { // continued private $info_class = 'Splfile. Info'; private $file_class = 'Spl. File. Object'; function get. File. Info($class_name = NULL) { if (!isset($class)) $class = $this->info_class; $r = new Reflection. Class($class); return $r->new. Instance($this->get. Filename()); } function open. File($mode = 'r') { $r = new Reflection. Class($this->file_class); return $r->new. Instance($this->get. Filename(), $mode); } function set. File. Class($class_name) { if ($class_name instanceof Spl. File. Info) $this->file_class = $class_name; } } Marcus Börger SPL - Standard PHP Library 88
File and directory handling þ Directory. Iterator for non recursive dir handling þ current() returns $this þ key() returns numeric index þ is. Dot() returns whether current entry is '. ' or '. . ' Marcus Börger SPL - Standard PHP Library 89
File and directory handling þ Recursive. Directory. Iterator goes into subdirs þ Supports different modes for key() and cirrent() CURRENT_AS_SELF = 0 KEY_AS_PATHNAME = 0 CURRENT_AS_PATHNAME KEY_AS_FILENAME CURRENT_AS_FILEINFO NEW_CURRENT_AND_KEY = KEY_AS_FILENAME | CURRENT_AS_FILEINFO Marcus Börger SPL - Standard PHP Library 90
Putting it to the tree? þ Example: Retrieving the hierarchy of a filesystem marcus@frodo /usr/src/php-cvs $ php ext/spl/examples/tree. php ext/spl |-CVS |-examples | |-CVS | -tests | -CVS -tests -CVS þ Need to recursively iterate over the filesystem è Recursive. Directory. Iterator þ Efficiently ignore files è Parent. Iterator þ On each level check whether more elements exist è Caching. Iterator Marcus Börger SPL - Standard PHP Library 91
Providing structure class Directory. Tree. Iterator extends Recursive. Iterator { function __construct($path) { parent: : __construct(new Recursive. Caching. Iterator( new Recursive. Directory. Iterator($path, Recursive. Directory. Iterator: : KEY_AS_FILENAME), Caching. Iterator: : CALL_TOSTRING), parent: : SELF_FIRST); } function current() { $cur = ""; for ($l = 0; $l < $this->get. Depth(); $l++) { $cur. = $this->get. Sub. Iterator($l)->has. Next() ? "| " : " "; } $l = $this->get. Sub. Iterator($l); return $cur. ($l->has. Next() ? "|-" : "-"). (string)$l; Marcus Börger SPL - Standard PHP Library 92 }
Like pieces of a puzzle þ Apply Parent. Iterator as filter class Directory. Graph. Iterator extends Directory. Tree. Iterator { function __construct($path) { parent: : __construct(new Recursive. Caching. Iterator( new Parent. Iterator( new Recursive. Directory. Iterator($path, Recursive. Directory. Iterator: : KEY_AS_FILENAME)), Caching. Iterator: : CALL_TOSTRING), parent: : SELF_FIRST); } } foreach(new Directory. Graph. Iterator($argv[1]) as $file) { echo $file. "n"; } Marcus Börger SPL - Standard PHP Library 93
File and directory handling þ Spl. File. Object allows to access files as an iterator þ Allows to skip empty lines þ Allows to retrieve lines as csv (5. 2, 5. 1. 4? ) Marcus Börger SPL - Standard PHP Library 94
At Last some Hints þ Reflection of a built-in class php --rc <Class> þ Reflection of a loaded extension php --re <Extension> þ List of all SPL classes php –r 'print_r(array_keys(spl_classes())); ' Marcus Börger SPL - Standard PHP Library 95
THANK YOU þ This Presentation http: //somabo. de/talks/ þ SPL Documentation http: //php. net/~helly Marcus Börger SPL - Standard PHP Library 96
- Slides: 93