一、概述
PHP的官方文档关于这命名空间讲得乱七八糟的,于是重新整理了一下。
从广义上来说,命名空间是一种封装事物的方法,PHP 命名空间提供了一种将相关的类、函数和常量组合到一起,并与其他的同类隔离的途径。
在PHP中,命名空间用来解决用户在创建可重用的代码如类或函数或常量时,发生的用户代码与PHP内部的类/函数/常量或与第三方的类/函数/常量之间的命名冲突(重复)的问题。其次,也提供了为很长的标识符名称(通常是为了缓解命名冲突问题)创建一个别名的功能,以提高源代码的可读性。
二、定义命名空间
A. 单个命名空间
命名空间通过关键字 namespace 来声明,以分号;结束。如果在一个文件中包含命名空间,那么在"<php?"标识符之前不得有任何内容(包括空格),而且必须在其它所有PHP代码之前声明命名空间,除了一个以外:declare关键字。
<?php
namespace MyName;
?>
PHP 命名空间也允许指定层次化的命名空间的名称。
<?php
namespace MyName\SubName;
?>
全局空间
<?php
namespace {
//code goes here
}
?>
虽然任意合法的PHP代码都可以包含在命名空间(同一个文件)中,但只有下列元素会受命名空间的影响:
- 类(包括抽象类和traits)
- 接口
- 函数
- 常量
其他代码语句如变量赋值、函数调用等,不管书写于哪个命名空间内,都作用于全局命名空间。
B. 多个命名空间
在同一个文件中定义多个命名空间,有两种方法可以做到,但在实际编程中,非常不提倡在同一个文件中定义多个命名空间。
- 第一种 顺序
<?php
namespace MyName1;
/* scope of MyName1 */
namespace MyName2;
/* scope of MyName2 */
?>
- 第二种 大括号(在大括号之外不应有任何代码)
<?php
namespace MyName1{
/* scope of MyName1 */
}
namespace MyName2{
/* scope of MyName2 */
}
namespace {
/* scope of global namespace
全局命名空间 */
}
namespace / {
/* Syntax ERROR */
}
?>
由此也可以引出命名空间作用域(scope)的概念,某个命名空间只在它的作用域以内有效。
命名空间最大的作用域只及于一个文件之内。
三、使用命名空间
A. 一般
在PHP中,可通过三种方式引用命名空间:
- 非限定名称
- 限定名称
- 绝对限定名称
如果以文件路径作类比的话,以上三者可分别对应:相对文件名,相对路径名,绝对路径名。
另外,关键字 namespace 可用来显式访问当前命名空间或子命名空间中的元素,它等价于类中的 self 操作符。
<?php
// file:/var/html/NS1.php
namespace MyName1\subName{
const NUM=10;
}
namespace { //全局空间
const NUM=0;
}
?>
<?php
// file:/var/html/NS2.php
namespace MyName1;
include("NS1.php"); //后续语句依然位于MyName1作用域内,不是MyName1\subName
const NUM=20;
echo NUM; //输出20,非限定名称引用,解析为\MyName1\NUM
echo subName\NUM; //输出10,限定名称引用,解析为\MyName1\SubName\NUM
echo \MyName1\SubName\NUM //输出10,绝对限定名称引用,解析为\MyName1\SubName\NUM
echo \NUM; //输出0,绝对限定名称引用,解析为\NUM
echo namespace\NUM; //输出20,namespace关键字引用,解析为\MyName1\NUM
?>
B. 别名/导入
允许通过别名引用或导入外部的完全限定名称,是命名空间的一个重要特征。这有点类似于在类 unix 文件系统中创建的对其它的文件的符号连接。
- 必须使用完全限定名称。
- 前导的反斜杠是不必要的也不推荐的,因为导入的名称必须是完全限定的,不会根据当前的命名空间作相对解析。
- 另外,use语句必须放在最外层。
<?php
namespace NS2;
include('NS1.php'); //不会继承NS1.php中的use语句
const NUM=20;
//导入命名空间
use MyName\SubName as SubName;
use MyName\SubName; //和上一句作用一样
//导入一个全局类
use ArrayObject;
//导入一个函数(需要PHP 5.6+)
use function MyName\SubName\functionName;
use function MyName\SubName\functionName as fun;
//导入一个常量(需要PHP 5.6+)
use const MyName\SubName\CONSTANT;
echo NUM; //输出20,解析为\NS2\NUM
$obj=new SubName\myClass; //解析为\MyName\SubName\myClass
SubName\functionName(); //解析为\MyName\SubName\functionName()
$var=new ArrayObject; //解析为\ArrayObject
fun(); //解析为\MyName\SubName\functionName()
?>
C. 解析优先级
- 对于命名空间中的类,如果使用非限定名称,那么解析后的类不存在时,会抛出异常错误。
- 对于命名空间中的函数和常量,如果使用非限定名称,那么解析后的结果不存在时,会尝试使用全局空间中的同名元素,最后才抛出异常错误。
四、PHP动态语言特性和命名空间
- 必须使用完全限定名称,因为字符串中不会做相对解析。
- 前导的反斜杠是非必要的。
<?php
// file:/var/html/NS1.php
function fun1(){ //全局空间
echo __FUNCTION__;
}
?>
<?php
// file:/var/html/NS2.php
namespace MyName;
include("NS1.php");
function fun1(){
echo __FUNCTION__;
}
$f='fun1';
$f(); //输出 fun1
$f='\MyName\fun1';
$f(); //输出 MyName\fun1
$f='MyName\fun1';
$f(); //输出 MyName\fun1
?>
参考 https://www.php.net/manual/zh/language.namespaces.php