まちがいさがし
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'http://hoge.com/fuga',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['hoge' => 'fuga'],
]);
$response = curl_exec($ch);
curl_close($ch);
どこにでもあるPHPコードに見えるけど、間違いがあります。
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'http://hoge.com/fuga',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query(['hoge' => 'fuga']),
]);
$response = curl_exec($ch);
curl_close($ch);
正解は、http_build_query するだけ!
なんで?
一番の問題は、どちらのコードも動いてしまうこと。ただ、 bad.php はすごく時間がかかるので、やっちゃだめ。
headerを見てみると、good.php は Content-Type: application/x-www-form-urlencoded なのに対して、bad.php は Content-Type: multipart/form-data; boundary=------------------------ffe0ce1791e44a80 みたいになっているはず。
要するに、(別に http_build_query じゃなくてもいいけど)ちゃんとエンコードしてあげないと multipart/form-data で通信してしまう。これはファイルの転送とかに使うプロトコルで、オーバーヘッドが大きい。
実際、
echo curl_getinfo($ch, CURLINFO_STARTTRANSFER_TIME);
とかやってみてほしい。good.php は数ミリ秒とかなのに、 bad.php は1秒以上かかっているのがわかると思う。
もう一度言う
そんな初心者みたいなこと。と思うかもしれない。ただ、これは案外発見が難しいことだと思う。
なぜなら、どちらもPHPとしては正常に動くし、アプリケーションとしても仕様通りの動作をするから。
ただ、少しばかり転送に時間がかかる。
一番厄介なのが、動作はしているということ。cURLの部分なのでUnitTestでは検査しにくいし、開発環境では多少の遅延はあまり気にならない。このミスにQA環境なりにあげるまでに気づけるタイミングがあるとしたらコードレビューだけど、いつもの馴染みのcURLだと思ってレビューすると、http_build_query してるかどうかなんてほとんど気にしない。
で、QA環境なりに入れて、結合試験のフェーズになって、「あれ?HTTPタイムアウトしてんぞ」と気づく。
そうなると、ミドルウェアとかRTTとか、低レイヤーなところを疑ってしまって、あまりコードに目がいかない。(なんたって、開発環境で動いているし、UnitTestもちゃんと書いているし、コードレビューも通っているんだから!!)
ということで、cURLのオプションはちゃんと気にして実装しよう&ちゃんと気にしてレビューしよう(あるいは生のcURLを書くのはもうやめよう)。