Gondoltam készítek egy részletes, de korántsem teljes leírást a PHP iterátorairól.
Az iterátorokkal kapcsolatos interfészek és osztályok az SPL (Standard PHP Library) csomagban találhatóak.
A PHP-s iterátorok az Iterator interfészen alapulnak, azt implementálják. Sok érdekesség nincs benne, a szokásos iterátorokkal kapcsolatos metódusokat szedi össze: current, key, next, rewind, valid.
Üres iterátor.
Tömbökön és objektumokon iterálhatunk végig.
Példa:
class Example { public $a; protected $b; private $_c; public function __construct($a, $b, $c) { $this->a = $a; $this->b = $b; $this->_c = $c; } } echo 'Iterate over an object', PHP_EOL; $iterator = new ArrayIterator(new Example(1, 2, 3)); foreach ($iterator as $item) { echo $item, PHP_EOL; } echo 'Iterate over an array', PHP_EOL; $iterator = new ArrayIterator(array(1,2,3)); foreach ($iterator as $item) { echo $item, PHP_EOL; }
Kimenet:
Iterate over an object 1 Iterate over an array 1 2 3
A Traversable interfészt implementáló objektumokból iterátort csinál.
Iterator, amit nem lehet rewindolni, azaz, ha egyszer végigmentünk az összes elemen, nem lehet újra kezdeni az iterálást.
Olyan iterátor, amellyel a végtelenségig iterálhatunk az elemeken, nem kell a rewindot meghívnunk.
Egy iterátor elemeinek részhalmazán mehetünk végig. A konstruktorában egy iterátoron kivül megadhatunk egy offset és egy count paramétert is.
Példa:
$limitIterator = new LimitIterator( new ArrayIterator(range(0, 10)), 5, 3 ); foreach ($limitIterator as $item) { echo $item, PHP_EOL; }
Kimenet:
5 6 7
A FilterIterator egy belső iterátor elemiből tud szűrni. A FilterIterator egy abstract osztály, amelyben egy darab abstract metódus van, az accept(). Az accept() visszatérési értéke boolean:
- true esetén az iterator elfogadja az elemet
- false esetén eldobja
Példa:
class OddFilterIterator extends FilterIterator { public function accept() { $current = $this->current(); return is_integer($current) && $current % 2; } } $iterator = new AppendIterator(); $iterator->append(new ArrayIterator(range(0, 10))); $iterator->append(new ArrayIterator(array('a', 'b'))); $filterIterator = new OddFilterIterator($iterator); foreach ($filterIterator as $item) { echo $item, PHP_EOL; }
Kimenet:
1 3 5 7 9
Több belső iteratort tárol, és azokon megy végig, egymás után.
A getArrayIterator() egy ArrayIterator-ban visszaadja az összes belső iteratort.
Példa:
$iterator1 = new ArrayIterator(range(0, 3)); $iterator2 = new ArrayIterator(range(4, 6)); $appendIterator = new AppendIterator(); $appendIterator->append($iterator1); $appendIterator->append($iterator2); foreach ($appendIterator as $key => $value) { echo $key, ' => ', print_r($value, true), PHP_EOL; } echo PHP_EOL, 'getArrayIterator()', PHP_EOL, PHP_EOL; foreach ($appendIterator->getArrayIterator() as $key => $value) { echo $key, ' => ', print_r($value, true), PHP_EOL; }
Kimenet:
0 => 0 1 => 1 2 => 2 3 => 3 0 => 4 1 => 5 2 => 6 getArrayIterator() 0 => ArrayIterator Object ( [storage:ArrayIterator:private] => Array ( [0] => 0 [1] => 1 [2] => 2 [3] => 3 ) ) 1 => ArrayIterator Object ( [storage:ArrayIterator:private] => Array ( [0] => 4 [1] => 5 [2] => 6 ) )
Az AppendIterator-hoz hasonlóan itt is több iterátoron mehetünk végig, de kicsit másképpen. Minden belső iterátorból kivesz egy elemet, majd egy tömbben összegyűjtve adja vissza iterációnként.
Flagek:
- MultipleIterator::MIT_KEYS_NUMERIC: a tömb numerikusan indexelt
- MultipleIterator::MIT_KEYS_ASSOC: a tömb asszociatív. A kulcsok az iterátorok hozzáadásánál adhatóak meg (attachIterator).
A belső iterátorok elemszáma különböző lehet, ezért két flaggel állíthatjuk be, hogy mi történjen akkor, ha valamelyikből elfogynak az elemek:
- MultipleIterator::MIT_NEED_ANY: annyi elemet ad vissza, amennyit tud, a hiányzó elemeket null-okkal tölti fel.
- MultipleIterator::MIT_NEED_ALL: ha valamelyik iterátorból elfogynak az elemek, a MultipleIterator leáll.
Példa:
$arrayIterator1 = new ArrayIterator(array('1','2','3','4',)); $arrayIterator2 = new ArrayIterator(array('5','6','7',)); $arrayIterator3 = new ArrayIterator(array('8','9','10','11',)); $miterator = new MultipleIterator( MultipleIterator::MIT_KEYS_ASSOC | MultipleIterator::MIT_NEED_ALL ); $miterator->attachIterator($arrayIterator1, 'foo'); $miterator->attachIterator($arrayIterator2, 'bar'); $miterator->attachIterator($arrayIterator3, 'baz'); foreach ($miterator as $item) { echo var_export($item, true), PHP_EOL; }
Kimenet:
Array ( [foo] => 1 [bar] => 5 [baz] => 8 ) Array ( [foo] => 2 [bar] => 6 [baz] => 9 ) Array ( [foo] => 3 [bar] => 7 [baz] => 10 )
Példa:
$arrayIterator1 = new ArrayIterator(array('1','2','3','4',)); $arrayIterator2 = new ArrayIterator(array('5','6','7',)); $arrayIterator3 = new ArrayIterator(array('8','9','10','11',)); $miterator = new MultipleIterator( MultipleIterator::MIT_KEYS_NUMERIC | MultipleIterator::MIT_NEED_ANY ); $miterator->attachIterator($arrayIterator1, 'foo'); $miterator->attachIterator($arrayIterator2, 'bar'); $miterator->attachIterator($arrayIterator3, 'baz'); foreach ($miterator as $item) { echo var_export($item, true), PHP_EOL; }
Kimenet:
Array ( [0] => 1 [1] => 5 [2] => 8 ) Array ( [0] => 2 [1] => 6 [2] => 9 ) Array ( [0] => 3 [1] => 7 [2] => 10 ) Array ( [0] => 4 [1] => [2] => 11 )
Egy könyvtár elemein mehetünk végig vele. Az elemek SplFileInfo objektumok.
Könyvtár szerkezet az iterátorok bemutatására:
|-example/b | \-example/b/b.txt |-example/c \-example/a |-example/a/e | \-example/a/e/e.txt |-example/a/a.txt \-example/a/d
Példa:
$iterator = new DirectoryIterator('example'); foreach ($iterator as $value) { echo $value->getFilename(), PHP_EOL; }
Kimenet:
b .. c . a
Hasonló a DirectoryIterator-hoz, abból öröklődik. A konstructor alap esetben az alábbi flageket kapja:
- KEY_AS_PATHNAME
- CURRENT_AS_FILEINFO
- SKIP_DOTS
További flagek:
- CURRENT_AS_PATHNAME: a current() az aktuális file elérésével tér vissza
- CURRENT_AS_FILEINFO: a current() egy SplFileInfo objektummal tér vissza
- CURRENT_AS_SELF: a current() az iterator-ral ($this) tér vissza
- KEY_AS_PATHNAME: a key() az aktuális file elérésével tér vissza
- KEY_AS_FILENAME: a key() az aktuális file nevével tér vissza
- FOLLOW_SYMLINKS: a hasChildren() követi a linkeket
- NEW_CURRENT_AND_KEY: ugyanaz mint a FilesystemIterator::KEY_AS_FILENAME | FilesystemIterator::CURRENT_AS_FILEINFO
- SKIP_DOTS: átugorja a .-ot és a ..-ot
- UNIX_PATHS: az elérési utakban /-t használ, a PHP-t futtató OS-től függetlenül (pl Windows-on is)
A glob() függvényhez hasonló funkcionalitással rendelkező iterátor.
A rekurzív iterátorokat "linearizálja", azaz nem kell a gyermekekkel foglalkozni (ellenőrizni, hogy van e, és ha igen, akkor azokon is végigmenni).
Három módon használható:
- RecursiveIteratorIterator::LEAVES_ONLY: Csak a leveleken megy végig. Ez a default.
- RecursiveIteratorIterator::SELF_FIRST - Minden elemen végig megy, a szülőkkel kezd.
- RecursiveIteratorIterator::CHILD_FIRST: Minden elemen végig megy, a levelekkel kezd.
Segítségével RecursiveIterator-okból lehet kiszűrni azon elemeket, amelyeknek nincs gyermekük. A RecursiveDirectoryIterator példához visszanyúlva, listázzuk ki csak a könyvtárakat:
$directoryIterator = new RecursiveDirectoryIterator( 'example', RecursiveDirectoryIterator::SKIP_DOTS ); $treeIterator = new RecursiveTreeIterator( new ParentIterator($directoryIterator) ); foreach ($treeIterator as $node) { echo $node . PHP_EOL; };
Kimenet:
Az üres könyvtárakat azért mutatja, mert ugye minden könyvtárban van egy hivatkozás önmagára (.) és a szülő könyvtárra (..), tehát van gyerekük.
|-example/b |-example/c \-example/a |-example/a/e \-example/a/d
A DirectoryIterator-hoz hasonlóan itt is egy könyvtár elemein mehetünk végig, de rekurzívan.
Péda:
$directoryIterator = new RecursiveDirectoryIterator( 'example', RecursiveDirectoryIterator::SKIP_DOTS ); $treeIterator = new RecursiveTreeIterator( $directoryIterator ); foreach ($treeIterator as $key => $node) { echo $node, PHP_EOL; }
Kimenet:
|-example/b | \-example/b/b.txt |-example/c \-example/a |-example/a/e | \-example/a/e/e.txt |-example/a/a.txt \-example/a/d
Szinte az összes (itt nem is említett) iterátornak van rekurzív párja:
- RecursiveArrayIterator
- RecursiveCachingIterator
- RecursiveCallbackFilterIterator
- RecursiveFilterIterator
- RecursiveRegexIterator
- RecursiveTreeIterator
A cikknek példákkal együtt van saját repoja: php-iterators
Lehet jönni forkolni, javítani, bővíteni :)