【PHP8.x】curl_multi_select関数の使い方
curl_multi_select関数の使い方について、初心者にもわかりやすく解説します。
基本的な使い方
curl_multi_select関数は、複数のcURLリクエストを同時に処理する際に、データが利用可能になるのを効率的に待つために使用される関数です。この関数は、指定されたcURLマルチハンドルが管理している複数の接続(ソケット)の状態に変化がないかを監視します。具体的には、データが読み込み可能になったり、書き込み準備が整ったり、例外的な状態が発生したりするまで、指定された時間(タイムアウト)まで待機します。
PHPにおける非ブロッキングI/O(入出力)処理、特に複数のネットワーク通信を並行して行う場合において、各通信の状態を効率的に管理するためにこの関数が利用されます。例えば、複数のWeb APIに同時にリクエストを送信し、それぞれの応答を待つようなシナリオで役立ちます。個々の接続の状態を繰り返し確認するポーリング方式と比較して、この関数を使用することで、システムリソースを無駄に消費することなく、実際のデータ転送の準備が整うまで待つことができます。
通常、第一引数には監視対象のcURLマルチリソース(ハンドル)を指定し、第二引数には最大待機時間(秒単位)を渡します。関数は、準備が整ったソケットの数を整数で返しますが、タイムアウトした場合は0を、エラーが発生した場合は-1を返します。このように、curl_multi_select関数は、大量のネットワーク処理を効率的かつスムーズに実行するための重要なツールとして機能します。
構文(syntax)
1<?php 2$multi_handle = curl_multi_init(); 3$select_result = curl_multi_select($multi_handle, 0.5); 4curl_multi_close($multi_handle); 5?>
引数(parameters)
CurlMultiHandle $multi_handle, float $timeout = 1.0
- CurlMultiHandle $multi_handle: 複数のcURL転送を管理するためのリソースハンドル
- float $timeout = 1.0: 待機する最大秒数(浮動小数点数)
戻り値(return)
int
curl_multi_select関数は、処理可能な接続ハンドルの数を返します。この値は、次に選択できる接続があるかどうかを示します。
サンプルコード
PHP cURLマルチセレクトで非同期取得する
1<?php 2 3/** 4 * 複数のURLからデータを並行して取得します。 5 * curl_multi_select 関数を使用して、データが利用可能になるまで効率的に待機します。 6 * 7 * @param array $urls キーとURLのペアの配列 (例: ['task1' => 'http://example.com/api/data1']) 8 * @return array 各URLの取得結果を含む連想配列 9 */ 10function fetch_urls_concurrently(array $urls): array 11{ 12 // マルチ cURL ハンドルを初期化します。 13 // これにより、複数の cURL リクエストを同時に管理できるようになります。 14 $multi_handle = curl_multi_init(); 15 if ($multi_handle === false) { 16 // 初期化に失敗した場合、エラーメッセージを出力し空の配列を返します。 17 echo "エラー: curl_multi_init の初期化に失敗しました。\n"; 18 return []; 19 } 20 21 $curl_handles = []; // 各URLに対応するcURLハンドルの配列を格納 22 $results = []; // 各URLの取得結果を格納する配列 23 24 // 各URLに対して個別の cURL ハンドルを作成し、設定します。 25 foreach ($urls as $id => $url) { 26 $ch = curl_init(); 27 if ($ch === false) { 28 echo "エラー: URL '{$url}' の cURL ハンドルの初期化に失敗しました。\n"; 29 continue; // 次のURLへ進みます 30 } 31 curl_setopt($ch, CURLOPT_URL, $url); // リクエストするURLを設定 32 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 取得したデータを文字列として返すように設定 33 // 必要に応じて、他のcURLオプションもここで設定できます。 34 // 例: curl_setopt($ch, CURLOPT_TIMEOUT, 10); // タイムアウトを10秒に設定 35 36 // 作成したcURLハンドルをマルチ cURL ハンドルに追加します。 37 // これで、このハンドルが並行処理の対象となります。 38 curl_multi_add_handle($multi_handle, $ch); 39 $curl_handles[$id] = $ch; // 後で結果を取得するためにハンドルを保存 40 } 41 42 $running_handles = null; // 現在アクティブなcURLハンドルの数を追跡するための変数 43 44 // 全てのcURL転送が完了するまでループします。 45 do { 46 // curl_multi_exec は、マルチハンドル内の全ての cURL ハンドルの転送を実行します。 47 // 第2引数 ($running_handles) には、まだ転送中のハンドルの数が設定されます。 48 $status = curl_multi_exec($multi_handle, $running_handles); 49 50 // curl_multi_exec がエラーを返した場合 51 if ($status !== CURLM_OK) { 52 echo "エラー: curl_multi_exec の実行中にエラーが発生しました: " . curl_multi_strerror($status) . "\n"; 53 break; 54 } 55 56 // まだ転送中のハンドルがある場合 (つまり、$running_handles が 0 より大きい場合) 57 if ($running_handles > 0) { 58 // curl_multi_select は、データが利用可能になるまでブロック (待機) します。 59 // これにより、CPU使用率を抑えつつ、効率的に待機できます。 60 // タイムアウト値 (ここでは 1.0 秒) は、最大で待機する時間を指定します。 61 // 戻り値は準備ができたハンドルの数、-1 はエラー、0 はタイムアウトを示します。 62 $select_result = curl_multi_select($multi_handle, 1.0); // 最大1秒待機 63 if ($select_result === -1) { 64 echo "エラー: curl_multi_select の実行中にエラーが発生しました。\n"; 65 break; // エラーが発生したためループを抜けます 66 } elseif ($select_result === 0) { 67 // タイムアウトしましたが、まだデータは利用可能になっていない場合。 68 // この場合でも、次の curl_multi_exec を呼び出す必要があります。 69 // 必要であればここにログなどを出力できますが、通常は処理を続行します。 70 } 71 } 72 } while ($running_handles > 0); // アクティブなハンドルがなくなるまでループを続行 73 74 // 全ての転送が完了した後、各 cURL ハンドルの結果を取得します。 75 foreach ($curl_handles as $id => $ch) { 76 $error_code = curl_errno($ch); // cURLハンドルのエラーコードを取得 77 78 if ($error_code === 0) { 79 // エラーがない場合、取得したコンテンツとHTTPステータスコードを取得します。 80 $content = curl_multi_getcontent($ch); 81 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 82 $results[$id] = [ 83 'status' => 'success', 84 'http_code' => $http_code, 85 'content' => $content, 86 ]; 87 } else { 88 // エラーがある場合、エラーメッセージとコードを記録します。 89 $error_message = curl_error($ch); 90 $results[$id] = [ 91 'status' => 'error', 92 'message' => $error_message, 93 'error_code' => $error_code, 94 ]; 95 } 96 97 // 各 cURL ハンドルをマルチハンドルから削除し、閉じます。 98 // これにより、リソースが解放されます。 99 curl_multi_remove_handle($multi_handle, $ch); 100 curl_close($ch); 101 } 102 103 // 最後に、マルチ cURL ハンドルを閉じます。 104 curl_multi_close($multi_handle); 105 106 return $results; 107} 108 109// --- サンプル使用方法 --- 110 111// 複数のURLを定義します。テスト用にダミーのAPIエンドポイントを含めます。 112$urls_to_fetch = [ 113 'user_data_1' => 'https://jsonplaceholder.typicode.com/todos/1', 114 'user_data_2' => 'https://jsonplaceholder.typicode.com/todos/2', 115 'post_data_1' => 'https://jsonplaceholder.typicode.com/posts/1', 116 'invalid_url' => 'https://example.com/non_existent_page_12345', // 存在しないURLの例 117]; 118 119echo "複数のURLからデータを非同期で取得を開始します...\n"; 120$start_time = microtime(true); // 処理開始時刻を記録 121 122$fetched_data = fetch_urls_concurrently($urls_to_fetch); 123 124$end_time = microtime(true); // 処理終了時刻を記録 125echo "データの取得が完了しました。処理時間: " . round($end_time - $start_time, 2) . "秒\n\n"; 126 127// 取得したデータを表示します。 128foreach ($fetched_data as $task_id => $data) { 129 echo "--- タスクID: {$task_id} ---\n"; 130 if ($data['status'] === 'success') { 131 echo "ステータス: 成功\n"; 132 echo "HTTPコード: " . $data['http_code'] . "\n"; 133 // コンテンツが長い場合があるので、先頭100文字だけ表示します。 134 echo "コンテンツ (先頭100文字): " . substr($data['content'], 0, 100) . "...\n"; 135 } else { 136 echo "ステータス: エラー\n"; 137 echo "エラーメッセージ: " . $data['message'] . " (コード: " . $data['error_code'] . ")\n"; 138 } 139 echo "\n"; 140} 141 142?>
curl_multi_select関数は、複数のcURLリクエストを並行して実行する際に、データ転送の準備が整うまで効率的に待機するための関数です。これは、繰り返し状態を確認するポーリング処理によるCPU負荷を軽減し、システムリソースを有効活用するために使用されます。
第一引数$multi_handleには、監視対象となるマルチcURLハンドル(curl_multi_initで初期化されたオブジェクト)を指定します。このハンドルに含まれるいずれかのcURLリクエストで、データの読み書きが可能になるのを待ちます。
第二引数$timeoutは、待機する最大時間を秒単位で浮動小数点数として指定します。この時間を過ぎると、データが利用可能になっていなくても関数は処理を中断し、次の処理へ進みます。デフォルト値は1.0秒です。
戻り値は整数で、データ転送の準備ができたハンドルの数を返します。もしエラーが発生した場合は-1、指定されたタイムアウト時間内に何も準備ができなかった場合は0を返します。
サンプルコードでは、do-whileループの中でcurl_multi_execと組み合わせて利用されています。curl_multi_execがまだ実行中のリクエストがあることを示した場合、curl_multi_selectを呼び出すことで、データが来るまでCPUを消費せずに待機します。これにより、全ての並行リクエストが効率的に完了するまで処理が進められ、取得したデータが最終的に返されます。処理完了後、各リクエストの結果が収集され、マルチcURLハンドルと個別のcURLハンドルは適切にクローズされます。
このサンプルコードは、複数のHTTPリクエストを並行して効率的に処理する方法を示しています。curl_multi_select関数は、データが利用可能になるまでプログラムの実行を一時停止させ、CPUの無駄な消費を防ぐ非常に重要な役割を担っています。引数で指定するタイムアウト値は、最大で待機する時間を秒単位で設定するため、適切な値を検討することが大切です。
初心者の方は、curl_multi_initやcurl_initで作成したリソースが、処理終了時に必ずcurl_multi_closeやcurl_closeで解放されているかを確認してください。これを怠るとメモリリークの原因となります。また、各curl_関数の戻り値やエラーコードを常にチェックし、エラー発生時の適切な処理(エラーハンドリング)を丁寧に行うことが、安全で堅牢なコードを書く上で不可欠です。