PHPのgotoを詳しく調べてみる。
高木です。こんばんは。
今日は思うところがあって、PHPのgotoを詳しく調べてみることにしました。
調べれば調べるほど、PHPのgotoは面白いネタを提供してくれます。
もしかすると、PHPのベテランであっても、PHPにgotoがあることを知らない可能性がありますね。
私がPHPを触りだしたのはここ数年のことなので、あまり古い話は知りません。
gotoがPHPに導入されたのはPHP 5.3.0からですので、私がPHPを始めたころに丁度導入されたように思います。
PHPのマニュアルを見ると、gotoは制御構造に分類されているのですが、一方では下記のように「goto演算子」となっています。
goto 演算子を使用すると、 プログラム中の他の命令にジャンプすることができます。
PHPでは、演算子について次のように定義しています。
演算子とは、ひとつ以上の値 (あるいはプログラミング用語における「式」) から別の値 (制御構造が式になるように) を生み出すものです。
はっきり明記されていませんが、オペランドを伴う演算子は「式」になると考えるのが妥当でしょう。
「式」であれば、PHPでは次のように定義しています。
最も簡単で最も正確な式の定義は、”全ての式には値がある。”です。
これらの情報から総合的に判断すれば、goto ラベル
というのは「式」であり、それは何らかの値を持っていると考えるべきです。
関数の返り値をvoid型にできるようになりましたので、「式」の値はvoid値であってもよいと思います。
実際、返り値がvoidの関数をva_dumpで調べると、NULLが返ってきます。
1 2 3 4 5 | <?php function f() : void { } var_dump(f()); |
ところが、下記のようにgoto式?をva_dumpで調べようとすると、
1 2 3 | <?php va_dump(goto label); label: |
Parse error: syntax error, unexpected 'goto' (T_GOTO) in ...
といったエラーが発生してしまうのです。
「式」のはずなのに、これはおかしいですね。
他にもgotoには面白いネタがあります。
PHPのgotoの説明では下記のようになっています。
対象となるラベルは同じファイル上の同じコンテキストになければなりません。 つまり、関数やメソッドの外に飛び出したり 関数やメソッドの中に突入したりすることはできないということです。 また、いかなるループや switch 構造の中にも突入することができません。 逆にループや switch 構造から抜け出すことはできます。
ちょっと意地悪をして、次のようなコードを試してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php namespace A { echo __LINE__ . PHP_EOL; goto label; echo __LINE__ . PHP_EOL; } namespace B { echo __LINE__ . PHP_EOL; label: echo __LINE__ . PHP_EOL; } |
実行結果は、
4
13
でした。
つまり、名前空間にまたがってgotoで分岐することは何ら妨げられないようです。
また、分岐先のラベルは名前空間で修飾されることはなく、常にグローバルのようなのです。
PHPはマニュアルを読んでもよくわからない仕様が多々あります。
私にいわせれば、PHPはCやC++よりずっと難しいのですが、どうも世間ではそのように認識されていないようです。
PHPを極めるにはまだまだ道は長く、どんどんバージョンアップしていくこともあるので生涯追いつくことはできないかもしれませんね。