PHPにはiterable型という疑似型があり、array
やTraversable
がこの型と互換している。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
でなければならないからだ。
この関数の説明には「イテレータを配列にコピーする」と書いてあるので、一見するとforeach
でイテレートできるものなら何でもいけそうな気がしてくるが、そこはドキュメントをちゃんと読まねばならない。Traversable
はiterable
だが、その逆のiterable
はTraversable
ではないし、Iterator
でもない。この場合**iterable
とIterator
は名前が似ているが別ものと考えたほうがいい**。
したがって、次のコードのように配列の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には残念ながら、iterable
をarray
に変換する関数が無いので、次のような関数を自作する必要がある。
function iterable_to_array(iterable $iterable): array
{
$array = [];
\array_push($array, ...$iterable);
return $array;
}
この実装であれば、array
のiterable
型を渡した場合でも、iterable
型を配列に変換することができる。
$files = (new ArrayFileList)->getList();
assert(is_array($files));
assert(iterable_to_array($files) === ['a.php', 'b.php', 'c.php']);
本稿で取り上げた、iterator_to_array
の実験コードはGitHubで公開しているので、試したい方はご覧ください。