PHP标准类库系列 Iterator

迭代器,让对象可用 foreach,for,while方法

啥是 Iterator

是一种 php 提供的接口标准,可以将不同的类迭代调用统一成一样的方法,打个比方(话说谁叫比方?):
这是调用 mysql 时取出资源的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$dbms='mysql';
$host='localhost';
$dbName='wechat';
$user='root';
$pass='123123';
$dsn="$dbms:host=$host:33060;dbname=$dbName;";
try {
$dbh = new PDO($dsn, $user, $pass); //初始化一个PDO对象
foreach ($dbh->query('SELECT * from migrations;') as $row) {
print_r($row);
}
$dbh = null;
} catch (PDOException $e) {
die ("Error!: " . $e->getMessage() . "<br/>");
}

而读取目录下所有文件的方法是:

1
2
3
4
5
$dirPath = '/Users/gpf/Desktop';
$list = opendir($dirPath);
while($filename = readdir($list)){
echo $filename . PHP_EOL;
}

而读取文件内容的方法是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$filePath = '/Users/gpf/Desktop/test.php';
$handler = @fopen($filePath,'r');
if(!$handler){
die('error');
}
while(!feof($handler)){
$line = fgets($handler);
echo $line;
}
foreach(new SplFileObject($filePath) as $line){
echo $line . PHP_EOL;
}

都是做的循环的事但是不统一,这就很烦躁,因为同样一件事你需要用不同的方法去实现,一个字要用四种写法
这种情况下在 php51 之后开发组也意识到问题了,于是就出现了 SPL,就有了一个通用的 Iterator
这个迭代器就是让不同的资源操作变得相同,这些接口可以自己实现,也可以用官方提供的一些迭代器
比如读取文本的迭代器就可以这样获取文本内容了:

1
2
3
4
5
6
7
8
$filePath = '/Users/gpf/Desktop/test.php';
$handler = new SplFileObject($filePath);
foreach($handler as $line){
echo $line . PHP_EOL;
}
//当然也可以直接跳行查找
$handler->seek(4);
var_dump($handler->current());

这样调用就显得比较统一了,那么

怎么使用 Iterator?

这里参照了php 文档阮一峰的博客
写的一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class IteratorDemo implements Iterator{
private $attributes = [];
protected $valid = FALSE;
function __construct(array $array)
{
$this->attributes = $array;
}
function rewind()
{
var_dump(__METHOD__);
$this->valid = (FALSE !== reset($this->attributes));
}
function current()
{
var_dump(__METHOD__);
return current($this->attributes);
}
function key()
{
var_dump(__METHOD__);
return key($this->attributes);
}
//如果 next 返回 false 循环将终止
function next()
{
var_dump(__METHOD__);
$this->valid = (FALSE !== next($this->attributes));
}
function valid()
{
var_dump(__METHOD__);
return $this->valid;
}
}
$arr = [
'red' => 'stop',
'green' => 'run',
'yellow' => 'wait'
];
$demo = new IteratorDemo($arr);
var_dump($demo);
foreach ($demo as $key => $value){
echo $key . '=>' . $value . PHP_EOL;
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
object(IteratorDemo)#1 (2) {
["attributes":"IteratorDemo":private]=>
array(3) {
["red"]=>
string(4) "stop"
["green"]=>
string(3) "run"
["yellow"]=>
string(4) "wait"
}
["valid":protected]=>
bool(false)
}
string(20) "IteratorDemo::rewind"
string(19) "IteratorDemo::valid"
string(21) "IteratorDemo::current"
string(17) "IteratorDemo::key"
red=>stop
string(18) "IteratorDemo::next"
string(19) "IteratorDemo::valid"
string(21) "IteratorDemo::current"
string(17) "IteratorDemo::key"
green=>run
string(18) "IteratorDemo::next"
string(19) "IteratorDemo::valid"
string(21) "IteratorDemo::current"
string(17) "IteratorDemo::key"
yellow=>wait
string(18) "IteratorDemo::next"
string(19) "IteratorDemo::valid"

这就实现了一个通用的 Iterator 接口,当然这是是为了实现而实现,中间可以做很多事情的,这要发挥一下想象力

在程序里经常看到 FooIterator 这类的对象,就是基于上面写的 Iterator 进行的延伸,比如刚才我们
例子中用到的SplFileObject,SimpleXMLIterator,ArrayIterator这些类,都是大同小异的,
不过是根据实现的迭代器添加一些更适用的方法,比如一个DirectoryIterator中就是继承了SplFileInfo的方法
并且实现了 Iterator 接口,再添加了一些自己的方法,有用 idea 的可以用代码追踪看一下也是蛮有意思的

参考资料:

  1. Iterator(迭代器)接口
  2. PHP SPL笔记
这个博客有个赞赏功能,不试一下吗?