📚

PHP 添字配列・連想配列と array_merge、配列結合演算子 (+) の挙動

2024/02/16に公開

array_merge の挙動が添字配列と連想配列で異なることは知られていますが、

  • キーが 文字列の数字 である配列
  • キーが 0始まりの連番ではない数値 である配列

の場合にどういう挙動をするのかが気になり、調査しました。

array_merge

PHPマニュアルに書かれている仕様は以下の通りです。

入力配列が同じキー文字列を有していた場合、そのキーに関する後に指定された値が、 前の値を上書きします。しかし、配列が同じ添字番号を有していても 値は追記されるため、このようなことは起きません。
入力配列の中にある数値添字要素の添字の数値は、 結果の配列ではゼロから始まる連続した数値に置き換えられます。

つまり、添字配列の場合はキーが振り直され、連想配列の場合は同一キー後勝ちです。

**調査の結果、キーが 文字列の数字 であっても、キーが 0始まりの連番ではない数値 であっても、添字配列と同じ挙動で、キーは連番で振り直されました。
**

配列結合演算子 (+)

PHPマニュアルに書かれている仕様は以下の通りです。

  • 演算子は、右側の配列を左側の配列に追加したものを返します。 両方の配列に存在するキーについては左側の配列の要素が優先され、 右側の配列にあった同じキーの要素は無視されます。

つまり、添字配列か連想配列かを問わず、同一キー先勝ちです。

調査の結果、キーが 文字列の数字 の場合、添字配列と同じ挙動、というよりキーが数値にキャストされた上で処理されていました。

補足

キーのキャストについては、ちゃんとPHPマニュアルに書いてありました。

つまり、array_merge や演算子の仕様ではなく、
配列を生成した時点で、すでにキャストされているということのようです。

10 進数の int として妥当な形式の String は、 数値の前に + 記号がついていない限り、 int 型にキャストされます。 つまり、キーに "8" を指定すると、実際には 8 として格納されるということです。一方 "08" はキャストされません。これは十進数として妥当な形式ではないからです。
floats もまた int にキャストされます。つまり、 小数部分は切り捨てられるということです。たとえばキーに 8.7 を指定すると、実際には 8 として格納されます。
bool も int にキャストされます。つまり、 キーに true を指定すると実際には 1 に格納され、 同様にキーを false とすると実際には 0 となります。
Null は空文字列にキャストされます。つまり、キーに null を指定すると、実際には "" として格納されます。

調査コード


$a = [
    'A-1',
    'A-2',
    'A-3',
];

$b = [
    'B-1',
    'B-2',
    'B-3',
];

$c = [
    1 => 'C-1',
    2 => 'C-2',
    3 => 'C-3',
];

$d = [
    '0' => 'D-1',
    '1' => 'D-2',
    '2' => 'D-3',
];

$e = [
    'first' => 'E-1',
    'second' => 'E-2',
    'third' => 'E-3',
];

var_dump(array_merge($a, $b));

/*
array(6) {
    [0] => string(3) "A-1"
    [1] => string(3) "A-2"
    [2] => string(3) "A-3"
    [3] => string(3) "B-1"
    [4] => string(3) "B-2"
    [5] => string(3) "B-3"
}
*/

var_dump(array_merge($a, $c));

/*
array(6) {
    [0] => string(3) "A-1"
    [1] => string(3) "A-2"
    [2] => string(3) "A-3"
    [3] => string(3) "C-1"
    [4] => string(3) "C-2"
    [5] => string(3) "C-3"
}
*/

var_dump(array_merge($a, $d));

/*
array(6) {
    [0] => string(3) "A-1"
    [1] => string(3) "A-2"
    [2] => string(3) "A-3"
    [3] => string(3) "D-1"
    [4] => string(3) "D-2"
    [5] => string(3) "D-3"
}
*/

var_dump(array_merge($a, $e));

/*
array(6) {
    [0] => string(3) "A-1"
    [1] => string(3) "A-2"
    [2] => string(3) "A-3"
    'first' =>
    string(3) "E-1"
    'second' =>
    string(3) "E-2"
    'third' =>
    string(3) "E-3"
}*/

var_dump(array_merge($c, $d));

/*
array(6) {
    [0] => string(3) "C-1"
    [1] => string(3) "C-2"
    [2] => string(3) "C-3"
    [3] => string(3) "D-1"
    [4] => string(3) "D-2"
    [5] => string(3) "D-3"
}
*/

var_dump(array_merge($c, $e));

/*
array(6) {
    [0] => string(3) "C-1"
    [1] => string(3) "C-2"
    [2] => string(3) "C-3"
    'first' => string(3) "E-1"
    'second' => string(3) "E-2"
    'third' => string(3) "E-3"
}
*/

var_dump(array_merge($d, $e));

/*
array(6) {
    [0] => string(3) "D-1"
    [1] => string(3) "D-2"
    [2] => string(3) "D-3"
    'first' => string(3) "E-1"
    'second' => string(3) "E-2"
    'third' => string(3) "E-3"
}
*/

var_dump(array_merge($a, $b, $e));

/*
array(9) {
    [0] => string(3) "A-1"
    [1] => string(3) "A-2"
    [2] => string(3) "A-3"
    [3] => string(3) "B-1"
    [4] => string(3) "B-2"
    [5] => string(3) "B-3"
    'first' => string(3) "E-1"
    'second' => string(3) "E-2"
    'third' => string(3) "E-3"
}
*/

var_dump($a + $b);

/*
array(3) {
    [0] => string(3) "A-1"
    [1] => string(3) "A-2"
    [2] => string(3) "A-3"
}
*/

var_dump($a + $c);

/*
array(4) {
    [0] => string(3) "A-1"
    [1] => string(3) "A-2"
    [2] => string(3) "A-3"
    [3] => string(3) "C-3"
}
*/

var_dump($a + $d);

/*
array(3) {
    [0] => string(3) "A-1"
    [1] => string(3) "A-2"
    [2] => string(3) "A-3"
}
*/

var_dump($a + $e);

/*
array(6) {
    [0] => string(3) "A-1"
    [1] => string(3) "A-2"
    [2] => string(3) "A-3"
    'first' => string(3) "E-1"
    'second' => string(3) "E-2"
    'third' => string(3) "E-3"
}
*/

var_dump($c + $d);

/*
array(4) {
    [1] => string(3) "C-1"
    [2] => string(3) "C-2"
    [3] => string(3) "C-3"
    [0] => string(3) "D-1"
}
*/

var_dump($c + $e);

/*
array(6) {
    [1] => string(3) "C-1"
    [2] => string(3) "C-2"
    [3] => string(3) "C-3"
    'first' => string(3) "E-1"
    'second' => string(3) "E-2"
    'third' => string(3) "E-3"
}
*/

var_dump($d + $e);

/*
array(6) {
    [0] => string(3) "D-1"
    [1] => string(3) "D-2"
    [2] => string(3) "D-3"
    'first' => string(3) "E-1"
    'second' => string(3) "E-2"
    'third' => string(3) "E-3"
}
*/

var_dump($a + $b + $e);

/*
array(6) {
  [0] => string(3) "A-1"
  [1] => string(3) "A-2"
  [2] => string(3) "A-3"
  'first' => string(3) "E-1"
  'second' => string(3) "E-2"
  'third' => string(3) "E-3"
}
*/

Discussion