对象迭代

如未特别标明,文章均属原创。转载请注明出处

by : 柳公子 http://huliuqing.github.io

php官网手册对于遍历对象解释如下:

PHP 5 提供了一种定义对象的方法使其可以通过单元列表来遍历,例如用 foreach 语句。默认情况下,所有可见属性都将被用于遍历。

不过使用简单的foreach遍历对象,仅仅能够遍历出对象实例中可见(public)属性的值。

foreach是如何实现对对象进行遍历的呢?

我们的鸟哥在关于一笔试题(Iterator模式) 一文中分享过foreach源码实现。这边我就拿来主义了:

switch (zend_iterator_unwrap(array, &iter TSRMLS_CC)) {
    default:
        case ZEND_ITER_INVALID:
               .....
               break
        case ZEND_ITER_PLAIN_OBJECT: {
                ......
            break;
     case ZEND_ITER_PLAIN_ARRAY:
            .....
            break;

        case ZEND_ITER_OBJECT:
            ......
            break;
}

具体如何使用foreach遍历对象,查看示例:

<?php

class Student{
    private $_identityCard = '360401190001011018';
    public  $_name = '三丰';
    public  $_age  = 800;

    protected $_bloodType = 'O';

    public function __construct(){}
}

$s = new Student();

foreach($s as $key => $v){
    echo 'key ',$key ,' val ',$v ,'</br>';
}

//输出
key :_name val 三丰
key :_age val 800

从输出的结果来看,实例化对象的所有可见(public)属性都给遍历输出出来了。

但是,当我们需要遍历那些非可见对象属性,明显简单的使用foreach遍历对象就不够用了。

好在PHP中有提供内置遍历接口IteratorIteratorAggregate完成对象的遍历工作。 关于Iterator与IteratorAggregate接口如下:

Iterator extends Traversable {
    /* 方法 */
    abstract public mixed current ( void )//返回当前元素。 
    abstract public scalar key ( void )   //返回当前元素的键
    abstract public void next ( void )    //移动当前位置到下一个元素
    abstract public void rewind ( void )  //返回到迭代器的第一个元素
    abstract public boolean valid ( void )//此方法在 Iterator::rewind() 和 Iterator::next()
                                          //方法之后被调用以此用来检查当前位置是否有效
}

IteratorAggregate extends Traversable {
    /* 方法 */
    abstract public Traversable getIterator ( void )//返回一个外部迭代器
}

我们可以通过实现这两个迭代器接口,定义自己的对象遍历规则

1、通过实现IteratorAggregate接口,完成对象属性遍历

我们只需要实现IteratorAggregate中getIterator()方法完成遍历。

class Kungfu implements IteratorAggregate{
    public $_name='飞鸿';
    public $_age = 121;

    protected $_bloodType = 'O';
    private $_isMarried = true;

    public function __construct(){}

    public function getIterator(){
        $members = get_class_vars(__CLASS__);

        return new ArrayIterator($members);
    }
}


$k = new Kungfu();

foreach($k as $key => $v){
    echo 'key ',$key ,' val ',$v ,'</br>';
}
/*
    key _name val 飞鸿
    key _age val 121
    key _bloodType val O
    key _isMarried val 1
*/

2、通过实现Iterator接口,完成对象属性遍历

相对于IteratorAggregate而言,Iterator接口实现起来稍微麻烦一些,需要在实现类中实现所有5个抽象方法。

不过如果我们需要对迭代的对象,进行具体控制的话,使用Iterator的话则更合适。

class Language implements Iterator{
    private $_lang;
    private $_counter=0;

    public function __construct(){      
        $this->_lang = explode(',', 'PHP,C++,C,GO,JAVA,JAVASCRIPT,RUBY,Node.js');
    }

    public function rewind(){
        $this->_counter = 0;
    }

    public function current(){
        //当编程语言的值为JAVA时,返回空
        if($this->_lang[$this->_counter] == 'JAVA'){
            return null;
        }
        return $this->_lang[$this->_counter];
    }

    public function valid(){
        return isset( $this->_lang[$this->_counter]);
    }

    public function next(){
        ++$this->_counter;
    }
    public function key(){
        return $this->_counter;
    }
}

$m = new Language();

foreach($m as $key => $v){
    echo 'key ',$key ,' val ',$v ,'</br>';
}

//结果输出
key 0 val PHP
key 1 val C++
key 2 val C
key 3 val GO
key 4 val 
key 5 val JAVASCRIPT
key 6 val RUBY
key 7 val Node.js

接口Iterator中抽象方法的执行顺序如下图所示: