PHP命名空间

命名空间

定义

PHP5.3.0 新增的一种封装事物的方法,解决编写的类库或应用程序时命名冲突的问题,类似window系统的文件夹

关键字 namespace

  • 必须声明命名空间,必须处于程序脚本的第一条语句,即声明前不能有任意的字符输出,如空格或html标签等;
  • 命名空间中可以包含任意合法的php代码,受命名空间影响的有类、函数、常量
  • PHP >=5.3.0 开始支持
  • namespace 可以代表当前空间,类似类中的self;namespace\Home\Student()

创建一个namespace.php文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
//声明一个App的命名空间
namespace App

//1 定义一个类
class student{
private $name = "小德";
private $age = "28";
public function __construct(){
echo "{$this->name}的年龄是{$this->age}岁";
}
}

//2 定义一个普通函数
function dump($target){
var_dump($target);
}

//3 定义一个局部常量
const WHO = "rufeike";

//4 其他任意代码
$a = 1000;

?>

使用命名空间

  • 使用时需要指定空间名称
  • 命名空间与类、函数、局部常量间使用\分割;
  • 普通代码 变量、常量不在命名空间中

在当前目录创建一个index.php,引入上面的创建的namespace.php文件,进行测试使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
//包含namespace.php 文件
require_once("./namespace.php");

//1 创建学生类对象
//注意:直接使用会报错,需要在类前指定命名空间App/Student
/*
报错信息:
Fatal error: Uncaught Error: Class 'Student' not found in C:\Users\rufeike\Desktop\phasher\test\index.php:3 Stack trace: #0 {main} thrown in C:\Users\rufeike\Desktop\phasher\test\index.php on line 3
*/

$student = new App\Student();

//2 调用dump方法
App\dump("hello world!");

//使用局部变量
echo App\WHO;

//普通代码可以直接使用
echo $a;

命名空间子空间

  • 使用\斜线分割子空间。如 Home\Controller\Student();

定义

1
2
3
4
5
6
7
<?php
//创建 App\Home\Controller的命名空间
namespace App\Home\Controller;
class Student{
.....
}
123456

使用

1
2
3
<?php 
$student = new App\Home\Controller\Student();
12

命名空间路径

  • 文件系统中访问文件的方式
    • 相对文件名形式:如:a.txt ,会在当前执行文件的所在文件夹中查找a.txt;
    • 相对路径名形式:如:sub/a.txt ,会在当前执行文件的所在文件夹中查找sub文件夹,再在sub中查找a.txt;
    • 绝对路径名形式:如:/main/a.txt ,会从根目录(window系统中,会从执行文件的所在盘符中)开始查找main文件夹,再在main中查走a.txt;
  • 访问命名空间中的元素方式
    • 非限定名称(不带任何前缀)。$obj = new User(),会在单前空间中查找
    • 限定名称(带相对空间的前缀)。$obj = new Home\User(),会在当前空间找子空间Home中的User类
    • 完成限定名称(从根空间开始)。$obj = new \Home\User(),会从根空间开始查找Home中的User类

命名空间use使用

  • 使用use关键字 use App\Student;
  • 如果引入类与当前空间的类起冲突时,可以使用as起别名;如use App\Student as Stu;
  • 使用use引入后,可以直接使用类名称;$student = new Student()

命名空间和动态语言特征

PHP 命名空间的实现受到其语言自身的动态特征的影响。因此,如果要将下面的代码转换到命名空间中,动态访问元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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
?>

必须使用完全限定名称(包括命名空间前缀的类名称)。注意因为在动态的类名称、函数名称或常量名称中,限定名称和完全限定名称没有区别,因此其前导的反斜杠是不必要的

命名空间的顺序

自从有了命名空间之后,最容易出错的该是使用类的时候,这个类的寻找路径是什么样的了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?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" 的 "foo" 方法
// 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
?>

名称解析遵循下列规则:

  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()*。