Skip to content

在上一篇文章中我们介绍了 php 的聚合迭代器接口,并对一些常见迭代器进行了简要的描述,正是这些 php 内置的迭代器,得以让我们应用的开发更具有灵活性,大大的提高了程序开发的速递。这一章节中我们将对其中的一些内置的迭代器做一个进一步的介绍。

OuterIterator 迭代器

OuterIterator 是一个迭代器接口它继承自 Iterator 迭代器,使用它你可以对多个对象进行迭代。除了来自 Iterator 迭代器的基本方法之外,OuterIterator 迭代器还额外提供了一个 getInnerIterator 方法用户获取当前迭代的对象,在实际的使用场景中我们往往不直接使用它二十使用它的实现 IteratorIterator 迭代器。它的使用场景为你需要对你的迭代对象进行额外的操作时。

php
<?php

class AnOuterIterator implements OuterIterator
{
    protected $iterators = [];
    protected $index = 0;

    public function __construct(Array $iterators)
    {
        $this->iterators = $iterators;
    }

    public function getInnerIterator()
    {
        return $this->iterators[$this->index];
    }

    public function current()
    {
        return $this->getInnerIterator()->current();
    }

    public function next()
    {
        $this->getInnerIterator()->next();
        if (!$this->getInnerIterator()->valid()) {
            if ($this->index iterators)) {
                $this->index++;
            } else {
                $this->getInnerIterator()->rewind();
            }
        }
    }

    public function valid()
    {
        return $this->index iterators);
    }

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

    public function key()
    {
        return $this->getInnerIterator()->key();
    }
}

$arrayIterator1 = new ArrayIterator([1, 2, 3, 4]);
$arrayIterator2 = new ArrayIterator([5, 6, 7, 8]);
$arrayIterator3 = new ArrayIterator([9, 10, 11, 12]);
$iterator = new AnOuterIterator([$arrayIterator1, $arrayIterator2, $arrayIterator3]);
foreach ($iterator as $key => $item) {
    var_dump($item);
}

IteratorIterator 迭代器

IteratorIterator 实际上是一个实现了 OuterIterator 迭代器的包装器。他接受一个实现了 Traversable 的包装器作为参数,你可以像使用普通的迭代器一样去使用它。

php
<?php

$arrayIterate = new ArrayIterator(['a','b','c','d']);
$iterate = new IteratorIterator($arrayIterate);
//获取传入的迭代器实例
var_dump($iterate->getInnerIterator());
//遍历迭代
for ($iterate->rewind();$iterate->valid();$iterate->next()){
    var_dump($iterate->current());
}

AppendIterator 迭代器

使用 AppendIterator 迭代器你将能够让你很方便的对迭代器进行顺序的遍历。AppendIterate 是来自 IteratorIterator 的子类,除了继承自使用 IteratorIterator 的方法外,他还额外提供了三个方法,他们的作用如下:

方法作用
append向 AppendIterator 迭代器追加一个迭代器
getIteratorIndex获取当前迭代的外部迭代器的序号
getArrayIterator获取通过 append 添加的 ArrayIterator 迭代器
php
<?php

$array_a = new ArrayIterator(['a', 'b', 'c']);
$array_b = new ArrayIterator(['d', 'e', 'f']);
//正则迭代器,在后边的内容我们将对他进行详细的介绍。
$array_c = new RegexIterator($array_a,'/^[acef]/');
$iterator = new AppendIterator();
$iterator->append($array_a);
$iterator->append($array_b);
foreach ($array_c as $item){
    var_dump($array_c->current());
}
$a = [];
var_dump($iterator->getArrayIterator());
for ($iterator->rewind();$iterator->valid();$iterator->next()){
    $currentIndex = $iterator->getIteratorIndex();
    if(in_array($currentIndex,$a)){
        var_dump($iterator->getInnerIterator());
        $a[] = $currentIndex;
    }
}

ArrayIterator 迭代器

ArrayIterator 迭代器是对 ArrayObject 的补充它为他提供了在遍历数组和对象时删除和更新值与键的能力。ArrayIterator 实际上实现了 ArrayAccess、CountAble、SeekableIterator 和 Serializable 所以它也具有了这些接口的能力。除了这些之外他还提供了一些额外的能力:

方法作用
asort按值排序
getArrayCopy获取传入迭代器数组的拷贝
getFlags获取传入迭代器的 flag 标识
ksort按键排序
natcasesort不区分大小写的排序
natsort自然排序
setflags设置行为标识
uasort使用用户定义的函数对值进行排序
uksort使用用户定义的函数对键进行排序
php
<?php

$iterator = new ArrayIterator(["111", '22', "111000", "123", "444", "55"]);
$iterator->asort();
$iterator->ksort();
var_dump($iterator->getFlags());
$iterator->setFlags(1);
var_dump($iterator->getFlags());

$iterator->natcasesort();
$iterator->natsort();
var_dump($iterator->offsetExists(6));
$iterator->uasort('u_asort');
$iterator->uasort('u_ksort');

for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
    var_dump($iterator->current());
}

function u_asort($a, $b)
{
    return $a > $b;
}

function u_ksort($a, $b)
{
    return $a rewind(); $iterator->hasNext(); $iterator->next()) {
    var_dump($iterator->current());
}
echo PHP_EOL;
foreach ($iterator as $item) {
    if ($iterator->hasNext()) {
        var_dump($iterator->current());
    }
}
var_dump($iterator->getCache());

FilterIterator 迭代器

FilterIterator 迭代器为你提供了对迭代对象进行过滤的能力,它继承自 IteratorIterator 并实现了 OuterIterator 接口,他还额外提供了一个 accept 方法,用来实现对当前迭代对象的过滤。

php
<?php

class AnFilterIterator extends FilterIterator
{
    public function accept()
    {
        if (parent::current() > 3) {
            return true;
        }
        return false;
    }
}

$arrayIterator = new ArrayIterator([1, 2, 3, 4, 5, 6, 7]);
$iterator = new AnFilterIterator($arrayIterator);
for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
    var_dump($iterator->current());
}

CallbackFilterIterator 迭代器

CallbackFilterIterator 迭代器继承自 FilterIterator 并实现了 OuterIterator 接口。与 FilterIterator 接口不同的是他的构造函数还接收一个额外的回调函数,用于处理对迭代的过滤。需要注意的是一旦你定义了 accept 方法用户自定义的 callback 方法将不会被调用。

php
<?php

class AnCallbackFilterIterator extends CallbackFilterIterator
{
    public function accept()
    {
        echo 'accept is called' . PHP_EOL;
        return $this->current() >= 3;
    }
}

function callback($value, $key, $iterator)
{
    echo "callback is called" . PHP_EOL;
    return $value >= 5;
}

$arrayIterator = new ArrayIterator([99, 2, 3, 4, 5, 6, 7]);
$iterator = new AnCallbackFilterIterator($arrayIterator, 'callback');
for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
    var_dump($iterator->current());
}

SeekableIterator 迭代器

SeekableIterator 是 Iterator 接口的实现接口,除了 Iterator 接口的原生能力外他还为你提供了通过键值查找迭代数值的能力。此外在查找的迭代数值不存在时你需要抛出一个 OutOfBoundsException 异常。

php
<?php

class AnSeekableIterator implements SeekableIterator
{
    private $array = [];
    private $position = 0;

    public function __construct(array $array)
    {
        $this->array = $array;
    }

    public function seek($position)
    {
        if (!isset($this->array[$this->position])) {
            throw new OutOfBoundsException("invalid seek position ($position)");
        }
        echo 'seek is called' . PHP_EOL;
        $this->position = $position;
    }

    public function current()
    {
        if (isset($this->array[$this->position])) {
            return $this->array[$this->position];
        }
        throw new OutOfBoundsException("invalid current position $this->position");
    }

    public function next()
    {
        $this->position++;
    }

    public function key()
    {
        return $this->position;
    }

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

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

try {
    $iterator = new AnSeekableIterator(['name', 'seven', 'sex', 'nan', 'age', '18']);
    $iterator->seek(1);
    var_dump($iterator->current());
    $iterator->seek(100);
    var_dump($iterator->current());
} catch (Exception $e) {
    var_dump($e->getMessage());
}

DirectoryIterator 迭代器

DirectoryIterator 迭代器继承自 SplFileInfo 并实现了 SeekableIterator 接口,通过它提供的一系列接口你可以很方便的对目录进行遍历。 方法列表:

方 法描述
DirectoryIterator::getSize获取文件的大小
DirectoryIterator::getAtime获取当前迭代项的上一次访问时间
DirectoryIterator::getBasename获取当前迭代项的基本名称
DirectoryIterator::getCtime获取当前迭代项 inode 的上一次更改时间
DirectoryIterator::getExtension获取当前迭代项的扩展名
DirectoryIterator::getFilename获取当前迭代项的文件名
DirectoryIterator::getGroup获取当前迭代项所属组的 id
DirectoryIterator::getInode获取当前迭代项的 inode 信息
DirectoryIterator::getMTime获取当前迭代项的上一次修改时间
DirectoryIterator::getOwner获取当前迭代项所属用户的 id
DirectoryIterator::getPath获取当前迭代项不带文件名的路径
DirectoryIterator::getPathname获取当前迭代项带文件名的路径
DirectoryIterator::getPerms获取当前迭代项的权限信息(int)
DirectoryIterator::getSize获取当前迭代项的大小信息
DirectoryIterator::getType推断当前迭代项的类型
DirectoryIterator::isDot当前迭代项是不是.或..,是返回 true 否则返回 false
DirectoryIterator::isDir当前迭代项是不是文件夹,是返回 true 否则返回 false
DirectoryIterator::isExecutable当前迭代项是不是可执行
DirectoryIterator::isFile获取当前迭代项是不是一个文件
DirectoryIterator::isLink获取当前项是不是一个符号链接
DirectoryIterator::isReadable获取当前项是不是可读
DirectoryIterator::isWritable获取当前项是不是可写
php
$iterator = new DirectoryIterator('../iterator');
for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
    if ($iterator->isFile() && $iterator->isReadable()) {
        var_dump($iterator->getSize());
    }
}

EmptyIterator 迭代器

EmptyIterator 迭代器的使用场景为某些异常情况下你需要返回一个迭代对象时使用,如在 getIterator 方法中你必须获取一个迭代器类型。

php
<?php

class AnEmptyIterator implements IteratorAggregate
{
    private $url;

    public function __construct($url)
    {
        $this->url = $url;
    }

    public function getIterator()
    {
        $content = file_get_contents($this->url);
        try {
            return @new SimpleXMLIterator($content);
        } catch (Exception $e) {
            // Case $content is not valid XML, but you don't care
            return new EmptyIterator();
        }
    }
}

FilesystemIterator 迭代器

FilesystemIterator 迭代器 DirectoryIterator 迭代器并实现了 SeekableIterator 接口。不同于 DirectoryIterator 迭代器,FilesystemIterator 迭代器每次迭代都是一个新的 SplFileInfo 对象,而且默认情况下返回的都是完整的路径名,并且你也可以通过设置 flag 参数来更改返回的信息。也就是说 DirectoryIterator 的每一次迭代迭代对象的值都会改变但是 FilesystemIterator 迭代器并不会。 FilesystemIterator 迭代器在迭代的过程中会忽略.和..

php
$iterator = new FilesystemIterator('./');
for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
    var_dump($iterator->getFilename() . "--" . $iterator->getSize());
}

GlobIterator 迭代器

GlobIterator 迭代器继承自 FilesystemIterator 并实现了 SeekableIterator 和 Countable 接口是一个带匹配模式的迭代器,使用它你可灵活的控制你的迭代需求。

php
<?php
$iterator = new GlobIterator('./*.txt');
for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
    var_dump($iterator->getFilename());
}

LimitIterator 迭代器

LimitIterator 迭代器对一个迭代器进行限定的迭代。

php
<?php
$arrayIterator = new ArrayIterator([1, 2, 3, 4, 5]);
foreach (new LimitIterator($arrayIterator, 1, 3) as $item) {
    var_dump($item);
}

InfiniteIterator 迭代器

InfiniteIterator 迭代器是一个无限循环的迭代器,当迭代到最后一个节点时内部会自动的实现将迭代重置到最开始的位置。

php
<?php
$arrayIterator = new ArrayIterator([1, 2, 3, 4, 5, 6, 7, 8, 9]);
$iterator = new InfiniteIterator($arrayIterator);
foreach (new LimitIterator($iterator, 0, 18) as $item) {//迭代18次后停止
    var_dump($item);
}

MultipleIterator 迭代器

MultipleIterator 是一个迭代连接器,使用它你可以对几个迭代器进行连接迭代。

php
<?php
$name = ["zhangsan", "lisi", "wangwu"];
$sex = ["男", "女", "男"];
$age = [17, 18, 19];
$nameIterator = new ArrayIterator($name);
$ageIterator = new ArrayIterator($age);
$sexIterator = new ArrayIterator($sex);
$iterator = new MultipleIterator();
$iterator->attachIterator($nameIterator, "name");
$iterator->attachIterator($ageIterator, 'age');
$iterator->attachIterator($sexIterator, 'sex');
for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
    var_dump($iterator->current());
}

array(3) {
  [0]=>
  string(8) "zhangsan"
  [1]=>
  int(17)
  [2]=>
  string(3) "男"
}
array(3) {
  [0]=>
  string(4) "lisi"
  [1]=>
  int(18)
  [2]=>
  string(3) "女"
}
array(3) {
  [0]=>
  string(6) "wangwu"
  [1]=>
  int(19)
  [2]=>
  string(3) "男"
}

NoRewindIterator 迭代器

NoRewindIterator 迭代器不允许 rewind 操作,这意味着你将只能对迭代对象进行一次迭代。

php
<?php
$arrayIterator = new ArrayIterator([1, 2, 3, 4, 5, 6, 7]);
$iterator = new NoRewindIterator($arrayIterator);
foreach ($iterator as $item) {
    var_dump($item);
}
foreach ($iterator as $item) {
    var_dump($item);
}

RegexIterator 迭代器

RegexIterator 迭代器是 FilterIterator 的子类,它为你的数据迭代提供了使用正则过滤数据的能力。除了来自父类的方法外他还额外提供了一些有用的方法:

方法解释
getFlags获取当前的 flag 参数
setFlags动态的设置 flag 参数
getRegex获取你传入的正则表达式
getPregFlags获取当前的 PregFlags 参数
setPregFlags动态的设置 setPregFlags 参数
getMode获取当前的匹配模式
setMode动态的设置匹配模式
accept在这里对当前迭代的元素进行进一步的过滤
php
<?php
$arrayIterator = new ArrayIterator(["a", "b", "c", "d"]);
$iterator = new RegexIterator($arrayIterator, '/^[abc]/');
foreach ($iterator as $item) {
    var_dump($item);
}