facebook twitter hatena line email

Php/curl/マルチリクエスト

提供: 初心者エンジニアの簡易メモ
移動: 案内検索

リトライ機能を実装したcurl並列リクエストを行うライブラリ

<?php
/**
 * curl並列処理コンテンツ取得ロジッククラス
 *
 * curlライブラリ必須
 * 取得に失敗した場合は失敗したリクエストのみを再リクエストする
 * @url http://www.php.net/manual/ja/function.curl-multi-exec.php
 *      http://techblog.ecstudio.jp/tech-tips/php-multi.html
 *      http://techblog.yahoo.co.jp/cat207/cat209/api1_curl_multi/
 *      http://www.phpied.com/simultaneuos-http-requests-in-php-with-curl/
 * @ex
 * $logic = new MultiRequestGetLogic();
 * $results = $logic->execLogic($urls, 3, $curl_options);
 */
class MultiRequestGetLogic
{
    // ハンドラ
    private $_mh;
    // 失敗時のretry回数
    private $_retry;
    // curlオプション
    private $_curl_options = array();
    /**
     * constructor
     */
    public function __construct()
    {
        // リクエストオプション
        $this->_curl_options = array(
            CURLOPT_CONNECTTIMEOUT => 1, // 接続タイムアウト
            CURLOPT_TIMEOUT => 1,        // curl関数タイムアウト
        );
    
    }
    /**
     * 処理実行
     *
     * @param  array $urls
     * @param  int   $retry
     * @param  array $options
     * @return array $results
     */
    public function execLogic($urls, $retry = 3, $options = null)
    {
        if (!$options) {
            $options = $this->_curl_options;
        }
        // データ初期化
        $results = array();
        
        // ハンドラ設定
        $this->_mh = curl_multi_init();
        
        // リトライ回数設定
        $this->_retry = $retry;
        
        if ($retry > 1) {
            // リトライ実行手続き
            $results = $this->_execRetry($urls, $options);
        } else {
            // 実行手続き
            $results = $this->_exec($urls, $options);
        }
        
        // 切断
        curl_multi_close($this->_mh);
        
        return $results;
    }
    /**
     * リトライ実行手続き
     *
     * @param  array $urls
     * @param  array $options
     * @return array $ret
     */
    private function _execRetry($urls, $options = array())
    {
        // リクエスト件数
        $urlcnt = count($urls);
        // レスポンス取得完了件数
        $complete = 0;
        // レスポンスリスト
        $ret = array();
        // リトライ回数だけループ
        for ($i = 1; $i <= $this->_retry; $i++) {
            // 実行手続き
            $results = $this->_exec($urls, $options);
            foreach ($results as $key => $result) {
                // status_codeが0以外のときはレスポンス取得成功とみなす
                if ($result['http_code'] != 0) {
                    // レスポンスを戻値に設定
                    $ret[$key] = $result;
                    // リクエストurlから消す
                    unset($urls[$key]);
                    // 完了件数インクリメント
                    $complete++;
                }
            }
            // 全レスポンスを取得したときは処理を抜ける
            if ($complete >= $urlcnt) {
                break;
            }
        }
        return $ret;
    }
    /**
     * 実行手続き
     *
     * @param  array $urls
     * @param  array $options
     * @return array $results
     */
    private function _exec($urls, $options = array())
    {
        // curlハンドラ初期化
        $curly = array();
        // データ初期化
        $results = array();
        
        // loop through $urls and create curl handles
        // then add them to the multi-handle
        foreach ($urls as $id => $d) {
            
            $curly[$id] = curl_init();
            
            $url = (is_array($d) && !empty($d['url'])) ? $d['url'] : $d;
            curl_setopt($curly[$id], CURLOPT_URL,            $url);
            curl_setopt($curly[$id], CURLOPT_HEADER,         0);
            curl_setopt($curly[$id], CURLOPT_RETURNTRANSFER, 1);
            
            // post?
            if (is_array($d)) {
                if (!empty($d['post'])) {
                    curl_setopt($curly[$id], CURLOPT_POST,       1);
                    curl_setopt($curly[$id], CURLOPT_POSTFIELDS, $d['post']);
                }
            }
            
            // extra options?
            if (!empty($options)) {
                curl_setopt_array($curly[$id], $options);
            }
            
            curl_multi_add_handle($this->_mh, $curly[$id]);
        }
        
        // execute the handles
        $active = null;
        do {
            $mrc = curl_multi_exec($this->_mh, $active);
        } while($mrc == CURLM_CALL_MULTI_PERFORM);
        
        while ($active and $mrc == CURLM_OK) {
            if (curl_multi_select($this->_mh) != -1) {
                do {
                    $mrc = curl_multi_exec($this->_mh, $active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }
        
        if ($mrc != CURLM_OK) {
            // echo '読み込みエラーが発生しました:' . $mrc;
        }
        
        // get content and remove handles
        foreach ($curly as $id => $c) {
             // ヘッダー取得
             $results[$id] = curl_getinfo($c);
             // コンテンツ取得
             $results[$id]["content"] = curl_multi_getcontent($c);
            // curl切断
            curl_multi_remove_handle($this->_mh, $c);
        }
        return $results;
    }
}
?>