【PHP8.x】FilterIterator::getInnerIterator()メソッドの使い方
getInnerIteratorメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
getInnerIteratorメソッドは、FilterIteratorオブジェクトが内部で保持している元のイテレータ(内部イテレータ)を取得するために実行するメソッドです。FilterIteratorクラスは、既存のイテレータを元にして、特定の条件を満たす要素だけを抽出するフィルタとして機能します。このとき、フィルタリングの対象となる元のイテレータが内部に保持されており、本メソッドはその元のイテレータオブジェクト自体を返します。これにより、フィルタリングが適用される前の、すべての要素を含んだデータソースにアクセスすることが可能になります。例えば、フィルタリング後の結果だけでなく、フィルタリングによって除外された要素も含めた全体の要素数を取得したい場合や、元のイテレータが持つ固有のメソッドを呼び出して操作したい場合などに利用されます。戻り値は、FilterIteratorのコンストラクタに渡された、Iteratorインターフェースを実装したオブジェクトとなります。
構文(syntax)
1$filterIterator = new class(new ArrayIterator(['a', 'b', 'c'])) extends FilterIterator { 2 public function accept(): bool { 3 return true; 4 } 5}; 6 7// 内部に保持している元のイテレータを取得します 8$innerIterator = $filterIterator->getInnerIterator();
引数(parameters)
引数なし
引数はありません
戻り値(return)
Iterator
FilterIteratorクラスで、このイテレータがラップしている元のイテレータを返します。
サンプルコード
PHP FilterIteratorで内部イテレータを取得する
1<?php 2 3/** 4 * FilterIteratorを継承したカスタムイテレータ。 5 * このクラスは、内部のイテレータから偶数のみをフィルタリングして提供します。 6 */ 7class EvenNumberFilterIterator extends FilterIterator 8{ 9 /** 10 * 現在の要素がフィルタリング条件を満たすかどうかを判断します。 11 * このメソッドはFilterIteratorを継承するクラスで必ず実装する必要があります。 12 * 13 * @return bool 条件を満たす場合はtrue、そうでない場合はfalse。 14 */ 15 public function accept(): bool 16 { 17 // current()メソッドで現在のイテレータ要素の値を取得します。 18 // ここでは、値が偶数(2で割った余りが0)の場合にtrueを返します。 19 return $this->current() % 2 === 0; 20 } 21} 22 23// フィルタリングしたい元のデータを用意します。 24$data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 25 26// 元のデータを扱うArrayIteratorを作成します。 27// ArrayIteratorは配列をイテレータとして扱えるようにするSPLクラスです。 28$originalIterator = new ArrayIterator($data); 29 30// EvenNumberFilterIteratorのインスタンスを作成し、元のイテレータを渡します。 31// これにより、$originalIteratorの内容がフィルタリングされるようになります。 32$filteredIterator = new EvenNumberFilterIterator($originalIterator); 33 34echo "--- フィルタリングされた偶数の要素 ---" . PHP_EOL; 35// フィルタリングされたイテレータをループして表示します。 36// ここでは、偶数の要素のみが表示されます。 37foreach ($filteredIterator as $key => $value) { 38 echo "Key: " . $key . ", Value: " . $value . PHP_EOL; 39} 40 41echo PHP_EOL; 42 43// getInnerIterator()メソッドを使用して、フィルタリング前の元のイテレータを取得します。 44// FilterIteratorは常に内部に別のイテレータ(この場合は$originalIterator)を保持しており、 45// このメソッドでその内部イテレータにアクセスできます。 46$retrievedInnerIterator = $filteredIterator->getInnerIterator(); 47 48echo "--- getInnerIterator() で取得した元のイテレータの全ての要素 ---" . PHP_EOL; 49// 取得した元のイテレータをループして表示します。 50// ここでは、フィルタリングされていない元の全ての要素が表示されます。 51foreach ($retrievedInnerIterator as $key => $value) { 52 echo "Key: " . $key . ", Value: " . $value . PHP_EOL; 53} 54 55// 注意: $originalIterator と $retrievedInnerIterator は同じインスタンスを指します。 56// var_dump($originalIterator === $retrievedInnerIterator); // true が出力されます
PHPのFilterIteratorクラスは、既存のイテレータ(データの繰り返し処理を行うオブジェクト)に、特定の条件でデータをフィルタリングする機能を追加する際に使用します。FilterIterator::getInnerIterator()メソッドは、このFilterIteratorが内部で保持している「元のイテレータ」を取得するためのメソッドです。
このメソッドは引数を一切取りません。戻り値はIterator型であり、フィルタリングが適用される前の、生のデータを提供するイテレータのインスタンスを返します。
サンプルコードでは、EvenNumberFilterIteratorが元の数字の配列をArrayIteratorとして受け取り、偶数のみをフィルタリングしています。getInnerIterator()メソッドを使うことで、フィルタリング後の$filteredIteratorから、フィルタリングされる前のすべての数字を含む元のイテレータを再び取得し、その全要素をループして表示しています。これにより、フィルタリングされたデータだけでなく、フィルタリング前の元のデータにも簡単にアクセスできる点がgetInnerIterator()の便利な使い方です。
getInnerIterator()メソッドは、FilterIteratorが要素のフィルタリングを行うために内部に保持している「元のイテレータ」を取得するものです。このメソッドによって、フィルタリング前の全ての要素にアクセスし、操作することが可能になります。
特に重要な点として、このメソッドが返すイテレータは、FilterIteratorを生成する際に渡した元のイテレータと全く同じインスタンスを指しています。そのため、取得したイテレータに対してrewind()などでポインタ位置を変更したり、要素を操作したりすると、FilterIterator自身の内部状態にも影響を与える可能性があります。フィルタリング後のイテレータと元のイテレータの状態を混同しないよう、注意深く利用してください。
FilterIteratorのgetInnerIterator()で内部イテレータを取得する
1<?php 2 3/** 4 * キーワード「ジェネレータ」に関連して、指定された範囲の数値を生成するジェネレータ関数を定義します。 5 * ジェネレータはイテレータの一種で、メモリ効率の良いデータ生成に適しています。 6 * 7 * @param int $start 開始数値 8 * @param int $end 終了数値 9 * @return Generator 数値を順次生成するジェネレータ 10 */ 11function numberGenerator(int $start, int $end): Generator 12{ 13 for ($i = $start; $i <= $end; $i++) { 14 yield $i; 15 } 16} 17 18/** 19 * キーワード「イテレータ」に関連して、FilterIteratorを継承したカスタムイテレータを定義します。 20 * このクラスは、内部のイテレータから特定の条件(偶数)を満たす要素のみをフィルタリングします。 21 */ 22class EvenNumberFilterIterator extends FilterIterator 23{ 24 /** 25 * コンストラクタ。 26 * フィルタリングしたいイテレータを親クラスに渡します。 27 * 28 * @param Iterator $iterator フィルタリング対象のイテレータ 29 */ 30 public function __construct(Iterator $iterator) 31 { 32 parent::__construct($iterator); 33 } 34 35 /** 36 * 現在の要素がフィルタリング条件(偶数であること)を満たすかどうかを決定します。 37 * 38 * @return bool 現在の要素が偶数であればtrue、そうでなければfalse 39 */ 40 public function accept(): bool 41 { 42 return $this->current() % 2 === 0; 43 } 44} 45 46// -------------------------------------------------------------------------------- 47// サンプルコードの実行部分 48// -------------------------------------------------------------------------------- 49 50echo "--- 1. ジェネレータを内部イテレータとして使用する例 ---\n"; 51 52// 新しいジェネレータインスタンスを作成し、EvenNumberFilterIteratorに渡します。 53$filteredGenerator = new EvenNumberFilterIterator(numberGenerator(1, 10)); 54 55echo "フィルタリングされた偶数 (ジェネレータ): "; 56// FilterIteratorをループすると、内部のジェネレータが消費されます。 57foreach ($filteredGenerator as $number) { 58 echo $number . " "; 59} 60echo "\n"; 61 62// FilterIterator::getInnerIterator() メソッドを使用して、 63// フィルタリングされたイテレータがラップしている元のイテレータを取得します。 64$innerGeneratorIterator = $filteredGenerator->getInnerIterator(); 65 66echo "getInnerIterator() で取得した元のジェネレータを再ループ (何も表示されないはず): "; 67// ジェネレータは一度消費されると空になるため、このループは何も出力しません。 68foreach ($innerGeneratorIterator as $number) { 69 echo $number . " "; // この行は実行されません 70} 71echo "(ジェネレータは一度しかイテレートできないため)\n\n"; 72 73 74echo "--- 2. ArrayIterator を内部イテレータとして使用する例 ---\n"; 75 76$dataArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 77// ArrayIteratorはRewindableIteratorなので、巻き戻して再利用可能です。 78 79// ArrayIteratorをEvenNumberFilterIteratorに渡します。 80$filteredArrayIterator = new EvenNumberFilterIterator(new ArrayIterator($dataArray)); 81 82echo "フィルタリングされた偶数 (ArrayIterator): "; 83// FilterIteratorをループします。内部のArrayIteratorも消費されます。 84foreach ($filteredArrayIterator as $number) { 85 echo $number . " "; 86} 87echo "\n"; 88 89// getInnerIterator() で取得した元の ArrayIterator を再ループします。 90$innerArrayIterator = $filteredArrayIterator->getInnerIterator(); 91 92echo "getInnerIterator() で取得した元の ArrayIterator を再ループ (元のデータが表示される): "; 93// ArrayIteratorはrewind()可能なので、イテレータを最初に戻して再利用できます。 94$innerArrayIterator->rewind(); // イテレータを最初に戻す 95foreach ($innerArrayIterator as $number) { 96 echo $number . " "; 97} 98echo "\n";
PHPのFilterIteratorクラスは、別のイテレータ(内部イテレータ)の要素を特定の条件でフィルタリングしながら処理する際に利用されます。このFilterIterator::getInnerIterator()メソッドは、FilterIteratorが内部に持つ、フィルタリング前の元のイテレータを取得するために使用します。
引数はなく、戻り値はIterator型で、取得したイテレータを直接操作できます。
サンプルコードでは、まずnumberGeneratorというジェネレータ関数をEvenNumberFilterIteratorで偶数のみにフィルタリングしています。その後、getInnerIterator()で元のジェネレータを取得し、再度ループを試みていますが、ジェネレータは一度データを読み出すと最初から読み出し直せないため、何も表示されません。
一方、ArrayIteratorを内部イテレータとして使用する例では、getInnerIterator()で取得したArrayIteratorはrewind()メソッドでデータを最初から読み出し直せるため、再度ループすると元のデータが全て表示されます。
このメソッドを使うことで、FilterIteratorがどのイテレータを対象としていたかを確認でき、特に内部イテレータの種類(一度きりのものか、巻き戻し可能なものか)によって、その後の挙動が異なることを理解するのに役立ちます。
FilterIterator::getInnerIterator()は、ラップしている元のイテレータを取得しますが、その後の利用には注意が必要です。特に、numberGeneratorのようなジェネレータは一度要素を生成し終えると内部状態が空になり、巻き戻しができません。そのため、FilterIteratorを通してジェネレータが消費された後、getInnerIterator()で取得したジェネレータを再度ループしても、何も要素は取得できません。一方、ArrayIteratorのように巻き戻し可能なイテレータであれば、getInnerIterator()で取得後、rewind()メソッドを呼び出すことでイテレータを先頭に戻し、再びループして利用することが可能です。内部のイテレータの種類によって再利用の可否が異なるため、この点を理解しておくことが重要です。