名前解決のルール
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
名前解決のルールを説明するにあたって、いくつかの重要な定義を示しておきます。
-
名前空間名の定義
- 非修飾名
-
これは名前空間区切り文字を含まない識別子で、
Foo
のようなものです。 - 修飾名
-
これは名前空間区切り文字を含む識別子で、
Foo\Bar
のようなものです。 - 完全修飾名
-
これは名前空間区切り文字を含む識別子のうち先頭が名前空間区切り文字で始まるもので、
\Foo\Bar
のようなものです。名前空間\Foo
も完全修飾名です。 - 相対名
-
namespace\Foo\Bar
のように、namespace
で始まる識別子です。
名前解決は、これらの解決ルールによって行われます。
-
完全修飾名は、先頭の名前空間区切り文字を除いた名前に常に解決されます。
たとえば、
\A\B
はA\B
と解釈されます。 -
相対名は、
namespace
という名前を、 現在の名前空間に置き換えたものに常に解決されます。 現在の名前空間がグローバル名前空間だった場合、namespace\
名前空間は取り除かれます。 たとえば、名前空間X\Y
の中にあるnamespace\A
は、X\Y\A
に解決されます。 グローバル名前空間の中にある同じ名前は、A
に解決されます。 -
修飾名の場合は、名前の最初の識別子を、
現在のクラス/名前空間のインポートテーブルに従って翻訳します。
たとえば、名前空間
A\B\C
がC
としてインポートされた場合、C\D\E
という名前は、A\B\C\D\E
と翻訳されます。 -
修飾名の場合で、適用すべきインポートルールがない場合、
現在の名前空間が名前の先頭に付加されます。
たとえば、名前空間
A\B
の中にあるC\D\E
という名前はA\B\C\D\E
に解決されます。 -
非修飾名の場合、名前はそれぞれのシンボルタイプの
現在のインポートテーブルに従って翻訳されます。
これは、クラスのような名前は、クラス/名前空間のインポートテーブルに従って
翻訳されるし、関数名は、関数のインポートテーブルに従うし、
定数は定数のインポートテーブルに従うということになります。
たとえば、
use A\B\C;
の後に、new C()
のようなことをすると、 C はA\B\C()
に解決されます。 同じように、use function A\B\foo;
の後にfoo()
のようなことをすると、A\B\foo
という名前に解決されます。 -
非修飾名について、適用すべきインポートルールが存在せず、
名前がクラスのようなシンボルを参照している場合、
現在の名前空間が先頭に付加されます。
たとえば、名前空間
A\B
の内部にあるnew C()
は、A\B\C
という名前に解決されます。 -
非修飾名について、適用すべきインポートルールが存在せず、
名前が関数や定数を参照しており、
コードがグローバル名前空間の外に存在する場合は
名前は実行時に解決されます。
コードが名前空間
A\B
の中にあると仮定すると、 関数foo()
のコールは、次のように解決されます。-
まず現在の名前空間から関数
A\B\foo()
を探します。 -
次に グローバル 関数
foo()
を探します。
-
まず現在の名前空間から関数
例1 名前解決の例
<?php
namespace A;
use B\D, C\E as F;
// 関数のコール
foo(); // まず名前空間 "A" で定義されている "foo" のコールを試み、
// 次にグローバル関数 "foo" をコールします
\foo(); // グローバルスコープで定義されている関数 "foo" をコールします
my\foo(); // 名前空間 "A\my" で定義されている関数 "foo" をコールします
F(); // まず名前空間 "A" で定義されている "F" のコールを試み、
// 次にグローバル関数 "F" をコールします
// クラスの参照
new B(); // 名前空間 "A" で定義されているクラス "B" のオブジェクトを作成します
// 見つからない場合は、クラス "A\B" の autoload を試みます
new D(); // インポートルールを使用し、名前空間 "B" で定義されているクラス "D" のオブジェクトを作成します
// 見つからない場合は、クラス "B\D" の autoload を試みます
new F(); // インポートルールを使用し、名前空間 "C" で定義されているクラス "E" のオブジェクトを作成します
// 見つからない場合は、クラス "C\E" の autoload を試みます
new \B(); // グローバルスコープで定義されているクラス "B" のオブジェクトを作成します
// 見つからない場合は、クラス "B" の autoload を試みます
new \D(); // グローバルスコープで定義されているクラス "D" のオブジェクトを作成します
// 見つからない場合は、クラス "D" の autoload を試みます
new \F(); // グローバルスコープで定義されているクラス "F" のオブジェクトを作成します
// 見つからない場合は、クラス "F" の autoload を試みます
// 別の名前空間から使用するstaticメソッド/関数
B\foo(); // 名前空間 "A\B" の関数 "foo" をコールします
B::foo(); // 名前空間 "A" で定義されているクラス "B" のメソッド "foo" をコールします
// クラス "A\B" が見つからない場合はクラス "A\B" の autoload を試みます
D::foo(); // インポートルールを使用し、名前空間 "B" で定義されているクラス "D" のメソッド "foo" をコールします
// クラス "B\D" が見つからない場合はクラス "B\D" の autoload を試みます
\B\foo(); // 名前空間 "B" の関数 "foo" をコールします
\B::foo(); // グローバルスコープのクラス "B" のメソッド "foo" をコールします
// クラス "B" が見つからない場合はクラス "B" の autoload を試みます
// 現在の名前空間から使用するstaticメソッド/関数
A\B::foo(); // 名前空間 "A\A" のクラス "B" のメソッド "foo" をコールします
// クラス "A\A\B" が見つからない場合はクラス "A\A\B" の autoload を試みます
\A\B::foo(); // 名前空間 "A" のクラス "B" のメソッド "foo" をコールします
// クラス "A\B" が見つからない場合はクラス "A\B" の autoload を試みます
?>
+add a note
User Contributed Notes 7 notes
kdimi ¶
13 years ago
If you like to declare an __autoload function within a namespace or class, use the spl_autoload_register() function to register it and it will work fine.
rangel ¶
15 years ago
The term "autoload" mentioned here shall not be confused with __autoload function to autoload objects. Regarding the __autoload and namespaces' resolution I'd like to share the following experience:
->Say you have the following directory structure:
- root
| - loader.php
| - ns
| - foo.php
->foo.php
<?php
namespace ns;
class foo
{
public $say;
public function __construct()
{
$this->say = "bar";
}
}
?>
-> loader.php
<?php
//GLOBAL SPACE <--
function __autoload($c)
{
require_once $c . ".php";
}
class foo extends ns\foo // ns\foo is loaded here
{
public function __construct()
{
parent::__construct();
echo "<br />foo" . $this->say;
}
}
$a = new ns\foo(); // ns\foo also loads ns/foo.php just fine here.
echo $a->say; // prints bar as expected.
$b = new foo; // prints foobar just fine.
?>
If you keep your directory/file matching namespace/class consistence the object __autoload works fine.
But... if you try to give loader.php a namespace you'll obviously get fatal errors.
My sample is just 1 level dir, but I've tested with a very complex and deeper structure. Hope anybody finds this useful.
Cheers!
safakozpinar at NOSPAM dot gmail dot com ¶
13 years ago
As working with namespaces and using (custom or basic) autoload structure; magic function __autoload must be defined in global scope, not in a namespace, also not in another function or method.
<?php
namespace Glue {
/**
* Define your custom structure and algorithms
* for autoloading in this class.
*/
class Import
{
public static function load ($classname)
{
echo 'Autoloading class '.$classname."\n";
require_once $classname.'.php';
}
}
}
/**
* Define function __autoload in global namespace.
*/
namespace {
function __autoload ($classname)
{
\Glue\Import::load($classname);
}
}
?>
Kavoir.com ¶
10 years ago
For point 4, "In example, if the namespace A\B\C is imported as C" should be "In example, if the class A\B\C is imported as C".
llmll ¶
9 years ago
The mentioned filesystem analogy fails at an important point:
Namespace resolution *only* works at declaration time. The compiler fixates all namespace/class references as absolute paths, like creating absolute symlinks.
You can't expect relative symlinks, which should be evaluated during access -> during PHP runtime.
In other words, namespaces are evaluated like __CLASS__ or self:: at parse-time. What's *not* happening, is the pendant for late static binding like static:: which resolves to the current class at runtime.
So you can't do the following:
namespace Alpha;
class Helper {
public static $Value = "ALPHA";
}
class Base {
public static function Write() {
echo Helper::$Value;
}
}
namespace Beta;
class Helper extends \Alpha\Helper {
public static $Value = 'BETA';
}
class Base extends \Alpha\Base {}
\Beta\Base::Write(); // should write "BETA" as this is the executing namespace context at runtime.
If you copy the write() function into \Beta\Base it works as expected.
rangel ¶
15 years ago
The term "autoload" mentioned here shall not be confused with __autoload function to autoload objects. Regarding the __autoload and namespaces' resolution I'd like to share the following experience:
->Say you have the following directory structure:
- root
| - loader.php
| - ns
| - foo.php
->foo.php
<?php
namespace ns;
class foo
{
public $say;
public function __construct()
{
$this->say = "bar";
}
}
?>
-> loader.php
<?php
//GLOBAL SPACE <--
function __autoload($c)
{
require_once $c . ".php";
}
class foo extends ns\foo // ns\foo is loaded here
{
public function __construct()
{
parent::__construct();
echo "<br />foo" . $this->say;
}
}
$a = new ns\foo(); // ns\foo also loads ns/foo.php just fine here.
echo $a->say; // prints bar as expected.
$b = new foo; // prints foobar just fine.
?>
If you keep your directory/file matching namespace/class consistence the object __autoload works fine.
But... if you try to give loader.php a namespace you'll obviously get fatal errors.
My sample is just 1 level dir, but I've tested with a very complex and deeper structure. Hope anybody finds this useful.
Cheers!
anrdaemon at freemail dot ru ¶
8 years ago
Namespaces may be case-insensitive, but autoloaders most often do.
Do yourself a service, keep your cases consistent with file names, and don't overcomplicate autoloaders beyond necessity.
Something like this should suffice for most times:
<?php
namespace org\example;
function spl_autoload($className)
{
$file = new \SplFileInfo(__DIR__ . substr(strtr("$className.php", '\\', '/'), 11));
$path = $file->getRealPath();
if(empty($path))
{
return false;
}
else
{
return include_once $path;
}
}
\spl_autoload_register('\org\example\spl_autoload');
?>
![To Top](/images/to-top@2x.png)