[关闭]
@a5635268 2016-02-19T17:19:19.000000Z 字数 12124 阅读 1249

【SPL标准库专题(2)】 Iterator

SPL


Iterator界面

本段内容来自阮一峰老师再加自己的部分注解

SPL规定,所有部署了Iterator界面的class,都可以用在foreach Loop中。Iterator界面中包含5个必须部署的方法:

* current()

This method returns the current index's value. You are solely
responsible for tracking what the current index is as the 
interface does not do this for you. (返回当前索引值)

* key()

This method returns the value of the current index's key. For 
foreach loops this is extremely important so that the key 
value can be populated. (返回当前的索引key)

* next()

This method moves the internal index forward one entry. (迭代中的内部指针往前进一步)

* rewind()

This method should reset the internal index to the first element. (重置迭代中的内部指针)

* valid()

This method should return true or false if there is a current 
element. It is called after rewind() or next(). (验证内部指针是否到最后一行)

Example

  1. class ArrayReloaded implements Iterator {
  2. /**
  3. * 如前一篇文章所说,该类实现了Iterator接口,所以该类对象就是ZEND_ITER_OBJECT,对于ZEND_ITER_OBJECT的类对象,会通过调用对象实现的Iterator接口相关函数来进行foreach。
  4. */
  5. private $array = array();
  6. private $valid = FALSE;
  7. function __construct($array) {
  8. $this->array = $array;
  9. }
  10. function rewind(){
  11. /**
  12. * reset: 将数组的内部指针指向第一个单元,如果数组为空则返回false;
  13. * 所以下述语句表示: 数组不为空并且已重置到第一个单元;
  14. */
  15. $this->valid = (FALSE !== reset($this->array));
  16. }
  17. function current(){
  18. return current($this->array);
  19. }
  20. function key(){
  21. return key($this->array);
  22. }
  23. function next(){
  24. /**
  25. * next: 将数组中的内部指针向前移动一位
  26. * 返回数组内部指针指向的下一个单元的值,或当没有更多单元时返回 FALSE。
  27. * 所以下述语句表示: 如果还有下一个单元的话,指针移动到下个单元并返回true;
  28. */
  29. $this->valid = (FALSE !== next($this->array));
  30. }
  31. function valid(){
  32. return $this->valid;
  33. }
  34. #↑↑ 以上5个方法是必须实现的接口方法,也可以再扩展prev和end等方法,后续会介绍一些SPL内置的实现了itertor接口的类,这些类可以拿来直接使用
  35. }
  36. $arr = array(
  37. 'color1' => 'red',
  38. 'color2' => 'blue',
  39. 'color3' => 'green',
  40. 'color4' => 'plack',
  41. 'color5' => 'purple'
  42. );
  43. $colors = new ArrayReloaded($arr);
  44. # 通过foreach来遍历
  45. foreach($colors as $k => $v){
  46. echo $k.':'.$v.'<br />';
  47. }
  48. # 通过while来遍历
  49. /**
  50. * 1: foreach的内部实现方式其实也是如此,事实上直接用while来遍历性能更高
  51. * 2: 在使用迭代器来遍历的时候,一定要记住要rewind和next,而PHP的foreach遍历早已把rewind和next给集成了;
  52. */
  53. echo '<hr />';
  54. $colors -> rewind();
  55. while($colors -> valid()){
  56. echo $colors -> key().':'.$colors -> current().'<br />';
  57. $colors -> next();
  58. }

ArrayIterator

  1. ArrayIterator implements ArrayAccess , SeekableIterator , Countable , Serializable

这是一个非常有用的迭代器,里面实现了排序,添加,筛选等foreach不能直接实现的方法(都是要全部遍历出来再进行判断处理,代码不优雅维护性差)

Example

  1. <?php
  2. $arr = array(
  3. 'color1' => 'red',
  4. 'color3' => 'green',
  5. 'color4' => 'plack',
  6. 'color2' => 'blue',
  7. 'color5' => 'purple'
  8. );
  9. // $colors = new ArrayIterator($arr); //可以直接通过实例一个数组迭代器对象,然后while这个迭代器;
  10. //但以下的方式要更易于扩展
  11. //先实例一个array对象
  12. $colorsObj = new ArrayObject($arr);
  13. $it = $colorsObj -> getIterator(); //获得当前的ArrayIterator
  14. # 通过while来遍历
  15. $it -> rewind();
  16. while($it -> valid()){
  17. echo $it -> key().':'.$it -> current().'<br />';
  18. $it -> next();
  19. }
  20. #通过iterator迭代器来遍历就变得很灵活
  21. echo $colorsObj -> count(); //元素数量统计
  22. //从第三个开始遍历
  23. $it -> rewind(); //凡是要使用迭代器之前先重置;
  24. if($it -> valid()){
  25. $it -> seek(2); //从0开始的,第二个位置
  26. while($it -> valid()){
  27. echo $it -> key().':'.$it -> current().'<br />';
  28. $it -> next();
  29. }
  30. }
  31. //对索引名进行升序排列
  32. $it -> ksort();
  33. foreach($it as $k => $v){
  34. echo $k .'-->'. $v.'<br />';
  35. }
  36. //对索引值进行排序
  37. $it -> asort();
  38. foreach($it as $k => $v){
  39. echo $k .'-->'. $v.'<br />';
  40. }
  41. //这些对象方法是否很熟悉? 这就是上一篇文章中说到的 "SPL是一种使object(物体)模仿array(数组)行为的interfaces和classes"

AppendIterator

按顺序迭代访问几个不同的迭代器。例如,希望在一次循环中迭代访问两个或者更多的组合。这个迭代器的append方法类似于array_merge()函数来合并数组。

Example

  1. $arr1 = array(
  2. 'color1' => 'red',
  3. 'color3' => 'green',
  4. 'color4' => 'plack',
  5. 'color2' => 'blue',
  6. 'color5' => 'purple'
  7. );
  8. $arr2 = array(
  9. 'fruit1' => 'apple',
  10. 'fruit2' => 'orange',
  11. 'fruit3' => 'banana',
  12. 'fruit4' => 'grape',
  13. 'fruit5' => 'strawberry',
  14. );
  15. $ao1 = new ArrayIterator($arr1);
  16. $ao2 = new ArrayIterator($arr2);
  17. $iterator = new AppendIterator();
  18. $iterator -> append($ao1);
  19. $iterator -> append($ao2);
  20. foreach($iterator as $k => $v){
  21. echo $k.':'.$v,'<br>';
  22. }

MultipleIterator

迭代器的链接器,更多参考连接 http://php.net/manual/en/class.multipleiterator.php

  1. $person_id = new ArrayIterator(array('001', '002', '003'));
  2. $person_name = new ArrayIterator(array('张三', '李四', '王五'));
  3. $person_age = new ArrayIterator(array(22, 23, 11));
  4. $mit = new MultipleIterator(MultipleIterator::MIT_KEYS_ASSOC);
  5. $mit->attachIterator($person_id, "ID");
  6. $mit->attachIterator($person_name, "NAME");
  7. $mit->attachIterator($person_age, "AGE");
  8. echo"连接的迭代器个数:".$mit->countIterators() . "\n"; //3
  9. foreach ($mit as $person) {
  10. print_r($person);
  11. }
  12. /**output
  13. Array
  14. (
  15. [ID] => 001
  16. [NAME] => 张三
  17. [AGE] => 22
  18. )
  19. Array
  20. (
  21. [ID] => 002
  22. [NAME] => 李四
  23. [AGE] => 23
  24. )
  25. Array
  26. (
  27. [ID] => 003
  28. [NAME] => 王五
  29. [AGE] => 11
  30. )
  31. **/

LimitIterator

返回给定数量的结果以及从集合中取出结果的起始索引点

  1. <?php
  2. //相当于sql中的limit
  3. $fruitArr = array(
  4. 'apple',
  5. 'banana',
  6. 'cherry',
  7. 'damson',
  8. 'elderberry'
  9. );
  10. $fruits = new ArrayIterator($fruitArr);
  11. //从第一个开始取三个
  12. foreach (new LimitIterator($fruits, 0, 3) as $fruit) {
  13. var_dump($fruit);
  14. }
  15. //从第二个开始取到结束
  16. foreach (new LimitIterator($fruits, 2) as $fruit) {
  17. print_r($fruit);
  18. }
  19. /**output
  20. string(5) "apple"
  21. string(6) "banana"
  22. string(6) "cherry"
  23. cherrydamsonelderberry
  24. */

FilterIterator

基于OuterIterator接口,用于过滤数据,返回符合条件的元素。必须实现一个抽象方法accept(),此方法必须为迭代器的当前项返回true或false

  1. class UserFilter extends FilterIterator{
  2. private $userFilter;
  3. public function __construct(Iterator $iterator , $filter){
  4. parent::__construct($iterator);
  5. //要过滤的参数
  6. $this->userFilter = $filter;
  7. }
  8. public function accept(){
  9. /*
  10. * getInnerIterator(): 获得内部的迭代器
  11. * current(): 然后获取当前的元素
  12. * in strcmp(string str1,string str2) 区分字符串中字母大小写地比较,返回0就相同
  13. * int strcasecmp(string str1,string str2) 忽略字符串中字母大小写地比较,返回0就相同
  14. * 如果accept返回false的话就过滤掉
  15. */
  16. $user = $this->getInnerIterator()->current();
  17. if (strcasecmp($user['name'] , $this->userFilter) == 0) {
  18. return false;
  19. }
  20. return true;
  21. }
  22. }
  23. $array = array (array ('name' => 'Jonathan' , 'id' => '5') , array ('name' => 'Abdul' , 'id' => '22'),array ('name' => 'zhouzhou' , 'id' => '9'));
  24. $object = new ArrayObject($array);
  25. //去除掉名为abdul的人员
  26. $iterator = new UserFilter($object->getIterator() , 'abdul');
  27. foreach ($iterator as $result) {
  28. echo $result['name'];
  29. }
  30. /**output
  31. * Jonathan
  32. **/

RegexIterator

继承FilterIterator,支持使用正则表达式模式匹配和修改迭代器中的元素。经常用于将字符串匹配。
更多参考: http://cn2.php.net/manual/zh/class.regexiterator.php

  1. //可以实现: preg_match_all(), preg_match(), preg_replace(),preg_split()等函数的功能
  2. $a = new ArrayIterator(array('test1', 'test2', 'test3'));
  3. $i = new RegexIterator($a, '/^(test)(\d+)/', RegexIterator::REPLACE);
  4. $i->replacement = '$2:$1';
  5. print_r(iterator_to_array($i));
  6. /**output
  7. Array
  8. (
  9. [0] => 1:test
  10. [1] => 2:test
  11. [2] => 3:test
  12. )
  13. **/

IteratorIterator

一种通用类型的迭代器,所有实现了Traversable接口的类都可以被它迭代访问。

CachingIterator

用来执行提前读取一个元素的迭代操作,例如可以用于确定当前元素是否为最后一个元素。

  1. $array = array ('koala' , 'kangaroo' , 'wombat' , 'wallaby' , 'emu' , 'kiwi' , 'kookaburra' , 'platypus');
  2. $object = new CachingIterator(new ArrayIterator($array));
  3. foreach ($object as $value) {
  4. echo $value;
  5. if ($object->hasNext()) {
  6. echo ','; //如果有下一项的话才输出 突出不了该迭代器的作用啊,其他迭代器也可以搞定的
  7. }
  8. }
  9. /**output
  10. * koala,kangaroo,wombat,wallaby,emu,kiwi,kookaburra,platypus
  11. **/

SeekableIterator

用于创建非顺序访问的迭代器,允许跳转到迭代器中的任何一点上。

  1. $array = array("apple", "banana", "cherry", "damson", "elderberry");
  2. $iterator = new ArrayIterator($array);
  3. $iterator->seek(3); //起始0从第3个开始取;
  4. echo $iterator->current().'<br />';
  5. /**output
  6. damson
  7. **/

NoRewindIterator

用于不能多次迭代的集合,适用于在迭代过程中执行一次性操作。

  1. $fruit = array('apple', 'banana', 'cranberry');
  2. $arr = new ArrayObject($fruit);
  3. $it = new NoRewindIterator($arr->getIterator());
  4. echo "Fruit A:\n";
  5. foreach ($it as $item) {
  6. echo $item . "\n";
  7. }
  8. echo "Fruit B:\n";
  9. // ↓↓ 由于NoRewindIterator没有rewind方法,所以foreach就不能用rewind重置游标,这个时候$it已经到最后了,所以为空;
  10. foreach ($it as $item) {
  11. echo $item . "\n";
  12. }
  13. /**output
  14. Fruit A:
  15. apple
  16. banana
  17. cranberry
  18. Fruit B:
  19. **/

EmptyIterator

一种占位符形式的迭代器,不执行任何操作。当要实现某个抽象类的方法并且这个方法需要返回一个迭代器时,可以使用这种迭代器。

InfiniteIterator

用于持续地访问数据,当迭代到最后一个元素时,会再次从第一个元素开始迭代访问。

  1. $arrayit = new ArrayIterator(array('cat', 'dog'));
  2. $infinite = new InfiniteIterator($arrayit);
  3. //必须限制否则就是死循环
  4. $limit = new LimitIterator($infinite, 0, 7);
  5. foreach ($limit as $value) {
  6. echo "$value\n";
  7. }

RecursiveArrayIterator

创建一个用于递归形式数组结构的迭代器,类似于多维数组.它为许多更复杂的迭代器提供了所需的操作,如RecursiveTreeIterator和RecursiveIteratorIterator迭代器。

  1. $fruits = array("a" => "lemon", "b" => "orange", array("a" => "apple", "p" => "pear"));
  2. $iterator = new RecursiveArrayIterator($fruits);
  3. while ($iterator->valid()) {
  4. //检查是否含有子节点
  5. if ($iterator->hasChildren()) {
  6. //输出所有字节点
  7. foreach ($iterator->getChildren() as $key => $value) {
  8. echo $key . ' : ' . $value . "\n";
  9. }
  10. } else {
  11. echo "No children.\n";
  12. }
  13. $iterator->next();
  14. }
  15. /**output
  16. No children.
  17. No children.
  18. a : apple
  19. p : pear
  20. **/

RecursiveIteratorIterator

将一个树形结构的迭代器展开为一维结构。

  1. $fruits = array("a" => "lemon", "b" => "orange", array("a" => "apple", "p" => "pear",'c' => ['a','b']));
  2. $arrayiter = new RecursiveArrayIterator($fruits);
  3. $iteriter = new RecursiveIteratorIterator($arrayiter);
  4. foreach ($iteriter as $key => $value) {
  5. $d = $iteriter->getDepth();
  6. echo "depth=$d k=$key v=$value\n";
  7. }
  8. /**output
  9. depth=0 k=a v=lemon
  10. depth=0 k=b v=orange
  11. depth=1 k=a v=apple
  12. depth=1 k=p v=pear
  13. depth=2 k=0 v=a
  14. depth=2 k=1 v=b
  15. **/

RecursiveTreeIterator

以可视在方式显示一个树形结构。

  1. $hey = array("a" => "lemon", "b" => "orange", array("a" => "apple", "p" => "pear"));
  2. $awesome = new RecursiveTreeIterator(
  3. new RecursiveArrayIterator($hey),
  4. null, null, RecursiveIteratorIterator::LEAVES_ONLY
  5. );
  6. foreach ($awesome as $line)
  7. echo $line . PHP_EOL;
  8. /**output
  9. |-lemon
  10. |-orange
  11. |-apple
  12. \-pear
  13. **/

ParentIterator

是一个扩展的FilterIterator迭代器,它可以过滤掉来自于RecursiveIterator迭代器的非父元素,只找出子节点的键值。通俗来说,就是去枝留叶。

  1. $hey = array ("a" => "lemon" , "b" => "orange" , array ("a" => "apple" , "p" => "pear"));
  2. $arrayIterator = new RecursiveArrayIterator($hey);
  3. $it = new ParentIterator($arrayIterator);
  4. print_r(iterator_to_array($it));
  5. /**output
  6. * Array
  7. * (
  8. * [0] => Array
  9. * (
  10. * [a] => apple
  11. * [p] => pear
  12. * )
  13. * )
  14. **/

RecursiveFilterIterator

是FilterIterator迭代器的递归形式,也要求实现抽象的accept()方法,但在这个方法中应该使用$this->getInnerIterator()方法访问当前正在迭代的迭代器。

  1. class TestsOnlyFilter extends RecursiveFilterIterator{
  2. public function accept(){
  3. // 找出含有“叶”的元素
  4. return $this->hasChildren() || (mb_strpos($this->current() , "叶") !== false);
  5. }
  6. }
  7. $array = array ("叶1" , array ("力2" , "叶3" , "叶4") , "叶5");
  8. $iterator = new RecursiveArrayIterator($array);
  9. $filter = new TestsOnlyFilter($iterator);
  10. $filter = new RecursiveIteratorIterator($filter);
  11. print_r(iterator_to_array($filter));
  12. /**output
  13. * Array
  14. * (
  15. * [0] => 叶1
  16. * [1] => 叶3 //只会找出含叶的元素,不会把元素成员全部显示出来
  17. * [2] => 叶5
  18. * )
  19. **/

RecursiveRegexIterator

是RegexIterator迭代器的递归形式,只接受RecursiveIterator迭代器作为迭代对象。

  1. $rArrayIterator = new RecursiveArrayIterator(array ('叶1' , array ('tet3' , '叶4' , '叶5')));
  2. $rRegexIterator = new RecursiveRegexIterator(
  3. $rArrayIterator , '/^叶/' , RecursiveRegexIterator::ALL_MATCHES
  4. );
  5. foreach ($rRegexIterator as $key1 => $value1) {
  6. if ($rRegexIterator->hasChildren()) {
  7. // print all children
  8. echo "Children: ";
  9. foreach ($rRegexIterator->getChildren() as $key => $value) {
  10. echo $value . " ";
  11. }
  12. echo "\n";
  13. } else {
  14. echo "No children\n";
  15. }
  16. }
  17. /**output
  18. * No children
  19. * Children: 叶4 叶5
  20. **/

RecursiveCachingIterator

在RecursiveIterator迭代器上执行提前读取一个元素的递归操作。

CallbackFilterIterator(PHP5.4)

同时执行过滤和回调操作,在找到一个匹配的元素之后会调用回调函数。

  1. $hey = array ("李1" , "叶2" , "叶3" , "叶4" , "叶5" , "叶6" ,);
  2. $arrayIterator = new RecursiveArrayIterator($hey);
  3. $isYe = function($current){
  4. return mb_strpos($current , '叶') !== false;
  5. };
  6. $rs = new CallbackFilterIterator($arrayIterator , $isYe);
  7. print_r(iterator_to_array($rs));
  8. /**output
  9. * Array
  10. * (
  11. * [0] => 叶2
  12. * [1] => 叶3
  13. * [2] => 叶4
  14. * [3] => 叶5
  15. * [4] => 叶6
  16. * )
  17. **/

RecursiveCallbackFilterIterator(PHP5.4)

在RecursiveIterator迭代器上进行递归操作,同时执行过滤和回调操作,在找到一个匹配的元素之后会调用回调函数。

  1. function doesntStartWithLetterT($current){
  2. $rs = $current->getFileName();
  3. return $rs[0] !== 'T';
  4. }
  5. $rdi = new RecursiveDirectoryIterator(__DIR__);
  6. $files = new RecursiveCallbackFilterIterator($rdi , 'doesntStartWithLetterT');
  7. foreach (new RecursiveIteratorIterator($files) as $file) {
  8. echo $file->getPathname() . PHP_EOL;
  9. }

DirectoryIterator

目录文件遍历器,提供了查询当前文件的所有信息的方法(是否可读可写,所属,权限等等),具体参考 http://cn2.php.net/manual/zh/class.directoryiterator.php

  1. $it = new DirectoryIterator("../");
  2. foreach ($it as $file) {
  3. //用isDot ()方法分别过滤掉“.”和“..”目录
  4. if (!$it->isDot()) {
  5. echo $file . "\n";
  6. }
  7. }

RecursiveDirectoryIterator

递归目录文件遍历器,可实现列出所有目录层次结构,而不是只操作一个目录。具体看:http://cn2.php.net/manual/zh/class.recursivedirectoryiterator.php

  1. //列出指定目录中所有文件
  2. $path = realpath('../');
  3. $objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path) , RecursiveIteratorIterator::SELF_FIRST);
  4. foreach ($objects as $name => $object) {
  5. echo "$name\n";
  6. }

FilesystemIterator

是DirectoryIterator的遍历器

  1. $it = new FilesystemIterator('../');
  2. foreach ($it as $fileinfo) {
  3. echo $fileinfo->getFilename() . "\n";
  4. }

GlobIterator

带匹配模式的文件遍历器

  1. $iterator = new GlobIterator('*.php');
  2. if (!$iterator->count()) {
  3. echo '无php文件';
  4. } else {
  5. $n = 0;
  6. printf("总计 %d 个php文件\r\n" , $iterator->count());
  7. foreach ($iterator as $item) {
  8. printf("[%d] %s\r\n" , ++ $n , $iterator->key());
  9. }
  10. }

SimpleXMLIterator

XMl文档访问迭代器,可实现访问xml中所有节点

  1. $xml = <<<XML
  2. <books>
  3. <book>
  4. <title>PHP Basics</title>
  5. <author>Jim Smith</author>
  6. </book>
  7. <book>XML basics</book>
  8. </books>
  9. XML;
  10. // SimpleXML转换为数组
  11. function sxiToArray($sxi){
  12. $a = array ();
  13. for ($sxi->rewind();$sxi->valid();$sxi->next()) {
  14. if (!array_key_exists($sxi->key() , $a)) {
  15. $a[$sxi->key()] = array ();
  16. }
  17. if ($sxi->hasChildren()) {
  18. $a[$sxi->key()][] = sxiToArray($sxi->current());
  19. } else {
  20. $a[$sxi->key()][] = strval($sxi->current());
  21. }
  22. }
  23. return $a;
  24. }
  25. $xmlIterator = new SimpleXMLIterator($xml);
  26. $rs = sxiToArray($xmlIterator);
  27. print_r($rs);
  28. /**output
  29. * Array
  30. * (
  31. * [book] => Array
  32. * (
  33. * [0] => Array
  34. * (
  35. * [title] => Array
  36. * (
  37. * [0] => PHP Basics
  38. * )
  39. * [author] => Array
  40. * (
  41. * [0] => Jim Smith
  42. * )
  43. * )
  44. * [1] => XML basics
  45. * )
  46. * )
  47. **/

参考链接:

http://www.ruanyifeng.com/blog/2008/07/php_spl_notes.html
http://www.cnblogs.com/ScriptZhang/archive/2010/05/25/1743875.html

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注