PHP json_decode() エラー修正:'expects parameter 1 to be string, null given'

beginner🐘 PHP2026-03-23| PHP 7.x / 8.x、任意のOS(Linux、macOS、Windows)、任意のフレームワーク(Laravel、Symfony、Plain PHP)

Error Message

json_decode() expects parameter 1 to be string, null given
#php#json#デコード#パース

エラーの内容

json_decode() を呼び出すと、次のエラーが発生します:

json_decode() expects parameter 1 to be string, null given

PHP 8 の場合:

json_decode(): Argument #1 ($json) must be of type string, null given

関数が JSON 文字列の代わりに null を受け取っています。エラーが発生している行は明白ですが、本当の原因はその数行上に潜んでいます。

根本原因

  • API レスポンスやファイル読み込みが null または空を返したにもかかわらず、チェックせずにそのまま json_decode() に渡している。これが最も一般的な原因です。
  • 文字列を返すと思っていた関数が、失敗時に実は null を返している — file_get_contents()$request->getContent()、DB カラムの読み取りはすべて暗黙的にこれを行います。
  • 変数が一度も代入されていない — 変数名のタイポ、代入の漏れ、または条件分岐でスキップされたなど。
  • JSON がユーザー入力や外部サービスから来ているが、何も送られてこなかった場合 — 空の POST ボディ、クエリパラメータの欠落、またはボディのない 4xx レスポンスなど。

修正1 — デコード前にガード処理を追加する

json_decode() に触れる前に、値が空でない文字列であることを確認します。シンプルな方法で、コードにおける「失敗」の意味を明確に決める必要があります。

<?php

$raw = file_get_contents('https://api.example.com/data');

if ($raw === false || $raw === null || $raw === '') {
    // ログに記録する、例外をスローする、早期リターンする — どれか一つを選ぶ
    throw new RuntimeException('Failed to fetch JSON data');
}

$data = json_decode($raw, true);

リクエストからのユーザー入力の場合:

<?php

$body = $request->getContent(); // 空文字列になる可能性がある

if (empty($body)) {
    return response()->json(['error' => 'Empty request body'], 400);
}

$data = json_decode($body, true);

修正2 — デコード後に json_last_error() を確認する

注意すべき落とし穴があります: json_decode()null を返すのは、まったく異なる2つの状況 — 入力が null だった場合と、かつ JSON が不正な形式だった場合 — のどちらでも起こります。確認しなければ、どちらが発生したか判断できません。

<?php

$data = json_decode($raw, true);

if (json_last_error() !== JSON_ERROR_NONE) {
    throw new RuntimeException('JSON decode failed: ' . json_last_error_msg());
}

これにより、無効な構文、予期しないエンコーディング、切り詰められたレスポンスなど、そのままでは静かに通過してデータを壊してしまう失敗を検出できます。

修正3 — オプションデータには Null 合体演算子を使う

値が存在しないことが完全に正常な場合もあります — nullable な DB カラムやオプションの設定フィールドなど。その場合は、処理を中断するのではなく、安全なフォールバックを提供します:

<?php

// $row['metadata'] が null の場合、代わりに空のオブジェクトをデコードする
$metadata = json_decode($row['metadata'] ?? '{}', true);

// 配列の場合
$tags = json_decode($row['tags'] ?? '[]', true);

データに対して実際に意味のあるフォールバックを選択してください。null に戻すフォールバックでは意味がありません。

修正4 — null の発生元を追跡する

原因が明らかでない場合は、デコード直前に値をダンプして実行を停止します:

<?php

var_dump($raw); // null か?false か?空文字列か?
die();

$data = json_decode($raw, true);

実際の値を確認すれば、通常は原因が明らかになります:

  • file_get_contents()false を返した — パスが間違っている、権限が拒否されている、またはネットワークタイムアウト
  • $_POST['json_data'] が未定義 — フィールド名が間違っているか、クライアントが application/json ではなく application/x-www-form-urlencoded を送信している
  • DB カラムが NULL — 行は存在するが、そのフィールドが一度も入力されていない
  • cURL のレスポンスボディが空 — サーバーがボディなしで 4xx/5xx を返した

cURL の場合は HTTP ステータスコードも確認してください — 401 や 500 のレスポンスにはボディがないことが多いです:

<?php

$ch = curl_init('https://api.example.com/data');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($response === false || $httpCode !== 200) {
    throw new RuntimeException("API request failed with HTTP $httpCode");
}

$data = json_decode($response, true);

修正5 — 一貫した処理のためにヘルパー関数でラップする

PHP 8 ではこれが警告からフルの TypeError に格上げされ、ログで発見しやすくなりました。いずれにせよ、複数の箇所で JSON をデコードしている場合は、処理を一元化しましょう:

<?php

function safe_json_decode(?string $json, bool $assoc = true): mixed
{
    if ($json === null || $json === '') {
        return null;
    }

    $data = json_decode($json, $assoc);

    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new \InvalidArgumentException('Invalid JSON: ' . json_last_error_msg());
    }

    return $data;
}

コードベース内のすべてのデコードが一つの関数を経由するようになります。エラーは、3層も深くに謎の null 値として伝播する代わりに、有用なメッセージと共に即座に表面化します。

修正の確認

スクラッチファイルで簡単なサニティチェックを行います:

<?php

// エラーなしで配列を返すはず
$result = json_decode('{"key": "value"}', true);
var_dump($result); // array(1) { ["key"] => string(5) "value" }

// null ガードにより空の配列が生成されるはず
$result = json_decode(null ?? '{}', true);
var_dump($result); // array(0) {}

Laravel や Symfony アプリでは、テストスイートを実行するか、エンドポイントに直接アクセスしてください:

curl -X POST https://yourapp.test/api/endpoint \
  -H 'Content-Type: application/json' \
  -d '{"test": true}'

異常系もテストしてください — 空のボディを送信し、クラッシュする代わりに適切な 400 が返ることを確認します。

予防策

本当の修正はデコード呼び出しの時点ではなく、その上流で行われます。API、ファイル、またはユーザー入力から来る JSON は、json_decode() に触れる前にバリデーションされるべきです。外部サービスは特に信頼性が低く、認証失敗時に空のボディを返したり、部分的なレスポンスで不正な JSON を返したり、何か問題が起きると普通の HTML エラーページを返したりします。

不正な JSON をデバッグする際は、ToolCraft の JSON Formatter & Validator をブックマークしておく価値があります — 生の文字列を貼り付けると、正確な構文エラーを即座に特定できます。すべてブラウザ内で動作し、何もアップロードされません。

このバグをほぼ不可能にする習慣:

  • strict_types を有効にする(declare(strict_types=1);)— PHP 8 では型が間違っている場合、json_decode() に到達する前に TypeError をスローします
  • 型付きクラスプロパティを使用する — $this->jsonDatastring として宣言されている場合、null の代入は3回の呼び出し後ではなく、代入時点で失敗します
  • 開発中はデコード前に生のレスポンスをログに記録する — 空または不正なレスポンスが現れた瞬間に検出できます
  • 入力をデコードするすべてのエンドポイントに対して、null、空文字列、破損した JSON を送るユニットテストを書く — エッジケースを明示的にする

Related Error Notes