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

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

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

作成日: 更新日:

基本的な使い方

STREAM_OPTION_READ_TIMEOUT定数は、PHPにおけるストリーム操作において、データの読み込み処理に関するタイムアウト時間を設定するためのオプションを表す定数です。

この定数は、主にネットワーク接続を介した通信やファイルからのデータ読み込みといった、外部リソースからの情報取得を行うストリームに対して利用されます。ストリームからのデータ読み込みが指定された時間内に完了しない場合に、プログラムが無限に待ち続ける状態、いわゆる「ハングアップ」を防ぐことを目的としています。

具体的には、stream_set_option()関数などのストリーム制御関数と組み合わせて使用し、読み込み処理がタイムアウトするまでの許容時間を設定します。この設定により、例えば応答の遅いサーバーからのデータ受信や、切断されたネットワーク接続からの読み込み試行時に、プログラムが長時間ブロックされることなく、設定された時間で処理を中断し、適切なエラー処理へ移行させることが可能になります。

タイムアウト時間は通常、秒単位とマイクロ秒単位の両方で指定することができ、アプリケーションの応答性と安定性を確保するために非常に重要な設定項目です。これにより、不安定な外部環境下でもアプリケーションが予期せぬ停止をすることなく、堅牢な動作を実現するための基盤を提供します。システムエンジニアリングにおいて、ネットワーク通信を伴うアプリケーションを設計する際には、このようなタイムアウト設定の適切な管理が不可欠となります。

構文(syntax)

1<?php
2$optionIdToCheck = 6; // 例: 何らかのシステムから受け取ったオプション識別子
3
4if ($optionIdToCheck === STREAM_OPTION_READ_TIMEOUT) {
5    // 受け取ったオプション識別子がストリームの読み込みタイムアウトに関するものである場合
6    $readTimeoutIsSet = true;
7    $timeoutValueInSeconds = 5; // 関連するタイムアウト値(例)
8} else {
9    $readTimeoutIsSet = false;
10}

引数(parameters)

引数なし

引数はありません

戻り値(return)

戻り値なし

戻り値はありません

サンプルコード

PHPでストリーム読み取りタイムアウトを設定し確認する

1<?php
2
3declare(strict_types=1);
4
5/**
6 * ストリームの読み取りタイムアウトを設定し、その動作を確認するサンプルコード。
7 *
8 * この関数は、指定されたホストとポートへのネットワーク接続を確立し、
9 * 開かれたストリームに対して読み取りタイムアウトを設定します。
10 * その後、ストリームからデータを読み取ろうと試み、
11 * タイムアウトが発生したかどうかを `stream_get_meta_data` を用いて確認します。
12 *
13 * @param string $host 接続するホスト名またはIPアドレス (例: 'www.example.com')
14 * @param int $port 接続するポート番号 (例: 80 for HTTP)
15 * @param int $timeoutSeconds 読み取りタイムアウトの秒数
16 */
17function setAndCheckStreamTimeout(string $host, int $port, int $timeoutSeconds): void
18{
19    echo "--- ストリーム読み取りタイムアウトのデモンストレーション ---\n";
20    echo "接続先: {$host}:{$port}\n";
21    echo "設定する読み取りタイムアウト: {$timeoutSeconds}秒\n\n";
22
23    // 1. ネットワークソケットストリームを開く
24    // fsockopen の第5引数は接続タイムアウトです。読み取りタイムアウトとは別の概念です。
25    // '@' をつけて警告を抑制し、接続失敗を if (!$fp) で明示的にハンドリングします。
26    $fp = @fsockopen($host, $port, $errno, $errstr, 5); // 接続タイムアウトは5秒
27
28    if (!$fp) {
29        echo "エラー: 接続に失敗しました。($errno) $errstr\n";
30        echo "ホスト名やポート番号が正しいか、ネットワーク接続が利用可能か確認してください。\n";
31        return;
32    }
33
34    echo "{$host}:{$port} への接続に成功しました。\n";
35
36    // 2. ストリームの読み取りタイムアウトを設定する
37    // stream_set_timeout() は、このストリームからのデータ読み取り操作に適用されます。
38    // 指定した秒数以内にデータが読み込めなかった場合、タイムアウトが発生します。
39    if (!stream_set_timeout($fp, $timeoutSeconds)) {
40        echo "警告: ストリームの読み取りタイムアウトの設定に失敗しました。\n";
41    }
42    echo "ストリームの読み取りタイムアウトを {$timeoutSeconds}秒 に設定しました。\n";
43
44    // 3. データを読み取ろうとする
45    // タイムアウトの動作を示すため、サーバーがすぐに応答しないか、
46    // またはデータ転送が遅い状況を想定します。
47    // ここでは簡単なHTTP GETリクエストを送信してみます。
48    fwrite($fp, "GET / HTTP/1.0\r\nHost: {$host}\r\nConnection: close\r\n\r\n");
49    echo "データを読み取ろうとしています (タイムアウト発生を期待する場合もあります)。\n";
50
51    $data = '';
52    // ストリームの終端 (feof) に達するか、タイムアウトするまで読み込みを試みます。
53    while (!feof($fp)) {
54        $chunk = fread($fp, 8192); // 最大8KBのデータを読み込む
55        if ($chunk === false || $chunk === '') { // 読み取りエラーまたはデータなし
56            // stream_get_meta_data でタイムアウト状態を確認
57            $meta = stream_get_meta_data($fp);
58            if ($meta['timed_out']) {
59                echo "読み取りタイムアウトが発生しました!\n";
60            } else {
61                echo "データの読み取り中に問題が発生しました、またはデータがありませんでした。\n";
62            }
63            break; // ループを終了
64        }
65        $data .= $chunk;
66
67        // データの途中でタイムアウトが発生していないか確認
68        $meta = stream_get_meta_data($fp);
69        if ($meta['timed_out']) {
70            echo "読み取りタイムアウトが発生しました! (途中で検出)\n";
71            break; // タイムアウトしたのでループを抜ける
72        }
73    }
74
75    echo "読み取り試行が完了しました。\n";
76    echo "受信したデータサイズ: " . strlen($data) . "バイト\n";
77
78    // 4. 最終的なストリームのメタデータを確認する
79    // 'timed_out' が true の場合、ストリームの読み取り操作がタイムアウトしたことを意味します。
80    $meta = stream_get_meta_data($fp);
81    if ($meta['timed_out']) {
82        echo "結果: ストリームは読み取りタイムアウト状態でした。\n";
83    } else {
84        echo "結果: ストリームは読み取りタイムアウト状態ではありませんでした。\n";
85    }
86
87    // 5. ストリームを閉じる
88    fclose($fp);
89    echo "ストリームを閉じました。\n";
90    echo "---------------------------------------------------\n";
91}
92
93// --- サンプルコードの実行 ---
94// 注意: 多くのウェブサーバーは非常に速く応答するため、
95// 以下の例で読み取りタイムアウトが実際に発生することは稀です。
96// タイムアウトを確実に発生させるには、応答が遅い、または応答を一時停止する
97// カスタムサーバーを使用する必要があります。
98// ここでは、概念的な動作を示すために、一般的なウェブサーバーを使用します。
99
100// 例: GoogleのHTTPポート(80)に接続し、2秒の読み取りタイムアウトを設定
101// ほとんどの場合、Googleは2秒以内に応答を返すため、タイムアウトは発生しません。
102setAndCheckStreamTimeout('www.google.com', 80, 2);
103
104// 短いタイムアウトで試す場合 (タイムアウトの可能性が高まりますが、環境によっては安定しません):
105// setAndCheckStreamTimeout('www.google.com', 80, 0.5);
106

このPHPサンプルコードは、ネットワークストリームにおけるデータの「読み取りタイムアウト」を設定し、その挙動を確認する方法を実演するものです。STREAM_OPTION_READ_TIMEOUTはPHPストリーム機能で読み取りタイムアウトに関連する設定概念を示す定数ですが、このコードではstream_set_timeout関数を直接使用しています。

setAndCheckStreamTimeout関数は、指定されたホストとポートに接続後、開いたストリームに対してstream_set_timeout関数で読み取りタイムアウトの秒数を設定します。stream_set_timeoutは、第一引数にストリームリソース、第二引数にタイムアウト秒数を指定し、設定に成功すればtrueを返します。この設定により、指定時間内にデータが読み取れなかった場合にタイムアウトが発生するようになります。

その後、データを読み取ろうとしながらstream_get_meta_data関数を使い、ストリームの現在の状態、特にtimed_outキーの値を定期的に確認します。stream_get_meta_dataはストリームリソースを引数にとり、ストリームのメタデータを格納した連想配列を返します。この配列内の['timed_out']trueになっていれば、ストリームの読み取り操作がタイムアウトしたことを意味します。この設定は、データが届かない場合にプログラムが無制限に待機し続けることを防ぎ、堅牢なネットワーク処理を構築するために重要です。

PHPのストリーム操作では、fsockopenの第5引数である接続タイムアウトと、stream_set_timeoutで設定する読み取りタイムアウトは異なる機能ですので混同しないよう注意が必要です。stream_set_timeoutはストリームからのデータ読み取り操作にのみ適用され、書き込みには影響しません。実際に読み取りがタイムアウトしたかどうかは、stream_get_meta_data()関数の戻り値で取得できるメタデータ内の'timed_out'キーで確認してください。サンプルコードのように通常応答が速いサーバーでは、設定したタイムアウト秒数で実際にタイムアウトが発生しないことが多いため、動作確認には意図的に応答を遅らせるサーバー環境などを用意すると良いでしょう。また、接続失敗時に@演算子で警告を抑制するだけでなく、$fpの真偽値でエラーを適切にハンドリングすることが安全な利用のために重要です。

PHPストリームのノンブロッキングとタイムアウト設定

1<?php
2
3/**
4 * ストリームのノンブロッキングモードと読み込みタイムアウトの設定例を示します。
5 * この関数は、指定されたホストとポートに接続し、
6 * データ受信時にストリームがブロックされることなく、
7 * かつ指定した時間内にデータが来ない場合にタイムアウトする挙動をデモンストレーションします。
8 *
9 * @param string $host 接続先のホスト名(例: 'www.example.com')
10 * @param int $port 接続先のポート番号(例: 80 for HTTP, 443 for HTTPS)
11 * @param int $readTimeoutSeconds 読み込みタイムアウトの秒数。この時間内にデータがないと読み込み操作がタイムアウトします。
12 * @param int $readTimeoutMicroseconds 読み込みタイムアウトのマイクロ秒数(1秒 = 1,000,000マイクロ秒)。
13 * @return void
14 */
15function demonstrateStreamReadTimeoutAndBlocking(
16    string $host,
17    int $port,
18    int $readTimeoutSeconds,
19    int $readTimeoutMicroseconds
20): void {
21    echo "--- PHPストリームオプションのデモンストレーション --- \n\n";
22
23    // 1. 指定されたホストとポートへのネットワーク接続を試みます。
24    // fsockopen() の最後の引数は、接続試行自体のタイムアウト時間(秒)です。
25    $errno = null; // 接続エラーが発生した場合のエラー番号を格納する変数
26    $errstr = null; // 接続エラーが発生した場合のエラーメッセージを格納する変数
27    $connectTimeoutSeconds = 5; // 接続試行のタイムアウトを5秒に設定
28    $stream = @fsockopen($host, $port, $errno, $errstr, $connectTimeoutSeconds);
29
30    if (!$stream) {
31        // 接続に失敗した場合、エラーメッセージを表示して終了します。
32        echo "エラー: {$host}:{$port} への接続に失敗しました。({$errno}) {$errstr}\n";
33        return;
34    }
35
36    echo "{$host}:{$port} への接続に成功しました。\n";
37
38    // 2. ストリームをノンブロッキングモードに設定します。
39    // stream_set_blocking(ストリームリソース, false) を設定すると、
40    // fread() や fgets() などの読み込み関数は、データがすぐに利用可能でなくても
41    // データが準備されるまで待機せず、直ちに制御を呼び出し元に返します。
42    // データがない場合、通常は空文字列 ('') を返します。
43    if (!stream_set_blocking($stream, false)) {
44        echo "エラー: ストリームをノンブロッキングモードに設定できませんでした。\n";
45        fclose($stream); // エラー時はストリームを閉じ、リソースを解放します
46        return;
47    }
48    echo "ストリームをノンブロッキングモードに設定しました。\n";
49
50    // 3. ストリームの読み込みタイムアウトを設定します。
51    // STREAM_OPTION_READ_TIMEOUT は stream_set_option() 関数で使用される定数です。
52    // このオプションを設定すると、fread() や fgets() などの読み込み操作が、
53    // 指定した時間(readTimeoutSeconds + readTimeoutMicroseconds)内にデータを取得できなかった場合に、
54    // タイムアウトとして扱われ、通常は false を返すようになります。
55    // これはノンブロッキングモードと組み合わせることで、
56    // 無限にデータ待機するのを防ぎつつ、一定の猶予時間を与えることができます。
57    if (!stream_set_option($stream, STREAM_OPTION_READ_TIMEOUT, $readTimeoutSeconds, $readTimeoutMicroseconds)) {
58        echo "エラー: 読み込みタイムアウトを設定できませんでした。\n";
59        fclose($stream); // エラー時はストリームを閉じます
60        return;
61    }
62    $totalReadTimeout = $readTimeoutSeconds + ($readTimeoutMicroseconds / 1000000);
63    echo "読み込みタイムアウトを {$totalReadTimeout}秒に設定しました。\n";
64    echo "(読み込み操作は、最大でこの時間までデータ待機し、その後タイムアウトまたは終了します。)\n\n";
65
66    // 4. 簡単なHTTP GETリクエストをサーバーに送信します。
67    $request = "GET / HTTP/1.1\r\nHost: {$host}\r\nConnection: Close\r\n\r\n";
68    fwrite($stream, $request);
69    echo "HTTP GET リクエストをサーバーに送信しました。\n";
70
71    // 5. サーバーからの応答を読み込みます。
72    // ノンブロッキングモードと読み込みタイムアウトの挙動を確認します。
73    echo "サーバーからの応答を読み込み中...\n";
74    $response = '';
75    $startTime = microtime(true);
76    // データが全く読み込めない場合に無限ループにならないよう、全体的な最大待機時間を設けます。
77    $maxTotalWaitTime = $totalReadTimeout * 3; 
78
79    while (!feof($stream)) {
80        // fread() は、ノンブロッキングモードの場合、データが利用可能であれば読み込み、
81        // なければ空文字列 ('') をすぐに返します。
82        // 読み込みタイムアウトが設定されている場合、指定時間データが来なければ false を返すことがあります。
83        $buffer = fread($stream, 4096);
84
85        if ($buffer === false) {
86            // 読み込み中にエラーが発生したか、設定された読み込みタイムアウトにより強制終了された場合
87            echo "警告: ストリーム読み込み中にエラーが発生したか、読み込みがタイムアウトしました。\n";
88            break;
89        } elseif ($buffer === '') {
90            // ノンブロッキングモードで、現在読み込むデータがない場合
91            // CPU使用率を抑えるため、少し待機してから再度試行します。
92            usleep(50000); // 50ミリ秒待機
93
94            // 長時間データが来ない場合は、無限ループを防ぐため処理を終了します。
95            if ((microtime(true) - $startTime) > $maxTotalWaitTime) {
96                echo "情報: 全体的な待機時間 ({$maxTotalWaitTime}秒) を超えたため、読み込みを終了します。\n";
97                break;
98            }
99            continue; // 次のループで再度読み込みを試みます
100        }
101
102        // データが正常に読み込まれた場合
103        $response .= $buffer;
104        $startTime = microtime(true); // データを読み込めた時間を更新 (無限ループ対策)
105        // echo "  一部のデータを読み込みました。長さ: " . strlen($buffer) . "バイト。\n"; // デバッグ用
106    }
107
108    echo "\n応答の読み込みが完了しました。\n";
109
110    // 読み込んだ応答の一部を表示します(長すぎる場合は切り詰めます)。
111    echo "--- サーバー応答の最初の500文字 --- \n";
112    echo mb_substr($response, 0, 500) . (mb_strlen($response) > 500 ? "..." : "") . "\n";
113    echo "---------------------------------\n\n";
114
115    // 6. ストリームを閉じ、関連するリソースを解放します。
116    fclose($stream);
117    echo "ストリームを閉じました。\n";
118}
119
120// デモンストレーションを実行します。
121// GoogleのWebサーバーに接続し、読み込みタイムアウトを2秒に設定します。
122// 実際の環境で動作させる際は、ファイアウォールやネットワーク設定にご注意ください。
123demonstrateStreamReadTimeoutAndBlocking('www.google.com', 80, 2, 0);
124
125?>

このサンプルコードは、PHPでネットワークストリームのノンブロッキングモードと読み込みタイムアウトを設定する方法を示しています。指定されたホストとポートへ接続し、データ受信時にストリームがブロックされずに、かつ指定時間内にデータがない場合にタイムアウトする動作をデモンストレーションする関数です。

まず、fsockopen関数で指定されたホストとポートへの接続を試みます。接続成功後、stream_set_blocking($stream, false)によってストリームをノンブロッキングモードに設定します。これにより、データがすぐに利用できない場合でも、freadなどの読み込み関数はデータの準備を待たずにすぐに空文字列を返します。

次に、STREAM_OPTION_READ_TIMEOUT定数を用いてstream_set_option関数で読み込みタイムアウトを設定します。この定数は、読み込み操作が指定した秒数とマイクロ秒数内にデータを取得できなかった場合に、タイムアウトとして扱うためのものです。ノンブロッキングモードと組み合わせることで、無限にデータ待機するのを防ぎつつ、一定の猶予時間を与えることが可能になります。

その後、サーバーにHTTP GETリクエストを送信し、freadで応答を読み込みます。この際、ノンブロッキングモードとタイムアウト設定により、データが利用できない場合はすぐに制御が戻り、設定されたタイムアウト時間内にデータが来なければ読み込み操作が終了またはエラーとして扱われる挙動を確認できます。この関数は引数として接続先のホスト、ポート、読み込みタイムアウトの秒数とマイクロ秒数を受け取り、ストリームの操作を行い、特別な戻り値はありません(void)。

このサンプルコードは、ストリームをノンブロッキングモードと読み込みタイムアウトを組み合わせて利用する方法を示しています。ノンブロッキングモードではデータがない場合に fread() がすぐに空文字列を返しますが、STREAM_OPTION_READ_TIMEOUT を設定することで、指定した時間内にデータが来ない場合にタイムアウトさせ、無駄なCPU利用や無限ループを防ぐことができます。fread() の戻り値が空文字列と false では意味が異なるため、適切に区別して処理することが重要です。また、データ待機中に usleep() でCPU負荷を抑え、全体的な読み込み待機時間を設けて確実に無限ループを回避している点も確認してください。ストリーム利用後は、必ず fclose() でリソースを解放するようにしましょう。ネットワーク接続は常に成功するとは限らないため、fsockopen() のエラーハンドリングも重要です。

関連コンテンツ

関連IT用語

関連プログラミング言語