命名空间

namespace在C++里见过,use namespace std;

命名空间可以解决两类问题

  • 用户编写的代码与PHP内部的类/常量/函数或第三方类/函数/常量之间的名字冲突
  • 为很长的标识符名称创建一个别名或简称,提高代码的可读性

定义命名空间

默认情况下,所有常量,函数和类都放在全局空间下,就和PHP支持命名空间之前一样

命名空间使用namespace关键字来声明。如果一个文件中包含命名空间,它必须在其他所有代码之前声明命名空间

语法

1
2
3
4
5
<?php  
// 定义代码在 'MyProject' 命名空间中
namespace MyProject;

// ... 代码 ...

你也可以在同一个文件中定义不同的命名空间代码

1
2
3
4
5
6
7
8
9
10
11
12
<?php  
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }

namespace AnotherProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }

这种写法一般是不建议的,所以为了提高代码的可读性,使用大括号的形式

1
2
3
4
5
6
7
8
9
10
11
12
<?php
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}

namespace AnotherProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}

将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用大括号形式的语法。且全局代码必须用一个不带名称的namespace语句加上大括号括起来

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}

namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}

在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的declare语句。所有非PHP代码包括空白符都不能出现在命名空间的声明之前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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();
}

如果写成这样就会出现语法错误

1
2
<html>
<?php namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句

子命名空间

与目录和文件的关系很像,PHP命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义

1
2
3
4
5
6
<?php
namespace MyProject\Sub\Level; //声明分层次的单个命名空间

const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */ }

类似于目录结构

命名空间使用

PHP命名空间中的类名可以通过三种方式引用

  • 非限定名称,或不包含前缀的类名称,例如$a=new foo();或foo::staticmethod();

  • 限定名称,或包含前缀的名称,例如$a=new subnamespace\foo();或subnamespace\foo::staticmethod();

  • 完全限定名称,或包含了全局前缀操作符的名称,例如,$a=new\currentnamespace\foo();

下面便是三种方式的实例
file1.php

1
2
3
4
5
6
7
8
9
<?php
namespace Foo\Bar\subnamespace;

const FOO = 1;
function foo() {}
class foo
{
static function staticmethod() {}
}

file2.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
26
<?php
namespace Foo\Bar;
include 'file1.php';

const FOO = 2;
function foo() {}
class foo
{
static function staticmethod() {}
}

/* 非限定名称 */
foo(); // 解析为函数 Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo ,方法为 staticmethod
echo FOO; // 解析为常量 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

注意访问任意全局类,函数或常量,都可以使用完全限定名称

在命名空间内部访问全局类,函数和常量

1
2
3
4
5
6
7
8
9
10
<?php
namespace Foo;

function strlen() {}
const INI_ALL = 3;
class Exception {}

$a = \strlen('hi'); // 调用全局函数strlen
$b = \INI_ALL; // 访问全局常量 INI_ALL
$c = new \Exception('error'); // 实例化全局类 Exception

命名空间和动态语言特征

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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
<?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; // 输出 classname::__construct
$b = 'funcname';
$b(); // 输出函数名
echo constant('constname'), "\n"; // 输出 global

/* 如果使用双引号,使用方法为 "\\namespacename\\classname"*/
$a = '\namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // 输出 namespacename\funcname
$b = '\namespacename\funcname';
$b(); // 输出 namespacename\funcname
echo constant('\namespacename\constname'), "\n"; // 输出 namespaced
echo constant('namespacename\constname'), "\n"; // 输出 namespaced

namespace关键字和_NAMESPACE_常量

PHP支持两种抽象的访问当前命名空间内部元素的方法,NAMESPACE 魔术常量和namespace关键字。

常量NAMESPACE的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串。

1
2
3
4
<?php
namespace MyProject;

echo '"', __NAMESPACE__, '"'; // 输出 "MyProject"

使用_NAMESPACE_动态创建名称

1
2
3
4
5
6
7
8
<?php
namespace MyProject;

function get($classname)
{
$a = __NAMESPACE__ . '\\' . $classname;
return new $a;
}

全局空间

如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与 PHP 引入命名空间概念前一样。在名称前加上前缀 \ 表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此。

1
2
3
4
5
6
7
8
9
<?php
namespace A\B\C;

/* 这个函数是 A\B\C\fopen */
function fopen() {
/* ... */
$f = \fopen(...); // 调用全局的fopen函数
return $f;
}

总结

1.命名空间的存在增加了代码的灵活度,可以更好的编写程序

2.命名空间的使用基本与文件目录相似,都是使用\来进行划分,如果只有一个\那就默认为是全局空间,不加也可以

3.完全命名空间可以看成是完整的绝对路径,而不完全的基本上可以看成是相对路径