PHP命名空间详解

2018-02-09 10:09:00
linefo
原创
1948


PHP的命名空间(从5.3版本开始),如字面意思,是为常量/类/函数的命名提供不同的空间。

在C#中早已有这个概念,学习命名空间主要是知道它的规则。


为什么要使用命名空间


主要是为了解决重名问题,在大型项目(特别是很多人开发)中,可能会存在同名的常量/类/函数,虽然功能不同,但因为同名,所以同时使用时会冲突。

在不同的命名空间中,我们可以声明同名的常量/类/函数,能够解决上面的这个问题。


全局空间


默认情况下,常量/类/函数都存在于全局空间中。

这个概念是PHP引入命名空间后才出现的,因为所谓的全局空间我们一直在使用着。


变量只存在于全局空间中,无法在自定义的命名空间中再使用同名的变量(因为没有必要,变量的值本来就是用来改变的)。


声明命名空间


我们使用namespace关键字可以声明新的命名空间:


<?php
	namespace space1;
	
	const A = 1;
	class Aclass{  }
	function a_func(){  }
	
	namespace space2; // 当前命名空间为最后声明的space2
	
	const A = 2;
	class Aclass{  }
	function a_func(){  }

        echo A; // 输出2


如果一个PHP文件包含命名空间,则声明空间的代码块必须要出现在其他代码之前。

在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的 declare 语句!)


这里要强调一个当前空间的概念,全局空间是默认的当前空间。同一个文件中,每次声明命名空间时,当前空间都会随之改变。


对已声明过的命名空间,再次使用namespace指名,则是改变当前命名空间为指名的命名空间:



<?php
	namespace space1;
	const A = 1;
	
	namespace space2;
	const B = 2;
	
	namespace space1;
	echo A; // 输出1



PS:

下面的index.php文件和space.php在同一个目录层级。

space.php文件中,声明命名空间的代码块要求出现在该文件其他代码之前;而对引用其的index.php来说,声明空间的这段代码没有在其他代码之前,通过这里可以更好的理解这个规则。

可以看到对index.php来说,引用文件中声明的命名空间space1并没能改变index.php的当前命名空间,所以还是输出1。




另一个语法结构


为了更清晰明了,我们可以使用大括号的方式包裹(这个还有不理解的,待研究):



<?php
	namespace space1{
	const A = 2;
	}


子命名空间


我们使用子命名空间的方式去表述一种目录关系


<?php
	namespace shop
	const A = 1;
	
	namespace shop\cart
	const B = 2;


PS:所谓子命名空间仅仅只从命名上表述了一种目录层级关系,没有实质性的作用

比如在上面的shop\cart命名空间中,并没有包含shop命名空间的常量A(继承)。


PS:可以理解所有自定义的命名空间,都是全局命名空间的子命名空间。


使用命名空间的类/函数/常量


(1)非限定名称、限定名称


资料一般会把非限定名称和限定名称划分开,但我觉得是一个东西。


<?php
	namespace A;
	const X = 1;
	
	namespace A\B;
	const X = 2;
	
	namespace A; // 指名当前空间为A
	echo X; // 输出1,非限定名称
	echo "<br />"; // 分隔
	echo B\X; // 输出2,限定名称


无论非限定、限定,其实都是从当前空间开始访问......用相对路径理解就可以了


(2)完全限定名称


完全限定名称就是使用时,在前面加一个 \ 就可以了,用绝对路径理解即可。


<?php
	namespace A;
	const X = 1;
	
	namespace A\B;
	const X = 2;
	
	namespace A\B; // 指名当前空间为A\B
	echo \A\X; // 输出1,完全限定名称


PS:如果当前空间是自定义的命名空间,并且我们要使用一个全局命名空间里面和当前命名空间有冲突的元素,我们需要用完全限定名称指定。

(引入的other.php文件,里面的demo()是在全局命名空间中的)


__NAMESPACE__魔术常量和namespace关键字


__NAMESPACE__魔术常量的值是当前命名空间



<?php
	namespace space1;
	function demo(){
		echo __NAMESPACE__; // 输出当前命名空间
	}

	demo(); // 输出A


namespace关键字上面说过可以声明一个新的命名空间和指定一个已声明的命名空间为当前命名空间。

除此之外,它还可以用来显示访问(应该用的不多?),直接使用时,值为当前命名空间,类似上面__NAMESPACE__魔术常量。



<?php
    namespace space1;
    const A = 1;

    echo namespace\A; // 输出1


命名空间和动态语言特征


当把命名空间作为动态名称使用时,都会被解析成完全限定。

看下面的例子可知,否则$name1()应该输出A\A。



<?php
	namespace A;
	function demo(){
		echo __NAMESPACE__; // 输出当前命名空间
	}

	$name1 = "A\demo"; // 写法上不属于完全限定
	$name2 = "\A\demo"; // 写法上属于完全限定
	
	$name1(); // 输出A
	echo "<br />";
	$name2(); // 输出A


别名/导入


主要和两个关键字有关:use和as

语法use...as...,use指定命名空间,as指定别名,as可以省略,但此时语法规则会有变化。


我们可以为命名空间使用别名,使用use不会改变当前命名空间。


<?php
	namespace A\B\C;
	const X = 1;
	
	use A\B\C as Na; // 将Na作为A\B\C的别名
	echo Na\X; // 输出1
	
	// use A\B\C; // 意义同 use A\B\C as C,这种方式就限定别名了
	// echo C\X; // 输出1
	


我们常常为类使用别名,或者说导入类

下面这个用法可以理解为将space1命名空间的Demo类,引入到了当前命名空间(此时是全局命名空间)下。

如果放开use space2\Demo这行语句的注释会报错,因为同一命名空间下不允许出现命名类。

但我们可以为space2\Demo该类指定一个别名Demo2,如下:


PHP5.6开始支持为函数、常量设置别名/或导入


命名空间中类/函数/常量的解析策略


在一个命名空间中,遇到非限定名称的类/函数/常量时,PHP对类名称的解析策略和对常量/函数名称的解析策略有出入,主要在于是否从全局命名空间中查找当前命名空间不存在的该名称。


解析非限定的类名称时,只会从当前命名空间找。要找到全局命名空间中的类,必须要加 \


而解析非限定的常量、函数名称时,如果在当前命名空间找不到,会自动到全局命名空间找。


补充


1、命名空间和引入文件的特殊处理思路

按上面使用的方式,如果我们在a文件中use某个命名空间,那么声明该命名空间的b文件已然在use之前引入。
然而这种加载方式明显过于消耗资源

在thinkphp的框架的设计理念中,我们use某个命名空间时,此时声明该命名空间的文件并没有引入

实际上是怎么回事呢?

其实就是设计成,使用到声明这个命名空间的类文件时,才去引用它。比如new它时。

文章分类
联系我们
联系人: Mr.Chen
QQ: 185391277