Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【PHP8.x】php_user_filter::filter()メソッドの使い方

filterメソッドの使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

filterメソッドは、PHPのphp_user_filterクラスに属し、ユーザーが定義するストリームフィルターが、実際にデータをフィルタリングする処理を実行するメソッドです。このメソッドは、ファイルやネットワーク接続など、ストリームを介してデータが読み書きされる際に、PHPのストリームI/Oサブシステムによって自動的に呼び出されます。

開発者はこのメソッドをオーバーライドし、ストリームを通過するデータをリアルタイムで加工する独自のロジックを実装します。引数としては、フィルタリング対象のデータが格納された入力バッファを表すストリームリソース($in)、フィルタリング後のデータを書き込む出力バッファを表すストリームリソース($out)、処理された入力データのバイト数を記録するための参照渡し変数($consumed)、そしてストリームが閉じられようとしているかを示す真偽値($closing)を受け取ります。

メソッドは処理結果に応じて、PSFS_PASS_ON(正常に処理を終え、データを出力バッファに書き込んだ場合)、PSFS_FEED_ME(入力バッファにデータが不足しており、さらなるデータが必要な場合)、またはPSFS_ERR_FATAL(処理中に致命的なエラーが発生した場合)のいずれかの定数を返します。これにより、データの圧縮、暗号化、特定フォーマットへの変換など、様々な用途でストリームデータを柔軟に処理することが可能になります。

構文(syntax)

1<?php
2
3class MyCustomFilter extends php_user_filter
4{
5    public function filter($in, $out, int &$consumed, bool $closing): int
6    {
7        // フィルタリング処理をここに実装します
8        // $in からデータを読み込み、処理し、$out へ書き込む
9        // 処理したバイト数を $consumed に加算する
10        // フィルタリング結果を示す定数 (例: PSFS_PASS_ON, PSFS_FEED_ME, PSFS_ERR_FATAL) を返します
11        return PSFS_PASS_ON;
12    }
13}

引数(parameters)

resource $in, resource $out, int &$consumed, bool $closing

  • resource $in: 読み込むためのストリームリソース
  • resource $out: 書き込むためのストリームリソース
  • int &$consumed: 読み込んだバイト数を格納する参照渡し整数
  • bool $closing: ストリームが閉じられる場合に true になるブール値

戻り値(return)

int

このメソッドは、ストリームフィルターの処理結果を示す整数値を返します。具体的には、成功した場合はPSFS_PASS_ON (1)、エラーが発生した場合はPSFS_ERR_FATAL (8)、または処理を中断した場合はPSFS_SPECIAL_USER_TRANSFORM (6) などの定数が返されます。

サンプルコード

PHPカスタムストリームフィルターでHTMLタグを除去する

1<?php
2
3/**
4 * カスタムストリームフィルターを定義するクラス。
5 * 入力ストリームからHTMLタグを除去します。
6 * これは、`filter_input(..., FILTER_SANITIZE_STRING)` のように、
7 * ユーザー入力から潜在的に危険なHTMLタグを取り除くサニタイズ処理を
8 * ストリームに適用する例です。
9 */
10class MyStripTagsFilter extends php_user_filter
11{
12    /**
13     * ストリームのフィルタリング処理を実行します。
14     *
15     * @param resource $in 入力バケットブリゲード (処理するデータを含むバケットのコレクション)
16     * @param resource $out 出力バケットブリゲード (処理後のデータを格納するバケットのコレクション)
17     * @param int      &$consumed これまでに消費されたバイト数を追跡するカウンター (参照渡し)
18     * @param bool     $closing ストリームが閉じられようとしているかを示すフラグ
19     * @return int フィルタリングのステータス
20     *             - `PSFS_PASS_ON`: フィルタリングが成功し、すべてのデータを処理した
21     *             - `PSFS_FEED_ME`: さらなる入力が必要である (通常は不要)
22     *             - `PSFS_ERR_FATAL`: 致命的なエラーが発生した
23     */
24    public function filter(resource $in, resource $out, int &$consumed, bool $closing): int
25    {
26        // 入力バケットブリゲードからすべてのバケットを繰り返し処理します。
27        while ($bucket = stream_bucket_make_writeable($in)) {
28            // キーワード 'php filter_input' に関連付けて、入力データからHTMLタグを除去するサニタイズ処理を実装します。
29            // これは `filter_input(INPUT_GET, 'param', FILTER_SANITIZE_STRING)` のような動作を模倣し、
30            // 潜在的に危険なHTMLタグ(例:<script>タグ)を取り除きます。
31            $processedData = strip_tags($bucket->data);
32
33            // 処理後のデータでバケットを更新し、その新しい長さを設定します。
34            $bucket->data = $processedData;
35            $bucket->datalen = strlen($processedData);
36
37            // 消費されたバイト数の合計を更新します。
38            $consumed += $bucket->datalen;
39
40            // 処理されたデータをアウトプットストリームに追加します。
41            stream_bucket_append($out, $bucket);
42        }
43
44        // フィルタリングが成功し、すべてのデータを処理したことを示します。
45        return PSFS_PASS_ON;
46    }
47}
48
49// --- 使用例 ---
50
51// 1. カスタムフィルターを登録し、'strip_tags_filter' という名前で利用可能にします。
52// stream_filter_register() は成功すると true を返します。
53if (!stream_filter_register("strip_tags_filter", MyStripTagsFilter::class)) {
54    error_log("エラー: ストリームフィルター 'strip_tags_filter' の登録に失敗しました。");
55    exit(1);
56}
57
58// 2. テスト用のHTMLを含む文字列データを用意します。
59$originalString = "Hello <b>World</b>! This is a <script>alert('XSS');</script> example with <p>some</p> HTML tags.";
60echo "=== オリジナル文字列 ===\n" . $originalString . PHP_EOL . PHP_EOL;
61
62// 3. メモリ上のストリームを作成します ('php://memory' は一時的なファイルとして動作します)。
63// fopen() は成功するとリソースを返します。
64$stream = fopen('php://memory', 'r+');
65if ($stream === false) {
66    error_log("エラー: 'php://memory' ストリームを開くことに失敗しました。");
67    exit(1);
68}
69
70// 4. 元のデータをストリームに書き込みます。
71fwrite($stream, $originalString);
72// ストリームポインタを先頭に戻し、読み込み準備をします。
73fseek($stream, 0);
74
75// 5. ストリームにカスタムフィルターを適用します。
76// 'strip_tags_filter' を読み込みモード (STREAM_FILTER_READ) で追加します。
77// これにより、ストリームからデータを読み出す際に `MyStripTagsFilter::filter` メソッドが自動的に呼び出されます。
78// stream_filter_append() は成功するとリソースを返します。
79$filter = stream_filter_append($stream, "strip_tags_filter", STREAM_FILTER_READ);
80if ($filter === false) {
81    error_log("エラー: ストリームフィルターの追加に失敗しました。");
82    fclose($stream);
83    exit(1);
84}
85
86// 6. フィルターされたデータをストリームから読み込みます。
87// stream_get_contents() はストリームの残りすべてを文字列として読み込みます。
88$filteredString = stream_get_contents($stream);
89
90echo "=== フィルター後の文字列 ===\n" . $filteredString . PHP_EOL;
91
92// 7. ストリームを閉じます。
93fclose($stream);
94
95?>

PHP 8 の php_user_filter::filter メソッドは、カスタムストリームフィルターの核となる処理ロジックを実装するために使われます。このメソッドは、入力ストリームからデータを受け取り、定義されたルールに基づいて加工し、出力ストリームへと渡す役割を担います。

引数 $in は、処理対象の入力データが格納されたバケットの集合です。$out は、加工後のデータが格納されるバケットの集合です。&$consumed は、これまでにフィルターで処理されたバイト数の合計を追跡するカウンターで、参照渡しのためメソッド内で値を更新できます。$closing は、ストリームが閉じられようとしているかを示すブール値のフラグです。戻り値はフィルタリングのステータスを示し、PSFS_PASS_ON はフィルタリングが成功し、すべてのデータを処理したことを意味します。

提供されたサンプルコードでは、filter_input 関数がユーザー入力からHTMLタグを除去してサニタイズするのと同様に、ストリームを通じて渡されるデータから潜在的に危険なHTMLタグを取り除くカスタムフィルターを実装しています。具体的には、入力バケットからデータを読み込み、PHPの strip_tags() 関数を使用してHTMLタグを除去します。その後、タグが除去されたデータを新しいバケットとして出力ストリームに追加することで、ストリームデータのサニタイズを自動的に行っています。このメソッドは、データが読み書きされる際に、リアルタイムで安全なデータ処理を適用するための非常に強力な仕組みです。

このサンプルコードは、filter_input のようなWeb入力の簡易なサニタイズとは異なり、ファイルやネットワークなどの「ストリーム」データそのものに独自の変換処理を適用する低レベルな方法です。データは「バケット」という単位で filter メソッドに渡されますので、各バケットを正しく処理し、変換後のデータを新しいバケットとして出力ストリームへ追加する流れを理解することが重要です。処理したバイト数は参照渡しの $consumed に正確に加算し、処理成功時には PSFS_PASS_ON を返すように注意してください。fopen で開いたストリームは、必ず fclose で閉じてリソースを解放する必要があります。この仕組みは、データ圧縮や暗号化、ログ加工など様々な用途に応用可能ですが、エラーハンドリングも丁寧に行うことをおすすめします。

PHPカスタムフィルターでfilter_var検証

1<?php
2
3/**
4 * php_user_filter を継承したカスタムストリームフィルターの例。
5 * このフィルターは、入力ストリームの各行を処理し、
6 * キーワードである filter_var を用いてデータの検証やサニタイズを行います。
7 */
8class MyCustomFilter extends php_user_filter
9{
10    /**
11     * ストリームデータのフィルタリングロジックを実装するメソッド。
12     *
13     * @param resource $in 入力バケットを格納するストリームリソース
14     * @param resource $out 出力バケットを格納するストリームリソース
15     * @param int      &$consumed フィルタリングによって消費されたデータのバイト数
16     * @param bool     $closing ストリームが閉じられようとしているかを示すフラグ
17     * @return int フィルタリング処理の結果を示す定数 (PSFS_PASS_ON, PSFS_FEED_ME, PSFS_ERR_FATAL)
18     */
19    public function filter(
20        resource $in,
21        resource $out,
22        int &$consumed,
23        bool $closing
24    ): int {
25        while ($bucket = stream_bucket_make_writeable($in)) {
26            $originalData = $bucket->data;
27            $processedData = '';
28
29            // 入力データを改行で分割し、各行を処理
30            $lines = explode("\n", $originalData);
31
32            foreach ($lines as $line) {
33                $trimmedLine = trim($line);
34
35                if ($trimmedLine === '') {
36                    // 空行はそのまま追加(またはスキップ)
37                    $processedData .= "\n";
38                    continue;
39                }
40
41                // キーワード: filter_var を使用してデータを検証・サニタイズする例
42                // 例1: 行が有効な整数であるかを検証
43                if (filter_var($trimmedLine, FILTER_VALIDATE_INT) !== false) {
44                    $processedData .= "VALID_INT: " . $trimmedLine . "\n";
45                }
46                // 例2: 行が有効なメールアドレスであるかを検証
47                elseif (filter_var($trimmedLine, FILTER_VALIDATE_EMAIL) !== false) {
48                    $processedData .= "VALID_EMAIL: " . $trimmedLine . "\n";
49                }
50                // 例3: それ以外の場合、HTMLエンティティをエスケープしてサニタイズ
51                else {
52                    $sanitizedLine = filter_var($trimmedLine, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
53                    $processedData .= "SANITIZED: " . $sanitizedLine . "\n";
54                }
55            }
56
57            // 処理されたデータを新しいバケットに格納し、出力ストリームに追加
58            $bucket->data = $processedData;
59            $bucket->datalen = strlen($processedData);
60            stream_bucket_append($out, $bucket);
61
62            // 消費されたバイト数を更新
63            $consumed += $bucket->datalen;
64        }
65
66        // 処理が成功し、残りのデータがあれば次に渡す
67        return PSFS_PASS_ON;
68    }
69
70    /**
71     * このカスタムフィルターの使用例を示す静的メソッド。
72     * 単体で動作可能なコードとしてデモンストレーションを行います。
73     */
74    public static function demonstrateUsage(): void
75    {
76        // カスタムフィルターを登録
77        // 登録名 "my_custom_filter" と MyCustomFilter クラスを関連付ける
78        if (!stream_filter_register("my_custom_filter", MyCustomFilter::class)) {
79            die("カスタムフィルターの登録に失敗しました。\n");
80        }
81
82        // テストデータ
83        $testData = <<<EOT
84Hello World
8512345
86test@example.com
87<script>alert('XSS');</script>
88Another line
8967890
90invalid-email
91EOT;
92
93        echo "--- 元のデータ ---\n";
94        echo $testData . "\n\n";
95
96        // メモリ上のストリームを作成
97        $fp = fopen("php://memory", "r+");
98        if ($fp === false) {
99            die("メモリストリームのオープンに失敗しました。\n");
100        }
101
102        // ストリームにテストデータを書き込む
103        fwrite($fp, $testData);
104        // ストリームのポインタを先頭に戻す
105        rewind($fp);
106
107        // 作成したカスタムフィルターをストリームに追加
108        // このフィルターは読み取り時に適用される
109        stream_filter_append($fp, "my_custom_filter", STREAM_FILTER_READ);
110
111        echo "--- フィルタリングされたデータ ---\n";
112        // フィルタリングされたデータを読み出し、出力
113        while (!feof($fp)) {
114            echo fread($fp, 8192); // データを読み出すたびにフィルターが適用される
115        }
116
117        // ストリームを閉じる
118        fclose($fp);
119    }
120}
121
122// スクリプトが直接実行された場合にデモンストレーションを実行
123MyCustomFilter::demonstrateUsage();

PHPのphp_user_filterクラスのfilterメソッドは、カスタムストリームフィルターの中核をなす機能です。このメソッドを実装することで、ファイルやネットワークなどあらゆるストリームのデータをリアルタイムで加工できます。

引数$inは入力されるストリームデータを、$outは処理後のデータが出力されるストリームをそれぞれリソースとして受け取ります。&$consumedは、フィルターがどれだけのデータを処理し消費したかをバイト単位で示す整数値で、参照渡しのためメソッド内で更新されます。$closingは、ストリームが閉じられようとしている場合にtrueとなる論理値です。メソッドは、処理が成功したか、追加データが必要か、エラーが発生したかを示す整数値(例: PSFS_PASS_ON)を戻り値として返します。

サンプルコードのMyCustomFilterクラスのfilterメソッドでは、入力されたデータを一行ずつ読み込み、キーワードであるfilter_var関数を使用してデータの検証とサニタイズを行っています。具体的には、各行が有効な整数値か、有効なメールアドレスであるかをチェックし、それ以外の場合はHTMLエンティティをエスケープして安全な文字列に変換しています。処理されたデータは整形され、出力ストリーム$outに追加されます。demonstrateUsageメソッドは、stream_filter_registerでフィルターを登録し、stream_filter_appendで実際のストリームに適用することで、データが動的にフィルタリングされる様子を示しています。

php_user_filterは、ファイルの読み書きを途中で加工する高度な機能であり、ストリーム処理の深い理解が必要です。filterメソッドの$consumed引数は、処理したデータ量を正確に更新しないと、データ破損の原因となるため特に注意してください。filter_varは、ユーザー入力の検証や無害化に必須の機能で、セキュリティ対策として非常に重要です。例えばメールアドレスの形式チェックやHTMLタグのエスケープに活用し、SQLインジェクションやXSS攻撃防止に役立ちます。検証は入力形式の確認、サニタイズは危険な文字の除去や変換を行う機能であり、違いを理解して適切に使い分けることが重要です。カスタムフィルターはstream_filter_registerで登録し、stream_filter_append等でストリームに適用する一連の手順を正しく行う必要があります。

関連コンテンツ