FAQ: 名前空間について知っておくべきこと
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
この FAQ は 2 つに別れています。一般的な質問と、 深く理解するために有用な実装に関する質問です。
まずは一般的な質問。
- 名前空間を使わない場合、 何か注意すべきことはありますか?
- 名前空間内での内部クラスあるいはグローバルクラスの使用法は?
- 同じ名前空間にあるクラス、関数あるいは定数を使用する方法は?
-
\my\name
や\name
のような名前はどのように解決される? -
my\name
のような名前はどのように解決される? -
修飾されていない
name
のようなクラス名はどのように解決される? -
修飾されていない
name
のような関数名/定数名はどのように解決される?
また、名前空間の実装を理解するために有用な実装の詳細は次のとおりです。
- インポートした名前が同一ファイルで定義されているクラスと衝突してはいけない
- 名前空間のネストはできない
- 動的な名前空間名 (クォートした名前) ではバックスラッシュのエスケープが必要
- バックスラッシュを含む未定義な定数を参照すると、致命的なエラーが発生する
-
特別な定数
null
,true
,false
は上書きできない
名前空間を使わない場合、何か注意すべきことはありますか?
いいえ。これまで書いてきたコード、今後書く名前空間を含まないコードのいずれについても、 名前空間が何らかの影響を及ぼすことはありません。 お望みなら名前空間を使わないコードを書くこともできます。
例1 名前空間の外部にあるグローバルクラスへのアクセス
<?php
$a = new \stdClass;
?>
これは、機能的に次と同等です。
例2 名前空間の外部にあるグローバルクラスへのアクセス
<?php
$a = new stdClass;
?>
名前空間内での内部クラスあるいはグローバルクラスの使用法は?
例3 名前空間内からの内部クラスへのアクセス
<?php
namespace foo;
$a = new \stdClass;
function test(\ArrayObject $parameter_type_example = null) {}
$a = \DirectoryIterator::CURRENT_AS_FILEINFO;
// 内部クラス/グローバルクラスの継承
class MyException extends \Exception {}
?>
同じ名前空間にあるクラス、関数あるいは定数を使用する方法は?
例4 名前空間内のクラス、関数あるいは定数へのアクセス
<?php
namespace foo;
class MyClass {}
// 現在の名前空間のクラスを引数の型として使う
function test(MyClass $parameter_type_example = null) {}
// 現在の名前空間のクラスを引数の型として使うもうひとつの方法
function test(\foo\MyClass $parameter_type_example = null) {}
// 現在の名前空間のクラスの継承
class Extended extends MyClass {}
// グローバル関数へのアクセス
$a = \globalfunc();
// グローバル定数へのアクセス
$b = \INI_ALL;
?>
\my\name
や \name
のような名前はどのように解決される?
\
から始まる名前は常に見た目のままに解釈されます。
つまり \my\name
は my\name
であり、
\Exception
は Exception
となります。
例5 完全修飾名
<?php
namespace 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 修飾名
<?php
namespace foo;
use blah\blah as foo;
$a = new my\name(); // "foo\my\name" クラスのインスタンスを作成します
foo\bar::name(); // "blah\blah\bar" のstaticメソッド "name" をコールします
my\bar(); // "foo\my\bar" 関数をコールします
$a = my\BAR; // $a に定数 "foo\my\BAR" の値を設定します
?>
修飾されていない name
のようなクラス名はどのように解決される?
バックスラッシュを含まない name
のようなクラス名は
2 通りの方法で解釈されます。
別の名前に name
というエイリアスを指定する import
文がある場合は、そのエイリアスが適用されます。
それ以外の場合は、現在の名前空間が name
の先頭に付け加えられます。
例7 非修飾クラス名
<?php
namespace foo;
use blah\blah as foo;
$a = new name(); // "foo\name" クラスのインスタンスを作成します
foo::name(); // "blah\blah" クラスのstaticメソッド "name" をコールします
?>
修飾されていない name
のような関数名/定数名はどのように解決される?
バックスラッシュを含まない name
のような関数名/定数名は
2 通りの方法で解釈されます。
まず、現在の名前空間が name
の先頭に付け加えられます。
現在の名前空間に name
という関数あるいは定数がない場合は、
グローバル関数あるいは定数に name
があればそれを使用します。
例8 非修飾関数/定数名
<?php
namespace 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
<?php
namespace my\stuff;
class MyClass {}
?>
another.php
<?php
namespace another;
class thing {}
?>
file2.php
<?php
namespace my\stuff;
include 'file1.php';
include 'another.php';
use another\thing as MyClass;
$a = new MyClass; // 名前空間 another のクラス "thing" のインスタンスを作成します。
?>
MyClass
クラスが名前空間
my\stuff
にあるとはいえ、名前の衝突はありません。
MyClass の定義は別のファイルにあるからです。
しかし、次の例は名前の衝突による致命的なエラーとなります。
MyClass の定義が同じファイル上の use 文で行われているからです。
<?php
namespace my\stuff;
use another\thing as MyClass;
class MyClass {} // fatal error: MyClass conflicts with import statement
$a = new MyClass;
?>
名前空間のネストはできない
PHP では名前空間のネストはできません。
<?php
namespace my\stuff {
namespace nested {
class foo {}
}
}
?>
<?php
namespace 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 未定義の定数
<?php
namespace 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
?>
User Contributed Notes 6 notes
There is a way to define a namespaced constant that is a special, built-in constant, using define function and setting the third parameter case_insensitive to false:
<?php
namespace foo;
define(__NAMESPACE__ . '\NULL', 10); // defines the constant NULL in the current namespace
var_dump(NULL); // will show 10
var_dump(null); // will show NULL
?>
No need to specify the namespace in your call to define(), like it happens usually
<?php
namespace foo;
define(INI_ALL, 'bar'); // produces notice - Constant INI_ALL already defined. But:
define(__NAMESPACE__ . '\INI_ALL', 'bar'); // defines the constant INI_ALL in the current namespace
var_dump(INI_ALL); // will show string(3)"bar". Nothing unespected so far. But:
define('NULL', 10); // defines the constant NULL in the current namespace...
var_dump(NULL); // will show 10
var_dump(null); // will show NULL
?>
If the parameter case_insensitive is set to true
<?php
namespace foo;
define (__NAMESPACE__ . '\NULL', 10, true); // produces notice - Constant null already defined
?>
When creating classes or calling static methods from within namespaces using variables, you need to keep in mind that they require the full namespace in order for the appropriate class to be used; you CANNOT use an alias or short name, even if it is called within the same namespace. Neglecting to take this into account can cause your code to use the wrong class, throw a fatal missing class exception, or throw errors or warnings.
In these cases, you can use the magic constant __NAMESPACE__, or specify the full namespace and class name directly. The function class_exists also requires the full namespace and class name, and can be used to ensure that a fatal error won't be thrown due to missing classes.
<?php
namespace Foo;
class Bar {
public static function test() {
return get_called_class();
}
}
namespace Foo\Foo;
class Bar extends \Foo\Bar {
}
var_dump( Bar::test() ); // string(11) "Foo\Foo\Bar"
$bar = 'Foo\Bar';
var_dump( $bar::test() ); // string(7) "Foo\Bar"
$bar = __NAMESPACE__ . '\Bar';
var_dump( $bar::test() ); // string(11) "Foo\Foo\Bar"
$bar = 'Bar';
var_dump( $bar::test() ); // FATAL ERROR: Class 'Bar' not found or Incorrect class \Bar used
Just like class names currently namespaces are not case sensitive. So no errors will be shown here:
<?php declare(strict_types=1);
namespace Foo;
class Bar {
public function __construct() {
echo 'Map constructed';
}
}
$foobar = new \foo\bar();
[Editor's note: that behavior is caused by a bug in PHP 7.0, which has been fixed as of PHP 7.0.7.]
Regarding the entry "Import names cannot conflict with classes defined in the same file".
- I found that since PHP 7.0 this is no longer the case.
In PHP 7.0 you can have a class with a name that matches an imported class (or namespace or both at the same time).
<?php
namespace ns1 {
class ns1 {
public static function write() {
echo "ns1\\ns1::write()\n";
}
}
}
namespace ns1\ns1 {
class ns1c {
public static function write() {
echo "ns1\\ns1\\ns1c::write()\n";
}
}
}
namespace ns2 {
use ns1\ns1 as ns1; // both a class in ns1, and a namespace ns1\ns1
// the next class causes fatal error in php 5.6, not in 7.0
class ns1 {
public static function write() {
echo "ns2\\ns1::write()\n";
}
}
ns1::write(); // calls imported ns1\ns1::write()
ns1\ns1c::write(); // calls imported ns1\ns1\ns1c::write()
namespace\ns1::write(); // calls ns2\ns1::write()
}
?>
Regarding "Neither functions nor constants can be imported via the use statement." Actually you can do it in PHP 5.6+:
<?php
// importing a function (PHP 5.6+)
use function My\Full\functionName;
// aliasing a function (PHP 5.6+)
use function My\Full\functionName as func;
// importing a constant (PHP 5.6+)
use const My\Full\CONSTANT;
?>