PHP
curl
generator
libcurl

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

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