PHP强化之23 - 命名空间
一、简介
什么是命名空间?从广义上来说,命名空间是一种封装事物的方法。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,目录就扮演了命名空间的角色。
而在PHP当中,命名空间就扮演了目录的角色,而PHP代码中的类(包括抽象类和traits)、接口、函数和常量则就扮演了文件的角色。
PHP 中的命名空间(namespace)是在PHP 5.3中加入的,主要是为了解决用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。其作用是按照一种虚拟的层次结构组织PHP代码,这种层次结构类似操作系统中文件系统的目录结构。现代的PHP组件和框架都放在各自全局唯一的厂商命名空间中,以免与其他厂商使用常见类名冲突。
二、命名空间的定义
默认情况下,所有常量、类和函数名都放在全局空间下,就和PHP支持命名空间之前一样。
1、声明命名空间
命名空间通过关键字namespace 来声明。如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间,除了一个以外:declare关键字。语法格式如下;
< ?php
// 定义代码在 'MyProject' 命名空间中
namespace MyProject;
另外,与PHP其它的语言特征不同,同一个命名空间可以定义在多个文件中,即允许将同一个命名空间的内容分割存放在不同的文件中。
你也可以在同一个文件中定义不同的命名空间代码,但非常不提倡这一种做法。
2、声明子命名空间
与目录和文件的关系很象,PHP 命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义:
<?php
namespace MyProject\Sub\Level;
三、命名空间的使用
在讨论如何使用命名空间之前,必须了解 PHP 是如何知道要使用哪一个命名空间中的元素的。可以将 PHP 命名空间与文件系统作一个简单的类比。在文件系统中访问一个文件有三种方式:
1)相对文件名形式如foo.txt。它会被解析为 currentdirectory/foo.txt,其中 currentdirectory 表示当前目录。因此如果当前目录是 /home/foo,则该文件名被解析为/home/foo/foo.txt。
2)相对路径名形式如subdirectory/foo.txt。它会被解析为 currentdirectory/subdirectory/foo.txt。
3)绝对路径名形式如/main/foo.txt。它会被解析为/main/foo.txt。
PHP 命名空间中的元素使用同样的原理。例如,类名可以通过三种方式引用:
1、Unqualified name(非限定名称)
名称中不包含命名空间分隔符的标识符。
例如:$a=new foo();
或foo::staticmethod();
。如果当前命名空间是currentnamespace,foo 将被解析为currentnamespace\foo
。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo
。
警告:对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量。
2、Qualified name(限制名称)
名称中含有命名空间分隔符的标识符。
例如:$a = new subnamespace\foo();
或subnamespace\foo::staticmethod();
。如果当前的命名空间是currentnamespace,则 foo 会被解析为currentnamespace\subnamespace\foo
。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo
。
3、Fully qualified name(完全限制名称)
名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如\Foo\Bar
。namespace\Foo
也是一个完全限定名称。
例如:$a = new \currentnamespace\foo();
或\currentnamespace\foo::staticmethod();
。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo
。
官方例子:
file1.php 文件代码:
namespace Foo\Bar\subnamespace;
const FOO = 1;
function foo(){}
class foo{
static function staticmethod(){}
}
file2.php文件代码:
namespace Foo\Bar;
include 'file1.php';
const FOO = 2;
function foo(){}
class foo{
static function staticmethod(){}
}
/* 非限定名称 */
foo(); // 解析为函数: Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo的静态方法staticmethod
echo FOO; // resolves to constant Foo\Bar\FOO
/* 限定名称 */
subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // 解析为类Foo\Bar\subnamespace\foo的方法staticmethod
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO
/* 完全限定名称 */
\Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // 解析为类 Foo\Bar\foo的方法 staticmethod
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO
注意:访问任意全局类、函数或常量,都可以使用完全限定名称,例如\strlen()
或\Exception
或\INI_ALL
。
四、使用命名空间:别名/导入
所有支持命名空间的PHP版本支持三种别名或导入方式:为类名称使用别名、为接口使用别名或为命名空间名称使用别名。PHP 5.6开始允许导入函数或常量或者为它们设置别名。
1、使用use操作符导入/使用别名
<?php
namespace foo;
use My\Full\Classname as Another;
// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;
// 导入一个全局类
use ArrayObject;
// 导入一个函数 importing a function (PHP 5.6+)
use function My\Full\functionName;
// 为一个函数设置别名 aliasing a function (PHP 5.6+)
use function My\Full\functionName as func;
// 导入一个常量 importing a constant (PHP 5.6+)
use const My\Full\CONSTANT;
$obj = new namespace\Another; // 实例化 foo\Another 对象
$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象
// 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象
func(); // calls function My\Full\functionName
echo CONSTANT; // echoes the value of My\Full\CONSTANT
?>
2、 一行中包含多个use语句
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
?>
为了代码更易于阅读和纠错,不推荐使用以上语法,建议一行写一个use语句。
五、注意事项
-
如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间。除了一个以外:declare关键字。
-
虽然任意合法的PHP代码都可以包含在命名空间中,但只有以下类型的代码受命名空间的影响,它们是:类(包括抽象类和traits)、接口、函数和常量。
-
PHP支持两种抽象的访问当前命名空间内部元素的方法,__NAMESPACE__魔术常量和namespace关键字。
常量__NAMESPACE__的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串。 -
关键字namespace可用来显式访问当前命名空间或子命名空间中的元素。它等价于类中的self操作符。
-
PHP命名空间与操作系统的物理文件系统不同,这是一个虚拟概念,没必要和文件系统中的目录结构完全对应。虽然如此,但是大多数PHP组件为了兼容广泛使用的PSR-4自动加载器标准,会把子命名空间放到文件系统的子目录中。
六、参考
-
《Modern PHP》Josh Lockhart 著 安道 译