【PHP8.x】RecursiveCallbackFilterIterator::getInnerIterator()メソッドの使い方
getInnerIteratorメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
getInnerIteratorメソッドは、RecursiveCallbackFilterIteratorが内部で保持している、フィルタリング対象の元のイテレータオブジェクトを取得するために実行するメソッドです。RecursiveCallbackFilterIteratorクラスは、既存の再帰的なイテレータオブジェクトをラップし、指定されたコールバック関数を用いて要素をフィルタリングする機能を提供します。この設計はデコレータパターンとして知られており、元のオブジェクトの機能を拡張します。このメソッドを呼び出すと、ラップされている、つまりフィルタリングが適用される前の元のイテレータオブジェクトそのものが返されます。これにより、フィルタリング後の状態だけでなく、元のデータソースを表現するイテレータに直接アクセスすることが可能になります。例えば、元のイテレータが持つ固有のメソッドを呼び出したい場合や、デバッグ目的でフィルタリング前の状態を確認したいといった特定の状況で利用されます。このメソッドは引数を取らず、戻り値としてコンストラクタで渡されたRecursiveIteratorインターフェースを実装したオブジェクトを返します。
構文(syntax)
1public function getInnerIterator(): Iterator
引数(parameters)
引数なし
引数はありません
戻り値(return)
Iterator|null
RecursiveCallbackFilterIterator::getInnerIterator() メソッドは、このイテレータが内部で使用しているイテレータ、またはイテレータが存在しない場合は null を返します。
サンプルコード
PHP RecursiveCallbackFilterIterator で内部イテレータを取得する
1<?php 2 3// RecursiveCallbackFilterIterator がフィルタリングする対象の元のデータを用意します。 4$data = [ 5 'id' => 101, 6 'name' => 'Sample Item', 7 'active' => true, 8 'tags' => ['php', 'iterator', 'example'], 9 'price' => 29.99, 10]; 11 12// このデータを RecursiveArrayIterator でラップし、イテレータとして扱えるようにします。 13// これは、RecursiveCallbackFilterIterator に渡す「内部イテレータ」になります。 14$originalIterator = new RecursiveArrayIterator($data); 15 16// フィルタリングのロジックを定義するコールバック関数です。 17// この例では、値が文字列型である要素のみを許可します。 18$filterCallback = function (mixed $current, mixed $key, RecursiveIterator $iterator): bool { 19 return is_string($current); 20}; 21 22// RecursiveCallbackFilterIterator をインスタンス化します。 23// $originalIterator と上記で定義した $filterCallback を渡します。 24// このイテレータは、$originalIterator の要素を $filterCallback に従ってフィルタリングします。 25$filteredIterator = new RecursiveCallbackFilterIterator($originalIterator, $filterCallback); 26 27// getInnerIterator() メソッドを使用して、フィルタリング対象となっている元のイテレータを取得します。 28// このメソッドは、RecursiveCallbackFilterIterator のコンストラクタに渡されたイテレータを返します。 29$innerIterator = $filteredIterator->getInnerIterator(); 30 31// 取得したイテレータが、最初に RecursiveCallbackFilterIterator に渡した 32// $originalIterator と同じインスタンスであることを確認します。 33echo "最初に RecursiveCallbackFilterIterator に渡したイテレータのクラス名: " . get_class($originalIterator) . "\n"; 34echo "getInnerIterator() で取得したイテレータのクラス名: " . get_class($innerIterator) . "\n"; 35 36// オブジェクトの厳密な比較 (===) を使用して、両者が同一インスタンスであるかを検証します。 37if ($innerIterator === $originalIterator) { 38 echo "結果: getInnerIterator() は、期待通りに元の RecursiveArrayIterator オブジェクトを返しました。\n"; 39} else { 40 echo "結果: getInnerIterator() は、元の RecursiveArrayIterator と異なるオブジェクトを返しました。\n"; 41} 42 43// 取得した元のイテレータ(フィルタリングされていない状態)の内容をいくつか表示してみます。 44echo "\n--- getInnerIterator() で取得した元のイテレータの内容 --- \n"; 45foreach ($innerIterator as $key => $value) { 46 // 配列でないシンプルな値のみ表示して、出力を簡潔にします 47 if (!is_array($value)) { 48 echo "キー: " . $key . ", 値: " . (is_bool($value) ? ($value ? 'true' : 'false') : $value) . "\n"; 49 } else { 50 echo "キー: " . $key . ", 値: [配列]\n"; 51 } 52} 53 54// フィルタリングされたイテレータの内容も参考に表示してみます (getInnerIterator() とは直接関係ありませんが比較用)。 55echo "\n--- フィルタリングされたイテレータの内容 --- \n"; 56// RecursiveCallbackFilterIterator は RecursiveIterator を実装しているため、 57// RecursiveIteratorIterator を使って再帰的に要素をたどれます。 58$recursiveIterator = new RecursiveIteratorIterator($filteredIterator); 59foreach ($recursiveIterator as $key => $value) { 60 echo "キー: " . $key . ", 値: " . (is_bool($value) ? ($value ? 'true' : 'false') : $value) . "\n"; 61} 62 63?>
RecursiveCallbackFilterIterator::getInnerIterator()メソッドは、PHPでデータ構造をフィルタリングする際に、そのフィルタリング対象となっている「元のイテレータ」を取得するために使われます。このメソッドは引数を取らず、RecursiveCallbackFilterIteratorのコンストラクタに渡されたイテレータオブジェクトを返します。戻り値はIteratorインターフェースを実装したオブジェクト、または内部イテレータが設定されていない場合にはnullとなります。
サンプルコードでは、まずRecursiveArrayIteratorで作成された$originalIteratorを、フィルタリングを行うRecursiveCallbackFilterIteratorに渡して$filteredIteratorを生成しています。その後、$filteredIterator->getInnerIterator()を呼び出すことで、フィルタリング前の元の$originalIteratorを再度手に入れることができます。これにより、一度フィルタリングを設定した後でも、元のすべてのデータにアクセスしたい場合などに非常に便利です。実際に、取得したイテレータが最初に渡した$originalIteratorと同一のインスタンスであることが確認できます。
getInnerIterator() は、RecursiveCallbackFilterIterator がフィルタリングの対象としている「元のイテレータ」を取得するメソッドです。フィルタリング後のデータを取得するわけではないため、利用目的を明確にしましょう。戻り値はイテレータオブジェクトか null の可能性があるため、常に null チェックを行うことで安全なコードになります。また、取得されるのはコンストラクタに渡したイテレータのインスタンスそのものであるため、そのイテレータに加えた変更は、RecursiveCallbackFilterIterator が参照している元のイテレータにも影響を与える点に留意してください。
PHPイテレータ: フィルタリングと内部イテレータの活用
1<?php 2 3/** 4 * RecursiveCallbackFilterIterator と getInnerIterator() メソッドのサンプル 5 * 6 * このコードは、PHPのイテレータ機能を用いてファイルシステムを再帰的に走査し、 7 * 特定の条件でファイルをフィルタリングする方法を示します。 8 * 特に、フィルタリング中に元のイテレータ(内部イテレータ)にアクセスする 9 * getInnerIterator() メソッドの利用方法に焦点を当てています。 10 * システムエンジニアを目指す初心者の方に、イテレータの強力さと柔軟性を理解してもらうことを目的としています。 11 */ 12 13// ヘルパー関数: テスト用のディレクトリとファイルを作成します。 14function createTestFiles(string $baseDir): void 15{ 16 if (!is_dir($baseDir)) { 17 mkdir($baseDir, 0777, true); 18 } 19 file_put_contents($baseDir . '/document.txt', 'This is an important text document.'); 20 file_put_contents($baseDir . '/image.jpg', 'This is an image file.'); 21 mkdir($baseDir . '/subfolder', 0777, true); 22 file_put_contents($baseDir . '/subfolder/script.php', '<?php echo "Hello PHP!";'); 23 file_put_contents($baseDir . '/subfolder/report.txt', 'Monthly sales report.'); 24 file_put_contents($baseDir . '/subfolder/configuration.ini', 'System configuration.'); 25} 26 27// ヘルパー関数: テスト用のディレクトリとファイルをクリーンアップします。 28function cleanupTestFiles(string $baseDir): void 29{ 30 if (!is_dir($baseDir)) { 31 return; 32 } 33 // RecursiveIteratorIterator を使用して、ディレクトリ内のすべてのファイルとサブディレクトリを再帰的に取得 34 // CHILD_FIRST を指定することで、子要素(ファイル)から先に削除し、その後に親ディレクトリを削除できます。 35 $files = new RecursiveIteratorIterator( 36 new RecursiveDirectoryIterator($baseDir, RecursiveDirectoryIterator::SKIP_DOTS), 37 RecursiveIteratorIterator::CHILD_FIRST 38 ); 39 40 foreach ($files as $fileinfo) { 41 // ファイルかディレクトリかによって削除方法を切り替える 42 $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink'); 43 $todo($fileinfo->getRealPath()); 44 } 45 rmdir($baseDir); // 最上位のディレクトリを削除 46} 47 48// --- メインのデモコード --- 49 50// テスト用データが保存される一時ディレクトリを設定 51$testDirectory = __DIR__ . '/php_iterator_example_data'; 52// テスト用のファイル構造を作成 53createTestFiles($testDirectory); 54 55try { 56 echo "--- PHP イテレータと RecursiveCallbackFilterIterator のデモ ---\n"; 57 echo "以下のテストディレクトリから '.txt' 拡張子のファイルのみを抽出します。\n\n"; 58 echo "テストディレクトリ: " . realpath($testDirectory) . "\n"; 59 echo "--------------------------------------------------\n"; 60 61 // 1. RecursiveDirectoryIterator: 指定されたディレクトリを再帰的に走査するためのイテレータです。 62 // RecursiveDirectoryIterator::SKIP_DOTS は '.' と '..' エントリ(現在のディレクトリと親ディレクトリ)をスキップします。 63 $directoryIterator = new RecursiveDirectoryIterator( 64 $testDirectory, 65 RecursiveDirectoryIterator::SKIP_DOTS 66 ); 67 68 // 2. RecursiveIteratorIterator: 再帰的なイテレータ(RecursiveDirectoryIteratorなど)を 69 // フラットな(階層を意識しない)イテレーションとして扱えるようにします。 70 // RecursiveIteratorIterator::LEAVES_ONLY はファイルのような「葉」要素のみを返し、ディレクトリ自体は返しません。 71 $recursiveIterator = new RecursiveIteratorIterator( 72 $directoryIterator, 73 RecursiveIteratorIterator::LEAVES_ONLY 74 ); 75 76 // 3. RecursiveCallbackFilterIterator: 内部イテレータの要素を、指定されたコールバック関数でフィルタリングします。 77 // この例では、ファイルであり、かつ拡張子が '.txt' であるファイルのみを通過させます。 78 // コールバック関数の引数: 79 // $current (SplFileInfo): 現在のイテレータ要素(ファイルまたはディレクトリ) 80 // $key (string): 現在の要素のキー(通常はパス名) 81 // $iterator (RecursiveIterator): 現在の内部イテレータオブジェクト(ここでは RecursiveIteratorIterator) 82 $filterIterator = new RecursiveCallbackFilterIterator( 83 $recursiveIterator, 84 function (SplFileInfo $current, string $key, RecursiveIterator $iterator): bool { 85 // ファイルであること、かつ拡張子が 'txt' であることを確認してフィルタリング 86 return $current->isFile() && strtolower($current->getExtension()) === 'txt'; 87 } 88 ); 89 90 echo "フィルタリングされたファイル:\n"; 91 echo "--------------------------------------------------\n"; 92 93 // フィルタリングされたイテレータを foreach ループで処理します。 94 foreach ($filterIterator as $path => $fileInfo) { 95 // $fileInfo は RecursiveCallbackFilterIterator が提供する SplFileInfo オブジェクトです。 96 // これはフィルタリング条件を満たした要素そのものです。 97 echo " - パス (フィルタされたイテレータから): " . $fileInfo->getPathname() . "\n"; 98 echo " ファイル名 : " . $fileInfo->getFilename() . "\n"; 99 100 // RecursiveCallbackFilterIterator::getInnerIterator() メソッドは、 101 // フィルタリング中の現在の要素を指す「内部イテレータ」(元のイテレータ)を返します。 102 // このケースでは、RecursiveIteratorIterator のインスタンスが返されます。 103 $innerIterator = $filterIterator->getInnerIterator(); 104 105 // 内部イテレータも SplFileInfo オブジェクトのように振る舞い、 106 // 同じ要素に関する情報を取得できます。 107 // これにより、フィルタリング前の元のイテレータが持っていた詳細な情報や 108 // メソッドにアクセスできます。 109 echo " 内部イテレータからのパス : " . $innerIterator->getPathname() . "\n"; 110 echo " 元のベース名 : " . $innerIterator->getBasename() . "\n"; 111 echo "--------------------------------------------------\n"; 112 } 113 114} finally { 115 // 処理が完了したら、作成した一時ファイルを確実に削除します。 116 cleanupTestFiles($testDirectory); 117 echo "\nデモ終了。一時ファイルは削除されました。\n"; 118} 119
このサンプルコードは、PHPのイテレータ機能を用いて、ファイルシステムを再帰的に走査し、特定の条件でファイルをフィルタリングする方法を解説しています。特に、RecursiveDirectoryIterator、RecursiveIteratorIterator、そしてフィルタリングを行うRecursiveCallbackFilterIteratorを組み合わせて使用しています。
RecursiveCallbackFilterIteratorは、内部に保持するイテレータの要素を、指定されたコールバック関数に基づいて選択的に通過させる機能を提供します。この例では、ファイルが.txt拡張子を持つ場合にのみ要素を通過させています。
そして、このコードの重要な要素がgetInnerIterator()メソッドです。このメソッドは引数を一切受け取らず、RecursiveCallbackFilterIteratorが現在処理している「内部のイテレータ」オブジェクトを返します。戻り値はIterator型、または該当する要素がない場合はnullとなります。
getInnerIterator()を使用することで、フィルタリングを適用している最中であっても、元のイテレータが持っていた詳細な情報(例えば、フィルタリング前の元のパス名など)に直接アクセスすることが可能になります。これにより、フィルタリングされたデータだけでなく、その背後にある元のデータ構造のコンテキストも同時に扱うことができるため、イテレータ処理の強力な柔軟性を理解していただけます。
getInnerIterator() メソッドは、RecursiveCallbackFilterIterator に渡された「元のイテレータ」を返します。この戻り値の型は、その元のイテレータの実際の型に依存するため、常に同じ型であるとは限らないことに注意が必要です。フィルタリングのコールバック関数では、引数として渡される SplFileInfo オブジェクトの isFile() や getExtension() といったメソッドを適切に活用して条件を記述します。イテレータを複数連結して利用する場合、getInnerIterator() がどの階層のイテレータを返すのか、そのネスト構造を理解することが重要です。また、サンプルコードのように一時ファイルやディレクトリを生成する処理では、プログラムの終了時に finally ブロックなどを用いて必ず適切にクリーンアップを行うことで、システムを安全に保ち、不要なリソースの残存を防ぐことができます。