名称解析规则
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
在说明名称解析规则之前,我们先看一些重要的定义:
-
命名空间名称定义
- 非限定名称 Unqualified name
-
名称中不包含命名空间分隔符的标识符,例如
Foo
- 限定名称 Qualified name
-
名称中含有命名空间分隔符的标识符,例如
Foo\Bar
- 完全限定名称 Fully qualified name
-
名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如
\Foo\Bar
。namespace\Foo
也是一个完全限定名称。
名称解析遵循下列规则:
-
对完全限定名称的函数,类和常量的调用在编译时解析。例如
new \A\B
解析为类A\B
。 -
所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间
A\B\C
被导入为C
,那么对C\D\e()
的调用就会被转换为A\B\C\D\e()
。 -
在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间
A\B
内部调用C\D\e()
,则C\D\e()
会被转换为A\B\C\D\e()
。 -
非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间
A\B\C
导入为C,则new C()
被转换为new A\B\C()
。 -
在命名空间内部(例如A\B),对非限定名称的函数调用是在运行时解析的。例如对函数
foo()
的调用是这样解析的:-
在当前命名空间中查找名为
A\B\foo()
的函数 -
尝试查找并调用 全局(global) 空间中的函数
foo()
。
-
在当前命名空间中查找名为
-
在命名空间(例如
A\B
)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用new C()
及new D\E()
的解析过程:new C()
的解析:-
在当前命名空间中查找
A\B\C
类。 -
尝试自动装载类
A\B\C
。
new D\E()
的解析:-
在类名称前面加上当前命名空间名称变成:
A\B\D\E
,然后查找该类。 -
尝试自动装载类
A\B\D\E
。
new \C()
。 -
在当前命名空间中查找
示例 #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"
?>