【PHP8.x】RecursiveFilterIterator::rewind()メソッドの使い方
rewindメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
rewindメソッドは、RecursiveFilterIteratorオブジェクトが指し示す要素の位置を、コレクションの先頭に巻き戻す処理を実行するメソッドです。イテレータは、配列やオブジェクトの集合体を一つずつ順番に処理するための仕組みであり、このメソッドはその処理を最初からやり直すために使用されます。通常、foreachループでイテレータを処理する場合、ループが開始されるタイミングでPHPエンジンによって内部的に自動で呼び出されるため、開発者がこのメソッドを直接記述することは稀です。しかし、一度ループを終えた後で、同じイテレータを使って再度最初から処理を行いたいといった特定の状況で明示的に呼び出すこともできます。RecursiveFilterIteratorの場合、このメソッドが呼び出されると、内部に保持している元のイテレータを巻き戻し、さらにフィルタリングの条件を満たす最初の要素が見つかるまでイテレータを進めるという動作も行います。このメソッドに引数はなく、戻り値もありません。
構文(syntax)
1<?php 2 3class MyRecursiveFilterIterator extends RecursiveFilterIterator 4{ 5 public function accept(): bool 6 { 7 // フィルタリング条件をここに実装します。 8 // この例では、常に true を返して全ての要素を許可します。 9 return true; 10 } 11} 12 13$array = ['a', 'b', ['c', 'd']]; 14$iterator = new RecursiveArrayIterator($array); 15$filterIterator = new MyRecursiveFilterIterator($iterator); 16 17// イテレータを最初の要素に巻き戻します。 18$filterIterator->rewind(); 19 20?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
戻り値なし
戻り値はありません
サンプルコード
PHP RecursiveFilterIteratorのrewindで再走査する
1<?php 2 3declare(strict_types=1); 4 5/** 6 * 数値のみを許可する再帰フィルタ。 7 * 8 * RecursiveFilterIteratorを継承し、accept()メソッドをオーバーライドして 9 * フィルタリングのロジックを定義します。 10 */ 11class NumericRecursiveFilter extends RecursiveFilterIterator 12{ 13 /** 14 * イテレータの現在の要素をイテレーションに含めるかを判定します。 15 * 16 * @return bool 現在の要素が数値または数値形式の文字列であればtrueを返します。 17 */ 18 public function accept(): bool 19 { 20 return is_numeric($this->current()); 21 } 22} 23 24// フィルタリング対象となる、入れ子構造を持つ配列データ 25$data = [ 26 'product_A', 27 100, 28 'product_B', 29 [ 30 'sub_item_1', 31 200, 32 'sub_item_2', 33 ], 34 '300', // 数値形式の文字列 35]; 36 37// 多次元配列を再帰的に走査するためのイテレータを作成します 38$arrayIterator = new RecursiveArrayIterator($data); 39 40// 作成したカスタムフィルタを適用します 41$filter = new NumericRecursiveFilter($arrayIterator); 42 43// 再帰的なイテレータをフラットに(一段階のループで)処理するために 44// RecursiveIteratorIteratorでラップします 45$iterator = new RecursiveIteratorIterator($filter); 46 47echo "--- 1回目のループ ---\n"; 48// foreach ループを開始すると、イテレータの rewind() メソッドが内部で自動的に呼び出されます。 49// これにより、イテレータのポインタが先頭にセットされます。 50foreach ($iterator as $value) { 51 echo $value . PHP_EOL; 52} 53 54echo "\n--- 2回目のループ ---\n"; 55// 内部で使われている RecursiveArrayIterator は「巻き戻し可能(rewindable)」です。 56// そのため、2回目の foreach でも再び rewind() が正常に呼び出され、最初から走査できます。 57// これが、一度しか走査できず巻き戻し不可能な、通常のジェネレータとの大きな違いです。 58foreach ($iterator as $value) { 59 echo $value . PHP_EOL; 60} 61 62?>
RecursiveFilterIterator::rewind()は、イテレータが指し示す現在の位置を、最初の要素に巻き戻すためのメソッドです。このメソッドはforeachのようなループ処理が開始される際に、PHPによって内部的に自動で呼び出されます。そのため、開発者が直接このメソッドを呼び出すことは稀です。rewind()メソッドは引数を受け取らず、何も値を返しません。
サンプルコードでは、入れ子になった配列から数値と数値形式の文字列のみを抽出しています。最初のforeachループが実行されると、内部でrewind()が呼び出され、イテレータは先頭からデータを走査し、フィルタリングされた結果を出力します。
このコードの重要な点は、使用されているイテレータが「巻き戻し可能(rewindable)」であることです。そのため、一度ループが終了した後でも、2回目のforeachループで再びrewind()が正常に機能し、もう一度最初からデータを走査できます。これは、一度しか走査できず巻き戻しができない通常のジェネレータなどとの大きな違いであり、同じデータに対して複数回のループ処理を行いたい場合に役立ちます。
foreach ループは、処理を開始する際にイテレータの rewind() メソッドを自動的に呼び出し、カーソルを先頭に戻します。このサンプルコードが2回ループできるのは、内部で使用している RecursiveArrayIterator が巻き戻し可能な特性を持つためです。注意点として、全てのイテレータが複数回ループできるわけではありません。例えば、ジェネレータ関数(yield)から作られたイテレータは巻き戻しができず、2回目の foreach では何も処理されません。RecursiveFilterIterator の rewind() は内部イテレータの rewind() を呼び出すため、その挙動は元となるイテレータに依存します。イテレータを利用する際は、そのオブジェクトが巻き戻し可能かどうかを意識することが重要です。
PHP RecursiveFilterIteratorのrewind()を理解する
1<?php 2 3declare(strict_types=1); 4 5/** 6 * 特定の条件(この例では葉ノードが文字列かつ長さ6以上)を満たす要素のみを 7 * 許可する再帰フィルタを定義します。 8 * 9 * RecursiveFilterIteratorを継承して独自のフィルタを作成します。 10 * accept()メソッドにフィルタリングのロジックを実装する必要があります。 11 */ 12class MinLengthStringFilter extends RecursiveFilterIterator 13{ 14 private int $minLength; 15 16 public function __construct(RecursiveIterator $iterator, int $minLength = 6) 17 { 18 parent::__construct($iterator); 19 $this->minLength = $minLength; 20 } 21 22 /** 23 * 現在の要素をイテレーションに含めるかどうかを判断します。 24 * 25 * @return bool 含める場合は true、除外する場合は false。 26 */ 27 public function accept(): bool 28 { 29 // 現在の要素が子を持つ場合(配列など)は、その中身を探索するため常に許可します。 30 if ($this->hasChildren()) { 31 return true; 32 } 33 34 // 現在の要素が子を持たない場合(スカラー値など)のフィルタリングロジックです。 35 // 文字列型で、かつ指定された最小長以上の場合のみ許可します。 36 $current = $this->current(); 37 return is_string($current) && strlen($current) >= $this->minLength; 38 } 39} 40 41// フィルタリング対象の再帰的なデータ構造(多次元配列) 42$data = [ 43 'short_words' => ['php', 'sql', 'git'], 44 'long_words' => ['iterator', 'recursive', 'filter'], 45 'mixed' => [ 46 'apple', // 長さ5 (除外) 47 'banana', // 長さ6 (許可) 48 12345, // 数値 (除外) 49 ], 50]; 51 52// 多次元配列から再帰的なイテレータを作成 53$arrayIterator = new RecursiveArrayIterator($data); 54 55// カスタムフィルタを適用 56$filteredIterator = new MinLengthStringFilter($arrayIterator, 6); 57 58// フィルタリングされた結果をフラットに表示するためのイテレータ 59// RecursiveIteratorIterator は内部で rewind() を呼び出します。 60$iterator = new RecursiveIteratorIterator($filteredIterator); 61 62echo "--- 1回目のループ ---" . PHP_EOL; 63// foreach ループが始まる際に、内部的に rewind() が自動で呼び出され、 64// イテレータが最初の要素にセットされます。 65foreach ($iterator as $value) { 66 echo $value . PHP_EOL; 67} 68 69echo PHP_EOL; 70echo "--- rewind() を明示的に呼び出し、2回目のループ ---" . PHP_EOL; 71 72// イテレータを先頭に戻します。 73// これにより、もう一度最初から要素をたどることができます。 74$iterator->rewind(); 75 76// 再度ループを実行し、rewind() によってイテレータがリセットされたことを確認します。 77foreach ($iterator as $value) { 78 echo $value . PHP_EOL; 79} 80 81?>
PHPのRecursiveFilterIterator::rewind()メソッドは、イテレータの内部ポインタを最初の要素に巻き戻す役割を持ちます。このメソッドは引数を取らず、戻り値もありません(void)。
サンプルコードでは、まず多次元配列に対し、「長さが6文字以上の文字列」という条件で要素を絞り込むフィルタ付きのイテレータを作成しています。そしてRecursiveIteratorIteratorを使い、このフィルタリング結果を順番に処理できるようにしています。
1回目のforeachループが実行されると、条件に合う要素(iterator, recursive, filter, banana)が表示されます。foreachはループを開始する際に、内部で自動的にrewind()を呼び出すため、イテレータは先頭から正しく処理を開始します。
ループが終了するとイテレータは終端に達しますが、コードの後半で$iterator->rewind()を明示的に呼び出しています。これにより、一度最後まで進んだイテレータが再び先頭の位置に戻ります。そのため、2回目のforeachループを実行すると、1回目と全く同じ結果が再度表示されます。このようにrewind()メソッドは、同じイテレータを使ってデータセットを何度も先頭から繰り返し処理したい場合に不可欠です。
foreachループは処理を開始する前に、内部で自動的にrewind()を呼び出しイテレータを先頭に戻します。そのため、通常は開発者が明示的にrewind()を呼び出す必要はありません。このメソッドを意図的に使用するのは、一度最後まで処理したイテレータを、もう一度最初から利用したい場合に限られます。rewind()の役割は、あくまでイテレータの内部ポインタを先頭に戻すことであり、データ自体を変更するものではありません。サンプルコードのようにRecursiveIteratorIteratorと組み合わせることで、フィルタリング済みの深い階層を持つデータ全体を、改めて最初からたどることが可能になります。