LoginSignup
64
66

More than 5 years have passed since last update.

今cURLで並列処理を書くならジェネレータを使え!

Last updated at Posted at 2016-01-21

はじめに

PHPでのcURLを用いたHTTPリクエストの並列処理,全てが完全な並列でよければcurl_multi_*関数群を使って「複数のRSSに並列リクエスト後マージして返す関数」の例のようにサクッと書けちゃうんですが,部分的に直列化したいケースが入ってくると一気にハードル高くなりますよね.

例えばこんな要件.これを「出来るだけ並列に効率よくやってください」って言われたらすぐ書けるでしょうか?あ,pcntl_fork()とかは禁止で.

    1. https://google.com のHTMLを取得
    1. https://github.com のHTMLを取得
    2. このバイト長を取得
    1. https://github.com/mpyw のHTMLを取得
    2. XPathでプロフィール画像のURLを抜き出す
    3. 更に画像に対してリクエストを実行し,レスポンスを画像ファイルとして保存する

mpyw/co を使えば非常に直感的に書くことが出来ます.

PHP 5.5~5.6, 7.0+ 共通関数
function curl_init_with($url, array $options = [])
{
    $ch = curl_init();
    $options = array_replace([
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
    ], $options);
    curl_setopt_array($ch, $options);
    return $ch;
}
function get_xpath_async($url) {
    $dom = new \DOMDocument;
    @$dom->loadHTML(yield curl_init_with($url));
    return new \DOMXPath($dom);
}
PHP 7.0+ からの呼び出し
var_dump(Co::wait([
    "google.com HTML" => curl_init_with("https://google.com"),
    "Content-Length of github.com" => function () {
        return strlen(yield curl_init_with("https://github.com"));
    },
    "Save mpyw's Gravatar Image URL to local" => function () {
        $xpath = yield get_xpath_async('https://github.com/mpyw');
        $src = $xpath->evaluate('string(//img[contains(@class,"avatar")]/@src)');
        yield curl_init_with($src, [CURLOPT_FILE => fopen('/tmp/mpyw.png', 'wb')]);
        return "Saved as /tmp/mpyw.png";
    },
]));
PHP 5.5~5.6 からの呼び出し
var_dump(Co::wait([
    "google.com HTML" => curl_init_with("https://google.com"),
    "Content-Length of github.com" => function () {
        yield Co::RETURN_WITH => strlen(yield curl_init_with("https://github.com"));
    },
    "Save mpyw's Gravatar Image URL to local" => function () {
        $xpath = (yield get_xpath_async('https://github.com/mpyw'));
        $src = $xpath->evaluate('string(//img[contains(@class,"avatar")]/@src)');
        yield curl_init_with($src, [CURLOPT_FILE => fopen('/tmp/mpyw.png', 'wb')]);
        yield Co::RETURN_WITH => "Saved as /tmp/mpyw.png";
    },
]));

備考: CURLOPT_*のデフォルトオプション結合の罠

今回はcURLリソースで割りきってPromiseインタフェースは使わないことにしたのですが,JavaScriptのtj/coあるいはasync/awaitを用いたコードによく似ていることがわかります.PHPでも頑張ればここまで出来るんです!

仕様

仕様はGitHubのドキュメントに英語で書いてあるので割愛します(オイ

64
66
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
64
66