PHP
curl

PHPでcURL POSTするときは、ちゃんとエンコードしましょう(しなくても動くからこそ!!)

More than 1 year has passed since last update.

まちがいさがし

bad.php
$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コードに見えるけど、間違いがあります。

good.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.phpContent-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を書くのはもうやめよう)。