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 :)