PHP アドバンス

PHP イテラブル (Iterables)

1. PHPにおけるイテラブルとは?

PHPにおいて、イテラブル(iterable)とは foreach() ループで反復処理ができる値のことです。

iterable という擬似型(pseudo-type)は PHP 7.1 で導入されました。これは、関数の引数(arguments)や戻り値(return values)のデータ型として使用することができます。

イテラブルとして扱えるのは、配列(array)、または Iterator インターフェースを実装したオブジェクトです。

2. イテラブルの使用方法

iterable キーワードは、関数のパラメータ(引数)の型指定や、関数の戻り値の型として使用できます。

2.1 イテラブルな引数を持つ関数の例

<?php
function printIterable(iterable $x) {
  foreach($x as $item) {
    echo $item;
  }
}

// 配列(array)を使用して呼び出し
printIterable(["a", "b", "c"]);

// オブジェクト(Iterator)を使用して呼び出し
$iterator = new ArrayIterator(["d", "e", "f"]);
printIterable($iterator);
?>

2.2 イテラブルな戻り値を持つ関数の例

<?php
function getIterable(): iterable {
  return ["a", "b", "c"];
}

foreach(getIterable() as $item) {
  echo $item;
}
?>

3. イテラブルの作成

3.1 配列 (Arrays)

すべての配列は自動的にイテラブルです。したがって、イテラブルを要求する関数の引数として、あらゆる配列を使用することができます。

3.2 イテレータ (Iterators)

Iterator インターフェースを実装している任意のオブジェクトは、イテラブルを要求する関数の引数として使用できます。

イテレータはアイテムのリストを保持し、それらをループで処理するためのメソッドを提供します。内部的にリスト内の要素の1つを指すポインタ(pointer)を保持しており、各アイテムは検索に使用できるキー(key)を持つ必要があります。

Iterator インターフェースを実装する場合、以下のメソッドを定義する必要があります:

  • current() - ポインタが現在指している要素を返します。データ型は任意です。
  • key() - リスト内の現在の要素に関連付けられたキーを返します。型は integer, float, boolean, string のいずれかです。
  • next() - ポインタをリスト内の次の要素に移動させます。
  • rewind() - ポインタをリストの最初の要素に戻します。
  • valid() - 内部ポインタがどの要素も指していない場合(例:リストの最後で next() が呼ばれた後など)は false を返します。それ以外の場合は true を返します。

4. Iteratorインターフェースの実装例

実際に Iterator インターフェースを実装し、それをイテラブルとして使用する例を見てみましょう。

<?php
// イテレータ(Iterator)の作成
class MyIterator implements Iterator {
  private $items = [];
  private $pointer = 0;

  public function __construct($items) {
    // array_values() でキーが数値であることを保証します
    $this->items = array_values($items);
  }

  public function current() {
    return $this->items[$this->pointer];
  }

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

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

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

  public function valid() {
    // count() はリスト内に何個のアイテムがあるかを返します
    return $this->pointer < count($this->items);
  }
}

// イテラブルを使用する関数
function printIterable(iterable $myIterable) {
  foreach($myIterable as $item) {
    echo $item;
  }
}

// 作成したイテレータをイテラブルとして使用する
$iterator = new MyIterator(["a", "b", "c"]);
printIterable($iterator);
?>