名称解析规则

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

在说明名称解析规则之前,我们先看一些重要的定义:

命名空间名称定义
非限定名称(Unqualified name)

名称中不包含命名空间分隔符的标识符,例如 Foo

限定名称(Qualified name)

名称中含有命名空间分隔符的标识符,例如 Foo\Bar

完全限定名称(Fully qualified name)

名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如 \Foo\Barnamespace\Foo 也是一个完全限定名称。

相对名称(Relative name)

这是个以 namespace 开头的标识符, 例如 namespace\Foo\Bar

名称解析遵循下列规则:

  1. 完全限定名称总是会解析成没有前缀符号的命名空间名称。 \A\B 解析为 A\B
  2. 解析相对名称时,会用当前命名空间的名称替换掉 namespace。 如果名称出现在全局命名空间,会截掉 namespace\ 前缀。 例如,在命名空间 X\Y 里的 namespace\A 会被解析成 X\Y\A。 在全局命名空间里,同样的名字却被解析成 A
  3. 对于限定名称,名字的第一段会根据当前 class/namespace 导入表进行翻译。 比如命名空间 A\B\C 被导入为 C, 名称 C\D\E 会被翻译成 A\B\C\D\E
  4. 对于限定名称,如果没有应用导入规则,就将当前命名空间添加为名称的前缀。 例如,位于命名空间 A\B 内的名称 C\D\E 会解析成 A\B\C\D\E
  5. 根据符号类型和对应的当前导入表,解析非限定名称。 这也就是说,根据 class/namespace 导入表翻译类名称; 根据函数导入表翻译函数名称; 根据常量导入表翻译常量名称。 比如,在 use A\B\C; 后,类似 new C() 这样的名称会解析为 A\B\C()。 类似的,use function A\B\foo; 后, foo() 的用法,解析名称为 A\B\foo
  6. 如果没有应用导入规则,对于类似 class 符号的非限定名称,会添加当前命名空间作为前缀。 比如命名空间 A\B 内的 new C() 会把名称解析为 A\B\C
  7. 如果没有应用导入规则,非限定名称指向函数或常量,且代码位于全局命名空间之外,则会在运行时解析名称。 假设代码位于命名空间 A\B 中, 下面演示了调用函数 foo() 是如何解析的:
    1. 在当前命名空间中查找函数: A\B\foo()
    2. 它会尝试找到并调用 全局 的函数 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

up
37
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.
up
33
rangel
16 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<?phpnamespace 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!
up
5
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.<?phpnamespace 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);    }}?>
up
0
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".
up
-2
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.
up
-5
rangel
16 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<?phpnamespace 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!
up
-5
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:<?phpnamespace 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