【PHP8.x】RecursiveFilterIterator::accept()メソッドの使い方
acceptメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
acceptメソッドは、再帰的なイテレータの現在の要素が、指定したフィルタ条件を満たすかどうかを判定する処理を実行するメソッドです。このメソッドは抽象メソッドであり、RecursiveFilterIteratorクラスを継承して独自のフィルタクラスを作成する際には、必ずこのメソッドを実装(オーバーライド)して具体的な判定ロジックを記述する必要があります。acceptメソッド内では、現在の要素を評価し、その要素をイテレーションの結果に含める場合はtrueを、含めない場合はfalseを返すようにロジックを組みます。特にRecursiveFilterIteratorの文脈では、現在の要素が子要素を持つ場合(例えばディレクトリ)の挙動が重要です。このメソッドがtrueを返すと、その要素の子要素群に対しても再帰的にフィルタが適用されます。逆にfalseを返した場合、その要素自身だけでなく、その配下にあるすべての子孫要素も反復処理の対象から除外されます。この仕組みを利用することで、ファイルシステムを走査して特定の拡張子を持つファイルのみを抽出するなど、階層構造を持つデータに対して柔軟なフィルタリングを実現できます。
構文(syntax)
1<?php 2class MyFilter extends RecursiveFilterIterator 3{ 4 public function accept(): bool 5 { 6 // ここにフィルタリングの条件を実装し、 7 // bool (true または false) の値を返す必要があります。 8 return true; 9 } 10}
引数(parameters)
引数なし
引数はありません
戻り値(return)
bool
このメソッドは、現在の要素がフィルタリング条件を満たすかどうかを示す真偽値(trueまたはfalse)を返します。
サンプルコード
PHPで許可拡張子ファイルのみacceptする
1<?php 2 3/** 4 * 特定の拡張子を持つファイルのみを許可するカスタムフィルタクラス。 5 * 6 * RecursiveFilterIteratorを継承し、accept()メソッドを実装することで、 7 * ディレクトリ内の要素(ファイルやサブディレクトリ)をフィルタリングします。 8 * このサンプルでは、ファイルアップロード先ディレクトリを想定し、 9 * 'jpg'と'png'の拡張子を持つファイルのみを「受け入れ」ます。 10 */ 11class AcceptedFileFilter extends RecursiveFilterIterator 12{ 13 /** 14 * 受け入れるファイルの拡張子リスト。 15 * @var string[] 16 */ 17 private const ALLOWED_EXTENSIONS = ['jpg', 'png']; 18 19 /** 20 * イテレータが指す現在の要素を受け入れるかどうかを判断します。 21 * 22 * このメソッドは、ディレクトリ内の各アイテムに対して自動的に呼び出されます。 23 * 24 * @return bool 現在のアイテムを結果に含める場合は true、除外する場合は false を返します。 25 */ 26 public function accept(): bool 27 { 28 // 現在のアイテムがファイルでなければ、サブディレクトリの可能性があるため探索を続行 29 if (!$this->current()->isFile()) { 30 return true; 31 } 32 33 // ファイルの拡張子を小文字で取得 34 $extension = strtolower($this->current()->getExtension()); 35 36 // 拡張子が許可リストに含まれているかチェックし、結果を返す 37 return in_array($extension, self::ALLOWED_EXTENSIONS, true); 38 } 39} 40 41// --- 単体で動作させるための準備 --- 42// サンプル用のディレクトリとファイルを作成します。 43$uploadDir = __DIR__ . '/temp_uploads'; 44if (!is_dir($uploadDir)) { 45 mkdir($uploadDir, 0777, true); 46} 47touch($uploadDir . '/image.jpg'); 48touch($uploadDir . '/logo.png'); 49touch($uploadDir . '/document.pdf'); // これは除外される 50touch($uploadDir . '/archive.zip'); // これも除外される 51 52// --- メイン処理 --- 53echo "許可された拡張子のファイルのみを表示します:\n"; 54 55try { 56 // 1. スキャン対象のディレクトリを指すイテレータを作成 57 $directoryIterator = new RecursiveDirectoryIterator( 58 $uploadDir, 59 FilesystemIterator::SKIP_DOTS // '.' と '..' をスキップ 60 ); 61 62 // 2. 作成したカスタムフィルタでディレクトリイテレータをラップ 63 $filterIterator = new AcceptedFileFilter($directoryIterator); 64 65 // 3. 再帰的にすべての要素をたどるためのイテレータでさらにラップ 66 $recursiveIterator = new RecursiveIteratorIterator($filterIterator); 67 68 // 4. フィルタリングされた結果をループで出力 69 foreach ($recursiveIterator as $file) { 70 // SplFileInfoオブジェクトからファイルパスを取得して表示 71 echo $file->getPathname() . PHP_EOL; 72 } 73} catch (Exception $e) { 74 echo "エラーが発生しました: " . $e->getMessage() . PHP_EOL; 75} finally { 76 // --- 後始末 --- 77 // 作成したサンプル用のディレクトリとファイルを削除します。 78 if (is_dir($uploadDir)) { 79 array_map('unlink', glob("$uploadDir/*.*")); 80 rmdir($uploadDir); 81 } 82}
このPHPサンプルコードは、指定したディレクトリ内を再帰的にスキャンし、特定の条件に合致するファイルだけを一覧表示する方法を示します。ファイルアップロード機能で、許可された拡張子のファイルのみを処理するような場面で役立ちます。
コードの中心は、RecursiveFilterIteratorを継承して作成したAcceptedFileFilterクラスと、その中のacceptメソッドです。acceptメソッドは、ディレクトリ内の各要素(ファイルやサブディレクトリ)をフィルタリングするための条件を定義する役割を担います。このメソッドは引数を取らず、各要素が見つかるたびに自動的に呼び出されます。戻り値は真偽値(bool)で、現在の要素を結果に含める場合はtrueを、除外する場合はfalseを返します。
このサンプルでは、acceptメソッドの中で、現在の要素がファイルであればその拡張子を取得し、許可リスト('jpg', 'png')に含まれているかを判定します。含まれていればtrue、そうでなければfalseを返します。ファイル以外(サブディレクトリ)の場合は、その中も探索を続けるため常にtrueを返します。この仕組みにより、最終的に許可された拡張子を持つファイルパスだけが出力されます。
このサンプルコードのacceptメソッドは、trueを返すとそのアイテムを結果に含め、falseを返すと除外します。サブディレクトリ内も再帰的に探索するため、ファイルでない要素(ディレクトリ自体)に対しては常にtrueを返す処理が重要です。これを忘れるとサブディレクトリ内のファイルがスキャンされません。また、拡張子をstrtolowerで小文字に統一することで、JPGのような大文字の拡張子も正しく判定できます。実際のファイルアップロード機能で利用する際は、拡張子偽装の可能性があるため、ファイルの中身からMIMEタイプを検証するなど、より厳格なチェックを併用することが推奨されます。
PHPでJSON POSTデータから公開済み商品を取得する
1<?php 2 3declare(strict_types=1); 4 5/** 6 * RecursiveFilterIterator を継承し、特定の条件でJSONデータをフィルタリングするクラス。 7 * 8 * このイテレータは、配列要素が 'status' キーを持ち、その値が 'published' でない場合、 9 * その要素(およびそのすべての子要素)をフィルタリングで除外します。 10 */ 11class PublishedItemFilter extends RecursiveFilterIterator 12{ 13 /** 14 * 現在の要素がイテレーション結果に含まれるべきかを判断します。 15 * 16 * @return bool 要素を含める場合は true、除外する場合は false を返します。 17 */ 18 public function accept(): bool 19 { 20 // 現在のイテレータが指す要素を取得します。 21 $currentItem = $this->current(); 22 23 // 要素が配列で、かつ 'status' キーを持っているかチェックします。 24 // これは商品データのようなオブジェクト構造を想定しています。 25 if (is_array($currentItem) && isset($currentItem['status'])) { 26 // 'status' の値が 'published' の場合のみ true (許可) を返します。 27 return $currentItem['status'] === 'published'; 28 } 29 30 // 上記の条件に当てはまらない要素 (例: 'products' 配列自体や、末端の文字列など) は 31 // 常に許可し、再帰的な探索を続行させます。 32 return true; 33 } 34} 35 36// --- メイン処理 --- 37 38// 1. HTTP POSTで送信されたJSONデータをシミュレートします。 39// 実際のWebアプリケーションでは $json = file_get_contents('php://input'); のように受け取ります。 40$postJson = '{ 41 "requestId": "abc-123", 42 "data": { 43 "products": [ 44 { 45 "id": 101, 46 "name": "高機能マウス", 47 "status": "published", 48 "price": 5000 49 }, 50 { 51 "id": 102, 52 "name": "静音キーボード", 53 "status": "draft", 54 "price": 8000 55 }, 56 { 57 "id": 103, 58 "name": "4Kモニター", 59 "status": "published", 60 "price": 45000 61 } 62 ] 63 } 64}'; 65 66// 2. JSONデータをPHPの連想配列にデコードします。 67$data = json_decode($postJson, true); 68 69// 3. イテレータを準備します。 70// まず、多次元配列を再帰的に走査できる RecursiveArrayIterator を作成します。 71$arrayIterator = new RecursiveArrayIterator($data); 72 73// 次に、カスタムフィルタ PublishedItemFilter でラップします。 74$filterIterator = new PublishedItemFilter($arrayIterator); 75 76// 最後に、フィルタリングされた結果をフラットに処理するため RecursiveIteratorIterator を使います。 77$iterator = new RecursiveIteratorIterator($filterIterator, RecursiveIteratorIterator::SELF_FIRST); 78 79// 4. フィルタリングされた結果から、公開済みの商品オブジェクトのみを抽出します。 80$publishedProducts = []; 81foreach ($iterator as $value) { 82 // フィルタリング後の要素のうち、'status' キーを持つ配列(=公開済みの商品データ)のみを収集します。 83 if (is_array($value) && isset($value['status'])) { 84 $publishedProducts[] = $value; 85 } 86} 87 88// 5. 最終的な結果をJSON形式で出力します。 89// Webサーバー環境で実行する場合、このヘッダーはレスポンスがJSONであることを示します。 90// header('Content-Type: application/json; charset=utf-8'); 91 92// 抽出したデータをJSONにエンコードして出力します。 93echo json_encode( 94 ['published_products' => $publishedProducts], 95 JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE 96); 97 98?>
このサンプルコードは、PHPのRecursiveFilterIteratorクラスを使い、独自のフィルタリングルールを定義する方法を示しています。Web APIなどでPOSTされる階層構造のJSONデータの中から、特定の条件(ステータスが「公開済み」)に合致する商品情報だけを効率的に抽出する例です。
コードの中心は、PublishedItemFilterクラス内に実装されたaccept()メソッドです。このメソッドは、イテレータがデータを一つずつ調べる際に、その要素を結果に含めるべきかを判断する役割を担います。accept()メソッドに引数はなく、戻り値としてtrueを返せばその要素は有効となり、falseを返せば除外されます。
この例のaccept()メソッドでは、配列要素が'status'キーを持ち、その値が'published'である場合にのみtrueを返すように実装されています。この条件に合わない商品データ(例:'status'が'draft'のもの)は、そのデータ全体がフィルタリングで取り除かれます。
メイン処理では、JSONデータをPHP配列に変換した後、RecursiveArrayIteratorと自作のPublishedItemFilterを組み合わせてフィルタリングを実行します。最終的に、抽出された「公開済み」の商品データだけを新しい配列に格納し、再びJSON形式で出力しています。
acceptメソッドは、データ構造を再帰的に走査する際に各要素をフィルタリングする役割を担います。trueを返した要素は処理が続行され、falseを返すとその要素および配下の子要素すべてが結果から除外されます。このコードのポイントは、statusがpublishedでない商品データのみをfalseで明確に除外し、それ以外の要素(例えばproductsというキーを持つ配列自体など)にはtrueを返して探索を続けさせる点です。実際の開発では、POSTで受け取るJSONデータが不正な形式である可能性を考慮し、json_decodeの戻り値がnullでないかを確認するエラー処理が必須です。また、foreachループ内で再度型をチェックすることで、意図したデータ構造(この場合は商品配列)のみを安全に抽出しています。