この記事で何が分かるか
cURL を使って非同期で送信を行った後、「結果のレスポンスコードといっしょに、送ったデータを1件ずつデータベースに保存する」という案件がありました。
cURL のレスポンスには、ステータスコードや実行にかかった時間などが入っていますが、送信したデータまでは返ってきません。
この記事は、レスポンスの返却時に、送信データの情報を記録するための方法を解説しています。
なお、私は cURL マスターではありませんし、この記事の要点を解説するため、細かな設定は省いていますので、その点はご容赦下さい。
内容の意図が伝わらなければ、どうぞご指摘下さい。
問題は cURL の特殊な書き方?
curl_getinfo を使えば、 レスポンスとして http_code(ステータスコード)を取得することができます。
が、マルチに(非同期で)処理を実行する場合は、先に送信する内容を設定し(ハンドラを設定し)、設定したハンドラを送信する分だけ追加し終わったあと、まとめて送信します。
何が言いたいのかというと、「1件ずつ実行してその結果を得る」、という使い方ができないということです。
非同期で一斉送信するから当然かも知れません。
が、「どんなパラメータを実行して、その結果がどうだったか」という結果が分かりにくいことは確かです。
送信前のデータをURL込みで別テーブルにひかえておき、あとでURLで結合して参照する…? とんでもないですね。
レスポンスで送信先のURLは分かりますが、POSTした内容は分かりません。
そこで調査した結果、ハンドラをキーとして使うことができることを発見しました。
まずは、マルチ cURL の使い方から見ていきます。
① cURL ハンドルをマルチで作成(追加)する(curl_multi_add_handle)
作成したハンドルを後でまとめて(非同期で)使うため、配列に入れる前処理です。
ハンドルを作成して(たくわえて)、一気になげるというのが、非同期で通信する cURL の手順です。
この時、実行後の後処理でハンドルを1件ずつ閉じる必要があるため、作成したハンドルはまた別の配列に入れていきます。
// マルチハンドラの初期化
$mh = curl_multi_init();
$timeout = 10;
// $json にはJSON文字列にしたデータが入っています
foreach($urlList as $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); // 送信するURL
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返り値を文字列で返す
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_POST, 1);
$chList[] = $ch; // ハンドルを後処理のため配列に入れておく
// マルチ cURL ハンドルを作成
curl_multi_add_handle($mh,$ch);
}
② 実行(curl_multi_exec)
③ 結果を取得 もしくはハンドルを閉じていきます(ハンドルの配列を回して実行する)
foreach($chList as $ch) {
$resInfo = curl_getinfo($ch); // レスポンスを取得
$res = $resInfo["http_code"] === 200 ? true : false;
if($res === false){
$res["url"] = $resInfo["url"];
}
curl_multi_remove_handle($mh, $ch);
}
問題は、③でレスポンスが返ってくるものの、送信に使った内容までは取得できないことです。
対応策
cURl のハンドラ(上記のソースでは$ch) はリソース型で、var_dump すると「Resource ID#3」といった一意の情報が取得できます。
そして、リソース型を int にすると、数値が取得できます。(上記だと「3」)。
ここで、勘のよい方はもう気づきましたね…。
ハンドラを一意のキーとして使えるなら、①の前処理と③の後処理で、$ch のハンドラを int で型変換すれば、
きちんとそのハンドラの情報として、情報の出し入れができるということです。
① の最終行に以下を追加します。
$result[(string)$ch] = $json;
③ はステータスコードを取得したあと、以下のようにして情報を取得しましょう。
$res["send_data"] = $result[(string)$ch];
以上の方法をとれば、送信したデータに限らず、任意のデータをレスポンスとして受け取ることが可能です。
最後に
「どうしたらハンドラのデータとして見分けられるか」、と考えて、ハンドラそのものを配列の添え字として入れてみる、という乱暴なことをしたところ、「添え字が int じゃないよ」、とのエラーが返りました。
そして、エラーは返ったものの、データは正しく取得できたのです。
そこで、ハンドラを int に型変換すると、数値が取得でき、想定の動きになりました。
この数値はなんだ、と調べたところ、リソース型の番号であることが分かりました。なるほど、それなら一意の番号なので、キーになる、という結論です。
最初からリソース型の性質を熟知していれば、スムーズにこの結論に至ったかと思いますが、まさに、エラーに教えられました。
cURL は libcurl をベースにされているせいか、正直 PHP とは思えないほどややこしい使い方になっていると思います。
が、非同期でも通信できるので、便利なことに変わりはありません。
今回のリソース番号の気づきは、他の機会でも使うことができそうです。
なお、cURL にもっと上手な使い方があるのは分かっていますので、繰り返しますが、その点はご容赦下さい。
もっとシンプルにリクエストの値をレスポンス時に渡せる方法がありましたらぜひコメントをお願いいたします。