😉

phpとJavaScriptでデータをやり取りするときに気をつける型の違い

2024/03/26に公開

はじめに

私はWebサービスのバックエンドをphp、フロントエンドをJavaScript(TypeScript)で実装することがよくあります。
サーバーとブラウザとのデータのやりとりはJSON形式で行いますが、php側で想定していた型とJavaScriptで実際に受け取る値の型が違うケースがあります。
普段は気にすることはないと思いますが、知っていると何かの役に立つこともあるかもしれません。

文字列"0"の論理値が異なる

文字列の "0" を論理値に変換した場合phpとJSでは結果が異なります。

php
> var_dump((bool)"0");
bool(false)
js
> Boolean("0");
true

例えばDBに 0 で保存したフラグの値をそのまま文字列で扱っていた場合、phpでは false の判定になりますがJSでは true と判定されてしまいます。

連想配列(オブジェクト)のキーに整数値を指定した場合のキーの型が異なる

phpでは整数値をキーに指定した場合、文字列型で指定しても数値型に変換されます。

php
> var_dump(array_keys(["100" => 100]));
array(1) {
  [0]=>
  int(100)
}

逆にJSでは数値型で指定しても文字列型に変換されます。

js
Object.keys({100: 100});
// ['100']

例えば以下のような連想配列をphpで定義します。

php
$array = [
    1 => 'value1',
    2 => 'value2',
];

これを使い以下のように選択済みIDの判定を行った場合、意図通りに動作しますが、

php
$selected_ids = [1];
foreach ($array as $id => $value) {
    var_dump(in_array($id, $selected_ids, true));
}

// bool(true)
// bool(false)

これをJSON形式で受け取ったJSで同じように処理すると異なる結果になります。

js
const array = JSON.parse('{"1":"value1","2":"value2"}');
const selected_ids = [1];
for (const id in array) {
  console.log(selected_ids.includes(id));
}

// false
// false

const selected_str_ids = ["1"];
for (const id in array) {
  console.log(selected_str_ids.includes(id));
}

// true
// false

連想配列(オブジェクト)のキーに整数値を指定した場合に順序が保証されない

phpで作成した数値キーの連想配列は指定した順に並びます。

php
> var_dump(array_values([3 => 3, 1 => 1, 2 => 2]));
array(3) {
  [0]=>
  int(3)
  [1]=>
  int(1)
  [2]=>
  int(2)
}

これをJSONで受け取ったJSではキーの値でソートされてしまいます。

js
const array = JSON.parse('{"3":3,"1":1,"2":2}');
Object.values(array);
// [1, 2, 3]

並び順が重要な場合は配列を使いましょう。

php
> var_dump(json_encode([[3, 3], [1, 1], [2, 2]]));
// "[[3,3],[1,1],[2,2]]"
js
const array = JSON.parse("[[3,3],[1,1],[2,2]]");
const map = new Map(array);
Array.from(map.values());
// [3, 1, 2]

連想配列はオブジェクトだけど空の連想配列はオブジェクトではない

なんらかの条件分岐によって空の連想配列ができることがあります。
これをそのまま json_encode すると空の方は配列になってしまいます。

php
$values = [1 => 1, 2 => 2, 3 => 3];

$empty = [];
foreach ($values as $key => $value) {
    if (/* 判定 */ false) {
        $empty[$key] = $value;
    }
}
$array = [];
foreach ($values as $key => $value) {
    if (/* 判定 */ true) {
        $array[$key] = $value;
    }
}

var_dump(json_encode(compact('empty', 'array')));
// "{"empty":[],"array":{"1":1,"2":2,"3":3}}"

空のオブジェクトとして渡したい場合は意図的にオプションを指定するかクラスを使う必要があります。

php
var_dump(json_encode(compact('empty', 'array'), JSON_FORCE_OBJECT));
// "{"empty":{},"array":{"1":1,"2":2,"3":3}}"

$empty = new stdClass;
var_dump(json_encode(compact('empty', 'array')));
// "{"empty":{},"array":{"1":1,"2":2,"3":3}}"

まとめ

みんな仲良くしようね😉

Discussion