PHP Tips | 文字列処理:実務向け便利系 - JSON デコード(例外対応)

PHP PHP
スポンサーリンク

なぜ「JSON デコード(例外対応)」が必要なのか

業務で JSON を扱うとき、ほぼ必ずやるのが「文字列 → 配列(オブジェクト)」への変換、つまり JSON デコードです。
ここで怖いのは、「壊れた JSON が来たときに、気づかないまま処理が進んでしまう」ことです。

json_decode() は、失敗しても「静かに null を返す」ことがあります。
そのまま null を配列だと思ってアクセスすると、Warning やバグの原因になります。

だからこそ、「JSON が壊れていたら必ずエラーとして扱う」「どこで・なぜ失敗したか分かる」ようにする——
これが「例外対応付き JSON デコード」ユーティリティの目的です。


json_decode の基本と「落とし穴」

素の json_decode の挙動

まずは基本形です。

$json = '{"name":"山田太郎","age":20}';

$data = json_decode($json, true);

var_dump($data);
// array(2) { ["name"]=> string(12) "山田太郎" ["age"]=> int(20) }
PHP

第二引数に true を渡すと「連想配列」、false か省略すると「オブジェクト」として返ってきます。

問題は、JSON が壊れていた場合です。

$badJson = '{"name":"山田太郎",}'; // 末尾に余計なカンマ

$data = json_decode($badJson, true);

var_dump($data);
// NULL
PHP

null が返ってきますが、「本当に JSON が壊れているのか」「JSON の中身が null なのか」が区別できません。
このまま $data['name'] などにアクセスすると、警告が出たり、ロジックが破綻します。


例外対応のキモ:JSON_THROW_ON_ERROR を使う

失敗したら例外を投げさせる

PHP 7.3 以降では、json_decode() にフラグ JSON_THROW_ON_ERROR を渡すことで、「失敗したら例外を投げる」モードにできます。

$json = '{"name":"山田太郎","age":20}';

$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
PHP

壊れた JSON を渡すと、JsonException が投げられます。

$badJson = '{"name":"山田太郎",}';

try {
    $data = json_decode($badJson, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    echo 'JSON デコード失敗: ' . $e->getMessage();
}
PHP

これで、「壊れた JSON が来たのに気づかないまま処理が進む」という状態を防げます。
例外が飛べば、その場で止まるか、上位でキャッチしてエラーレスポンスを返すなど、明示的な対応ができます。


実務で使いやすい「ラッパー関数」を作る

連想配列で返す版

毎回 json_decode(..., true, 512, JSON_THROW_ON_ERROR) と書くのは面倒なので、
ユーティリティ関数にまとめてしまうのがおすすめです。

/**
 * JSON 文字列を「連想配列」としてデコードする(失敗時は JsonException)
 */
function json_decode_array(string $json): array
{
    return json_decode($json, true, 512, JSON_THROW_ON_ERROR);
}
PHP

使い方の例です。

try {
    $data = json_decode_array($requestBody);

    // ここまで来れば $data は必ず配列
    echo $data['name'] ?? '名前なし';
} catch (JsonException $e) {
    // 400 Bad Request などを返す
    http_response_code(400);
    echo '不正な JSON です: ' . $e->getMessage();
}
PHP

ここでの重要ポイントは、「戻り値の型がはっきりしている」ことです。
array を返すと決めておけば、呼び出し側は「配列として扱っていい」と安心してコードを書けます。

オブジェクトで返す版

オブジェクトとして扱いたい場合のラッパーも用意できます。

/**
 * JSON 文字列を「オブジェクト」としてデコードする(失敗時は JsonException)
 */
function json_decode_object(string $json): object
{
    $result = json_decode($json, false, 512, JSON_THROW_ON_ERROR);

    // 念のため型を保証
    if (!is_object($result)) {
        throw new RuntimeException('JSON はオブジェクトではありません');
    }

    return $result;
}
PHP

例題:API のリクエストボディを安全にパースする

よくある「危ない」書き方

例えば、JSON API のエンドポイントで、
リクエストボディをこう書いているコードがあったとします。

$body = file_get_contents('php://input');
$data = json_decode($body, true);

// ここで $data['name'] を使い始める…
PHP

JSON が壊れていた場合、$datanull になり、
$data['name'] で警告が出たり、意図しない挙動になります。

例外対応版に書き換える

これを、先ほどのユーティリティを使って書き換えます。

$body = file_get_contents('php://input');

try {
    $data = json_decode_array($body);
} catch (JsonException $e) {
    http_response_code(400);
    echo 'リクエストボディが不正な JSON です。';
    exit;
}

// ここから先は $data が必ず配列として使える
$name = $data['name'] ?? null;
PHP

こうしておけば、「JSON が壊れている」という入力エラーを、
アプリケーションのロジックエラーと混同せずに扱えます。


json_last_error 系との違いと使い分け

古い書き方:json_last_error を使う

JSON_THROW_ON_ERROR がない時代は、こんな書き方がよく使われていました。

$data = json_decode($json, true);

if (json_last_error() !== JSON_ERROR_NONE) {
    // エラー処理
}
PHP

これはこれで動きますが、

毎回 json_last_error() を呼ぶのを忘れがち
どこで失敗したかがコード上で分かりにくい
戻り値の型が null か配列か曖昧

といった弱点があります。

今の時代のおすすめは「例外ベース」

JSON_THROW_ON_ERROR を使えば、

失敗した瞬間に例外が飛ぶ
try-catch の範囲で「ここで JSON が壊れていたらエラーにする」と明示できる
戻り値の型を関数のシグネチャで固定しやすい

というメリットがあります。

初心者のうちから「JSON デコードは例外で扱う」という癖をつけておくと、
後からコードを読む人(未来の自分も含む)がかなり楽になります。


まとめ:今日からの「JSON デコード(例外対応)」ユーティリティ

押さえておきたいポイントは、次のとおりです。

json_decode() は失敗しても静かに null を返すので、そのまま使うとバグの温床になる。
JSON_THROW_ON_ERROR フラグを使えば、壊れた JSON で必ず JsonException が飛ぶ。
「配列用」「オブジェクト用」のラッパー関数を作っておくと、呼び出し側のコードがすっきりする。

実務ユーティリティとしては、まずこの 2 本を持っておくとかなり安心です。

function json_decode_array(string $json): array
{
    return json_decode($json, true, 512, JSON_THROW_ON_ERROR);
}

function json_decode_object(string $json): object
{
    $result = json_decode($json, false, 512, JSON_THROW_ON_ERROR);

    if (!is_object($result)) {
        throw new RuntimeException('JSON はオブジェクトではありません');
    }

    return $result;
}
PHP

もし、あなたのプロジェクトのどこかで「json_decode(...); の戻り値をそのまま信じて使っている」コードを見つけたら、そこがこのユーティリティに差し替えるべきポイントです。
その一箇所を直すだけで、「壊れた JSON に気づかないまま処理が進む」という事故をかなり減らせます。

PHPPHP
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました