表題の件で深夜にハマったのでメモとして残す。
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() が使えるようになる。
jsonが正しいのになぜがjson_decode()が失敗する問題、json_validate()でsyntax errorでよく調べたら末尾に謎のnull文字がいたっぽくて解決できたのでみんなjson_validate()のためにPHP8.3を使うべき。
— そーだい@初代ALF (@soudai1025) 2024年7月9日
マニュアルにあるとおり、さっと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));
参考資料
*1:nullとか空文字とか改行コードとか