名称解析规则
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
在说明名称解析规则之前,我们先看一些重要的定义:
- 命名空间名称定义
- 非限定名称(Unqualified name)
-
名称中不包含命名空间分隔符的标识符,例如
Foo
- 限定名称(Qualified name)
-
名称中含有命名空间分隔符的标识符,例如
Foo\Bar
- 完全限定名称(Fully qualified name)
-
名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如
\Foo\Bar
。namespace\Foo
也是一个完全限定名称。 - 相对名称(Relative name)
-
这是个以
namespace
开头的标识符, 例如namespace\Foo\Bar
。
名称解析遵循下列规则:
-
完全限定名称总是会解析成没有前缀符号的命名空间名称。
\A\B
解析为A\B
。 -
解析相对名称时,会用当前命名空间的名称替换掉
namespace
。 如果名称出现在全局命名空间,会截掉namespace\
前缀。 例如,在命名空间X\Y
里的namespace\A
会被解析成X\Y\A
。 在全局命名空间里,同样的名字却被解析成A
。 -
对于限定名称,名字的第一段会根据当前 class/namespace 导入表进行翻译。
比如命名空间
A\B\C
被导入为C
, 名称C\D\E
会被翻译成A\B\C\D\E
。 -
对于限定名称,如果没有应用导入规则,就将当前命名空间添加为名称的前缀。
例如,位于命名空间
A\B
内的名称C\D\E
会解析成A\B\C\D\E
。 -
根据符号类型和对应的当前导入表,解析非限定名称。
这也就是说,根据 class/namespace 导入表翻译类名称;
根据函数导入表翻译函数名称;
根据常量导入表翻译常量名称。
比如,在
use A\B\C;
后,类似new C()
这样的名称会解析为A\B\C()
。 类似的,use function A\B\foo;
后,foo()
的用法,解析名称为A\B\foo
。 -
如果没有应用导入规则,对于类似 class 符号的非限定名称,会添加当前命名空间作为前缀。
比如命名空间
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"
new D(); // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象
// 如果未找到,则尝试自动装载类 "B\D"
new F(); // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象
// 如果未找到,则尝试自动装载类 "C\E"
new \B(); // 创建定义在全局空间中的类 "B" 的一个对象
// 如果未发现,则尝试自动装载类 "B"
new \D(); // 创建定义在全局空间中的类 "D" 的一个对象
// 如果未发现,则尝试自动装载类 "D"
new \F(); // 创建定义在全局空间中的类 "F" 的一个对象
// 如果未发现,则尝试自动装载类 "F"
// 调用另一个命名空间中的静态方法或命名空间函数
B\foo(); // 调用命名空间 "A\B" 中函数 "foo"
B::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
// 如果未找到类 "A\B" ,则尝试自动装载类 "A\B"
D::foo(); // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法
// 如果类 "B\D" 未找到,则尝试自动装载类 "B\D"
\B\foo(); // 调用命名空间 "B" 中的函数 "foo"
\B::foo(); // 调用全局空间中的类 "B" 的 "foo" 方法
// 如果类 "B" 未找到,则尝试自动装载类 "B"
// 当前命名空间中的静态方法或函数
A\B::foo(); // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法
// 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B"
\A\B::foo(); // 调用命名空间 "A\B" 中定义的类 "B" 的 "foo" 方法
// 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
?>
+添加备注
用户贡献的备注 7 notes
kdimi ¶
14 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 ¶
14 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 ¶
11 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 ¶
10 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 ¶
9 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');
?>
备份地址:http://www.lvesu.com/blog/php/language.namespaces.rules.php