命名空间

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

在 PHP 中,命名空间是用来解决名字冲突的。

<?php
namespace my\name; // 参考 "定义命名空间" 小节

class MyClass {}
function myfunction() {}
const MYCONST = 1;

$a = new MyClass;
$c = new \my\name\MyClass; // 参考 "全局空间" 小节

$a = strlen('hi'); // 参考 "使用命名空间:后备全局函数/常量" 小节

$d = namespace\MYCONST; // 参考 "namespace操作符和__NAMESPACE__常量” 小节

$d = __NAMESPACE__ . '\MYCONST';
echo constant($d); // 参考 "命名空间和动态语言特征" 小节

定义命名空间

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

命名空间通过关键字 namespace 来声明,只作用于类(包括抽象类和 trait)、接口、函数和常量。

如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间,除了declare关键字。

示例 #1 声明单个命名空间

<?php
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的 declare 语句。

示例 #2 声明单个命名空间

<html>
<?php
namespace MyProject; // 致命错误 - 命名空间必须是程序脚本的第一条语句

与 PHP 其它的语言特征不同,同一个命名空间可以定义在多个文件中。


定义子命名空间

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

与目录和文件的关系很象,PHP 命名空间也允许指定层次化的命名空间的名称。

示例 #1 声明分层次的单个命名空间

<?php
namespace MyProject\Sub\Level;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

上面的例子创建了常量 MyProject\Sub\Level\CONNECT_OK,类 MyProject\Sub\Level\Connection 和函数 MyProject\Sub\Level\connect。


__NAMESPACE__ 常量

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

PHP支持使用__NAMESPACE__ 魔术常量访问当前命名空间内部元素。

示例 #1 __NAMESPACE__ 示例,在命名空间中的代码

<?php
namespace MyProject;

echo '"', __NAMESPACE__, '"'; // 输出 "MyProject"

使用命名空间:别名/导入

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

在PHP中,命名空间的别名和导入是通过操作符 use 来实现的。

<?php
namespace foo;
use My\Full\Classname as Another;

// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;

// 导入一个全局类
use ArrayObject;

全局空间

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

如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,在名称前加上前缀 \ 表示该名称是全局空间中的名称。

示例 #1 使用全局空间说明

<?php
namespace A\B\C;

/* 这个函数是 A\B\C\fopen */
function fopen() { 
     /* ... */
     $f = \fopen(...); // 调用全局的fopen函数
     return $f;
}

使用命名空间:后备全局函数/常量

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

在一个命名空间中,当 PHP 遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。

类名称总是解析到当前命名空间中的名称,因此在访问系统内部或不包含在命名空间中的类名称时,必须使用完全限定名称,例如:

示例 #1 在命名空间中访问全局类

<?php
namespace A\B\C;
class Exception extends \Exception {}
$a = new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象
$b = new \Exception('hi'); // $b 是类 Exception 的一个对象
$c = new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类

名称解析规则

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

名称解析遵循下列规则:

    1. 对完全限定名称的函数,类和常量的调用在编译时解析。例如 new \A\B 解析为类 A\B。

    2. 所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间 A\B\C 被导入为 C,那么对 C\D\e() 的调用就会被转换为 A\B\C\D\e()。

    3. 在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间 A\B 内部调用 C\D\e(),则 C\D\e() 会被转换为 A\B\C\D\e() 。

    4. 非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间 A\B\C 导入为C,则 new C() 被转换为 new A\B\C() 。

    5. 在命名空间内部(例如A\B),对非限定名称的函数调用是在运行时解析的。例如对函数 foo() 的调用是这样解析的:

        1) 在当前命名空间中查找名为 A\B\foo() 的函数

        2) 尝试查找并调用 全局(global) 空间中的函数 foo()。

    6. 在命名空间(例如A\B)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用 new C() 及 new D\E() 的解析过程: new C() 的解析:

        1) 在当前命名空间中查找A\B\C类。

        2) 尝试自动装载类A\B\C。

<?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"