我們知道,php中,foreach可以很方便地對可迭代結構(例如數組,再如對象)進行迭代操作:
foreach( $array as $elem){var_dump($elem);}
foreach($obj as $key=>$value){echo "$key=>$value".PHP_EOL;}
因而我們想:如果對於一個執行個體化對象,對其進行foreach操作,會發生什麼事情呢?
首先我們定義的基礎類為:
Class Test{/* one public variable */public $a;public $b;/* one private variable */private $c;public function __construct(){$this->a = "public";$this->b = "public";$this->c = "private";}public function traverseInside(){foreach($this as $key=>$value){echo $key."=>".$value.EOL;}}}
然後我們執行個體化該類,對其進行迭代,並與內部迭代的結果進行比較:
$test = new Test;echo "<hr>";echo "traverse outside:".EOL;foreach( $test as $key=>$value ){echo $key."=>".$value.EOL;}echo "<hr>";echo "traverse inside:".EOL;$test->traverseInside();
迭代的結果為:
可以看出:外部foreach迴圈的結果,只是將對象的公有屬性(public)迴圈出來了,而對於私人屬性(private),外部foreach是無法迴圈出來的。因而我們如果想要在外部通過foreach迴圈出類的所有的屬性(公有的和私人的),僅僅依靠foreach是不行的,必須要對類進行“改造”。如何對類進行改造呢?如果你瞭解foreach的實現(參考laruence的部落格:http://www.laruence.com/2008/11/20/630.html),那麼可以很輕鬆地找到相應的方案。另外一方面,《設計模式-可複用物件導向軟體設計的基礎》中也提到:通過將對象的訪問和遍曆從對象中分離出來並放入一個迭代器對象中,迭代器模式可以實現以不同的方式對對象進行遍曆。我們暫時不去深挖這句話的意思,只要知道,使用迭代器可以對對象進行遍曆即可。
PHP手冊<預定義介面>部分指出:要實現迭代器模式,需要在可迭代對象中實現如下介面:
abstractpublicmixedcurrent( void )abstractpublicscalarkey( void )abstractpublicvoidnext( void )abstractpublicvoidrewind( void )abstractpublicbooleanvalid( void )
有了這個。實現迭代器模式就很方便了,一個簡單的執行個體如下:
class TestIterator implements Iterator { private $point = 0; private $data = array( "one","two","three", ); public function __construct() { $this->point = 0; } function rewind() { $this->point = 0; } function current() { return $this->data[$this->point]; } function key() { return $this->point; } function next() { ++$this->point; } function valid() { return isset($this->data[$this->point]); }}$it = new TestIterator;foreach($it as $key => $value) { echo $key, $value; echo "\n";}
當然,使用了迭代器的對象可以以如下方式進行遍曆:
$it = new TestIterator;$it->rewind();while ($it->valid()){ $key = $it->key(); $value = $it->current(); echo "$key=>$value"; $it->next();}
最後附上YII中ListIterator(顧名思義,實現對List的迭代操作的迭代器)的實現:
<?php/** * CListIterator class file. * * @author Qiang Xue <qiang.xue@gmail.com> * @link http://www.yiiframework.com/ * @copyright Copyright 2008-2011 Yii Software LLC * @license http://www.yiiframework.com/license/ *//** * CListIterator implements an interator for {@link CList}. * * It allows CList to return a new iterator for traversing the items in the list. * * @author Qiang Xue <qiang.xue@gmail.com> * @version $Id$ * @package system.collections * @since 1.0 */class CListIterator implements Iterator{/** * @var array the data to be iterated through */private $_d;/** * @var integer index of the current item */private $_i;/** * @var integer count of the data items */private $_c;/** * Constructor. * @param array $data the data to be iterated through */public function __construct(&$data){$this->_d=&$data;$this->_i=0;$this->_c=count($this->_d);}/** * Rewinds internal array pointer. * This method is required by the interface Iterator. */public function rewind(){$this->_i=0;}/** * Returns the key of the current array item. * This method is required by the interface Iterator. * @return integer the key of the current array item */public function key(){return $this->_i;}/** * Returns the current array item. * This method is required by the interface Iterator. * @return mixed the current array item */public function current(){return $this->_d[$this->_i];}/** * Moves the internal pointer to the next array item. * This method is required by the interface Iterator. */public function next(){$this->_i++;}/** * Returns whether there is an item at current position. * This method is required by the interface Iterator. * @return boolean */public function valid(){return $this->_i<$this->_c;}}