16
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHP: iterable型はiterator_to_array()に渡しちゃいけない

Last updated at Posted at 2019-01-10

PHPにはiterable型という疑似型があり、arrayTraversableがこの型と互換している。iterable型は「foreachで回せるもの」を指す型と考えて良い。

例えば、次のように戻り値にiterableの型を宣言すると、配列を返してもTraversableを返すことになるジェネレーター構文(yield)を使ってもどちらでも構わないことになる。

interface FileList {
    public function getList(): iterable;
}

final class ArrayFileList implements FileList {
    public function getList(): iterable {
        return ['a.php', 'b.php', 'c.php'];
    }
}

final class GeneratorFileList implements FileList {
    public function getList(): iterable {
       yield 'a.php';
       yield 'b.php';
       yield 'c.php';
    }
}

iterator_to_array関数はイテレータを配列に変換する

PHPにはiterator_to_array関数がある。これは「イテレータを配列にコピーする」関数だ。第一引数に「コピーしたいイテレータ」を渡すと、配列に変換して返してくれる。

$files = (new GeneratorFileList)->getList();
assert($files instanceof Generator);
assert(iterator_to_array($files) === ['a.php', 'b.php', 'c.php']);

iterator_to_array関数にiterable型は渡さないほうがいい

このiterator_to_arrayには注意点がある。iterable型は渡さないほうがいいという点だ。公式マニュアルをよく読むと分かるが、iterator_to_arrayの第一引数の型はTraversableでなければならないからだ。

Screenshot_2019_01_10_22_45.png

この関数の説明には「イテレータを配列にコピーする」と書いてあるので、一見するとforeachでイテレートできるものなら何でもいけそうな気がしてくるが、そこはドキュメントをちゃんと読まねばならない。Traversableiterableだが、その逆のiterableTraversableではないし、Iteratorでもない。この場合**iterableIteratorは名前が似ているが別ものと考えたほうがいい**。

したがって、次のコードのように配列のiterable型の値を渡すと、「TypeError: Argument 1 passed to iterator_to_array() must implement interface Traversable, array given」というエラーが発生する。

$files = (new ArrayFileList)->getList();
assert(is_array($files));
iterator_to_array($files); // TypeError

iterable型をarray型に変換する方法

PHPには残念ながら、iterablearrayに変換する関数が無いので、次のような関数を自作する必要がある。

function iterable_to_array(iterable $iterable): array
{
    $array = [];
    \array_push($array, ...$iterable);
    return $array;
}

この実装であれば、arrayiterable型を渡した場合でも、iterable型を配列に変換することができる。

$files = (new ArrayFileList)->getList();
assert(is_array($files));
assert(iterable_to_array($files) === ['a.php', 'b.php', 'c.php']);

本稿で取り上げた、iterator_to_arrayの実験コードはGitHubで公開しているので、試したい方はご覧ください。

16
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?