【PHP8.x】RecursiveFilterIterator::getInnerIterator()メソッドの使い方
getInnerIteratorメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『getInnerIteratorメソッドは、フィルタリングの対象となっている内部のイテレータオブジェクトを取得する処理を実行するメソッドです。RecursiveFilterIteratorクラスは、コンストラクタで指定された別のRecursiveIteratorオブジェクトを内包し、その要素をフィルタリングする機能を提供します。このgetInnerIteratorメソッドを呼び出すと、その内包されている、つまりフィルタリングされる前の元のイテレータオブジェクトそのものが返されます。このメソッドは、より基本的なIteratorIteratorクラスから継承されたものであり、フィルタ適用後のイテレータから、大元となるデータソースに再度アクセスしたい場合に役立ちます。例えば、フィルタリング処理とは別に、元のイテレータが持つ独自のメソッドを呼び出したり、内部状態を確認したりする必要がある際に便利です。これにより、フィルタ機能と元のデータ操作を分離しつつ、必要に応じて両者にアクセスすることが可能になります。』
構文(syntax)
1<?php 2 3$arrayIterator = new RecursiveArrayIterator(['value1', ['value2']]); 4 5$filterIterator = new class($arrayIterator) extends RecursiveFilterIterator { 6 public function accept(): bool 7 { 8 return true; 9 } 10}; 11 12$originalIterator = $filterIterator->getInnerIterator();
引数(parameters)
引数なし
引数はありません
戻り値(return)
RecursiveIterator
RecursiveIteratorインターフェースを実装したイテレータです。これは、RecursiveFilterIteratorがフィルタリングを行うために内部で使用している元のイテレータを表します。
サンプルコード
PHP: RecursiveFilterIterator で内部イテレータを取得する
1<?php 2 3/** 4 * .txt 拡張子を持つファイルとディレクトリのみを許可するフィルタリングイテレータの例。 5 * RecursiveFilterIterator は抽象クラスなので、accept() メソッドを実装する必要があります。 6 */ 7class TxtFileFilterIterator extends RecursiveFilterIterator 8{ 9 /** 10 * 現在の要素を受け入れるかどうかを決定します。 11 * 12 * @return bool 受け入れる場合は true、それ以外は false 13 */ 14 public function accept(): bool 15 { 16 // ディレクトリは、再帰的な探索を可能にするために常に受け入れます。 17 if ($this->current()->isDir()) { 18 return true; 19 } 20 21 // ファイルの場合、拡張子が 'txt' の場合のみ受け入れます。 22 return $this->current()->isFile() && pathinfo($this->current()->getFilename(), PATHINFO_EXTENSION) === 'txt'; 23 } 24} 25 26/** 27 * このサンプルコードのメイン処理。 28 * RecursiveFilterIterator::getInnerIterator() の動作を示します。 29 */ 30// テスト用のディレクトリとファイルを作成(一時的なもの) 31$tempDir = __DIR__ . '/temp_iterator_test'; 32if (!file_exists($tempDir)) { 33 mkdir($tempDir, 0777, true); 34} 35file_put_contents($tempDir . '/document.txt', 'これはテキストファイルです。'); 36file_put_contents($tempDir . '/log.log', 'これはログファイルです。'); 37file_put_contents($tempDir . '/report.txt', '別のテキストファイルです。'); 38mkdir($tempDir . '/subfolder', 0777, true); 39file_put_contents($tempDir . '/subfolder/nested_doc.txt', 'ネストされたテキストファイルです。'); 40file_put_contents($tempDir . '/subfolder/nested_config.ini', 'ネストされた設定ファイルです。'); 41 42 43try { 44 // 1. 内部イテレータ (RecursiveDirectoryIterator) を作成します。 45 // このイテレータは、指定されたディレクトリの内容を再帰的に探索します。 46 $directoryIterator = new RecursiveDirectoryIterator($tempDir, FilesystemIterator::SKIP_DOTS); 47 echo "--- 内部イテレータ (元のイテレータ) の作成 ---\n"; 48 echo "クラス名: " . get_class($directoryIterator) . "\n\n"; 49 50 // 2. フィルタリングイテレータ (TxtFileFilterIterator) を作成します。 51 // コンストラクタに内部イテレータを渡すことで、そのイテレータの内容をフィルタリングします。 52 $filterIterator = new TxtFileFilterIterator($directoryIterator); 53 echo "--- フィルタリングイテレータの作成 ---\n"; 54 echo "クラス名: " . get_class($filterIterator) . "\n\n"; 55 56 // 3. getInnerIterator() メソッドを使用して、元の内部イテレータを取得します。 57 // このメソッドは、フィルタリングの対象となっている RecursiveIterator オブジェクトを返します。 58 $retrievedInnerIterator = $filterIterator->getInnerIterator(); 59 echo "--- getInnerIterator() の呼び出し結果 ---\n"; 60 echo "取得されたイテレータのクラス名: " . get_class($retrievedInnerIterator) . "\n"; 61 62 // 4. 取得されたイテレータが、元の内部イテレータと同じインスタンスであることを確認します。 63 // getInnerIterator() は、コンストラクタに渡された元のイテレータオブジェクトそのものを返します。 64 if ($retrievedInnerIterator === $directoryIterator) { 65 echo "取得されたイテレータは、元の内部イテレータと同じインスタンスです。\n\n"; 66 } else { 67 echo "取得されたイテレータは、元の内部イテレータと異なるインスタンスです。\n\n"; 68 } 69 70 // 5. フィルタリングイテレータを使って、再帰的にファイルリストを表示します。 71 // これにより、フィルタリングが正しく機能していることを確認できます。 72 echo "--- フィルタリングされたファイルリスト (.txt ファイルとディレクトリのみ) ---\n"; 73 $recursiveIteratorIterator = new RecursiveIteratorIterator($filterIterator, RecursiveIteratorIterator::SELF_FIRST); 74 foreach ($recursiveIteratorIterator as $path => $fileInfo) { 75 // ディレクトリまたはフィルタを通過したファイルを表示 76 if ($fileInfo->isDir()) { 77 echo " [D] " . $path . "\n"; 78 } elseif ($fileInfo->isFile()) { 79 echo " [F] " . $path . "\n"; 80 } 81 } 82 83} finally { 84 // 6. テスト用ディレクトリとファイルのクリーンアップ処理 85 // この関数は、作成した一時ディレクトリとその中のファイルをすべて削除します。 86 $deleteDir = function (string $dirPath) { 87 if (!is_dir($dirPath)) { 88 return; 89 } 90 $it = new RecursiveDirectoryIterator($dirPath, FilesystemIterator::SKIP_DOTS); 91 $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST); 92 foreach ($files as $file) { 93 if ($file->isDir()) { 94 rmdir($file->getRealPath()); 95 } else { 96 unlink($file->getRealPath()); 97 } 98 } 99 rmdir($dirPath); 100 }; 101 $deleteDir($tempDir); 102 echo "\n--- テスト用ディレクトリとファイルをクリーンアップしました ---\n"; 103}
PHP 8のRecursiveFilterIterator::getInnerIteratorは、特定の条件で要素をフィルタリングするRecursiveFilterIteratorクラスに属するメソッドです。このメソッドは引数を一切取らず、フィルタリング対象となっている元のRecursiveIteratorオブジェクトを返します。
RecursiveFilterIteratorは、内部にもう一つのイテレータ(「内部イテレータ」と呼びます)を持ち、その内部イテレータの要素を条件に基づいてフィルタリングします。getInnerIterator()を呼び出すと、この内部イテレータ、つまりRecursiveFilterIteratorのコンストラクタに渡された元のイテレータインスタンスそのものを取得することができます。
サンプルコードでは、RecursiveDirectoryIteratorでディレクトリ内のファイルやディレクトリを探索し、それを基に作成されたTxtFileFilterIteratorが.txtファイルのみを許可するようにフィルタリングを行います。TxtFileFilterIteratorインスタンスに対してgetInnerIterator()を呼び出すことで、最初に渡したRecursiveDirectoryIteratorのインスタンスが正確に取得され、元のイテレータへの直接的なアクセスが可能になることを示しています。このメソッドは、フィルタリングされたデータだけでなく、フィルタリング前の元のデータソース自体にもアクセスしたい場合に役立ちます。
RecursiveFilterIteratorは、独自のフィルタリングルールを定義するために、必ずaccept()メソッドを実装する必要があります。このgetInnerIterator()メソッドは、フィルタリングの対象となっている「元のイテレータ」を、コンストラクタに渡されたものと全く同じインスタンスとして返します。これにより、フィルタリングされたデータだけでなく、フィルタリング前の元のイテレータの状態を確認したり、直接操作したりすることが可能になります。サンプルコードは一時的なファイルを作成・削除していますが、これはテスト用の準備と片付けであり、実際のシステムではファイルのパスやアクセス権に注意し、エラー処理を適切に行うことが重要です。
PHPイテレータでフィルタリングと元イテレータ取得
1<?php 2 3/** 4 * RecursiveFilterIterator を継承したカスタムフィルタクラス。 5 * このフィルタは、文字列型の値で、かつ小文字にしたときに 'php' を含む要素のみを許可します。 6 */ 7class MyRecursiveFilterIterator extends RecursiveFilterIterator 8{ 9 /** 10 * 現在の要素がフィルタリング条件に適合するかどうかを判断します。 11 * 12 * @return bool 条件に適合する場合は true、それ以外は false。 13 */ 14 public function accept(): bool 15 { 16 $current = $this->current(); // 現在のイテレータの現在の要素を取得します。 17 // 要素が文字列型であり、その中に 'php' が含まれている場合に true を返します。 18 return is_string($current) && str_contains(strtolower($current), 'php'); 19 } 20} 21 22// フィルタリング対象となる再帰的なデータ構造(多次元配列)を準備します。 23$data = [ 24 'lang' => 'PHP', 25 'version' => '8.x', 26 'details' => [ 27 'framework' => 'Laravel', 28 'database' => 'MySQL', 29 'concept' => 'イテレータの活用', // この値は 'php' を含まないのでフィルタリングされます 30 'php_script' => 'script.php', // この値は 'php' を含むので許可されます 31 ], 32 'other_lang' => 'Python', 33 'php_info' => 'PHP info details' // この値は 'php' を含むので許可されます 34]; 35 36// RecursiveArrayIterator を使用して、配列を RecursiveIterator (再帰的なイテレータ) としてラップします。 37// これにより、多次元配列をイテレータとして再帰的に走査できるようになります。 38$recursiveArrayIterator = new RecursiveArrayIterator($data); 39 40// MyRecursiveFilterIterator のインスタンスを作成し、 41// 元の RecursiveArrayIterator を渡してフィルタリングを適用します。 42$filteredIterator = new MyRecursiveFilterIterator($recursiveArrayIterator); 43 44echo "--- フィルタリングされたデータ (値に 'php' を含む要素のみ) ---\n"; 45// RecursiveIteratorIterator を使用して、フィルタリングされたイテレータを再帰的に走査します。 46// これにより、子要素も深く探索され、フィルタリング条件に合致する要素のみが表示されます。 47foreach (new RecursiveIteratorIterator($filteredIterator) as $key => $value) { 48 echo "Key: " . $key . ", Value: " . $value . "\n"; 49} 50 51echo "\n--- RecursiveFilterIterator::getInnerIterator() メソッドのデモンストレーション ---\n"; 52// getInnerIterator() メソッドを呼び出し、フィルタリングが適用される前の元のイテレータを取得します。 53// このメソッドは RecursiveFilterIterator の親クラスである FilterIterator クラスから継承されています。 54// 戻り値は RecursiveIterator インターフェースを実装したオブジェクトです。 55$innerIterator = $filteredIterator->getInnerIterator(); 56 57// 取得したイテレータが、フィルタリング前に渡した元の RecursiveArrayIterator と同じオブジェクトであるか確認します。 58if ($innerIterator instanceof RecursiveArrayIterator) { 59 echo "getInnerIterator() は RecursiveArrayIterator のインスタンスを返しました。\n"; 60 echo "元の RecursiveArrayIterator と同じオブジェクトですか?: " . ($innerIterator === $recursiveArrayIterator ? "はい" : "いいえ") . "\n"; 61 62 echo "\n--- getInnerIterator() で取得した元のデータ (フィルタリングなし) ---\n"; 63 // 取得した inner iterator を再度 RecursiveIteratorIterator でラップし、 64 // フィルタリングされていない元のデータをすべて走査できることを示します。 65 // getInnerIterator() が、フィルタリング前の元の状態のイテレータを返していることが分かります。 66 foreach (new RecursiveIteratorIterator($innerIterator) as $key => $value) { 67 echo "Key: " . $key . ", Value: " . $value . "\n"; 68 } 69} else { 70 echo "getInnerIterator() は予期しない型のイテレータを返しました。\n"; 71}
このサンプルコードは、PHPのRecursiveFilterIteratorクラスと、そのgetInnerIterator()メソッドの使い方を実演します。最初に定義されているMyRecursiveFilterIteratorは、渡されたイテレータの要素が文字列型であり、かつ小文字にしたときに「php」という部分文字列を含む場合のみを許可するカスタムフィルタです。
コードでは、多次元配列$dataをRecursiveArrayIteratorでラップし、MyRecursiveFilterIteratorを適用して、条件に合う要素だけを再帰的に抽出しています。前半部分では、フィルタリングされた結果、指定した条件に合致する要素のみが表示されることを確認できます。
RecursiveFilterIterator::getInnerIterator()メソッドは、このフィルタリング処理が適用される前の、元のイテレータを取得する役割を持ちます。このメソッドは引数を取りません。戻り値はRecursiveIteratorインターフェースを実装したオブジェクトです。コードの後半では、getInnerIterator()によって取得されたイテレータが、フィルタリングを適用する前の元のRecursiveArrayIteratorと同一であることを確認し、そのイテレータを使って全ての元のデータがアクセス可能であることを示しています。これにより、フィルタリング後も必要に応じて元のデータソースへ戻れる、柔軟なイテレータ処理が実現できることを理解できます。
RecursiveFilterIterator::getInnerIterator() メソッドは、フィルタリングが適用される前の元のイテレータを取得するために使用されます。フィルタリングされた結果が必要な場合は、RecursiveFilterIteratorオブジェクト自体を直接走査してください。
このメソッドの戻り値は、RecursiveIteratorインターフェースを実装するオブジェクトであり、通常はフィルタリングの元になったイテレータのインスタンスが返されます。取得したイテレータの型が元のオブジェクトと一致することを確認すると、より安全に利用できます。この機能は、フィルタリングされたデータと元のデータの両方をプログラム内で柔軟に扱いたい場合に特に有用です。