PHP Tips | 文字列処理:入力補助 - JSON → 配列(型安全)

PHP PHP
スポンサーリンク

何をしたいユーティリティなのか

「JSON → 配列(型安全)」は、 文字列として渡されてきた JSON を「PHP の配列」に変換するときに、

  • ちゃんと JSON として正しいかチェックする
  • 期待している“形・型”になっているか確認する

ところまでをセットでやってくれるユーティリティです。

ただ json_decode するだけだと、

  • 間違った JSON でも null が返って終わり
  • 数値だと思っていたら文字列だった
  • 配列だと思っていたらオブジェクトだった

みたいな「型のズレ」が、あとからバグの原因になります。 そこを最初にきっちりチェックしておくのが「型安全」な JSON → 配列変換です。

まずは json_decode の基本を押さえる

json_decode のよくある使い方

PHP には json_decode があります。

$json = '{"id":123,"name":"Taro"}';

$data = json_decode($json, true);

var_dump($data);
PHP

第 2 引数に true を渡すと、「連想配列として」デコードされます。

結果はこうです。

array(2) {
  ["id"]   => int(123)
  ["name"] => string(4) "Taro"
}
PHP

ここまではよく見る使い方ですが、 実務ではもう少し気をつけたいポイントがあります。

エラー時の挙動

間違った JSON を渡すと、json_decodenull を返します。

$json = '{"id":123,"name":"Taro"'; // 末尾の } がない

$data = json_decode($json, true);

var_dump($data); // NULL
PHP

「ただの NULL」と「JSON として失敗した NULL」が区別できないと、 あとから「なんでここ NULLなんだっけ?」と迷子になります。

そこで、「エラーをちゃんと検知する」書き方が必要になります。

型安全の第一歩:「エラーを例外で知らせる」ユーティリティ

json_decode に JSON_THROW_ON_ERROR を付ける

PHP 7.3 以降では、json_decodeJSON_THROW_ON_ERROR フラグを付けると、 エラー時に例外を投げてくれます。

function decode_json_to_array(string $json): array
{
    try {
        $data = json_decode(
            $json,
            true,   // 連想配列として
            512,    // 最大深さ(デフォルト)
            JSON_THROW_ON_ERROR
        );
    } catch (JsonException $e) {
        // 実務ではここでログを出したり、別の例外に包んだりする
        throw new RuntimeException('JSON decode failed: ' . $e->getMessage(), 0, $e);
    }

    // 念のため、配列であることを確認
    if (!is_array($data)) {
        throw new RuntimeException('Decoded JSON is not an array');
    }

    return $data;
}
PHP

ここでの重要ポイントは 2 つです。

  • JSON_THROW_ON_ERROR で「間違った JSON」を例外として検知できること
  • is_array で「配列として扱える形か」を確認していること

これで、「JSON としておかしい」「配列じゃない」というケースを、 呼び出し側にきちんと伝えられるようになります。

例題で挙動を確認する

正しい JSON の場合

$json = '{"id":123,"name":"Taro"}';

$data = decode_json_to_array($json);

var_dump($data);
PHP

結果はこうです。

array(2) {
  ["id"]   => int(123)
  ["name"] => string(4) "Taro"
}
PHP

普通に連想配列として扱えます。

間違った JSON の場合

$json = '{"id":123,"name":"Taro"'; // 末尾の } がない

$data = decode_json_to_array($json); // RuntimeException が投げられる
PHP

このときは、RuntimeException('JSON decode failed: ...') が投げられます。 「失敗したこと」がはっきり分かるので、 呼び出し側で try/catch して、エラーメッセージを出したり、ログに残したりできます。

型安全の第二歩:「中身の型・形をチェックする」

期待する構造を決める

例えば、「ユーザー情報の JSON」を受け取るとします。

期待する形はこうだと決めます。

  • id … 整数
  • name … 文字列
  • tags … 文字列の配列

この「期待する形」に合っているかをチェックするのが、型安全の次のステップです。

チェック付きのユーティリティを作る

/**
 * ユーザー情報 JSON を「型安全な配列」にデコードする
 *
 * 期待する形:
 * {
 *   "id":   int,
 *   "name": string,
 *   "tags": string[]
 * }
 */
function decode_user_json(string $json): array
{
    $data = decode_json_to_array($json);

    // id が存在し、整数であること
    if (!array_key_exists('id', $data) || !is_int($data['id'])) {
        throw new RuntimeException('Invalid JSON: "id" must be int');
    }

    // name が存在し、文字列であること
    if (!array_key_exists('name', $data) || !is_string($data['name'])) {
        throw new RuntimeException('Invalid JSON: "name" must be string');
    }

    // tags が存在し、配列であること
    if (!array_key_exists('tags', $data) || !is_array($data['tags'])) {
        throw new RuntimeException('Invalid JSON: "tags" must be array');
    }

    // tags の中身がすべて文字列であること
    foreach ($data['tags'] as $tag) {
        if (!is_string($tag)) {
            throw new RuntimeException('Invalid JSON: "tags" must be array of string');
        }
    }

    return $data;
}
PHP

ここでの重要ポイントは、「期待する型をコードで明示している」ことです。

  • id は int じゃないとダメ
  • name は string じゃないとダメ
  • tags は配列で、その中身も string じゃないとダメ

こうしておくと、「型が違う JSON」が来たときにすぐに分かります。

例題:型がズレている JSON を検知する

id が文字列になっている場合

$json = '{"id":"123","name":"Taro","tags":["php","backend"]}';

$data = decode_user_json($json); // RuntimeException: "id" must be int
PHP

json_decode 自体は成功しますが、 id が文字列なので、型チェックで弾かれます。

tags の中に数値が混ざっている場合

$json = '{"id":123,"name":"Taro","tags":["php", 100]}';

$data = decode_user_json($json); // RuntimeException: "tags" must be array of string
PHP

tags の 2 番目が数値なので、 「文字列の配列」という期待に合わず、例外になります。

こうして「型のズレ」を早い段階で検知しておくと、 後の処理で「想定外の型」によるバグが起きにくくなります。

実務での使いどころ

外部 API からのレスポンスを扱うとき

外部 API から JSON が返ってきて、それを配列として扱うケースはよくあります。

$responseJson = $client->getBody();

try {
    $user = decode_user_json($responseJson);
} catch (RuntimeException $e) {
    // レスポンスがおかしいのでログに残して、処理を中断する
    error_log('Invalid user JSON: ' . $e->getMessage());
    // 必要ならユーザー向けエラーを返す
}
PHP

「レスポンスがおかしい」ことを早めに検知できるので、 変なデータをそのまま DB に保存してしまう、といった事故を防げます。

設定ファイル(JSON)を読み込むとき

設定を JSON ファイルで持っていて、 起動時に読み込むようなケースでも同じです。

$json = file_get_contents(__DIR__ . '/config.json');

$config = decode_json_to_array($json);

// ここでさらに「必須キーがあるか」「型が合っているか」をチェックする
PHP

設定の型がズレていると、 本番環境でだけ変な挙動をする、という怖いバグにつながります。 型安全なデコードをしておくことで、そのリスクを減らせます。

まとめ:今日からの「JSON → 配列(型安全)」ユーティリティ

このユーティリティの本質は、「JSON をただ配列にするだけじゃなく、“期待する形・型”まで保証してあげる」ことです。

そのために、

  • JSON_THROW_ON_ERROR で「JSON としてのエラー」を例外で検知する
  • is_array で「配列として扱えるか」を確認する
  • 必須キーの存在と、各キーの型をチェックする

という処理を、ユーティリティ関数に閉じ込めておきます。

もう一度、基本形を載せておきます。

function decode_json_to_array(string $json): array
{
    try {
        $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
    } catch (JsonException $e) {
        throw new RuntimeException('JSON decode failed: ' . $e->getMessage(), 0, $e);
    }

    if (!is_array($data)) {
        throw new RuntimeException('Decoded JSON is not an array');
    }

    return $data;
}
PHP

そして、必要に応じて「期待する構造ごとに専用のチェック関数」を作っていく。 それだけで、JSON 周りのバグと「よく分からない NULL」に悩まされることが、ぐっと減ります。

タイトルとURLをコピーしました