🐕

PHPのjson_decodeでハマった話

2023/02/17に公開

要約

PHPのjson_decode($arg, true)はencodeしたときにいつの間にか{}[]に変えちゃったりするのでもうホント気をつけたい

REPLで確認

例えばこんなjson

※わかりやすくするため極度に簡略化しています。

$ php -a
Interactive shell

php > $json = '{"empty_object": {}}';

empty_objectは空のオブジェクトにしたいという仮定で、{}としてみます。
var_dumpで中身を確認

php > var_dump($json);
string(20) "{"empty_object": {}}"

ふむふむ。ここでぼやっと連想配列形式[1]でdecode

php > $contents = json_decode($json, true);

中身を確認

php > var_dump($contents);
array(1) {
  ["empty_object"]=>
  array(0) {
  }
}

ふむふむ。連想配列だからarrayか。
では元に戻したいのでencodeしよう(なにか違和感)

php > $restore_json = json_encode($contents);

あらためて、中身を確認

php > var_dump($restore_json);
string(19) "{"empty_object":[]}"

{"empty_object":[]}

配列???

なにが起きたのかもう一度

php > $json_bracket = '{"empty_object":[]}';
php > $contents_bracket = json_decode($json_bracket, true);
php >
php > var_dump($contents_bracket);
array(1) {
  ["empty_object"]=>
  array(0) {
  }
}

php > $json_brace = '{"empty_object":{}}';
php > $contents_brace = json_decode($json_brace, true);
php >
php > var_dump($contents_brace);
array(1) {
  ["empty_object"]=>
  array(0) {
  }
}

あらためて見てもやっぱりわかりにくいですが、配列の中身が空の場合、配列([])であっても連想配列({})であってもvar_dumpの結果が変わらないのがわかると思います。

PHP においては添字配列と連想配列の間に違いはなく、配列型は 1 つだけで、 同じ配列で整数のインデックスと文字列のインデックスを同時に使えます。

中身が空、つまり添字が無いのでPHPはどちらの配列なのかわからず、この言語仕様により{}[]も同じ「配列型」へとデコードされ、差異をなくされてしまったわけです。

値を入れるとどうなるか確認してみる

配列の場合

php > $json_bracket = '{"empty_object":["subject"]}';
php > $contents_bracket = json_decode($json_bracket, true);
php > var_dump($contents_bracket);
array(1) {
  ["empty_object"]=>
  array(1) {
    [0]=>
    string(7) "subject"
  }
}
php > $restore_bracket = json_encode($contents_bracket);
php > var_dump($restore_bracket);
string(28) "{"empty_object":["subject"]}"

配列はデコード->エンコード後も配列になりました。

連想配列の場合

php > $json_brace = '{"empty_object":{"subject":"sample"}}';
php > $contents_brace = json_decode($json_brace, true);
php > var_dump($contents_brace);
array(1) {
  ["empty_object"]=>
  array(1) {
    ["subject"]=>
    string(6) "sample"
  }
}
php > $restore_brace = json_encode($contents_brace);
php > var_dump($restore_brace);
string(37) "{"empty_object":{"subject":"sample"}}"

連想配列も、デコード->エンコード後は連想配列になりました。

流れを追ってみると当たり前のようにも見えますが、値が空かどうかで結果がコロリと変わってしまうので、PHPにおける配列の特性を知っておかないと思わぬところでハマる(ハマった)、という事例でした。

ではまた!

脚注
  1. https://www.php.net/manual/ja/function.json-decode.php ↩︎

Discussion