简介
当不使用命名空间时,定义的类、函数等都存在于全局命名空间中。如果要为定义的类、函数、接口等指定命名空间,可以在脚本的开头进行声明:namespace MyNameSpace;
注意:
1.脚本声明了命名空间后,只有以下类型的代码受命名空间的影响,它们是:类(包括抽象类和traits)、接口、函数和常量。
2.如果一个文件中包含命名空间,它必须在其它所有代码(declare除外)之前声明命名空间。
3.命名空间的名字可以具有层次,就像文件系统的层次结构一样:namespace ns1\ns2\ns3;
此外,同一个命名空间可以定义在多个文件中,即允许将同一个命名空间的内容分割存放在不同的文件中。
namespace MyNameSpace;
class MyClass {}
$a = new \MyNameSpace\MyClass;
var_dump($a);
/*
object(MyNameSpace\MyClass)#1 (0) {
}
*/
一个文件中定义多个命名空间
可以在一个文件中定义多个命名空间,使用花括号包围即可,不过应注意的是,除了开始的declare语句外,命名空间的括号外不得有任何PHP代码。如果在脚本来定义过命名空间后还需要定义全局命名空间,则只需要定义一个名字为空的命名空间即可。
namespace MyNameSpace
{
class MyClass {}
$a = new \MyNS1\MyNS2\MyClass;
var_dump($a);
/*
object(MyNS1\MyNs2\MyClass)#1 (0) {
}
*/
}
namespace MyNS1\MyNs2
{
class MyClass {}
$a = new \MyNameSpace\MyClass;
var_dump($a);
/*
object(MyNameSpace\MyClass)#2 (0) {
}
*/
}
namespace
{
// 全局命名空间
class MyClass {}
$a = new \MyClass;
var_dump($a);
/*
object(MyClass)#1 (0) {
}
*/
}
实事上在一个文件中定义多个命名空间时,花括号是可以省略的,不过前提是没有定义全局命名空间,如果包含了全局命名空间,那么花括号就是必须的了。命名空间限定
如果使用了命名空间,则在使用一个命名空间中的名字时,需要指定其所在的命名空间,例如:echo \A\MyClassA::Str."\n";具体的前缀解释规则如下:
1.非限定名称,或不包含前缀的类名称,会被解释在当前命名空间下。注意,对于函数或常量(类除外),如果当前命名空间中不存在相应的名称,则解释到全局命名空间中。
2.不以\开头的限定名称,或包含前缀的名称,也解释在当前命名空间下。
3.以\开头的完全限定名称,或包含了全局前缀操作符的名称,则完全按指定的命名空间解释。
namespace
{
class MyClass
{
public const Str = "Local";
}
function getStrL() { return "Local"; }
}
namespace A
{
class MyClassA
{
public const Str = "A";
}
function getStrA() { return "A"; }
//echo MyClass::Str."\n"; 出错
echo MyClassA::Str."\n"; // A
echo B\MyClassAB::Str."\n"; // AB
echo \MyClass::Str."\n"; // Local
echo \A\MyClassA::Str."\n"; // A
echo \A\B\MyClassAB::Str."\n"; // AB
echo getStrL()."\n"; // Local
echo getStrA()."\n"; // A
echo B\getStrAB()."\n"; // AB
}
namespace A\B
{
class MyClassAB
{
public const Str = "AB";
}
function getStrAB() {return "AB";}
}
通过字符串间接访问
通过字符串变量间接访问类或方法时,字符串中必须包含命名空间限定,否则出错。
namespace MyNS;
class MyClass
{
public const PI = 3.14;
}
function sayHello() {
return "hello\n";
}
$a = "world";
$sa = "a";
$sf = 'MyNS\sayHello'; // 必须包含命名空间限定
$sc = 'MyNS\MyClass'; // 必须包含命名空间限定
echo $$sa."\n"; // world
echo $sf(); // hello
echo $sc::PI."\n"; // 3.14
echo $a."\n"; // world
echo sayHello(); // hello
echo MyClass::PI."\n"; // 3.14
魔术常量 __NAMESPACE__
常量__NAMESPACE__的值是包含当前命名空间名称的字符串。在不包括在任何命名空间中的代码(全局命名空间中),它包含一个空的字符串。
namespace MyNS1
{
function getns() {
return __NAMESPACE__."\n";
}
echo getns(); // MyNS1
}
namespace MyNS2
{
echo \MyNS1\getns(); // MyNS1
}
namespace关键字也可以用在命名空间限定中,显式地表示当前命名空间
namespace MyNS1
{
function getns() {
return __NAMESPACE__."\n";
}
echo getns();
echo \MyNS1\getns();
echo namespace\getns();
}
use 导入命名空间
通过导入命名空间,可以简化命名空间限定的书写,还可以在导入时设置别名。导入的一般方式如下:
use [function|const] 限定名称 [as 别名], 限定名称 [as 别名] ... ;
注意:
1.可导入的名称包括:类、接口、命名空间、函数和常量。
2.导入函数须添加function关键字,导入常量须添加const关键字。
3.别名为可选,如果没有别名,则仍按原名访问,只是省略了命名空间限定。
4.导入不适用于字符串变量动态类名、函数名等,如果通过字符串变量间接访问,仍须包含命名空间限定。
5.导入语句必须是在脚本的最外层,因为此语句是在编译时生效的,不能在代码运行时导入。
namespace MyNS1\MyNS2
{
function getns() {
return __NAMESPACE__."\n";
}
const Name = "Lucy";
class MyClass {
public const PI = 3.14;
}
}
namespace MyNS3
{
use \MyNS1\MyNS2;
//相当于如下导入:
//use \MyNS1\MyNS2 as MyNS2;
echo MyNS2\getns();
echo MyNS2\Name."\n";
}
namespace MyNS1\MyNS2
{
function getns() {
return __NAMESPACE__."\n";
}
const Name = "Lucy";
const Color = "Red";
class MyClass {
public const PI = 3.14;
}
}
namespace MyNS3
{
use function \MyNS1\MyNS2\getns as getns3;
use const \MyNS1\MyNS2\Name, \MyNS1\MyNS2\Color;
use \MyNS1\MyNS2\MyClass;
echo getns3();
echo Name."\n";
echo Color."\n";
echo MyClass::PI."\n";
}
名称解析
在一个命名空间中,当 PHP 遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。1.类名称总是解析到当前命名空间中的名称。因此在访问系统内部或不包含在命名空间中的类名称时,必须使用完全限定名称。
2.对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量。这也称为“后备全局函数/常量"。
namespace
{
function getns() {
return "Global\getns()\n";
}
const Name = "Lucy";
class MyClass {
public const PI = 3.14;
}
}
namespace MyNS3
{
echo getns();
echo Name."\n";
echo \MyClass::PI."\n";
}
具体的解析规则如下:
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。
new D\E()的解析:
1.在类名称前面加上当前命名空间名称变成:A\B\D\E,然后查找该类。
2.尝试自动装载类 A\B\D\E。