6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

名前空間内クラスを変数で呼び出すときはグローバルプレフィックス演算子がいらない

Last updated at Posted at 2018-10-26

ちょっと何言ってるのかよくわからない。

別の名前空間のクラスを呼び出す

別の名前空間のクラスを呼び出すときは、以下のように完全修飾名で記述します。

namespace FOO;
	class BAR{
		public function __construct(){echo 'BAZ';}
	}

namespace HOGE;
	new \FOO\BAR(); // BAZ
	new FOO\BAR(); // Fatal error: Uncaught Error: Class 'HOGE\FOO\BAR' not found

newの次にある名前空間の頭の\をグローバルプレフィックス演算子と言いますが、これを入れ忘れると、Class not foundのFatal errorが発生します。
なぜなら、\を使わない指定は名前空間\HOGEからの相対パス、\HOGE\FOO\BARを表しているからで、当然そのようなクラスは存在しないためエラーになるわけです。
Linuxや<script>タグなどのディレクトリ指定と同じように、頭に\があるかないかで絶対パス1か相対パスを識別しているということですね。

現象

ところでちょっとこれを見てくれ。

namespace HOGE;
	$class = 'FOO\BAR';
	new $class; // BAZ ← ???

なんで?

さらに$classの中身は'FOO\\BAR'でも'\FOO\BAR'でも'\\FOO\\BAR'でも'\\FOO\BAR'でもどれでも動きます。
いみがわからなすぎるぞ。

どうやら変数からnewした場合、常に完全修飾名であると見做されてしまうようです。

namespace FOO;
	class BAR{
		public function __construct(){echo 'BAZ';}
	}

	$class = 'BAR';
	new $class; // Fatal error: Uncaught Error: Class 'BAR' not found

	$class = 'FOO\BAR';
	new $class; // こちらならOK

BAR\FOOからの相対パス\FOO\BARではなく、絶対パス\BARとして扱われています。

実はマニュアルの名前空間と動的言語機能という項目に、これについての記載があります。
あるのですが、

動的なクラス名、関数名あるいは定数名においては修飾名と完全修飾名に差はないので、 先頭のバックスラッシュはなくてもかまいません。

何言ってるのかわかんねえ。

原因

さて、この差の原因ですが、直接べた書きしたときはコンパイル時に名前解決が行われ、変数を経由したときは実行時に行われる、という違いのようです。

namespace FOO;
	echo __NAMESPACE__; // FOO
	eval('echo __NAMESPACE__'); // "" ←

evalは実行時に評価が行われますが、その時点での__NAMESPACE__は空白になっています。

コンパイル時は名前空間に従って構文の解釈が行われます。
そして、コンパイル時に名前空間に関する情報は全て取り払われ、実行時には名前空間は無いものとして扱われます。

まあソースを読んだわけではなく現象からの推測ですが。
ソースコードリーディングは挫折した。

おまけ

名前空間内であれば、既存の組み込み関数やクラス、定数などと被る名前であっても堂々と命名できます。
わかりにくいのでやめたほうがいいですが。

namespace FOO;
    function printf(){}
    class stdClass{}
    const PHP_VERSION = '6.0.0';

そして、何故かごく一部の定数だけ例外があります。

namespace FOO;
    const NULL = 1; // Fatal error: Cannot redeclare constant NULL

NULL・TRUE・FALSE・ZEND_THREAD_SAFE・ZEND_DEBUG_BUILDの5定数だけは、名前空間内であろうとも定義することができません。
前三つはわかるとしても、後ろの二つはなんぞ?

あと言語構造などのキーワードは関数名として使えません。

namespace FOO;
    function print(){} // Parse error: syntax error, unexpected print
    function null(){} // こっちは使える

これに関する記載はユーザノートしか見当たらなかったんだけど、マニュアルにはどこかに記載はないのだろうか。

その他

別名前空間の変数を見たいときはどうすればいいんだっけ。

namespace FOO;
	const BAR = 1;
	$baz = 1;

namespace HOGE;
	echo constant('\FOO\BAR'); // 1
	echo \FOO\$vaz; // syntax error
  1. 正確にはルート相対パス

6
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?