json_encode/decodeのオプション、Unicodeエスケープと連想配列と深さ
json_encodeとjson_decodeとは、PHPの値とJSON文字列を相互変換する関数です。Unicodeエスケープや連想配列化、深さ上限などをオプションで制御できます。
日本語がエスケープされて読めない問題
json_encode に日本語を素で渡すと、あ のようなユニコードエスケープになって出力されます。仕様上は正しいんですが、ログやレスポンスを目視するときに読めなくて困りますよね。JSON_UNESCAPED_UNICODE を付けると、そのまま日本語で出してくれます。スラッシュも同様に JSON_UNESCAPED_SLASHES でエスケープを止められます。
echo json_encode(['msg' => 'こんにちは']);
// {"msg":"こんにちは"}
echo json_encode(['msg' => 'こんにちは'], JSON_UNESCAPED_UNICODE);
// {"msg":"こんにちは"}
API のレスポンスなら、私はこの二つに JSON_UNESCAPED_SLASHES を足したセットをほぼ常用しています。バイト数も減りますしね。
decodeの第2引数で連想配列にする
json_decode はデフォルトで stdClass オブジェクトを返します。配列として扱いたい場面が多いので、第二引数に true を渡して連想配列にするのが定番ですね。PHP 8.1 以降なら、可読性を上げるために JSON_THROW_ON_ERROR を付けて、失敗時に例外を投げさせるのがおすすめです。
$data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
// 第2引数 true → 連想配列
// 失敗すると JsonException が飛ぶ(戻り値 null を握りつぶさずに済む)
echo $data['user']['name'];
THROW_ON_ERROR を付けないと、パース失敗が null で返るだけなので、json_last_error のチェックを忘れると壊れたデータに気づけません。例外に倒しておくと見逃しがなくなります。
深さの上限に注意する
encode も decode も第三引数(または depth)でネストの上限を指定でき、既定は512です。再帰的な構造や深い入れ子を扱うと、この上限に達して失敗することがあります。逆に外部から来るJSONは、深すぎる入れ子を弾く安全弁として上限を低めに設定する使い方もありますね。
$deep = json_decode($untrusted, true, 32, JSON_THROW_ON_ERROR);
// ネストが32を超えたら JsonException で拒否
// 悪意ある超深構造への簡易的な防御になる
まとめ
json_encode/decode はオプションを知っているかどうかで使い勝手がかなり変わる関数だと思います。日本語は UNESCAPED_UNICODE、配列で欲しいなら decode の true、失敗を握りつぶしたくないなら THROW_ON_ERROR、そして深さの上限。このあたりを押さえておくと、文字化けや静かな失敗に悩まされる機会が減る気がします。
よくある質問
Q. 日本語がエスケープされて読めません。
A. JSON_UNESCAPED_UNICODEを付けると、そのまま日本語で出力されます。スラッシュはJSON_UNESCAPED_SLASHESで止められます。
Q. decodeで連想配列にするには?
A. 第二引数にtrueを渡します。省くとstdClassオブジェクトが返ります。
Q. パース失敗を確実に検知するには?
A. JSON_THROW_ON_ERRORを付けると失敗時にJsonExceptionが飛ぶので、nullの握りつぶしを防げます。