Traversable
遍历接口
在介绍Iterator
之前,不得不介绍PHP的另外一个用于遍历的预定义接口Traversable
。
Traversable
接口是PHP内部的迭代器的抽象基类,只用于内部使用。一般我们用它来检测一个类是否可以使用foreach进行遍历。
$myarray = array('one', 'two', 'three');
$myobj = (object)$myarray;
if ( !($myarray instanceof \Traversable) ) {
print "myarray is NOT Traversable";
}
if ( !($myobj instanceof \Traversable) ) {
print "myobj is NOT Traversable";
}
foreach ($myarray as $value) {
print $value;
}
foreach ($myobj as $value) {
print $value;
}
上面的例子输出为:
myarray is NOT Traversable
myobj is NOT Traversable
one
two
three
one
two
three
需要注意到,虽然array没有继承Traversable
,但是它仍然可以使用foreach遍历,所以我们一般使用下面的方法检测一个变量是否能够用foreach遍历:
if (!is_array(items) && !items instanceof \Traversable) {
// 不能用foreach遍历,可以抛出异常
}
Iterator
迭代器接口
定义
Iterator
是PHP预定义的可在内部迭代自己的外部迭代器或类的接口。实现该接口的类,可以使用foreach进行遍历,该接口在内部是继承自上面介绍的Traversable
接口。
Iterator
接口的定义如下:
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
接口并且实现上面定义的5个遍历基本方法,它们分别控制着foreach遍历时的行为。
实现方法
下面展示如何实现Iterator
接口,从而能够让foreach正确遍历自定义类:
<?php
class UIterator implements Iterator {
private position = 0; // 定义遍历时候的索引
privatedata = array(
'first_element',
'second_element',
'last_element'
);
// 构造函数,初始化位置索引
public function __construct()
{
this->position = 0;
}
// 初始化位置索引
public function rewind()
{
var_dump(__METHOD__);this->position = 0;
}
// 获取当前元素
public function current()
{
var_dump(__METHOD__);
return this->data[this->position];
}
// 返回当前元素的键
public function key()
{
var_dump(__METHOD__);
return this->position;
}
// 返回下一个元素
public function next()
{
var_dump(__METHOD__);
++this->position;
}
// 返回当前元素是否有效
public function valid()
{
var_dump(__METHOD__);
return isset(this->data[this->position]);
}
}
it = new UIterator();
foreach (it as key =>value) {
var_dump(key,value);
echo "\n";
}
以上例子输出结果为:
string(18) "myIterator::rewind"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(0)
string(12) "first_element"
string(16) "myIterator::next"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(1)
string(13) "second_element"
string(16) "myIterator::next"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(2)
string(11) "last_element"
string(16) "myIterator::next"
string(17) "myIterator::valid"
显然foreach中各个方法调用的顺序如下:
- 首次调用
rewind
方法获得开始的键 - 调用
valid
方法查看当前索引是否有效 - 调用
current
方法获得当前的元素 - 调用
key
方法获得当前元素的键,如果foreach中没有使用key=>value的形式,则不会调用该方法 - 重复上面的过程,知道
valid
方法返回false,键无效则结束
IteratorAggregate
聚合式迭代器接口
定义
你可能会对上面的Iterator
是PHP预定义的可在内部迭代自己的外部迭代器或类的接口疑惑,其实迭代器分为可在内部迭代和不可内部迭代,换种说法,可在内部迭代相当于可以在内部自定义实现,反之则不然。
IteratorAggregate
接口创建外部迭代器的接口,仅仅只是返回一个外部迭代器实例,实现该接口并不能像Iterator
自定义迭代行为。它的定义如下:
IteratorAggregate extends Traversable {
abstract public Traversable getIterator ( void ) // 获取一个外部迭代器
}
显然,该接口只能返回一个迭代器实例,并不能像Iterator
那样自定义迭代行为,如定义迭代元素等。
使用方法
<?php
class myData implements IteratorAggregate {
public property1 = "Public property one";
publicproperty2 = "Public property two";
public property3 = "Public property three";
public function __construct() {this->property4 = "last property";
}
public function getIterator() {
// 返回一个数组迭代器实例
return new ArrayIterator(this);
}
}obj = new myData;
foreach(obj askey => value) {
var_dump(key, $value);
echo "\n";
}
上面实例的输出结果如下:
string(9) "property1"
string(19) "Public property one"
string(9) "property2"
string(19) "Public property two"
string(9) "property3"
string(21) "Public property three"
string(9) "property4"
string(13) "last property"
可见返回一个ArrayIterator
实例,就相当于把类的属性当成了一个数组。
其中ArrayIterator
是PHP内部定义的迭代器,是一个数组迭代器,还记得上面的Iterator
接口的那个实例吗?我们可以改一下使得它更简单:
<?php
class UIAIterator implements IteratorAggregate {
private position = 0; // 定义遍历时候的索引
privatedata = array(
'first_element',
'second_element',
'last_element'
);
public function getIterator()
{
return new ArrayIterator(this->data);
}
}it = new UIAIterator();
foreach (it askey => value) {
var_dump(key, $value);
echo "\n";
}
上面的类UIAIterator
和UIterator
的效果是一样的,都能够用foreach正确遍历类内部的data数组。而且UIAIterator
更加简单。
当然,在实现IteratorAggregate
接口的时候,除了可以返回ArrayIterator
迭代器,还可以返回其他的PHP内部迭代器,比如SPL系列迭代器,甚至自定义迭代器。