読者です 読者をやめる 読者になる 読者になる

PHP言語エンジンのif文の数は世界一ぃ〜〜〜

PHP

PHP5.3.6ではこの問題が発生しなくなっていました。

PHP5.2.6なんだけど。

イテレータでまわせるオブジェクトを書けるので、まわしてみた。

<?php
class MyArrayObject implements IteratorAggregate {
    // ... 配列みたいなオブジェクトを実装
    public function getIterator() {
        return new MyArrayObjectIterator($this);
    }
}
class MyArrayObjectIterator implements Iterator {
    // ... イテレータの実装
}
function myarray($a,$b,$c){
    return new MyArrayObject($a,$b,$c);
}
?>

というようなオブジェクトと関数があるとして、以下のようにやりたい。

<?php
$vs = myarray(1,2,3);
foreach($vs as $v) {
    echo $v; // OK: 123 が表示される
}
?>

まあ仕様どおり。じゃあ変数を省略する。

<?php
foreach(myarray(1,2,3) as $v) {
    echo $v; // OK: 123 が表示される
}
?>

OK。そろそろ実用で、本物Arrayと偽Arrayの相互運用。

<?php
$cond = TRUE;
$vs = $cond ? myarray(1,2,3) : array(4,5,6);
foreach($vs as $v) {
    echo $v; // OK: 123 が表示される
}
?>

これもOK。さてそれじゃ、上の二つを組み合わせて…

<?php
$cond = TRUE;
foreach(($cond ? myarray(1,2,3) : array(4,5,6)) as $v) {
    echo $v; // BUG: ここ通らない
}
?>

なんじゃこりゃ、動かん。なぜに?! 個々に単体で使うとOKで、組み合わせるとバグるって、そんなの、プログラム言語じゃねーよ。(ちなみに、$cond=FALSE なら echo のとこ通るのよね。)

似たようなこと書いても、別の理由で偶然動くかもしれないから、完全な再現性があるのかわからないけど、少なくとも、特定のシチュエーションで起こったのは確かで、それも、2回も遭遇した。

なんか、PHPってもしかしてこんな実装?

foreach構文の第一要素について...

  1. 変数の場合:変数がarrayか->Traversableか->他のオブジェクトか->X
  2. 関数コールの場合:戻り値がarrayか->Traversableか->他のオブジェクトか->X
  3. 式の場合:式の結果値かarrayか->オブジェクトか->X (PHP5開発時の実装漏れ)

ふつうの言語なら、こう↓だよね。

  1. 式(単変数や関数コールも含む)を評価して値を得る。
  2. 値に対して反復インターフェース(C言語で仕様概念上のね)を要求する。※反復できない値はゼロ回反復するインターフェースを提供する。
  3. 反復インターフェースを使用。

こんな感じのマトモな言語じゃないと、リファクタリング結果が等価になるって保障できないよなぁ…。つくづく、PHP5のときやるべきだったのは、インターフェースじゃなくて、配列や構文解析など、従来の言語コアへのてこ入れだったと思う。