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

【PHP8.x】STREAM_OPTION_BLOCKING定数の使い方

STREAM_OPTION_BLOCKING定数の使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

STREAM_OPTION_BLOCKING定数は、PHPのストリーム操作において、I/O(入出力)のブロックモードを設定するために使用される定数です。

この定数を用いることで、ネットワークソケットやファイルなどのストリームが、データの読み書き操作中に処理をブロック(待機)するか、ブロックせずに即座に制御を返すかを指定できます。具体的には、この定数とともにtrue(あるいは1)を指定した場合、ストリームはブロックモードで動作します。ブロックモードでは、データの読み込みを試みた際、データが到着するまでプログラムの実行が一時停止します。同様に、データの書き込みを試みた際も、データが書き込まれるか、書き込みが可能になるまで処理が待機します。これは、データの到着や処理の完了を確実に待つ必要がある場合に適しています。

一方で、この定数とともにfalse(あるいは0)を指定した場合は、ストリームは非ブロックモードで動作します。非ブロックモードでは、データの読み込みや書き込みを試みた際に、データがすぐに利用可能でなくても、あるいは書き込みバッファがいっぱいでも、関連する関数は即座に制御をプログラムに返します。この際、読み込み操作では利用可能なデータがないことを示す値が返され、書き込み操作では実際に書き込まれたバイト数が返されます。プログラムは他の処理を継続し、後でデータの準備ができたか、あるいは書き込みが可能になったかを再度確認する必要があります。

STREAM_OPTION_BLOCKING定数は主にstream_context_set_option()関数などのストリーム関連関数で、コンテキストオプションの一部として利用されます。システム全体の応答性やリソースの効率的な利用を考慮する際に重要な設定であり、特にネットワーク通信を行うアプリケーションで、プログラムの応答性を向上させるために非ブロックモードが活用されます。

構文(syntax)

1<?php
2var_dump(STREAM_OPTION_BLOCKING);

引数(parameters)

引数なし

引数はありません

戻り値(return)

int

STREAM_OPTION_BLOCKING は、ストリームのノンブロッキングモードを設定または取得するための定数です。この定数を stream_set_option() 関数で使用すると、ストリームがノンブロッキングモードで動作するかどうかを制御できます。

サンプルコード

PHP: stream_set_blocking でストリームモードを切り替える

1<?php
2
3/**
4 * ストリームのブロッキングモード設定方法をデモンストレーションする関数。
5 *
6 * この関数は、PHPの stream_set_blocking 関数と STREAM_OPTION_BLOCKING 定数の使用例を示します。
7 * 初心者向けに、ストリームのブロッキングとノンブロッキングモードへの切り替えを分かりやすく解説します。
8 *
9 * @param string $streamUri 操作対象のストリームURI (例: 'php://temp', 'php://stdin' など)。
10 *                          デフォルトは 'php://temp' で、自動的に破棄される一時ストリームです。
11 */
12function demonstrateStreamBlockingMode(string $streamUri = 'php://temp'): void
13{
14    // ストリームを開く。ここでは一時的な読み書き可能なストリームを使用します。
15    // 'r+' モードで開くことで、読み書き両方の操作が可能です。
16    $stream = fopen($streamUri, 'r+');
17
18    if (!$stream) {
19        echo "エラー: ストリーム '{$streamUri}' を開けませんでした。\n";
20        return;
21    }
22
23    echo "--- ストリームのブロッキングモード設定デモンストレーション ---\n";
24    echo "対象ストリーム: {$streamUri}\n\n";
25
26    // 1. stream_set_blocking 関数を使ってブロッキングモードに設定
27    //    `true` を渡すと、ストリームはブロッキングモードになります。
28    //    読み込み操作 (例: fread) は、データが利用可能になるまでプログラムの実行を停止 (ブロック) します。
29    if (stream_set_blocking($stream, true)) {
30        echo "1. stream_set_blocking(true) でブロッキングモードに設定しました。\n";
31        echo "   (このモードでは、読み込み操作はデータが来るまで待機します。)\n";
32    } else {
33        echo "1. stream_set_blocking(true) の設定に失敗しました。\n";
34    }
35
36    echo "\n";
37
38    // 2. stream_set_option 関数と STREAM_OPTION_BLOCKING 定数を使ってノンブロッキングモードに設定
39    //    STREAM_OPTION_BLOCKING は、stream_set_option の第2引数として使用される定数です。
40    //    第3引数で 0 を指定するとノンブロッキング、1 を指定するとブロッキングになります。
41    //    `0` を渡すと、ストリームはノンブロッキングモードになります。
42    //    読み込み操作はデータがなくてもすぐに戻り、利用可能なデータがない場合は空の文字列などを返します。
43    if (stream_set_option($stream, STREAM_OPTION_BLOCKING, 0)) {
44        echo "2. stream_set_option と STREAM_OPTION_BLOCKING(0) でノンブロッキングモードに設定しました。\n";
45        echo "   (このモードでは、読み込み操作はデータがなくてもすぐに戻ります。)\n";
46        // 例: ノンブロッキングでの読み込みは、すぐに実行され、利用可能なデータがなければ空を返します。
47        // fwrite($stream, "Hello"); // テストデータ書き込み
48        // $readData = fread($stream, 1024);
49        // echo "   ノンブロッキング読み込み結果: " . (empty($readData) ? "[データなし]" : $readData) . "\n";
50    } else {
51        echo "2. stream_set_option と STREAM_OPTION_BLOCKING(0) の設定に失敗しました。\n";
52    }
53
54    echo "\n";
55
56    // 3. 再び stream_set_blocking を使ってブロッキングモードに戻す
57    if (stream_set_blocking($stream, true)) {
58        echo "3. stream_set_blocking(true) で再度ブロッキングモードに戻しました。\n";
59    } else {
60        echo "3. stream_set_blocking(true) の設定に失敗しました。\n";
61    }
62
63    // 開いたストリームを閉じる
64    fclose($stream);
65    echo "\nストリームを閉じました。\n";
66}
67
68// 関数を実行してデモンストレーションを開始
69demonstrateStreamBlockingMode();
70
71// 例: 標準入力 (php://stdin) で試す場合はコメントアウトを解除
72// echo "\n--- php://stdin でのデモンストレーション (入力待機動作を確認できます) ---\n";
73// echo "何か入力してEnterキーを押してください。\n";
74// demonstrateStreamBlockingMode('php://stdin');

PHP 8のSTREAM_OPTION_BLOCKINGは、ストリーム(ファイルやネットワーク接続など、データが流れる経路)の動作モードを制御するための定数です。この定数自体は引数を持たず、PHP内部で定義された整数値(int)を返します。主にstream_set_option()関数と組み合わせて使用され、ストリームを「ブロッキングモード」または「ノンブロッキングモード」に設定する際に利用されます。

ブロッキングモードに設定すると、ストリームからのデータ読み込み操作(例えばファイルからデータを読み込む際など)は、必要なデータが利用可能になるまでプログラムの実行を一時的に停止(ブロック)します。これにより、データが完全に揃うのを待ってから次の処理に進めます。一方、ノンブロッキングモードでは、データが利用可能でなくても読み込み操作はすぐに処理を返し、利用可能なデータがない場合は空の文字列などを返します。これにより、プログラムはデータの到着を待つ間に他の処理を実行できるようになります。

サンプルコードでは、まず一時ストリームを開き、stream_set_blocking(true)でブロッキングモードに設定する様子を示しています。次に、stream_set_option()関数を使用し、STREAM_OPTION_BLOCKING定数と値0(ノンブロッキングを意味します)を渡すことで、ストリームをノンブロッキングモードへ切り替える方法を実演しています。最後に、再度ブロッキングモードに戻すことで、これらのモード間の設定変更が可能であることを示しています。この定数を利用することで、プログラムのデータ処理を柔軟に制御することができます。

PHPのストリームは、stream_set_blocking関数、またはstream_set_option関数とSTREAM_OPTION_BLOCKING定数を用いて、ブロッキングモードとノンブロッキングモードを切り替えられます。ブロッキングモードでは、データが利用可能になるまでプログラムの実行が一時停止します。一方、ノンブロッキングモードでは、データがなくてもすぐに処理が継続されるため、データがない場合の継続的な確認(ポーリング)が必要になる場合があります。

これらの設定関数は成功・失敗を真偽値で返すため、必ず戻り値を確認し、適切にエラー処理を行うことが重要です。また、すべてのストリームがブロッキングモードの変更に対応しているわけではありませんので、対象ストリームの特性を理解して利用してください。特に、ネットワーク通信を伴うアプリケーションでこのモードの選択は重要になります。

PHP: STREAM_OPTION_BLOCKING と stream_select でノンブロッキングサーバーを構築する

1<?php
2
3/**
4 * STREAM_OPTION_BLOCKING 定数と stream_select を使用したシンプルなノンブロッキングTCPサーバーの例。
5 *
6 * この関数は、TCPソケットをノンブロッキングモードで作成し、
7 * stream_select を使用して複数のクライアントからの接続やデータを効率的に処理する方法を示します。
8 * STREAM_OPTION_BLOCKING の値は0であり、これはブロッキングを無効にする(ノンブロッキングにする)効果があります。
9 * stream_select は、ノンブロッキングモードのストリームの監視に適しています。
10 */
11function runNonBlockingTcpServer(): void
12{
13    // STREAM_OPTION_BLOCKING 定数を使用して、ソケットコンテキストをノンブロッキングに設定します。
14    // 'blocking' => STREAM_OPTION_BLOCKING は、'blocking' => false と同じ意味を持ちます。
15    $context = stream_context_create([
16        'socket' => [
17            'blocking' => STREAM_OPTION_BLOCKING,
18        ],
19    ]);
20
21    // 指定されたアドレスとポートでノンブロッキングサーバーソケットを作成します。
22    $server = stream_socket_server(
23        "tcp://127.0.0.1:8000",
24        $errno,
25        $errstr,
26        STREAM_SERVER_LISTEN,
27        $context
28    );
29
30    if (!$server) {
31        echo "エラー: サーバーの作成に失敗しました - {$errstr} ({$errno})\n";
32        return;
33    }
34
35    echo "サーバーが 127.0.0.1:8000 でリッスンを開始しました (ノンブロッキング)\n";
36
37    $clients = []; // 現在接続しているクライアントソケットの配列
38    $readStreams = [$server]; // stream_select で読み込み可能イベントを監視するストリームのリスト
39
40    while (true) {
41        // stream_select は引数の配列を直接変更するため、コピーを渡します。
42        $readable = $readStreams;
43        $writable = null; // 書き込み可能イベントは監視しない
44        $exceptional = null; // 例外イベントは監視しない
45
46        // 読み込み可能なストリーム、またはタイムアウト (1秒) まで待機します。
47        // タイムアウトを設けることで、他の処理を実行したり、無限ループを防いだりできます。
48        $numChangedStreams = stream_select($readable, $writable, $exceptional, 1);
49
50        if ($numChangedStreams === false) {
51            echo "エラー: stream_select が失敗しました\n";
52            break;
53        } elseif ($numChangedStreams === 0) {
54            // 活動がなかった場合、次のループに進みます。
55            continue;
56        }
57
58        // サーバーソケットに新しい接続があるか確認します。
59        if (in_array($server, $readable, true)) {
60            // 新しいクライアント接続を受け入れます。タイムアウト0でノンブロッキング動作。
61            $newClient = stream_socket_accept($server, 0, $peername);
62            if ($newClient) {
63                // 受け入れたクライアントソケットもノンブロッキングモードに設定します。
64                // STREAM_OPTION_BLOCKING (0) は false と同義で、ノンブロッキングを設定します。
65                stream_set_blocking($newClient, STREAM_OPTION_BLOCKING);
66                $clients[] = $newClient;
67                $readStreams[] = $newClient; // 監視リストに新しいクライアントを追加
68                echo "新しいクライアントが接続しました: {$peername}\n";
69            }
70            // サーバーソケットは新しい接続を処理済みなので、$readable から削除します。
71            $readable = array_diff($readable, [$server]);
72        }
73
74        // 読み込み可能になった各クライアントソケットからのデータを処理します。
75        foreach ($readable as $client) {
76            $data = fread($client, 8192); // 最大8192バイトを読み込む
77
78            if ($data === false || $data === '') {
79                // クライアントが切断されたか、読み込みエラーが発生しました。
80                $clientKey = array_search($client, $clients, true);
81                if ($clientKey !== false) {
82                    unset($clients[$clientKey]);
83                    fclose($client);
84                    echo "クライアントが切断しました。\n";
85                }
86                // このクライアントを監視リストから削除します。
87                $readStreams = array_diff($readStreams, [$client]);
88            } else {
89                $message = trim($data);
90                echo "クライアントから受信: {$message}\n";
91                // 受信したメッセージをクライアントに返信します。
92                fwrite($client, "サーバーが受信しました: {$message}\n");
93            }
94        }
95    }
96
97    // サーバーシャットダウン時に、開いているすべてのクライアント接続とサーバーソケットを閉じます。
98    foreach ($clients as $client) {
99        fclose($client);
100    }
101    fclose($server);
102    echo "サーバーが停止しました。\n";
103}
104
105// ノンブロッキングTCPサーバーを起動します。
106runNonBlockingTcpServer();

PHPのSTREAM_OPTION_BLOCKING定数は、ストリーム(ファイルやネットワークソケットなど、データの流れ)の操作を「ノンブロッキング」モードに設定するための値で、その値は整数 0 です。これにより、データの読み書き操作が完了するのを待たずに、プログラムはすぐに次の処理へ進むことができます。

このサンプルコードは、STREAM_OPTION_BLOCKING定数とstream_select関数を用いたノンブロッキングTCPサーバーの例です。複数のクライアントからの接続やデータを効率的に処理することを目指しています。最初に、stream_context_create関数でノンブロッキング設定を含むコンテキストを作成し、それを使ってstream_socket_serverでノンブロッキングサーバーソケットを生成します。

サーバーのメインループでは、stream_select関数がサーバーソケットと接続中のクライアントソケットを監視します。この関数は、監視対象のストリーム配列を引数として受け取り、読み書き可能になったストリームの数を整数で返します。これにより、サーバーは特定のソケットのイベント発生を待つ「ブロッキング」状態を回避し、多数のクライアントからの要求に同時に応答できるようになります。

新しい接続はstream_socket_acceptで受け入れられ、そのクライアントソケットもノンブロッキングに設定されます。その後、読み込み可能になったソケットからfreadでデータを受信し、fwriteで返信を行うことで、単一のプログラムで多数のクライアントとの効率的な通信を実現しています。

STREAM_OPTION_BLOCKING定数は値が0で、ソケットを「ノンブロッキング」モードに設定し、処理が待機せずに即座に実行されるようにします。これはfalseと同義で、複数の接続を同時に扱うサーバー構築に不可欠です。stream_select関数を使用する際は、引数として渡すストリームリストが関数内で変更されるため、ループごとに元のリストをコピーして渡すようにしてください。また、stream_selectに適切なタイムアウトを設定することで、サーバーが無限に待機することを避け、他の処理も実行できる柔軟性を持たせます。新しいクライアント接続を受け入れた際も、そのソケットを明示的にノンブロッキングに設定し、監視リストに加える必要があります。クライアントが切断された場合は、そのソケットを閉じ、監視対象から確実に削除することが重要です。これにより、サーバーは安定した動作を維持します。

関連コンテンツ

関連IT用語

関連プログラミング言語