そーだいなるらくがき帳

そーだいが自由気侭に更新します。

JSONのフォーマットが正しいのにphpでjson_decode()できないときはBOMを疑う

 表題の件で深夜にハマったのでメモとして残す。

JSONとしても正しくてもBOM付きのファイルからJSONを読み込むとエラーになる

 結論としてはUTF-8+BOMのファイルは json_decode() の際にBOMのせいで失敗する。 保存するときにBOM無しのフォーマットを指定するかBOMを削除しましょう。

 今回の自分のパターンは渡されたJSONのファイルを file_get_contents() して json_decode() しているだけなのに失敗するというもの。 json_decode() は失敗してもnullを返すだけなので何に失敗したかわからない。 S3に他者が置いたファイルを使うみたいなパターンのときは要注意。

 file_get_contents() の結果をコピペしてチェッカーにかけたり、文字列としてコピペしてdecodeするとうまくいく場合などは疑うと良い。

PHP 8.3 以上ならエラーの理由を知ることができる。

 今すぐ自分のローカルのPHPのversionを8.3にしよう。 すると json_validate() が使えるようになる。

 マニュアルにあるとおり、さっとPHPを用意すればJSONのcheckができる。 今回の場合は JSON_ERROR_SYNTAX になる。 JSONのフォーマットが正しいのに JSON_ERROR_SYNTAX が出る場合は見えない文字列*1を意識すると良い。

サッと用意したチェックのためのコードを置いて置く。

<?php

$json = trim(file_get_contents('target.json'));

var_dump($json);
var_dump(json_validate($json));

    switch (json_last_error()) {
        case JSON_ERROR_NONE:
            echo ' - No errors';
        break;
        case JSON_ERROR_DEPTH:
            echo ' - Maximum stack depth exceeded';
        break;
        case JSON_ERROR_STATE_MISMATCH:
            echo ' - Underflow or the modes mismatch';
        break;
        case JSON_ERROR_CTRL_CHAR:
            echo ' - Unexpected control character found';
        break;
        case JSON_ERROR_SYNTAX:
            echo ' - Syntax error, malformed JSON';
        break;
        case JSON_ERROR_UTF8:
            echo ' - Malformed UTF-8 characters, possibly incorrectly encoded';
        break;
        default:
            echo ' - Unknown error';
        break;
    }

var_dump(json_last_error_msg());
var_dump(json_decode($json, true));

参考資料

www.php.net php.watch

*1:nullとか空文字とか改行コードとか