Edited at

PHPのiterableとTraversableは違う

PHPのiterable型とTraversableインターフェイスはどういう性質があるのか?異なる点は何か?どういう関係か?について説明する。


iterable


  • いわゆる疑似型で、arrayもしくはTraversableインターフェイスを実装したクラスを指す型。


  • foreachで反復できる。


  • yield fromのジェネレーター構文が使える。

  • PHP7.1で導入された。Traversableインターフェイスより新しい。


Traversableインターフェイス


  • このインターフェイスを実装したオブジェクトはforeachで反復できるようになる。

  • 内部エンジンのインターフェイスであるため、PHPコードでこのインターフェイスを直接実装することはできない特殊なインターフェイスである。

  • PHP5で追加された。

class SomeClass implements Traversable {}

//=> Fatal error: Class SomeClass must implement interface Traversable as part of either Iterator or IteratorAggregate


  • 実装できないだけで、型宣言で用いたり、他のインターフェイスがTraversableインターフェイスを継承することはできる。

interface SomeInterface extends Traversable {} // ok

$obj instanceof Traversable; // ok
function (Traversable $traversable) {} // ok


is_iterable()関数


  • 変数の内容が反復可能な値であることを確認する関数。

is_iterable ( mixed $var ) : bool



  • iterable疑似型、つまり、arrayTraversableのときtrueが返る。

assert(is_iterable([])); // (1) array

assert(is_iterable(new ArrayIterator())); // (2) Traversable
assert(is_iterable((function (): iterable { yield; })())); // (3) Generator

ちなみに(3)はジェネレータ構文を使ったクロージャーを呼び出した結果だが、これはGeneratorクラスとして評価される:

$closure = function (): iterable { yield; };

echo get_class($closure()); //=> Generator


そういえばobjectforeachできたよね……?

PHPはオブジェクトもforeachで反復することができる。

$obj = new stdClass();

$obj->key1 = null;
foreach ($obj as $k => $v) {
echo $k, PHP_EOL; //=> key1
}

しかし、is_iterable関数ではfalseになる。

$obj = new stdClass();

$obj->key1 = null;
assert(!is_iterable($obj));


iterableTraversableの関係性



  • Traversableiterableである。


  • arrayiterableである。


  • arrayTraversableではない。


  • GeneratorTraversableであり、よってiterableでもある。


  • **IteratorTraversableであり、よってiterableでもある。


  • Traversableを実装していないオブジェクトはiterableではない。(foreachで回せるが)

iterable-2.png


合わせて読みたい