【PHP8.x】ParentIterator::accept()メソッドの使い方
acceptメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
acceptメソッドは、ParentIteratorが反復処理を行う際に、現在の要素を結果に含めるべきかどうかを判断するメソッドです。ParentIteratorは、再帰的なデータ構造、例えばディレクトリ構造や多次元配列などの中から、子要素を持つ「親」となる要素だけを抽出するためのイテレータです。このフィルタリング処理の核となるのがacceptメソッドの役割です。イテレータが内部で次の要素に進むたびに、このメソッドが自動的に呼び出されます。メソッドの内部では、現在の要素に対してhasChildrenメソッドを実行し、その要素が子を持っているかどうかを確認します。もし子を持っている場合、acceptメソッドはtrueを返し、その要素は有効なものとして反復処理の結果に含まれます。逆に、子を持たない要素の場合はfalseを返し、その要素はフィルタリングされてスキップされます。このように、このメソッドはParentIteratorがその名前の通り親要素のみを返すという挙動を実現するための重要な判定処理を担っています。
構文(syntax)
1<?php 2 3// 親要素(子を持つ配列)と子要素(文字列)が混在する配列 4$array = [ 5 'fruits' => ['apple', 'banana'], // 親要素 6 'vegetable' => 'carrot', // 子要素のみ 7 'nuts' => ['almond', 'walnut'], // 親要素 8]; 9 10// RecursiveArrayIteratorをParentIteratorでラップします。 11// これにより、子を持つ要素(配列など)のみを反復処理の対象とします。 12$iterator = new ParentIterator(new RecursiveArrayIterator($array)); 13 14// ParentIteratorをforeachでループさせると、 15// 内部で accept() メソッドが自動的に呼び出され、 16// 親要素('fruits'と'nuts')のみが選択されます。 17foreach ($iterator as $key => $value) { 18 echo $key . PHP_EOL; 19} 20 21?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
bool
ParentIterator::accept メソッドは、現在の要素をフィルタリングするかどうかを示す真偽値 (bool) を返します。true ならば要素が受け入れられ、false ならばスキップされます。
サンプルコード
PHPのacceptで画像ファイルのみを抽出する
1<?php 2 3/** 4 * 特定の拡張子を持つファイルのみを "受け入れる" (accept) フィルタクラス。 5 * 6 * このサンプルでは、アップロードされたファイルを想定したディレクトリから、 7 * 画像ファイル (.jpg, .png, .gif) のみを選択的に処理します。 8 * RecursiveFilterIteratorを継承し、accept()メソッドを実装することで、 9 * どのアイテムをイテレーションに含めるかを定義します。 10 */ 11class ImageFileFilter extends RecursiveFilterIterator 12{ 13 /** 14 * 許可する拡張子のリスト 15 * @var array<int, string> 16 */ 17 private const ACCEPTABLE_EXTENSIONS = ['jpg', 'png', 'gif']; 18 19 /** 20 * 現在のイテレータの要素を受け入れるかどうかを判断します。 21 * 22 * @return bool 現在の要素が有効な場合は true、それ以外は false を返します。 23 */ 24 public function accept(): bool 25 { 26 // カレントアイテム(ファイルまたはディレクトリ)を取得 27 $currentItem = $this->current(); 28 29 // ディレクトリの場合は再帰的に探索するため、常に受け入れる 30 if ($currentItem->isDir()) { 31 return true; 32 } 33 34 // ファイルの場合、拡張子が許可リストに含まれているかチェック 35 if ($currentItem->isFile()) { 36 $extension = strtolower($currentItem->getExtension()); 37 return in_array($extension, self::ACCEPTABLE_EXTENSIONS, true); 38 } 39 40 // 上記以外は受け入れない 41 return false; 42 } 43} 44 45/** 46 * サンプルコードの実行部分 47 */ 48function main(): void 49{ 50 // --- 準備: アップロードされたファイルを模したディレクトリとファイルを作成 --- 51 $uploadDir = sys_get_temp_dir() . '/php_accept_upload_test'; 52 if (!is_dir($uploadDir)) { 53 mkdir($uploadDir, 0777, true); 54 } 55 // ダミーファイルを作成 56 $dummyFiles = [ 57 'document.pdf', 58 'photo_01.jpg', 59 'archive.zip', 60 'logo.png', 61 'temporary.tmp', 62 'animation.gif', 63 'script.php' 64 ]; 65 foreach ($dummyFiles as $file) { 66 touch($uploadDir . '/' . $file); 67 } 68 // --- 準備完了 --- 69 70 echo "アップロードディレクトリ内のファイルをスキャンし、画像ファイルのみを抽出します。\n"; 71 echo "対象ディレクトリ: " . $uploadDir . "\n\n"; 72 echo "受け入れられた(accepted)ファイル:\n"; 73 74 try { 75 // 1. スキャン対象のディレクトリを指定してイテレータを作成 76 $directoryIterator = new RecursiveDirectoryIterator( 77 $uploadDir, 78 FilesystemIterator::SKIP_DOTS 79 ); 80 81 // 2. 作成したカスタムフィルタでディレクトリイテレータをラップ 82 $imageFilter = new ImageFileFilter($directoryIterator); 83 84 // 3. フィルタリングされた結果を再帰的に処理するためのイテレータ 85 $iterator = new RecursiveIteratorIterator($imageFilter); 86 87 // 4. フィルタリングされたファイル(画像ファイル)をループ処理して表示 88 $found = false; 89 foreach ($iterator as $fileInfo) { 90 echo "- " . $fileInfo->getFilename() . "\n"; 91 $found = true; 92 } 93 94 if (!$found) { 95 echo "画像ファイルが見つかりませんでした。\n"; 96 } 97 } catch (Exception $e) { 98 echo "エラーが発生しました: " . $e->getMessage() . "\n"; 99 } finally { 100 // --- 後片付け: 作成したダミーファイルとディレクトリを削除 --- 101 foreach ($dummyFiles as $file) { 102 $filePath = $uploadDir . '/' . $file; 103 if (file_exists($filePath)) { 104 unlink($filePath); 105 } 106 } 107 if (is_dir($uploadDir)) { 108 rmdir($uploadDir); 109 } 110 // --- 後片付け完了 --- 111 } 112} 113 114// 実行 115main();
FilterIteratorを継承したクラスで使われるacceptメソッドは、イテレータが持つ要素をフィルタリング(選別)するためのものです。イテレーション(繰り返し処理)の過程で各要素が処理対象として適切かどうかを判断する役割を担います。
このメソッドは引数を持たず、戻り値として真偽値(bool)を返します。acceptメソッドがtrueを返した場合、イテレータの現在の要素は有効とみなされ、繰り返し処理の対象となります。反対にfalseを返すと、その要素は無視され、処理から除外されます。
サンプルコードは、ファイルアップロードの場面を想定しています。ここではRecursiveFilterIteratorを継承したImageFileFilterクラスを作成し、その中でacceptメソッドに独自の選別ルールを定義しています。具体的には、ディレクトリ内のアイテムを一つずつチェックし、それが指定された拡張子(.jpg, .png, .gif)を持つ画像ファイルである場合にのみtrueを返します。これにより、様々な種類のファイルが混在する中から、目的の画像ファイルだけを安全に抽出し、リストアップすることが可能になります。このようにacceptメソッドは、条件に基づいてデータを選別するための関所のような仕組みを提供します。
acceptメソッドは、イテレータが処理する要素を選ぶための判定を行うメソッドです。このサンプルでは、ファイルの拡張子をチェックし、許可された画像ファイルのみを選び出しています。ディレクトリ自体は画像ファイルではありませんが、その中を再帰的に探索するためにtrueを返している点が重要です。実際のファイルアップロード機能にこの仕組みを応用する場合、拡張子だけのチェックはセキュリティ上不十分です。悪意あるファイルが拡張子を偽装する可能性があるため、finfo_file関数などでファイルの内容(MIMEタイプ)も必ず検証するようにしてください。strtolower関数で拡張子を小文字に統一しているのは、大文字の拡張子(例: .JPG)も正しく扱うための工夫です。
PHP POST JSONでアクティブユーザーをフィルタリングする
1<?php 2 3declare(strict_types=1); 4 5/** 6 * FilterIterator を継承し、特定の条件を持つ要素のみを許可するカスタムイテレータ。 7 * このクラスは ParentIterator::accept の概念を実装します。 8 */ 9class ActiveUserFilter extends FilterIterator 10{ 11 /** 12 * イテレータの現在の要素を受け入れる (accept) かどうかを判定します。 13 * 14 * このメソッドでは、ユーザーデータの 'status' が 'active' である要素のみを 15 * true (受け入れる) と判定します。 16 * 17 * @return bool 現在の要素が有効な場合は true, それ以外は false 18 */ 19 public function accept(): bool 20 { 21 $user = $this->current(); 22 23 return is_array($user) 24 && isset($user['status']) 25 && $user['status'] === 'active'; 26 } 27} 28 29// --- メイン処理 --- 30 31// レスポンスがJSON形式であることをクライアントに通知 32header('Content-Type: application/json; charset=utf-8'); 33 34// HTTP POSTリクエストのボディから生のJSONデータを取得 35$requestBody = file_get_contents('php://input'); 36 37// POSTデータが空の場合、動作確認用のサンプルデータを使用 38if (empty($requestBody)) { 39 $data = [ 40 ['id' => 1, 'name' => 'Alice', 'status' => 'active'], 41 ['id' => 2, 'name' => 'Bob', 'status' => 'inactive'], 42 ['id' => 3, 'name' => 'Carol', 'status' => 'active'], 43 ]; 44} else { 45 // 受け取ったJSON文字列をPHPの連想配列に変換 46 $data = json_decode($requestBody, true); 47 48 // JSONの形式が正しくない場合はエラーメッセージを返して終了 49 if (json_last_error() !== JSON_ERROR_NONE) { 50 http_response_code(400); // Bad Request 51 echo json_encode(['error' => 'Invalid JSON format.']); 52 exit; 53 } 54} 55 56try { 57 // 配列データから ArrayIterator オブジェクトを作成 58 $iterator = new ArrayIterator($data); 59 60 // 作成したイテレータをカスタムフィルタ (ActiveUserFilter) に渡す 61 $filter = new ActiveUserFilter($iterator); 62 63 // フィルタリングされた結果を配列に変換 64 $filteredUsers = iterator_to_array($filter); 65 66 // 結果の配列はキーが維持されるため、JSON配列として返すために値をリセット 67 echo json_encode(array_values($filteredUsers), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); 68} catch (Throwable $e) { 69 // 予期せぬエラーが発生した場合 70 http_response_code(500); // Internal Server Error 71 echo json_encode(['error' => 'An internal server error occurred.']); 72} 73
このPHPスクリプトは、HTTP POSTリクエストで送信されたJSON形式のユーザーリストの中から、特定の条件を満たすデータのみを抽出して返すWeb APIのサンプルコードです。
コードの中心となるのは、FilterIteratorを継承して定義されたActiveUserFilterクラスです。このクラスは、データの選別(フィルタリング)を行うためのacceptメソッドを実装しています。acceptメソッドは、イテレータがコレクションの各要素を一つずつ処理する際に自動的に呼び出されます。このメソッドは引数を持たず、戻り値としてbool型、つまりtrueかfalseのどちらかを返します。
このサンプルコードのacceptメソッドでは、ユーザーデータのstatusキーの値が'active'であるかを判定します。判定結果がtrueの場合、その要素は有効とみなされ結果として残ります。falseの場合は無効とみなされ、最終的な結果から除外されます。
メイン処理では、受け取ったJSONデータをPHPの配列に変換し、このActiveUserFilterを適用することでフィルタリングを実行します。その結果、statusが'active'であるユーザーのデータだけを抽出し、JSON形式でクライアントに応答として返します。
このコードの核心は、FilterIteratorを継承しacceptメソッドを独自に実装している点です。acceptメソッドは、配列などの各要素を順に評価し、条件に合う要素だけを残すフィルターの役割を担います。trueを返した要素のみが、最終的な結果に含まれます。php://inputから外部データを取得する際は、json_decodeのエラーチェックや、acceptメソッド内でのissetを用いたキーの存在確認が、予期せぬエラーを防ぐために非常に重要です。また、フィルタリング後の配列はキーが連番でなくなる可能性があるため、array_valuesでキーを振り直してからjson_encodeすることで、意図した通りのJSON配列形式で出力できます。