php进阶之路 -- 03 命名空间
- 命名空间概述
- 定义命名空间
- 空间成员和子空间
- 空间成员的访问
- 空间引入
- 全局空间
一、 命名空间概述
什么是命名空间?从广义上来说,命名空间是一种封装事物的方法。在很多地方都可以见到这种抽象概念。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命名空间的角色。具体举个例子,文件 foo.txt 可以同时在目录/home/greg 和 /home/other 中存在,但在同一个目录中不能存在两个 foo.txt 文件。另外,在目录 /home/greg 外访问 foo.txt 文件时,我们必须将目录名以及目录分隔符放在文件名之前得到 /home/greg/foo.txt。这个原理应用到程序设计领域就是命名空间的概念。
在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:
①用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
②为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
二、定义命名空间
命名空间在实际开发中的作用是非常大的,那么想要使用命名空间需要通过namespace关键字:
1 <?php
2 /**
3 * Created by PhpStorm.
4 * User: liujunhang
5 * Date: 2018/6/18
6 * Time: 12:11
7 */
8
9 namespace ONE;
10
11 class MyClass1{
12
13 public function say(){
14 echo __NAMESPACE__;
15 }
16 }
17 $obj = new MyClass1();
18
19
20 namespace TWO;
21 class MyClass1 {
22 public function say(){
23 echo __NAMESPACE__;
24 }
25 }
26 $obj = new MyClass1();
上面代码中,我们在同一个文件中创建了两个相同类名的类,但是代码并没有报错,原因是因为我们开启了两个命名空间,相当于在两篇内存地址中,并不冲突。
那么在使用命名空间时有以下需要注意的点:
- 如果一个脚本定义命名空间,那么必须写在有效代码的第一行。
- 在同一个文件中,可以使用魔术常量__NAMESPACE__来查看当前的命名空间。
- 一个脚本周期内,可以定义多个命名空间,并且不同的空间可以定义相同的函数、常量和类。
不同的命名空间当中,所有的内容并不会冲突和干扰。
下面的例子中存在着一种特殊情况:
原因:变量不属于空间成员。
三、空间成员和子空间
空间成员
首先,一个命名的空间内,可以有任何的php代码。但是命名空间本身只负责三种类型:常量、函数、类。其他的都不属于空间成员。
上面代码的运行结果是:
所以说,命名空间内,除了使用空间成员(类、函数和常量)受命名空间的限制之外,其他的代码都是该怎么执行就怎么执行,跟不存在命名空间一样。
子空间
同我们计算机可以存在子目录一样,内存中也同样可以存在子空间。
例如:
目录:A/B/C ,其中C就是B的子目录,B就是A的子目录。
空间:namespace A\B\C,其中C就是B的子空间,B就是A的子空间。注意使用反斜杠\,其表示的含义是创建了一个名为C的子空间,并且A和B空间也一起创建了。
例如:
进入B空间可以如下:
思考:我们能不能够在B空间中访问其子空间C的成员呢?
上面的问题不能访问的原因:所谓的父子空间只是逻辑上的划分,实际上还是处在不同的空间当中,所以并不能直接进行访问。
那么我们如何在不同的空间中进行访问呢?
四、空间成员的访问
如何在众多的命名空间中如何访问某个空间中的某个成员。
php中提供了三种访问空间成员的方式:
非限定名称访问
限定名称访问
完全限定名称访问
非限定名称访问
所谓的非限定名称访问,就是非指定名称访问,就是在访问空间成员的时候,没有指定具体的空间的名字,到目前为止,我们前面使用的所有的访问方式都属于非限定名称访问。
那么上面这种访问方式缺点是什么呢?
例如我们之前写过的实例,当我们在某个空间中直接访问(采用非限定名称访问)另外一个空间的常量时,系统就会提示我们未找到。
那么很显然,非限定名称访问,只能访问到当前所属的空间,所谓的当前所属的空间,就是当前代码向下寻找的第一个空间,存在即访问,不存在就报错。
限定名称的访问
所谓的限定名称的访问,就是从当前名称开始访问其子空间的成员。
语法形式为:
子空间名\空间成员名
我们回到上面的问题,想要从B里访问到C中的内容。可以如下:
完全限定名称访问
所谓的完全限定名称访问,就是直接从根空间(类比根目录)开始对绝对的路径的访问!
语法形式:
\空间1\空间2\...... 其中的反斜杠代的是根路径
例如:
所以,采用完全限定名称访问的最大好处是可以在任意的空间访问其他任意空间的成员。
五、空间引入
概念:
当一个脚本包含了另外一个脚本,就出现了空间引入的问题。
例如我们下面碰到的问题:
t1.php
t2.php
此时我们的问题是,t1.php中的getName()方法调用,输出结果为什么?
是HELLO,还是WORLD?
结果为什么为HELLO,而不是WORLD呢?why?
按照我们的理解,在调用getName()方法之前我们明明就已经把t2.php引入进t1.php中,那么相当于getName调用的应该是t2里面的getName()方法。
原因:
出现这种情况是由于加载机制导致的。
因为文件的载入是发生在代码的执行阶段,而不是预编译阶段。
所以此时的getName()调用其实还是属于HELLO空间内,所以还是调用的是HELLO空间内的getName()方法。
思考:
如何访问加载进来的WORLD空间的成员?
首先,不能使用非限定名称访问,因为非限定名称访问只能访问其默认的上级空间(刚才例子中getNamef()调用访问的默认的上级空间HELLO)。
其次,也不能使用限定空间名称访问,因为限定空间名称访问只能访问当前的子空间成员。除非被引入的空间是当前空间的子空间!
而如果想要使用WORLD空间的getName方法,可以使用完全限定名称访问。
空间类的引入
所谓的空间类的引入,就是把其他的空间的类引入到当前的空间,在当前的空间直接使用这个类。
具体实现方式主要是通过use的形式来进行实现。
如下:
通过use关键字我们成功的使用了另外一个空间,但是还有一个问题,如果引入的空间中的类与本空间中的类相同,例如下面:
如果真的存在上面的情况,那么代码就会报错,那么该如何解决呢?
解决方案如下:
六、 全局空间
概念
如果不给脚本定义命名空间,那么脚本中定义的内容就属于全局空间。
例如:
按照我们之前的逻辑如果想要使用这个文件,需要引入这个文件 ,如下:
我们可以尝试下面的解决方案: