PHP 命名空间
PHP 命名空间(namespace)是在 PHP 5.3 中加入的。如果你学过C#和Java,那么命名空间就不是什么新鲜事了。但是,它在 PHP 中仍然具有非常重要的意义。
PHP 命名空间可以解决两类问题:
用户编写的代码与 PHP 内部类/函数/常量或第三方类/函数/常量之间的名称冲突。为非常长的标识符名称(通常定义为缓解第一类问题)创建别名(或简称),提高源代码的可读性。定义命名空间
默认情况下,所有常量、类和函数名称都放在全局空间中,就像之前 PHP 支持的名称空间一样。
命名空间是用关键字命名空间声明的。如果文件包含名称空间,则它必须在所有其他代码之前声明名称空间。语法格式如下;
<?php // 定义代码在 'MyProject' 命名空间中 namespace MyProject; // ... 代码 ...
也可以在同一个文件中定义不同的命名空间代码,例如:
<?php namespace MyProject; const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } namespace AnotherProject; const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } ?>
不建议使用此语法在单个文件中定义多个名称空间。建议使用以下花括号语法。
<?php namespace MyProject { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace AnotherProject { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } } ?>
要将全局非命名空间中的代码与命名空间中的代码组合在一起,请仅使用花括号形式的语法。全局代码必须用没有名称的名称空间语句括在花括号中,例如:
<?php namespace MyProject { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace { // 全局代码 session_start(); $a = MyProjectconnect(); echo MyProjectConnection::start(); } ?>
声明命名空间之前唯一合法的代码是定义源文件编码方式的声明语句。包括空格在内的所有非 PHP 代码不得出现在名称空间声明之前。
<?php declare(encoding='UTF-8'); //定义多个命名空间和不包含在命名空间中的代码 namespace MyProject { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace { // 全局代码 session_start(); $a = MyProjectconnect(); echo MyProjectConnection::start(); } ?>
以下代码产生语法错误:
<?php namespace MyProject; // 命名空间前出现了“” 会致命错误 - 命名空间必须是程序脚本的第一条语句 ?>
子命名空间
与目录和文件之间的关系非常相似,PHP 命名空间也允许指定分层命名空间名称。因此,命名空间名称可以以分层方式定义:
<?php namespace MyProjectSubLevel; //声明分层次的单个命名空间 const CONNECT_OK = 1; class Connection { /* ... */ } function Connect() { /* ... */ } ?>
上面的示例创建了常量 MyProjectSubLevelCONNECT_OK、类 MyProjectSubLevelConnection 和函数 MyProjectSubLevelConnect。
命名空间使用
PHP 命名空间中的类名可以通过三种方式引用:
一个不合格的名字,或者一个没有前缀的类名,比如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespacefoo。如果使用 foo 的代码是全局的,代码不包含在任何命名空间中,那么 foo 将被解析为 foo. 警告:如果命名空间中的函数或常量未定义php网站空间,则未限定的函数或常量名称将解析为全局函数或常量名称。
限定名称,或包含前缀的名称,例如 $a = new subnamespacefoo(); 或子命名空间foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespacesubnamespacefoo。如果使用 foo 的代码是全局的,代码不包含在任何命名空间中,foo 将被解析为 subnamespacefoo。
完全限定名称,或包含全局前缀运算符的名称,例如 $a = new currentnamespacefoo(); 或 currentnamespacefoo::staticmethod();. 这种情况下,foo在代码中总是被解析为字面名称(literal name)currentnamespacefoo。
这是使用所有三种方法的示例:
file1.php文件代码
<?php namespace FooBarsubnamespace; const FOO = 1; function foo() {} class foo { static function staticmethod() {} } ?>
file2.php文件代码
<?php namespace FooBar; include 'file1.php'; const FOO = 2; function foo() {} class foo { static function staticmethod() {} } /* 非限定名称 */ foo(); // 解析为函数 FooBarfoo foo::staticmethod(); // 解析为类 FooBarfoo ,方法为 staticmethod echo FOO; // 解析为常量 FooBarFOO /* 限定名称 */ subnamespacefoo(); // 解析为函数 FooBarsubnamespacefoo subnamespacefoo::staticmethod(); // 解析为类 FooBarsubnamespacefoo, // 以及类的方法 staticmethod echo subnamespaceFOO; // 解析为常量 FooBarsubnamespaceFOO /* 完全限定名称 */ FooBarfoo(); // 解析为函数 FooBarfoo FooBarfoo::staticmethod(); // 解析为类 FooBarfoo, 以及类的方法 staticmethod echo FooBarFOO; // 解析为常量 FooBarFOO ?>
请注意,要访问任何全局类、函数或常量,您可以使用完全限定名称,例如 strlen() 或 Exception 或 INI_ALL。
访问命名空间内的全局类、函数和常量:
<?php namespace Foo; function strlen() {} const INI_ALL = 3; class Exception {} $a = strlen('hi'); // 调用全局函数strlen $b = INI_ALL; // 访问全局常量 INI_ALL $c = new Exception('error'); // 实例化全局类 Exception ?>
命名空间和动态语言特性
PHP 命名空间的实现受语言本身的动态特性的影响。因此,如果您要将下面的代码转换为名称空间,动态访问元素。
example1.php文件代码:
<?php class classname { function __construct() { echo __METHOD__,"n"; } } function funcname() { echo __FUNCTION__,"n"; } const constname = "global"; $a = 'classname'; $obj = new $a; // prints classname::__construct $b = 'funcname'; $b(); // prints funcname echo constant('constname'), "n"; // prints global ?>
必须使用完全限定名称(包括名称空间前缀的类名)。请注意,前导反斜杠是不必要的,因为在动态类名、函数名或常量名中,限定名和完全限定名之间没有区别。
动态访问命名空间元素
<?php namespace namespacename; class classname { function __construct() { echo __METHOD__,"n"; } } function funcname() { echo __FUNCTION__,"n"; } const constname = "namespaced"; include 'example1.php'; $a = 'classname'; $obj = new $a; // 输出 classname::__construct $b = 'funcname'; $b(); // 输出函数名 echo constant('constname'), "n"; // 输出 global /* 如果使用双引号,使用方法为 "\namespacename\classname"*/ $a = 'namespacenameclassname'; $obj = new $a; // 输出 namespacenameclassname::__construct $a = 'namespacenameclassname'; $obj = new $a; // 输出 namespacenameclassname::__construct $b = 'namespacenamefuncname'; $b(); // 输出 namespacenamefuncname $b = 'namespacenamefuncname'; $b(); // 输出 namespacenamefuncname echo constant('namespacenameconstname'), "n"; // 输出 namespaced echo constant('namespacenameconstname'), "n"; // 输出 namespaced ?>
命名空间关键字和 __NAMESPACE__ 常量
PHP 支持两种访问当前命名空间内元素的抽象方法,__NAMESPACE__ 魔法常量和 namespace 关键字。
常量 __NAMESPACE__ 的值是一个包含当前命名空间名称的字符串。在全局代码中,不包含在任何名称空间中,它包含一个空字符串。
__NAMESPACE__ 示例,命名空间中的代码
<?php namespace MyProject; echo '"', __NAMESPACE__, '"'; // 输出 "MyProject" ?>
__NAMESPACE__ 示例,全局代码
<?php echo '"', __NAMESPACE__, '"'; // 输出 "" ?>
常量 __NAMESPACE__ 在动态创建名称时很有用,例如:
使用 __NAMESPACE__ 动态创建名称
<?php namespace MyProject; function get($classname) { $a = __NAMESPACE__ . '\' . $classname; return new $a; } ?>
关键字 namespace 可用于显式访问当前命名空间或子命名空间中的元素。它相当于类中的 self 运算符。
命名空间运算符php网站空间,命名空间中的代码
<?php namespace MyProject; use blahblah as mine; // 引入了 blahblah 命名空间,并定义了个别名mine minemine(); // 调用函数 blahblahmine() namespaceblahmine(); // 调用函数 MyProjectblahmine() namespacefunc(); // 调用函数 MyProjectfunc() namespacesubfunc(); // 调用函数 MyProjectsubfunc() namespacecname::method(); // 调用 MyProjectcname 类的静态方法 $a = new namespacesubcname(); // 实例化 MyProjectsubcname 类的对象 $b = namespaceCONSTANT; // 将常量 MyProjectCONSTANT 的值赋给 $b ?>
命名空间运算符,全局代码
<?php namespacefunc(); // calls function func() namespacesubfunc(); // calls function subfunc() namespacecname::method(); // calls static method "method" of class cname $a = new namespacesubcname(); // instantiates object of class subcname $b = namespaceCONSTANT; // assigns value of constant CONSTANT to $b ?>
使用命名空间:别名/导入
PHP 命名空间支持有两种使用别名或导入的方式:对类名使用别名,或对命名空间名称使用别名。
在 PHP 中,别名是使用 use 运算符实现的。这是一个使用所有三种可能导入的示例:
1.使用use运算符导入/使用别名
<?php namespace foo; use MyFullClassname as Another; // 下面的例子与 use MyFullNSname as NSname 相同 use MyFullNSname; // 导入一个全局类 use ArrayObject; $obj = new namespaceAnother; // 实例化 fooAnother 对象 $obj = new Another; // 实例化 MyFullClassname 对象 NSnamesubnsfunc(); // 调用函数 MyFullNSnamesubnsfunc $a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象 // 如果不使用 "use ArrayObject" ,则实例化一个 fooArrayObject 对象 ?>
2.一行包含多个use语句
<?php use MyFullClassname as Another, MyFullNSname; $obj = new Another; // 实例化 MyFullClassname 对象 NSnamesubnsfunc(); // 调用函数 MyFullNSnamesubnsfunc ?>
导入操作在编译时执行,但动态类名、函数名或常量名不是。
3.导入和动态名称
<?php use MyFullClassname as Another, MyFullNSname; $obj = new Another; // 实例化一个 MyFullClassname 对象 $a = 'Another'; $obj = new $a; // 实际化一个 Another 对象 ?>
此外,导入操作仅影响非限定名称和限定名称。完全限定名称不受导入影响,因为它们是确定性的。
4.导入和完全合格的名称
<?php use MyFullClassname as Another, MyFullNSname; $obj = new Another; // 实例化 MyFullClassname 类 $obj = new Another; // 实例化 Another 类 $obj = new Anotherthing; // 实例化 MyFullClassnamething 类 $obj = new Anotherthing; // 实例化 Anotherthing 类 ?>
使用命名空间:后备全局函数/常量
在命名空间内,当 PHP 遇到不合格的类、函数或常量名称时,它会使用不同的优先级策略来解析名称。类名总是解析为当前命名空间中的名称。因此在访问系统内部或不包含在命名空间中的类名时,必须使用完全限定名,例如:
1.访问命名空间中的全局类
<?php namespace ABC; class Exception extends Exception {} $a = new Exception('hi'); // $a 是类 ABCException 的一个对象 $b = new Exception('hi'); // $b 是类 Exception 的一个对象 $c = new ArrayObject; // 致命错误, 找不到 ABCArrayObject 类 ?>
对于函数和常量,如果函数或常量在当前命名空间中不存在,PHP 将退回到使用全局空间中的函数或常量。
2.备份命名空间中的全局函数/常量
<?php namespace ABC; const E_ERROR = 45; function strlen($str) { return strlen($str) - 1; } echo E_ERROR, "n"; // 输出 "45" echo INI_ALL, "n"; // 输出 "7" - 使用全局常量 INI_ALL echo strlen('hi'), "n"; // 输出 "1" if (is_array('hi')) { // 输出 "is not array" echo "is arrayn"; } else { echo "is not arrayn"; } ?>
全球空间
如果没有定义命名空间,所有的类和函数定义都在全局空间,和之前PHP引入命名空间的概念一样。在名称前加上 表示该名称是全局空间中的名称,即使该名称在另一个名称空间中也是如此。
使用全局空间规范
<?php namespace ABC; /* 这个函数是 ABCfopen */ function fopen() { /* ... */ $f = fopen(...); // 调用全局的fopen函数 return $f; } ?>
命名空间的顺序
既然有了命名空间,那么最容易出错的就是在使用一个类的时候,这个类的搜索路径是什么。
<?php namespace A; use BD, CE as F; // 函数调用 foo(); // 首先尝试调用定义在命名空间"A"中的函数foo() // 再尝试调用全局函数 "foo" foo(); // 调用全局空间函数 "foo" myfoo(); // 调用定义在命名空间"Amy"中函数 "foo" F(); // 首先尝试调用定义在命名空间"A"中的函数 "F" // 再尝试调用全局函数 "F" // 类引用 new B(); // 创建命名空间 "A" 中定义的类 "B" 的一个对象 // 如果未找到,则尝试自动装载类 "AB" new D(); // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象 // 如果未找到,则尝试自动装载类 "BD" new F(); // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象 // 如果未找到,则尝试自动装载类 "CE" new B(); // 创建定义在全局空间中的类 "B" 的一个对象 // 如果未发现,则尝试自动装载类 "B" new D(); // 创建定义在全局空间中的类 "D" 的一个对象 // 如果未发现,则尝试自动装载类 "D" new F(); // 创建定义在全局空间中的类 "F" 的一个对象 // 如果未发现,则尝试自动装载类 "F" // 调用另一个命名空间中的静态方法或命名空间函数 Bfoo(); // 调用命名空间 "AB" 中函数 "foo" B::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法 // 如果未找到类 "AB" ,则尝试自动装载类 "AB" D::foo(); // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法 // 如果类 "BD" 未找到,则尝试自动装载类 "BD" Bfoo(); // 调用命名空间 "B" 中的函数 "foo" B::foo(); // 调用全局空间中的类 "B" 的 "foo" 方法 // 如果类 "B" 未找到,则尝试自动装载类 "B" // 当前命名空间中的静态方法或函数 AB::foo(); // 调用命名空间 "AA" 中定义的类 "B" 的 "foo" 方法 // 如果类 "AAB" 未找到,则尝试自动装载类 "AAB" AB::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法 // 如果类 "AB" 未找到,则尝试自动装载类 "AB" ?>
名称解析遵循以下规则:
对具有完全限定名称的函数、类和常量的调用在编译时解析。例如 new AB 解析为类 AB。所有非限定和限定名称(非完全限定名称)都在编译时根据当前导入规则进行转换。例如,如果命名空间 ABC 作为 C 导入,则对 CDe() 的调用将转换为 ABCDe()。在命名空间内,所有未根据导入规则转换的限定名称都将以当前命名空间名称为前缀。例如,如果在命名空间 AB 中调用 CDe(),则 CDe() 将转换为 ABCDe()。不合格的类名在编译时根据当前导入规则进行转换(用全名替换短导入名称)。例如,如果命名空间 ABC 被导入为 C,然后将 new C() 转换为 new ABC()。在命名空间(例如 AB )内,对非限定名称的函数调用在运行时解析。例如,函数 foo() 的调用解析如下: 在当前命名空间中查找名为 ABfoo() 的函数 尝试在全局空间中查找并调用函数 foo()。对命名空间(例如 AB )内的非限定命名或限定命名类(非完全限定名称)的调用在运行时解析。下面是调用new C()和new DE()的解析过程: New C()解析:在当前命名空间中找到ABC类。尝试自动加载类 ABC。new DE()分析:在类名前加上当前命名空间名变为:ABDE,然后搜索类。尝试自动加载类 ABDE。为了引用全局命名空间中的全局类,