hnwの日記

PHPのキー名を省略した配列アクセスの挙動

先日id:ichii386さんとPHPの配列のキーに数値文字列を渡した場合の挙動について少しお話をして、「ひょっとしたら僕がまだ書いてないネタを先に書かれてしまいそうだ!まずい!」と思ったのですが、既に僕のネタは「PHPの配列のキーについて調べてみる」で書いていたことに気づきました。書いたこと自体を本気で忘れていて、危うく前回より内容の薄い記事をまた書くところでした。


それはさておき、まだ書いていないネタをもう一つ思い出したので書いてみます。配列の「[ ]」でのアクセスに関するネタです。


PHPの配列は他の言語同様キーを省略した形で「$array[]="hoge";」のように配列末尾に要素を追加することができます。配列末尾というのを正確に言うと、その配列のキーのうち整数のものの中で最大のもの+1(1つも無ければ0)のキーを指定したのと同じことになります。

<?php
$array=array();
$array[4]=1;
$array[-4]=2;
$array[]=3;
var_dump($array);


この例であれば4+1ということで、$array[]で配列アクセスしている個所は$array[5]と書いているのと同じ意味になります。

$ php array-maxint-test01.php
array(3) {
  [4]=>
  int(1)
  [-4]=>
  int(2)
  [5]=>
  int(3)
}


さて、本題です。下記のようなプログラムを実行してみました。

<?php
$array=array();
$array[-2147483648]=2;
$array[2147483647]=1;
$array[]=3;
var_dump($array);


符号つき32bit整数の最大値や最小値あたりをキーにしたい気分だったんです。きっと深い理由はありません。

$ php array-maxint-test02.php
PHP Warning:  Cannot add element to the array as the next element is already occupied in /home/hanawa/php/array-maxint-test.php on line 6
array(2) {
  [-2147483648]=>
  int(2)
  [2147483647]=>
  int(1)
}


すると、これまで8年ほどのPHP生活で見た事の無いエラーが出ました。次の要素が既に入っていたために、[]による配列アクセスができなかったようです。色々疑問もありますが、試しに配列に要素を入れる順序を入れ替えてみましょう。

<?php
$array=array();
$array[2147483647]=1;
$array[-2147483648]=2;
$array[]=3;
var_dump($array);
$ php array-maxint-test03.php
array(3) {
  [2147483647]=>
  int(1)
  [-2147483648]=>
  int(2)
  [-2147483647]=>
  int(3)
}


ええええ?なんで今度はエラーが出ないの?


ソースコードを紹介するのはやめておきますが、Zend/zend_hash.cの_zend_hash_index_update_or_next_insert()あたりを読めば理由はわかると思います。


いつも通りの「誰も迷惑しないけどバグっぽい気がする」というお話でした。多分この辺は中の人が考え足りていないんでしょう。何が何でも修正したいというなら、$array[2147483647]が存在する状況では$array[]へのアクセスが確実に失敗するようにするくらいでしょうか。


本当につまらないネタですけど、偉い人たちを見習って僕も本家にパッチを送ってみますかね。どういう手順でやったものかあまり想像がついてませんけど…。