エラーハンドリングの基本
PHPのlibcurl拡張でHTTPリクエストをする際、色々なエラーが起きることが考えられる。
curl_errno
とcurl_error
で直近のexecで発生したエラーを調べることができる。
curl_exec()
の戻り値がfalse
だったらエラーだと判定できるのだけど、詳細なところがわからないので、明示的にエラーが起きたかを調べた方がよさそう。
例外を使いたいなら、ラッパークラス/関数でハンドリングして適当なException型を投げればよいだろう。
<?php
// 単純なcurl関数のラッパー
// 毎回コネクションを切断するので非効率かも
function getHttpContent($url)
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = curl_exec($ch);
$info = curl_getinfo($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
curl_close($ch);
if (CURLE_OK !== $errno) {
throw new RuntimeException($error, $errno);
}
return [$body, $info];
}
後で「どんなURLにアクセスしようとしてエラーが起きたのか?」とか調べたいだろうから、もう少し例外型には情報を含めるよう工夫した方がいいかもしれない。
cURLは何をエラーとみなすのか?
libcurl公式ページに、エラー一覧が載っている。
libcurl - Error Codes
- サポートしていないプロトコルを指定した
- URLがURLの形式になっていない
などなど、基本的なところと、HTTP通信は成功したけれど40X系のステータスコードが返ってきた場合などが書いてある。
デフォルト状態だと、400系が返ってきたとしてもlibcurlはエラーとみなしてくれない。
冒頭のサンプルコードは404 Not Foundが返ってきたとしても例外は発生しない。
オプションでCURLOPT_FAILONERROR
をtrueにセットすると、400以上のステータスコードの場合にエラーとみなしてくれるようになる。
が、この場合、400以上のステータスコードならレスポンスボディを読めなくなる。
400で詳細なエラーの理由をボディに送ってくるケースもあるので、FAILONERRORをfalseにして(デフォルト状態である)、手動でハンドリングした方がいいと思う。
<?php
// ステータスコードが400以上だった場合も例外が発生するバージョン
// ただしレスポンスボディがわからない
function getHttpContent($url)
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FAILONERROR => true,
]);
$body = curl_exec($ch);
$info = curl_getinfo($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
curl_close($ch);
if (CURLE_OK !== $errno) {
throw new RuntimeException($error, $errno);
}
return [$body, $info];
}
CURLOPT_HTTP200ALIASESオプションの正体
なんか謎のオプションがあるが、思ってたのと違った。最初見たとき、DELETEのときは404も200 OKとみなしてくれるように設定したりできる便利な何かかと思ったんですが。。
PHP: curl_setopt - Manual
- CURLOPT_HTTP200ALIASES
- エラーではなく正常な応答として扱われる、HTTP 200 レスポンスの配列。
404とか400を200扱いしてくれるとかではなく、HTTPのRFCに従わないレスポンスが返ってきた時に、200とみなしてくれるオプションの模様。HTTPを上書きすることはできないようだった。
例として、SHOUTcastというストリーミングサーバーではHTTP/1.1 200 OK
ではなくICY 200 OK
を返すことがあるらしい。これを200扱いしたい場合は以下のようにセットしておくとよいようだ。
curl_setopt($ch, CURLOPT_HTTP200ALIASES, ['ICY 200 OK']);
配列で渡すものはステータスラインの文字列。