Archive for November, 2013

curl_multiの結果取得方法(HTTPエラーコードならびにL2〜L4でのエラー理由など)

cURLのマルチハンドルにおける、各ハンドルごとの通信内容の結果取得方法についてです。
データ本体ではなく、HTTPコードや通信時間、エラー理由(名前解決失敗やネットワーク接続失敗やconnection refusedなどといった、MACレイヤやTCP/IPレベルでの理由)の取得方法になります。

PHPの公式ドキュメントがクソわかりづらかったので、まとめました。

その1。
HTTPコードや通信時間などについては、curl_getinfo がそのまま使えます。
PHP: curl_getinfo – Manual

その2。
cURLが返すエラー理由については、curl_errorは使えません。curl_multi_infi_readの結果で取得できます。
PHP: curl_multi_info_read – Manual
参考:PHP: curl_error – Manual

なお、cURLのエラーコードについてはこちらに説明があります。
libcurl – Error Codes

curl_multiの使い方については以下のブログが詳しいのでそちらに説明を譲ります。
APIとの通信効率をよくする実装例(1) curl_multi – Yahoo! JAPAN Tech Blog

以下は実際に取得するためのサンプルコードです。
いろいろ端折ってます。

// make multi handle
$mh = curl_multi_init();

// make each handle (同時実行数分だけ行う)
$ch1 = curl_init() ;
curl_setopt( $ch1 , OPTION , VALUE ) ;         // option はここで設定
curl_multi_add_handle( $mh , $ch1 ) ;
$ch2 = curl_init() ;
curl_setopt( $ch2 , OPTION , VALUE ) ;         // option はここで設定
curl_multi_add_handle( $mh , $ch2 ) ;
$ch3 = curl_init() ;
curl_setopt( $ch3 , OPTION , VALUE ) ;         // option はここで設定
curl_multi_add_handle( $mh , $ch3 ) ;
    /* 途中省略 */
// ここまで each handle 分くりかえす
$mchary = array( $ch1 , $ch2 , $ch3 /* 中略 */ ) ;

// execute 
do {
    $mr = curl_multi_exec( $mh , $active ) ;
} while( $mr == CURLM_CALL_MULTI_PERFORM );

// get result 
$curlinfoary = array () ;
while( ( $cary = curl_multi_info_read( $mh ) ) != FALSE )   
{
    /* 1回の実行で1つのハンドル($chN)しか取得できないので注意 
     * $cary['handle'] ... curl_init で取得したハンドル 
     * $cary['result'] ... 結果コード
     */
    $curlinfoary[] = $cary ;

}

// get each handle results
foreach ( $mchary as $lch )
{
     /* content本体 */
     $content = curl_multi_getcontent($lch);
     /* info data */
     $infodata = curl_getinfo($c) ;     // curl single系のものをそのまま使えます
     /* どんな内容が得られるかは、 curl_getinfo のドキュメントを参照すべし */
     $httpconde = $infodata['http_code'] ;
     $tttime    = $infodata['total_time'] ;
     $ftime     = $infodata['filetime'] ; 
     /* 各ハンドルの result(エラーコード) 取得 */
     $rh = -1 ;
     foreach ( $curlinfoary as $cj )
     {
         if ( $cj['handle'] === $lch ) 
         {
              $rh = $cj['result'] ;
              break ; 
         }
     }
     /* $rh の値は、cURLのエラーコード(CURLE_*)になります。
        $rh に -1 が入ってるわけがないので、今回はそのまま強行します
       (念のためチェックはしたほうがいいかも?) 
      */
     curl_multi_remove_handle( $mh , $lch ) ;
}

L2〜L4で失敗した詳細の理由を欲しいとかいう場面は滅多にないと思いますが、どうしても原因がわからずに詰んでしまったときのデバッグのための手助けにはなると思います。