【PHP8.x】RecursiveCallbackFilterIterator::accept()メソッドの使い方
acceptメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『acceptメソッドは、RecursiveCallbackFilterIteratorが処理している現在の要素が、指定されたフィルタリング条件を満たすかどうかを判定するために呼び出されるメソッドです。このメソッドは、イテレータによる繰り返し処理の中で、各要素に対して内部的に自動で実行されます。実際の判定ロジックは、RecursiveCallbackFilterIteratorのインスタンスを作成する際にコンストラクタへ渡されたコールバック関数によって定義されます。acceptメソッドは、このコールバック関数を、現在の要素、キー、そしてイテレータ自身を引数として呼び出します。コールバック関数がtrueを返した場合、現在の要素はフィルタリング条件を満たすものとみなされ、イテレーションの結果として含まれます。一方、コールバック関数がfalseを返した場合、その要素は条件を満たさないものとして扱われ、結果から除外されます。このように、acceptメソッドはユーザーが定義した柔軟なルールに基づいて、再帰的なデータ構造を効率的にフィルタリングするための核となる機能です。
構文(syntax)
1<?php 2 3$data = [ 4 'fruits' => ['apple', 'banana', 'avocado'], 5 'vegetables' => ['carrot', 'asparagus'], 6]; 7 8$arrayIterator = new RecursiveArrayIterator($data); 9$iterator = new RecursiveIteratorIterator($arrayIterator); 10 11// コンストラクタに渡したコールバック関数が、accept()メソッドの実質的な処理を定義します。 12$filterIterator = new RecursiveCallbackFilterIterator($iterator, function ($current, $key, $iterator) { 13 // 現在の要素が文字列で、かつ'a'で始まる場合にtrueを返します。 14 // この戻り値がaccept()メソッドの評価結果となります。 15 return is_string($current) && str_starts_with($current, 'a'); 16}); 17 18// foreachループは内部的にaccept()を呼び出し、trueを返した要素のみを処理します。 19foreach ($filterIterator as $value) { 20 echo $value . PHP_EOL; 21} 22 23?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
bool
このメソッドは、現在の要素がフィルタリング条件を満たす場合に true を、満たさない場合に false を返します。
サンプルコード
PHPでファイルアップロードを許可する処理
1<?php 2 3declare(strict_types=1); 4 5/** 6 * ファイルアップロード後の処理をシミュレートするクラスです。 7 * 指定されたディレクトリ内を再帰的にスキャンし、許可された拡張子を持つファイルのみを 8 * "accept" (受理) して一覧表示します。 9 */ 10class FileUploadProcessor 11{ 12 /** 13 * テスト環境をセットアップし、ファイルフィルタリングを実行して結果を表示します。 14 * 15 * @param string $uploadDir 走査対象のディレクトリパス 16 * @param array<int, string> $allowedExtensions 許可する拡張子の配列 (例: ['jpg', 'png']) 17 */ 18 public function processFiles(string $uploadDir, array $allowedExtensions): void 19 { 20 // 処理の前にテスト用のディレクトリとファイルを作成します 21 $this->setupTestDirectory($uploadDir); 22 23 echo "スキャン対象ディレクトリ: {$uploadDir}\n"; 24 echo "許可する拡張子: " . implode(', ', $allowedExtensions) . "\n"; 25 echo "----------------------------------------\n"; 26 27 try { 28 // 1. ディレクトリを再帰的に走査するためのイテレータを作成します 29 $directoryIterator = new RecursiveDirectoryIterator( 30 $uploadDir, 31 \FilesystemIterator::SKIP_DOTS 32 ); 33 34 // 2. フィルタリングのためのコールバック関数を定義します。 35 // この関数が RecursiveCallbackFilterIterator::accept() の実質的な処理となり、 36 // bool値を返すことで、各項目を受理するかどうかを決定します。 37 $filterCallback = function (SplFileInfo $current, string $key, RecursiveIterator $iterator) use ($allowedExtensions): bool { 38 // ディレクトリの場合は、その中身を走査するため常に受理 (true) します 39 if ($iterator->hasChildren()) { 40 return true; 41 } 42 // ファイルの場合は、拡張子が許可リストに含まれているかチェックします 43 if ($current->isFile()) { 44 $extension = strtolower($current->getExtension()); 45 return in_array($extension, $allowedExtensions, true); 46 } 47 // それ以外は受理しません (false) 48 return false; 49 }; 50 51 // 3. 上記のコールバック関数を使って、ディレクトリイテレータをフィルタリングします 52 $filterIterator = new RecursiveCallbackFilterIterator($directoryIterator, $filterCallback); 53 54 // 4. フィルタリングされた結果を再帰的にすべて取得するためのイテレータ 55 $recursiveIterator = new RecursiveIteratorIterator($filterIterator); 56 57 echo "受理されたファイル一覧:\n"; 58 $found = false; 59 foreach ($recursiveIterator as $fileInfo) { 60 // 受理されたファイルのフルパスを表示します 61 echo $fileInfo->getPathname() . "\n"; 62 $found = true; 63 } 64 65 if (!$found) { 66 echo "該当するファイルは見つかりませんでした。\n"; 67 } 68 } catch (Exception $e) { 69 echo "エラーが発生しました: " . $e->getMessage() . "\n"; 70 } finally { 71 // 処理の後にテスト用のディレクトリとファイルを削除します 72 $this->cleanupTestDirectory($uploadDir); 73 } 74 } 75 76 /** 77 * シミュレーション用のディレクトリとファイルを準備します。 78 * 79 * @param string $dirPath 80 */ 81 private function setupTestDirectory(string $dirPath): void 82 { 83 // 既存のディレクトリがあれば一度削除してクリーンな状態にします 84 if (is_dir($dirPath)) { 85 $this->cleanupTestDirectory($dirPath); 86 } 87 mkdir($dirPath, 0777, true); 88 mkdir($dirPath . '/images', 0777, true); 89 90 // テスト用のファイルをいくつか作成します 91 touch($dirPath . '/images/photo.jpg'); 92 touch($dirPath . '/images/logo.png'); 93 touch($dirPath . '/document.pdf'); 94 touch($dirPath . '/archive.zip'); 95 touch($dirPath . '/images/icon.svg'); 96 } 97 98 /** 99 * シミュレーション用のディレクトリとファイルを削除します。 100 * 101 * @param string $dirPath 102 */ 103 private function cleanupTestDirectory(string $dirPath): void 104 { 105 if (!is_dir($dirPath)) { 106 return; 107 } 108 $iterator = new RecursiveIteratorIterator( 109 new RecursiveDirectoryIterator($dirPath, \FilesystemIterator::SKIP_DOTS), 110 \RecursiveIteratorIterator::CHILD_FIRST 111 ); 112 foreach ($iterator as $path) { 113 $path->isDir() ? rmdir($path->getRealPath()) : unlink($path->getRealPath()); 114 } 115 rmdir($dirPath); 116 echo "----------------------------------------\n"; 117 echo "テスト用ディレクトリ ({$dirPath}) をクリーンアップしました。\n"; 118 } 119} 120 121// --- 実行コード --- 122 123// 一時的なアップロードディレクトリのパスを定義 124$uploadDirectory = __DIR__ . '/temp_upload_dir'; 125 126// 受理したいファイルの拡張子リスト 127$acceptedExtensions = ['jpg', 'png']; 128 129// プロセッサをインスタンス化して実行 130$processor = new FileUploadProcessor(); 131$processor->processFiles($uploadDirectory, $acceptedExtensions); 132
RecursiveCallbackFilterIteratorは、ディレクトリ階層などを再帰的にたどりながら、指定した条件に合う要素だけを絞り込む(フィルタリングする)ためのクラスです。そのフィルタリングの判断を行うのがacceptメソッドです。
このメソッドを直接呼び出すことはなく、クラスのインスタンス作成時に渡す「コールバック関数」の処理内容として定義します。イテレータがディレクトリ内のファイルやサブディレクトリを一つずつチェックするたびに、内部的にacceptメソッドがこのコールバック関数を呼び出します。
サンプルコードでは、ファイルアップロード後のディレクトリを想定し、拡張子が 'jpg' または 'png' のファイルのみを抽出する例を示しています。$filterCallbackがその判断基準となる関数です。現在の項目がディレクトリの場合、その中身も走査対象とするため常にtrueを返します。ファイルの場合は、拡張子を調べて許可リストに含まれていればtrue、そうでなければfalseを返します。
acceptメソッドの戻り値はbool型です。実質的にはコールバック関数が返すこのtrueかfalseによって、その項目が結果に含まれるか(受理されるか)が決まります。最終的に、foreachループでは受理されたファイルだけが一覧表示されます。
RecursiveCallbackFilterIteratorのacceptメソッドは直接呼び出すものではなく、コンストラクタで渡したコールバック関数の戻り値によって、各項目を処理対象とするか(受理するか)が決まります。サンプルコードの$filterCallbackがその役割を担っています。重要な点は、サブディレクトリ内を走査するために、ディレクトリ自体には必ずtrueを返す必要があることです。もしfalseを返すと、そのディレクトリ配下は一切スキャンされません。また、実際のファイルアップロード処理では、拡張子だけでなくMIMEタイプも検証することがセキュリティ上推奨されます。ファイル名に不正な文字列がないかのチェックも重要です。
PHPでPOST JSONをacceptする
1<?php 2 3declare(strict_types=1); 4 5/** 6 * POSTされたJSONデータをフィルタリングするクラス 7 */ 8class JsonPostFilter 9{ 10 /** 11 * POSTされたJSONデータから承認済みの製品のみを抽出して表示します。 12 * RecursiveCallbackFilterIterator::accept() は、内部的にコールバック関数を 13 * 呼び出すことで、どの要素を "accept" (受け入れる) するかを決定します。 14 * 15 * @param string $jsonString POSTリクエストのボディとして想定されるJSON文字列 16 */ 17 public function displayApprovedProducts(string $jsonString): void 18 { 19 // JSONをPHPの連想配列にデコードします。 20 $data = json_decode($jsonString, true); 21 if ($data === null) { 22 echo "無効なJSONデータがPOSTされました。" . PHP_EOL; 23 return; 24 } 25 26 // 配列を再帰的に走査するためのイテレータを作成します。 27 $arrayIterator = new RecursiveArrayIterator($data); 28 29 /** 30 * フィルタリング条件を定義するコールバック関数。 31 * true を返す要素のみがイテレーションの対象となります。 32 */ 33 $filterCallback = function ($current, $key, RecursiveIterator $iterator): bool { 34 // 子要素を持つ配列(カテゴリなど)は、さらに中身を探索するため常に許可します。 35 if ($iterator->hasChildren()) { 36 return true; 37 } 38 39 // 'approved' キーを持つ配列で、その値が true の場合のみ許可 (accept) します。 40 if (is_array($current) && isset($current['approved'])) { 41 return $current['approved'] === true; 42 } 43 44 return false; 45 }; 46 47 // コールバックを使ってフィルタリングを行うイテレータを作成します。 48 $filterIterator = new RecursiveCallbackFilterIterator($arrayIterator, $filterCallback); 49 50 // フィルタリングされた結果を再帰的にたどるためのイテレータを作成します。 51 $recursiveIterator = new RecursiveIteratorIterator($filterIterator); 52 53 echo "POSTされたJSONから承認済み(approved: true)の製品を抽出:\n"; 54 55 // フィルタリングされた結果から製品名を取り出して表示します。 56 foreach ($recursiveIterator as $key => $value) { 57 if ($key === 'name') { 58 echo "- " . $value . "\n"; 59 } 60 } 61 } 62} 63 64// --- 実行コード --- 65 66// POSTリクエストで送信されたJSONデータをシミュレートします。 67// 実際のWebアプリケーションでは `file_get_contents('php://input')` で受け取ります。 68$mockPostJson = <<<JSON 69{ 70 "category": "Electronics", 71 "products": [ 72 { "name": "Smartphone", "price": 800, "approved": true }, 73 { "name": "Laptop", "price": 1200, "approved": true }, 74 { "name": "Tablet", "price": 600, "approved": false }, 75 { 76 "name": "Peripherals", 77 "items": [ 78 { "name": "Wireless Mouse", "price": 50, "approved": true }, 79 { "name": "Keyboard", "price": 80, "approved": false } 80 ] 81 } 82 ] 83} 84JSON; 85 86// クラスをインスタンス化し、メソッドを実行します。 87$filter = new JsonPostFilter(); 88$filter->displayApprovedProducts($mockPostJson); 89 90?>
このPHPサンプルコードは、WebサーバーがPOSTリクエストで受け取ったJSONデータの中から、特定の条件を満たす製品情報だけを抽出して表示する例です。
コードの中心となるRecursiveCallbackFilterIteratorは、多次元配列のような複雑なデータ構造を再帰的に(階層を深くたどりながら)チェックし、指定した条件に合う要素だけを選別するフィルターの役割を果たします。
このフィルターが各要素を処理する際、内部でacceptメソッドが自動的に呼び出されます。acceptメソッドは引数を取らず、bool型(trueまたはfalse)の値を返します。この戻り値がtrueであれば、その要素は処理対象として「受け入れられ(accept)」、falseであれば無視されます。
どの要素をtrueと判断するかという具体的なルールは、RecursiveCallbackFilterIteratorのインスタンス作成時に渡されるコールバック関数によって定義されます。このサンプルコードでは、「approvedというキーを持ち、その値がtrueである製品データ」の場合のみtrueを返す、という条件が設定されています。
この仕組みを利用することで、承認されていない製品情報などを効率的に除外し、承認済みの製品名だけを正確に一覧表示することができます。
サンプルコードではaccept()メソッドを直接呼び出しませんが、この機能の核心を理解することが重要です。コンストラクタに渡したコールバック関数がtrueを返す要素がaccept(承認)されたと判断され、処理対象となります。フィルタリングの条件はすべてこの関数内で定義します。特に、子要素を持つ配列を探索するためには、$iterator->hasChildren()の条件でtrueを返す処理が不可欠です。これがないと階層の深いデータが無視されてしまいます。また、json_decodeの第二引数にtrueを指定することで、データを扱いやすい連想配列に変換できます。実際の開発では、POSTで受け取ったデータが意図した形式であるか必ず検証するセキュリティ対策も忘れないようにしましょう。