【PHP8.x】RecursiveIteratorIterator::current()メソッドの使い方
currentメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『currentメソッドは、再帰的な反復処理中にイテレータが現在指している要素の値を取得するために実行するメソッドです。RecursiveIteratorIteratorクラスは、多次元配列やディレクトリ階層のような、ネストされたデータ構造をフラットな一連の要素として順番に処理する機能を提供します。このメソッドは、その一連の要素をたどる過程で、現在位置にある要素の値そのものを返します。foreach構文でRecursiveIteratorIteratorのインスタンスをループ処理する場合、各反復で変数に代入される値が、このcurrentメソッドの返り値に相当します。RecursiveIteratorIteratorはコンストラクタで動作モードを指定でき、例えば末端の要素(葉)のみを対象とするか、あるいは親要素(コンテナ)も対象に含めるかによって、currentメソッドが返す値の対象が変わります。返される値のデータ型は、元のイテレータが持つ要素の型に依存するためmixed型となります。このメソッドは、再帰構造内の各データへアクセスするための基本的な操作を担います。
構文(syntax)
1<?php 2 3$array = [ 4 'Apple', 5 'Banana', 6 [ 7 'Cherry', 8 'Durian', 9 ], 10 'Elderberry' 11]; 12 13$iterator = new RecursiveIteratorIterator( 14 new RecursiveArrayIterator($array) 15); 16 17foreach ($iterator as $value) { 18 // イテレータが指している現在の要素の値を取得します。 19 echo $iterator->current() . PHP_EOL; 20}
引数(parameters)
引数なし
引数はありません
戻り値(return)
mixed
現在のイテレータが指し示す要素を返します。要素の型は mixed で、PHP のあらゆる型に対応します。
サンプルコード
PHPでRecursiveIteratorIterator::current()を使う
1<?php 2 3/** 4 * カレントディレクトリ配下にサンプルディレクトリを作成し、 5 * その内容を再帰的に走査して、各要素のパスを表示します。 6 * 7 * このコードは、RecursiveIteratorIterator::current() メソッドが 8 * ループ中の現在の要素(ファイルやディレクトリの情報)を取得する様子を示します。 9 */ 10function displayCurrentDirectoryContents(): void 11{ 12 // このスクリプトが置かれている場所に、テスト用のディレクトリを作成します。 13 $sampleDir = __DIR__ . '/php_current_directory_sample'; 14 15 // try-finallyブロックを使い、処理の最後に必ず後片付け(ディレクトリ削除)が実行されるようにします。 16 try { 17 // --- 1. 準備: テスト用のディレクトリとファイルを作成 --- 18 // 階層構造を持つディレクトリを作成 19 mkdir($sampleDir . '/documents', 0777, true); 20 // ファイルを作成 21 file_put_contents($sampleDir . '/index.html', '<html></html>'); 22 file_put_contents($sampleDir . '/documents/report.txt', 'This is a report.'); 23 24 echo "ディレクトリ '{$sampleDir}' 内の要素を走査します:" . PHP_EOL; 25 26 // --- 2. 実行: ディレクトリを再帰的に走査 --- 27 // 指定したディレクトリを再帰的にたどるイテレータを準備します。 28 $directoryIterator = new RecursiveDirectoryIterator( 29 $sampleDir, 30 FilesystemIterator::SKIP_DOTS // '.' と '..' をスキップします。 31 ); 32 // 再帰イテレータを、foreachで扱えるフラットなリストに変換します。 33 $flatIterator = new RecursiveIteratorIterator($directoryIterator); 34 35 // イテレータを使ってループ処理します。 36 foreach ($flatIterator as $fileInfo) { 37 // current()メソッドは、ループで現在指している要素を返します。 38 // この場合、ファイルやディレクトリの情報を持つ SplFileInfo オブジェクトが返ります。 39 // (ループ変数 $fileInfo と同じオブジェクトです) 40 $currentItem = $flatIterator->current(); 41 42 // 取得した現在の要素から、フルパスを取得して表示します。 43 echo ' - ' . $currentItem->getPathname() . PHP_EOL; 44 } 45 } finally { 46 // --- 3. 後片付け: 作成したテストディレクトリとファイルを再帰的に削除 --- 47 if (is_dir($sampleDir)) { 48 $it = new RecursiveDirectoryIterator($sampleDir, FilesystemIterator::SKIP_DOTS); 49 $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST); 50 foreach ($files as $file) { 51 if ($file->isDir()) { 52 rmdir($file->getRealPath()); 53 } else { 54 unlink($file->getRealPath()); 55 } 56 } 57 rmdir($sampleDir); 58 echo "テストディレクトリ '{$sampleDir}' をクリーンアップしました。" . PHP_EOL; 59 } 60 } 61} 62 63// 作成した関数を実行します。 64displayCurrentDirectoryContents();
RecursiveIteratorIterator::current()メソッドは、イテレータ(繰り返し処理を行うための仕組み)が現在指し示している要素を取得します。このメソッドに引数は不要です。
サンプルコードでは、まずphp_current_directory_sampleというテスト用のディレクトリと、その中にいくつかのファイルやサブディレクトリを作成します。次に、RecursiveDirectoryIteratorとRecursiveIteratorIteratorを用いて、このディレクトリ構造を再帰的に、つまり階層の奥深くまでたどって走査する準備をします。
foreachループが実行されると、ディレクトリ内の各要素(ファイルやサブディレクトリ)が順番に処理されます。ループの中で$flatIterator->current()を呼び出すと、その時点で処理対象となっている要素が返されます。このコードの場合、返されるのはファイルやディレクトリの情報を持つSplFileInfoオブジェクトです。
foreach ($flatIterator as $fileInfo)という構文では、ループ変数である$fileInfoに現在の要素が自動的に代入されるため、$fileInfoと$flatIterator->current()が返す値は同一のものとなります。コードでは、このオブジェクトからgetPathname()メソッドを使って要素のフルパスを取得し、画面に出力しています。戻り値の型はmixedで、状況によって様々な値を返します。
このサンプルコードのforeachループでは、ループ変数$fileInfoにcurrent()メソッドの戻り値が自動的に代入されます。そのため、ループ内で改めて$flatIterator->current()を呼び出す必要は通常なく、$fileInfoを直接利用するのが一般的です。このコードはcurrent()の働きを明示的に示すためのものです。
また、try-finallyブロックの使用は、処理中にエラーが発生してもディレクトリの削除といった後片付けを保証する上で重要です。ディレクトリを再帰的に削除する際は、RecursiveIteratorIterator::CHILD_FIRSTを指定し、中身のファイルから先に削除しないとエラーになる点にも注意してください。
PHPで再帰配列から過去タイムスタンプを検索する
1<?php 2 3declare(strict_types=1); 4 5/** 6 * 再帰的なデータ構造から過去のタイムスタンプを検索して表示します。 7 * 8 * この関数は、入れ子になった配列内のUnixタイムスタンプを再帰的に検索し、 9 * 現在時刻より過去のものだけを日付形式にフォーマットして出力します。 10 * RecursiveIteratorIterator は、複雑な配列をフラットなリストのように扱うことを可能にし、 11 * ループ内で暗黙的に current() メソッドが各要素の値を取得するために使われます。 12 * 13 * @param array $logData タイムスタンプを含む可能性のある多次元配列 14 */ 15function findAndDisplayPastTimestamps(array $logData): void 16{ 17 // 多次元配列を扱うための再帰イテレータを作成します。 18 $arrayIterator = new RecursiveArrayIterator($logData); 19 20 // 再帰イテレータをフラットに処理するためのイテレータを作成します。 21 // `LEAVES_ONLY` を指定すると、配列の末端の値のみが対象になります。 22 $iterator = new RecursiveIteratorIterator($arrayIterator, RecursiveIteratorIterator::LEAVES_ONLY); 23 24 echo "過去のイベントタイムスタンプ一覧:\n"; 25 $currentTime = time(); // 現在のUnixタイムスタンプを取得 26 27 // イテレータをループ処理します。 28 // 各ループの `$value` には、`$iterator->current()` と同じ値が格納されます。 29 foreach ($iterator as $value) { 30 // 値が整数であり、かつ現在時刻より過去のタイムスタンプであるかを確認します。 31 if (is_int($value) && $value < $currentTime) { 32 // 条件に一致したタイムスタンプを、人間が読める日付形式に変換して出力します。 33 echo date('Y-m-d H:i:s', $value) . "\n"; 34 } 35 } 36} 37 38// サンプルとなる多次元配列データ 39// イベントログのように、様々な階層にタイムスタンプが格納されています。 40$eventLogData = [ 41 'system_events' => [ 42 'login' => [ 43 'user' => 'Alice', 44 'timestamp' => 1672531200, // 2023-01-01 00:00:00 45 ], 46 'error' => [ 47 'code' => 500, 48 'timestamp' => 1675209600, // 2023-02-01 00:00:00 49 ], 50 ], 51 'user_activities' => [ 52 'post' => 1704067200, // 2024-01-01 00:00:00 53 'comment' => 1706745600, // 2024-02-01 00:00:00 54 ], 55 'scheduled_tasks' => [ 56 'maintenance' => 2145916800, // 将来のタイムスタンプ (2038-01-01) 57 ], 58 'misc' => 'some other data', 59]; 60 61// 関数を実行して結果を表示します。 62findAndDisplayPastTimestamps($eventLogData); 63 64?>
このPHPサンプルコードは、入れ子になった多次元配列の中から、過去のUnixタイムスタンプだけを効率的に探し出して表示するものです。
ここで中心的な役割を果たすのがRecursiveIteratorIteratorクラスです。このクラスを使うことで、複雑な階層構造を持つ配列を、あたかも一次元のリストのように先頭から順番に処理できます。
foreachループが始まると、内部ではRecursiveIteratorIteratorクラスのcurrent()メソッドが各要素に対して自動的に呼び出されます。current()メソッドは、イテレータが現在指している要素の値を返します。このメソッドは引数を取らず、戻り値は要素のデータ型(数値、文字列など)によって異なるためmixed型となります。サンプルコードの$valueには、このcurrent()メソッドが返した値がループの各回で代入されます。
コード全体の流れとして、まずtime()関数で現在のタイムスタンプを取得します。次にforeachループで配列の値を一つずつ取り出し、それが整数であり、かつ現在より過去のタイムスタンプであるかを確認します。条件に一致した値は、date()関数によって人間が読みやすい日付形式に変換されて出力されます。
foreachループでイテレータを処理する際、各値は内部的にcurrent()メソッドで取得されます。このメソッドの戻り値は様々な型を取りうるmixed型であるため、サンプルコードのis_int()のように、期待するデータ型かどうかを必ずチェックする必要があります。このチェックを怠ると、意図しないデータに対して処理を行い、エラーの原因となります。また、整数チェックだけではタイムスタンプ以外の整数値も条件に合致する可能性があるため、扱うデータによっては値の範囲を検証するなど、より厳密な判定が求められる場合があります。RecursiveIteratorIteratorにLEAVES_ONLYを指定することで、配列の末端の値のみを安全に処理対象としています。