PHP 命名空间
1.概述
- 用户自己编写的代码与php内部或第三方的类/函数/常量之间的name conflict.
- 为很长的标识符名称创建一个别名的名称,提高源代码的可读性.
2.定义namespace
Example #1 声明单个命名空间
<?php
namespace MyProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>
3.定义subNamespace
Example #1 声明分层次的单个命名空间
<?php
namespace MyProject\Sub\Level;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>
4.在同一个文件中定义namespace
</pre><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)"><?php</span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">namespace </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">MyProject </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">{const </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">CONNECT_OK </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">= </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">1</span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">;class </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">Connection </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">{ </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(255,128,0)">/* ... */ </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">}function </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">connect</span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">() { </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(255,128,0)">/* ... */ </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">}}namespace </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">AnotherProject </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">{const </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">CONNECT_OK </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">= </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">1</span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">;class </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">Connection </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">{ </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(255,128,0)">/* ... */ </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">}function </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">connect</span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">() { </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(255,128,0)">/* ... */ </span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,119,0)">}}</span><span style="font-family:'Fira Mono','Source Code Pro',monospace; font-size:14px; line-height:24px; color:rgb(0,0,187)">?></span><pre>
将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用{}形式的语法。全局代码必须用一个不带名称的namespace语句加上{}括起来。
Example #4 定义多个命名空间和不包含在命名空间中的代码
<?php
declare(encoding='UTF-8');
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>
5.use namespace
- 非限定名称,或不包含前缀的类名称:例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。
- 限定名称,或包含前缀的名称:例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo。
- 完全限定名称,或包含了全局前缀操作符的名称:例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo。
file1.php
<?php
namespace Foo\Bar\subnamespace;
const FOO = 1;
function foo() {}
class foo
{
static function staticmethod() {}
}
?>
file2.php
<?php
namespace Foo\Bar;
include 'file1.php';
const FOO = 2;
function foo() {}
class foo
{
static function staticmethod() {}
}
/* 非限定名称 */
foo(); // 解析为 Foo\Bar\foo resolves to function Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo的静态方法staticmethod。resolves to class Foo\Bar\foo, method 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。
6.namespace和动态语言特征
Example #1 动态访问元素
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
?>
Example #2 动态访问命名空间的元素
<?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; // prints classname::__construct
$b = 'funcname';
$b(); // prints funcname
echo constant('constname'), "\n"; // prints global
/* note that if using double quotes, "\\namespacename\\classname" must be used */
$a = '\namespacename\classname';
$obj = new $a; // prints namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // also prints namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // prints namespacename\funcname
$b = '\namespacename\funcname';
$b(); // also prints namespacename\funcname
echo constant('\namespacename\constname'), "\n"; // prints namespaced
echo constant('namespacename\constname'), "\n"; // also prints namespaced
?>
7.namespace关键字和__NAMESPACE__
Example #1 __NAMESPACE__ 示例, 在命名空间中的代码
<?php
namespace MyProject;
echo '"', __NAMESPACE__, '"'; // 输出 "MyProject"
?>
Example #2 __NAMESPACE__ 示例,全局代码
<?php
echo '"', __NAMESPACE__, '"'; // 输出 ""
?>
__NAMESPACE__
在动态创建名称时很有用,例如:
Example #3 使用__NAMESPACE__动态创建名称
<?php
namespace MyProject;
function get($classname)
{
$a = __NAMESPACE__ . '\\' . $classname;
return new $a;
}
?>
Example #4 namespace操作符,命名空间中的代码
<?php
namespace MyProject;
use blah\blah as mine; // see "Using namespaces: importing/aliasing"
blah\mine(); // calls function MyProject\blah\mine()
namespace\blah\mine(); // calls function MyProject\blah\mine()
namespace\func(); // calls function MyProject\func()
namespace\sub\func(); // calls function MyProject\sub\func()
namespace\cname::method(); // calls static method "method" of class MyProject\cname
$a = new namespace\sub\cname(); // instantiates object of class MyProject\sub\cname
$b = namespace\CONSTANT; // assigns value of constant MyProject\CONSTANT to $b
?>
Example #5 namespace操作符, 全局代码
<?php
namespace\func(); // calls function func()
namespace\sub\func(); // calls function sub\func()
namespace\cname::method(); // calls static method "method" of class cname
$a = new namespace\sub\cname(); // instantiates object of class sub\cname
$b = namespace\CONSTANT; // assigns value of constant CONSTANT to $b
?>
8.使用命名空间:别名/导入
Example #1 使用use操作符导入/使用别名
<?php
namespace foo;
use My\Full\Classname as Another;
// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;
// 导入一个全局类
use \ArrayObject;
$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 对象
?>
Example #2 通过use操作符导入/使用别名,一行中包含多个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
?>
导入操作是在编译执行的,但动态的类名称、函数名称或常量名称则不是。 Importing is performed at compile-time, and so does not affect dynamic class, function or constant names.
Example #3 导入和动态名称
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // 实例化一个 My\Full\Classname 对象
$a = 'Another';
$obj = new $a; // 实际化一个 Another 对象
?>
另外,导入操作只影响非限定名称和限定名称。完全限定名称由于是确定的,故不受导入的影响。
Example #4 导入和完全限定名称
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // instantiates object of class My\Full\Classname
$obj = new \Another; // instantiates object of class Another
$obj = new Another\thing; // instantiates object of class My\Full\Classname\thing
$obj = new \Another\thing; // instantiates object of class Another\thing
?>
9.全局namespace
Example #1 使用全局空间说明
<?php
namespace A\B\C;
/* 这个函数是 A\B\C\fopen */
function fopen() {
/* ... */
$f = \fopen(...); // 调用全局的fopen函数
return $f;
}
?>
Example #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 会退而使用全局空间中的函数或常量
Example #2 命名空间中后备的全局函数/常量
<?php
namespace A\B\C;
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 array\n";
} else {
echo "is not array\n";
}
?>
10.名称解析规则
-
命名空间名称定义
非限定名称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。
- 在类名称前面加上当前命名空间名称变成:A\B\D\E,然后查找该类。
- 尝试自动装载类 A\B\D\E。
Example #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"
?>
Example #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"
?>