PHPで配列をJSON形式の文字列に変換する際に利用するjson_encode、またはJSON形式の文字列を配列に変換するjson_decodeの標準関数はそのまま使わないほうが良いと、という投稿です。
PHP玄人の皆さんには常識かと思われますが、意外と知られていない気がしたので共有です。
そのまま使うとどうなるのか
ドキュメントを参照すると次のように説明されています
json_encode
成功した場合に、JSON エンコードされた文字列を返します。 失敗した場合に FALSE を返します。
json_decode
json でエンコードされたデータを、適切な PHP の型として返します。 true、false および null はそれぞれ TRUE、FALSE そして NULL として返されます。 json のデコードに失敗したり エンコードされたデータが再帰制限を超えているなどの場合、NULL を返します。
失敗しても例外にはならず、それぞれfalse
、null
が返却されます。
これをちゃんとハンドリングして実装していれば問題ないですが、うっかり失敗していたら例外になってくれるんじゃないかと淡い期待をしていると意図しないコードになっている可能性があります。(ちゃんと静的解析してれば問題ないんじゃないですかね)
エラーだった場合、原因が知りたい
エンコード、デコードでエラーがあった場合はjson_last_errorで直近のエラーを調べることができます。
また、json_last_error_msgでエラーメッセージが確認できます。
それ、Guzzleのヘルパー関数が使えるよ
GuzzleといえばPHPでHTTP APIをコールする際にはなくてはならないライブラリ(個人の感想)ですが、このライブラリに用意されているラッパー関数を使うとこの辺のハンドリングが施されています。
function json_encode($value, int $options = 0, int $depth = 512): string
{
return Utils::jsonEncode($value, $options, $depth);
}
public static function jsonEncode($value, int $options = 0, int $depth = 512): string
{
$json = \json_encode($value, $options, $depth);
if (\JSON_ERROR_NONE !== \json_last_error()) {
throw new InvalidArgumentException('json_encode error: ' . \json_last_error_msg());
}
/** @var string */
return $json;
}
function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
{
return Utils::jsonDecode($json, $assoc, $depth, $options);
}
public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
{
$data = \json_decode($json, $assoc, $depth, $options);
if (\JSON_ERROR_NONE !== \json_last_error()) {
throw new InvalidArgumentException('json_decode error: ' . \json_last_error_msg());
}
return $data;
}
失敗していたら例外になってくれるんじゃないか
という淡い期待に答えてくれます。
追記
PHP玄人さんたちが色々教えてくれたので追記
PHP 7.3以降の場合
json_encode/json_decodeのオプションにJSON_THROW_ON_ERROR
を指定することでJsonException
を発生させることが出来ます。
単純にこれをラップして使うだけでも良さそう。
SafePHPを使う
thecodingmachine/safe
そもそもGuzzleはHTTP APIをコールする際に利用するライブラリなので、こういう専用のライブラリを使うべきですね。こんなライブラリがあるのを知らなかったですw