PHP配列のインデックス外アクセス

構文チェックを厳しくしてると、

<?php $a=array(1,2,3); $t = $a[3]; ?>

PHP Notice: Undefined offset: 3
という警告が出る。でも、

<?php $a=array(1,2,3); $t =& $a[3]; ?>
--警告なし
<?php $a=array(1,2,3); $t =& $a[3]; echo "a[3] is " . $t; ?>
--警告なしでなぞの非表示値が表示されてる

配列アクセスと同時に実体コピーが起こるとだめっぽい。配列アクセスと実体コピーが別の文ならいいらしい。

謎の非表示値を調べてみると、

<?php $a=array(1,2,3); $t =& $a[3];
 echo "a[3] is a " . ($t === FALSE ? 'FALSE' :
                     ($t === NULL  ? 'NULL'  : 
                     ($t === ''    ? 'Empty' : $t)));
?>

a[3] is a NULL
なんだそうな。

インデックス外アクセスを推奨しない言語なのか、インデックス外の場合はNULLで応答する言語なのか、どっちかはっきりしない。ていうか、例によって、「PHPはプログラム言語じゃない」か。

ついでの考察:インデックス外だとNULLが出てくるので、isset()はNULLの要素と要素がないのを区別できない、ということかな。配列要素の検査には、issetを使わず、array_key_existsとis_nullを使うべきらしい。

ところでこれに、リファレンス引数な関数がからむと厄介だ。

<?php
function nocheck_fetch(&$ref){
 $copy=$ref; return $copy;
}
function check_fetch($ref){
 $copy=$ref; return $copy;
}
$a=array(1,2,3);
check_fetch($a[3]);   #警告あり
nocheck_fetch($a[3]); #警告なし
check_fetch($a[3]);   #警告ありのはずなのに警告がなくなる 謎(追記)
?>

クライアントコードの見た目が同じなのに、文句言われたり言われなかったりする。他のどの言語にも似てないので、普通の(PHP以外でちゃんとできる)プログラマにとって、この違いを理解するのは難しい。

インデックス外アクセスを起こすのがダメといえばダメなんだけど、問題なのは、そのダメな行為を注意したりしなかったりすること。プログラムの修正より、現象の理解に手間を取られる。プログラムを修正してしまう方が手軽なので、仕事を多くこなす経験豊富な人ほど探求を避ける傾向にあり、技術に熟練しないままベテランになるし、逆に、探究心の強い素養のある人ほど、本質的な課題に結びつかないバッドノウハウばかりを身に着けてしまい、良い仕事のためのスキルから遠くなってしまう。

こういう類の問題って、ユーザコミュニティの成長にも影響してるんじゃないかな?たしかにブラウザのDOMはバッドノウハウの塊かもしれないけど、JavaScriptだけ取り出せば非常に一貫性が高いので、ハッカーの仕事が急激に成長できるんだと思う。Perlはたしかに汚い構文かもしれないけど、学習を阻害するような一貫性の欠如はないので、ちゃんとしたハッカーを排出できるように思う。

PHPPerlに似た別のランタイムを持つんじゃなくて、Perlサーバページとして進歩してたら、世の中もっと違ってたのかな。