Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

はじめに

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のドキュメントに英語で書いてあるので割愛します(オイ

mpyw
古い記事はそのまま参考にしないようにご注意ください
synapse
Synapseは、オンラインサロンサービスにおけるパイオニアとして、かつて存在していたスタートアップです。
https://synapseam.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away