FAQ: 名前空間について知っておくべきこと
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
この FAQ は 2 つに別れています。一般的な質問と、 深く理解するために有用な実装に関する質問です。
まずは一般的な質問。
- 名前空間を使わない場合、 何か注意すべきことはありますか?
- 名前空間内での内部クラスあるいはグローバルクラスの使用法は?
- 同じ名前空間にあるクラス、関数あるいは定数を使用する方法は?
-
\my\name
や\name
のような名前はどのように解決される? -
my\name
のような名前はどのように解決される? - 修飾されていない
name
のようなクラス名はどのように解決される? - 修飾されていない
name
のような関数名/定数名はどのように解決される?
また、名前空間の実装を理解するために有用な実装の詳細は次のとおりです。
- インポートした名前が同一ファイルで定義されているクラスと衝突してはいけない
- 名前空間のネストはできない
- 動的な名前空間名 (クォートした名前) ではバックスラッシュのエスケープが必要
- バックスラッシュを含む未定義な定数を参照すると、致命的なエラーが発生する
- 特別な定数 NULL, TRUE, FALSE, ZEND_THREAD_SAFE そして ZEND_DEBUG_BUILD は上書きできない
名前空間を使わない場合、何か注意すべきことはありますか?
いいえ。これまで書いてきたコード、今後書く名前空間を含まないコードのいずれについても、 名前空間が何らかの影響を及ぼすことはありません。 お望みなら名前空間を使わないコードを書くこともできます。
例1 名前空間の外部にあるグローバルクラスへのアクセス
<?php$a = new \stdClass;?>
これは、機能的に次と同等です。
例2 名前空間の外部にあるグローバルクラスへのアクセス
<?php$a = new stdClass;?>
名前空間内での内部クラスあるいはグローバルクラスの使用法は?
例3 名前空間内からの内部クラスへのアクセス
<?phpnamespace foo;$a = new \stdClass;function test(\ArrayObject $typehintexample = null) {}$a = \DirectoryIterator::CURRENT_AS_FILEINFO;// 内部クラス/グローバルクラスの継承class MyException extends \Exception {}?>
同じ名前空間にあるクラス、関数あるいは定数を使用する方法は?
例4 名前空間内のクラス、関数あるいは定数へのアクセス
<?phpnamespace foo;class MyClass {}// 現在の名前空間のクラスをタイプヒントに使用する方法function test(MyClass $typehintexample = null) {}// 現在の名前空間のクラスをタイプヒントに使用するもうひとつの方法function test(\foo\MyClass $typehintexample = null) {}// 現在の名前空間のクラスの継承class Extended extends MyClass {}// グローバル関数へのアクセス$a = \globalfunc();// グローバル定数へのアクセス$b = \INI_ALL;?>
=== \my\name
や \name
のような名前はどのように解決される? ===
\
から始まる名前は常に見た目のままに解釈されます。
つまり \my\name
は my\name
であり、
\Exception
は Exception
となります。
例5 完全修飾名
<?phpnamespace foo;$a = new \my\name(); // "my\name" クラスのインスタンスを作成しますecho \strlen('hi'); // "strlen" 関数をコールします$a = \INI_ALL; // $a に定数 "INI_ALL" の値を設定します?>
my\name
のような名前はどのように解決される?
名前にバックスラッシュを含むが先頭はバックスラッシュでない名前、たとえば
my\name
のような名前は 2 通りの方法で解釈されます。
別の名前に my
というエイリアスを指定する import
文がある場合は、そのエイリアスが my\name
の my
部分に適用されます。
それ以外の場合は、現在の名前空間が my\name
の先頭に付け加えられます。
例6 修飾名
<?phpnamespace foo;use blah\blah as foo;$a = new my\name(); // "foo\my\name" クラスのインスタンスを作成しますfoo\bar::name(); // "blah\blah\bar" の静的メソッド "name" をコールしますmy\bar(); // "foo\my\bar" 関数をコールします$a = my\BAR; // $a に定数 "foo\my\BAR" の値を設定します?>
修飾されていない name
のようなクラス名はどのように解決される?
バックスラッシュを含まない name
のようなクラス名は
2 通りの方法で解釈されます。
別の名前に name
というエイリアスを指定する import
文がある場合は、そのエイリアスが適用されます。
それ以外の場合は、現在の名前空間が name
の先頭に付け加えられます。
例7 非修飾クラス名
<?phpnamespace foo;use blah\blah as foo;$a = new name(); // "foo\name" クラスのインスタンスを作成しますfoo::name(); // "blah\blah" クラスの静的メソッド "name" をコールします?>
修飾されていない name
のような関数名/定数名はどのように解決される?
バックスラッシュを含まない name
のような関数名/定数名は
2 通りの方法で解釈されます。
まず、現在の名前空間が name
の先頭に付け加えられます。
現在の名前空間に name
という関数あるいは定数がない場合は、
グローバル関数あるいは定数に name
があればそれを使用します。
例8 非修飾関数/定数名
<?phpnamespace foo;use blah\blah as foo;const FOO = 1;function my() {}function foo() {}function sort(&$a){ \sort($a); // グローバル関数 "sort" をコールします $a = array_flip($a); return $a;}my(); // "foo\my" をコールします$a = strlen('hi'); // "foo\strlen" が存在しないので、グローバル関数 "strlen" をコールします$arr = array(1,3,2);$b = sort($arr); // "foo\sort" 関数をコールします$c = foo(); // calls function "foo\foo" - import is not applied$a = FOO; // sets $a to value of constant "foo\FOO" - import is not applied$b = INI_ALL; // sets $b to value of global constant "INI_ALL"?>
インポートした名前が同一ファイルで定義されているクラスと衝突してはいけない
次のようなスクリプトの組み合わせは、正当なものです。
file1.php
<?phpnamespace my\stuff;class MyClass {}?>
another.php
<?phpnamespace another;class thing {}?>
file2.php
<?phpnamespace my\stuff;include 'file1.php';include 'another.php';use another\thing as MyClass;$a = new MyClass; // 名前空間 another のクラス "thing" のインスタンスを作成します。?>
MyClass
クラスが名前空間
my\stuff
にあるとはいえ、名前の衝突はありません。
MyClass の定義は別のファイルにあるからです。
しかし、次の例は名前の衝突による致命的なエラーとなります。
MyClass の定義が同じファイル上の use 文で行われているからです。
<?phpnamespace my\stuff;use another\thing as MyClass;class MyClass {} // fatal error: MyClass conflicts with import statement$a = new MyClass;?>
名前空間のネストはできない
PHP では名前空間のネストはできません。
<?phpnamespace my\stuff { namespace nested { class foo {} }}?>
しかし、それっぽいことをするのは簡単です。次のようにすればいいのです。
<?phpnamespace my\stuff\nested { class foo {}}?>
動的な名前空間名 (クォートした名前) ではバックスラッシュのエスケープが必要
バックスラッシュは文字列のエスケープ文字として使われることに注意しましょう。 文字列の中で使う際にはバックスラッシュを二重に書く必要があります。 そうしないと、予期せぬ結果を引き起こしてしまいます。
例9 ダブルクォートで囲んだ文字列内で名前空間名を扱う際の危険
<?php$a = "dangerous\name"; // ダブルクォートの中では \n が改行文字になってしまいます!$obj = new $a;$a = 'not\at\all\dangerous'; // これなら大丈夫です$obj = new $a;?>
シングルクォートで囲んだ文字列内では、 バックスラッシュによるエスケープシーケンスをより安全に使うことができます。 しかし、文字列内では常にバックスラッシュをエスケープする習慣をつけておくことをお勧めします。
バックスラッシュを含む未定義な定数を参照すると、致命的なエラーが発生する
未定義の定数のうち FOO
のような修飾されていないものは、
PHP が FOO
を定数の値と解釈したという notice が発生します。
修飾あるいは完全修飾形式の定数、つまりバックスラッシュを含む定数の場合、
それが未定義なら致命的なエラーが発生します。
例10 未定義の定数
<?phpnamespace bar;$a = FOO; // notice - undefined constants "FOO" assumed "FOO";$a = \FOO; // fatal error, undefined namespace constant FOO$a = Bar\FOO; // fatal error, undefined namespace constant bar\Bar\FOO$a = \Bar\FOO; // fatal error, undefined namespace constant Bar\FOO?>
特別な定数 NULL, TRUE, FALSE, ZEND_THREAD_SAFE そして ZEND_DEBUG_BUILD は上書きできない
名前空間内で特別な組み込み定数を定義しようとすると、致命的なエラーが発生します。
例11 未定義の定数
<?phpnamespace bar;const NULL = 0; // 致命的なエラーconst true = 'stupid'; // これも、致命的なエラー// etc.?>