PHP の配列は常に連想配列であり、そのキーの型は string か int のどちらかになります。素直に string か int をキーにするのが安全なのですが、そうなっていないプログラムを相手にすることもあります。もし string や int 以外を渡した場合は型変換が起きます。この変換は次の様に起きます。この型変換ルールについて説明します。
$a = [];
$a[true] = '元true';
var_dump($a);
/*
array(1) {
[1]=>
string(7) "元true"
}
*/
詳しい説明は公式ドキュメントの次にあります。
公式ドキュメントから引用した詳しいキーの変換ルールは次です。
-
10 進数の int として妥当な形式の String は、
数値の前に+記号がついていない限り、
int 型にキャストされます。
つまり、キーに"8"を指定すると、実際には
8として格納されるということです。一方"08"
はキャストされません。これは十進数として妥当な形式ではないからです。 -
floats もまた int にキャストされます。つまり、
小数部分は切り捨てられるということです。たとえばキーに8.7を指定すると、実際には
8として格納されます。 -
bool も int にキャストされます。つまり、
キーにtrueを指定すると実際には1に格納され、
同様にキーをfalseとすると実際には0となります。 -
Null は空文字列にキャストされます。つまり、キーに
nullを指定すると、実際には""として格納されます。 -
array や object は、キーとして使えません。
キーとして使おうとするとIllegal offset typeという警告が発生します。
よりざっくばらんな言葉にすると次です。
-
\A(-)?[0-9]+\zに当てはまる文字列型のみがint型にキャストされます。それ以外の文字列型の値は文字列型のままです。 - float型とbool型はint型にキャストされます
- Null は空文字列にキャストされます。つまり、キーに null を指定すると、実際には “” として格納されます。
- array や object は、キーとして使えません。 キーとして使おうとすると Illegal offset type という警告が発生します。
具体例が先述のマニュアル内にあり、次の様になります。
<?php
$array = array(
1 => 'a',
'1' => 'b', // 値 "a" は "b" で上書きされます。
1.5 => 'c', // 値 "b" は "c" で上書きされます。
-1 => 'd',
'01' => 'e', // この値は数値文字列ではないので、キー1を上書きしません
'1.5' => 'f', // この値は数値文字列ではないので、キー1を上書きしません
true => 'g', // 値 "c" は "g" で上書きされます。
false => 'h',
'' => 'i',
null => 'j', // 値 "i" は "j" で上書きされます。
'k', // 値 "k" にはキー2が割り当てられます。なぜなら、これより前のキーの最大値は1だからです。
2 => 'l', // 値 "k" は "l" で上書きされます。
);
var_dump($array);
/*
array(7) {
[1]=>
string(1) "g"
[-1]=>
string(1) "d"
["01"]=>
string(1) "e"
["1.5"]=>
string(1) "f"
[0]=>
string(1) "h"
[""]=>
string(1) "j"
[2]=>
string(1) "l"
}
*/
ちなみに引用内では数値文字列とありますが、PHP内で定められている数値形式の文字列とまた違う文字列が数値文字列です。
数値形式の文字列の定義に従うと" 2"はINT_NUM_STRINGであり、10進数として妥当な形式の文字列の様に見えます。実際、次の様に int 型へのキャストでは期待通り int 型の 2 として扱われます。
<?php var_dump(1 + " 2"); // int(3)
しかしながら配列のキーとしては int 型に変換すべき値として扱われずそのまま文字列型のキーとなります。
<?php
var_dump([" 2" => 1, "2" => 2]);
/*
array(2) {
[" 2"]=>
int(1)
[2]=>
int(2)
}
*/
こんな感じで割とややこしいです機能で迂闊にキーが任意の値になる様なプログラムは書かない方が無難です。もし O(1) の検索ロジックなど何がしかのためにどうしてもキーを任意の値にする必要がある時は変換が起きないと確信できるキーを用意しましょう。例えば次の様にシリアライズを使う方法があります。
<?php
$k1 = serialize("2");
$k2 = serialize(2);
var_dump([$k1 => 1, $k2 => 2]);// 衝突は起きない前提
/*
array(2) {
["s:1:"2";"]=>
int(1)
["i:2;"]=>
int(2)
}
*/