まちがいさがし
$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を書くのはもうやめよう)。