Iterator を再帰的に処理する Generator

`array_walk_recursive` の Generator 版のようなものです。

処理される Iterator は次のように Generator で実装することにします。

```php:g.php
<?php
return call_user_func(function () {
    yield 1;
    yield 2;
    yield call_user_func(function () {
        yield 3;
        yield 4;
        yield call_user_func(function () {
            yield 5;
            yield 6;
        });
    });
});
```

とりあえず普通に `array_walk_recursive` っぽいものを作ってみます。

```php
<?php
$recursive = function ($arr, callable $callback) use (&$recursive) {
    foreach ($arr as $key => $val) {
        if (is_array($val) || $val instanceof \Traversable) {
            $recursive($val, $callback);
        } else {
            $callback($val, $key);
        }
    }
};

$iterator = include __DIR__ . '/g.php';
$recursive($iterator, function ($val, $key) {
    echo "$key => $val,\n";
});
/*
0 => 1,
1 => 2,
0 => 3,
1 => 4,
0 => 5,
1 => 6,
*/
```

単純に callback のところを yield にすれば大丈夫かなーと思いましたが、そんなことはありません。

```php
<?php
$recursive = function ($arr) use (&$recursive) {
    foreach ($arr as $key => $val) {
        if (is_array($val) || $val instanceof \Traversable) {
            $recursive($val);
        } else {
            yield $key => $val;
        }
    }
};

$iterator = include __DIR__ . '/g.php';
foreach ($recursive($iterator) as $key => $val) {
    echo "$key => $val\n";
}
/*
0 => 1
1 => 2
*/
```

`$recursive($val)` は Generator を返しているので、呼び出し側でそれを回してやる必要があります。

```php
<?php
$recursive = function ($arr) use (&$recursive) {
    foreach ($arr as $key => $val) {
        if (is_array($val) || $val instanceof \Traversable) {
            foreach ($recursive($val) as $k => $v) {
                yield $k => $v;
            }
        } else {
            yield $key => $val;
        }
    }
};

$iterator = include __DIR__ . '/g.php';
foreach ($recursive($iterator) as $key => $val) {
    echo "$key => $val\n";
}
/*
0 => 1
1 => 2
0 => 3
1 => 4
0 => 5
1 => 6
*/
```

なお、いくつかの Iterator の実装では Iterator の current が Iterator 自身になることがあります、そのような Iterator にこの方法は使えません。無限に再帰してしまいます。

```php
<?php
$iterator = new \DirectoryIterator(__DIR__);
foreach ($iterator as $it) {
    var_dump($iterator === $it);
}
/*
bool(true)
bool(true)
bool(true)
*/
```

再帰の条件を付け足してそのような Iterator が渡されても無限再帰しないようにします。

```php
<?php
$recursive = function ($arr) use (&$recursive) {
    foreach ($arr as $key => $val) {
        if (is_array($val) || ($val instanceof \Traversable && $arr !== $val)) {
            foreach ($recursive($val) as $k => $v) {
                yield $k => $v;
            }
        } else {
            yield $key => $val;
        }
    }
};

$iterator = new \DirectoryIterator(__DIR__);
foreach ($recursive($iterator) as $key => $val) {
    echo "$key => $val\n";
}
/*
0 => .
1 => ..
2 => 01.php
3 => 02.php
 :
*/
```

---

ところで、SPL に再帰的な Iterator をフラットに反復する Iterator があります。RecursiveIteratorIterator です。

RecursiveIteratorIterator は RecursiveIterator をインプリメントした再帰的な Iterator をフラットに反復します。
ただの配列なら RecursiveArrayIterator と組み合わせて次のようにできます。

```php
<?php
$arr = [1, 2, [3, 4, [5, 6]]];

$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($arr));

foreach ($iterator as $key => $val) {
    echo "$key => $val,\n";
}
/*
0 => 1,
1 => 2,
0 => 3,
1 => 4,
0 => 5,
1 => 6,
*/
```

Iterator を返す Iterator は RecursiveIterator ではないため、これをそのまま使うことはできません。
折角なので RecursiveArrayIterator の Iterator 版を作ってみます（もはや Generator はあまり関係ないですが）。

RecursiveArrayIterator に対して RecursiveIteratorIterator かなーと思ったのですがその名前は使えないので RecursiveRecursiveIterator としました。

IteratorIterator を継承しています。Traversable を Iterator にするためのものですが、既存の Iterator に Decorator 的なことをやるために使えます。

```php
<?php
class RecursiveRecursiveIterator extends \IteratorIterator implements \RecursiveIterator
{
    public function hasChildren()
    {
        return $this->current() instanceof \Traversable && $this !== $this->current();
    }

    public function getChildren()
    {
        if ($this->current() instanceof \RecursiveIterator) {
            return $this->current();
        } else {
            return new static($this->current());
        }
    }
}

$iterator = include __DIR__ . '/g.php';
$recursiveIterator = new \RecursiveIteratorIterator(
    new \RecursiveRecursiveIterator($iterator)
);

foreach ($recursiveIterator as $key => $val) {
    echo "$key => $val,\n";
}
/*
0 => 1,
1 => 2,
0 => 3,
1 => 4,
0 => 5,
1 => 6,
*/
```