PHPのconst
について、
PHP
1<?php 2const Y = "hoge"; 3const X = Y; 4var_dump(X);
これはstring(4) "hoge"
が表示されます。では、
PHP
1<?php 2const X = Y; 3const Y = "hoge"; 4var_dump(X);
とすると、Yを定義せずに使っているという警告と共にstring(1) "Y"
が表示されます。ではでは、
PHP
1<?php 2class A { 3 const X = A::Y; 4 const Y = "hoge"; 5} 6var_dump(A::X);
とすると、string(4) "hoge"
が表示され、定義前のA::Y
を見に行けているようです。
どうしてclass外では後方のconstを参照することはできないのに、class内だとできるのでしょうか?そのとき、どのような順番で何を処理しているのでしょうか?
###他の言語からの考察
C++はそもそも前方に宣言がないと使えないので、宣言があることが前提になります。char *のようなポインタだと逆順でも同じ物になりますが、std::stringのようなものでは逆順だと結果が変わります。
C++
1#include <iostream> 2#include <string> 3class A 4{ 5public: 6 static const char *const XC; 7 static const char *const YC; 8 static const std::string XS; 9 static const std::string YS; 10}; 11 12const char *const A::XC = YC; 13const char *const A::YC = "hoge"; 14const std::string A::XS = YS; 15const std::string A::YS = "hoge"; 16 17int main() 18{ 19 std::cout << A::XC << std::endl; // hoge 20 std::cout << A::XS << std::endl; // (空) 21 return 0; 22}
Javaはコンパイル時にエラーになります。
Java
1class A { 2 public static final String X = Y; // エラー: 前方参照が不正です 3 public static final String Y = "hoge"; 4 public static void main(String[] args) { 5 System.out.println(X); 6 } 7}
Rubyは未定義であるとして同じくエラーになります。
Ruby
1class A 2 X = A::Y # uninitialized constant A::Y (NameError) 3 Y = "hoge" 4end 5p A::X
Haskellはオブジェクト指向におけるクラスに相当する物はないですが、通常の定数(そもそも全てが定数ですが)についても、遅延評価なので、順番とかはなんも関係無く動作できます。
Haskell
1x = y 2y = "hoge" 3main = print x -- "hoge"
C++がそういう動作になる理由はちょっと複雑になるので除きますが、JavaやRubyはclass内の定数であっても、定義されていない定数は使えません。定数の定義処理を順番にしていくと考えれば妥当な仕様だと思います。しかし、PHPだと、順番に定義していくと考えることはできません。となると、A::Y
部分をHaskellのように遅延評価しているとか、そんな感じなのでしょうか?
###参考にした質問
PHP - 定数の定義より前にその定数を使う関数の定義を書いても大丈夫なのか(73837)|teratail
上の質問については、関数内の式の評価が呼び出し時だから問題ないという物です。では、constの右辺の評価も呼び出し時だから…と考えると、Xの右辺が評価されるときにYの右辺が評価済みというのもおかしな話ですし、class外だとうまくいかない理由も付けられなくなります。
定数式のバグ - Qiitaを読んで、ふと気になりました。なお、この現象は記事に書いているバグと関係はありますが、バグそのもののことではありません。
回答4件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。