はじめに
PHPでのcURLを用いたHTTPリクエストの並列処理,全てが完全な並列でよければcurl_multi_*
関数群を使って「複数のRSSに並列リクエスト後マージして返す関数」の例のようにサクッと書けちゃうんですが,部分的に直列化したいケースが入ってくると一気にハードル高くなりますよね.
例えばこんな要件.これを「出来るだけ並列に効率よくやってください」って言われたらすぐ書けるでしょうか?あ,pcntl_fork()
とかは禁止で.
-
-
https://google.com
のHTMLを取得
-
-
-
https://github.com
のHTMLを取得 - このバイト長を取得
-
-
-
https://github.com/mpyw
のHTMLを取得 - XPathでプロフィール画像のURLを抜き出す
- 更に画像に対してリクエストを実行し,レスポンスを画像ファイルとして保存する
-
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";
},
]));
今回はcURLリソースで割りきってPromiseインタフェースは使わないことにしたのですが,JavaScriptのtj/coあるいはasync/await
を用いたコードによく似ていることがわかります.PHPでも頑張ればここまで出来るんです!
仕様
仕様はGitHubのドキュメントに英語で書いてあるので割愛します(オイ