PHP命名空间
PHP 命名空间(namespace)是在PHP 5.3中加入的。
PHP 命名空间可以解决以下两类问题:
- 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
- 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
定义命名空间
默认情况下,所有常量、类和函数名都放在全局空间下,就和PHP支持命名空间之前一样。
命名空间通过关键字namespace
来声明。如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间。语法格式如下:
<?php
// 定义代码在 'MyProject' 命名空间中
namespace MyProject;
// ... 代码 ...
?>
也可以在同一个文件中定义不同的命名空间代码,如:
<?php
namespace MyProject;
const CONNECT_OK = 1; //命名空间对const变量管用
class Connection { /* ... */ } //对类管用
function connect() { /* ... */ } //同样对函数管用
namespace AnotherProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>
但是,命名空间对define
定义的常量不管用:
<?php
namespace one;
define('value1', 'value2');
namespace two;
define('value1', 'value3');
?>
就会报错:
Notice: Constant value1 already defined in A:\myphp_www\PHPTutorial\WWW\tp5\test.php on line 19
- 注意事项:
- 声明命名空间之前唯一合法的代码是用于定义源文件编码方式的
declare
语句。所有非 PHP 代码包括空白符都不能出现在命名空间的声明之前。
<?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();
}
?>
以下代码会出现语法错误:
<html>
<?php
namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句
?>
就会出现致命错误:
Fatal error: Namespace declaration statement has to be the very first statement in the script in
A:\myphp_www\PHPTutorial\WWW\tp5\test.php on line 23
- 将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用大括号形式的语法。全局代码必须用一个不带名称的
namespace
语句加上大括号括起来。
<?php
namespace MyProject { //命名空间代码
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>
子命名空间
与目录和文件的关系很像,PHP 命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义:
<?php
namespace MyProject\Sub\Level; //声明分层次的单个命名空间
const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */ }
?>
上面的例子创建了常量MyProject\Sub\Level\CONNECT_OK
,类 MyProject\Sub\Level\Connection
和函数 MyProject\Sub\Level\Connect
。
命名空间使用
PHP 命名空间中的类名可以通过三种方式引用:
非限定名称,或不包含前缀的类名称,即访问的是当前命名空间里的内容。例如
$a=new foo();
或foo::staticmethod();
。如果当前命名空间是currentnamespace
,foo
将被解析为currentnamespace\foo
。如果使用foo
的代码是全局的,不包含在任何命名空间中的代码,则foo
会被解析为foo
。 警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。限定名称,或包含前缀的名称,以当前命名空间所处的路径为相对路径,继续向下寻找指定的命名空间,类似于文件相对路径。例如
$a = new subnamespace\foo();
或subnamespace\foo::staticmethod();
。如果当前的命名空间是currentnamespace
,则foo
会被解析为currentnamespace\subnamespace\foo
。如果使用foo
的代码是全局的,不包含在任何命名空间中的代码,foo
会被解析为subnamespace\foo
。完全限定名称,或包含了全局前缀操作符的名称,和文件绝对路径(根目录)的访问方式相似。例如,
$a = new \currentnamespace\foo();
或\currentnamespace\foo::staticmethod();
。在这种情况下,foo
总是被解析为代码中的文字名(literal name)currentnamespace\foo
。
- file1.php
<?php
namespace Foo\Bar\subnamespace; //子命名空间
const FOO = 1;
function foo() {}
class foo
{
static function staticmethod() {}
}
?>
- file2.php
<?php
namespace Foo\Bar;
include 'file1.php';//引入file.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
?>
注意:访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen()
或 \Exception
或 \INI_ALL
。
在命名空间内部访问全局类、函数和常量:
<?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
?>
引入空间成员
use
空间名\空间名 【as
别名】:将指定空间引入到当前空间。同可以使用as关键字为被引入的空间起个别名。use
空间名\空间名\成员类 【as
别名】:将指定的空间中的成员引入到当前空间,引入空间成员只能引入类。
<?php
namespace foo;
use My\Full\Classname as Another;
// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;
// 导入一个全局类
use \ArrayObject;
$obj = new namespace\Another; // 实例化 foo\Another 对象
$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象
// 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象
?>
一行包含多个use语句情况:
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
?>
导入操作是在编译执行的,但动态的类名称、函数名称或常量名称则不是。
- 导入和动态名称
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // 实例化一个 My\Full\Classname 对象
$a = 'Another';
$obj = new $a; // 实际化一个 Another 对象
?>
另外,导入操作只影响非限定名称和限定名称。完全限定名称由于是确定的,故不受导入的影响。
- 导入和完全限定名称
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // 实例化 My\Full\Classname 类
$obj = new \Another; // 实例化 Another 类
$obj = new Another\thing; // 实例化 My\Full\Classname\thing 类
$obj = new \Another\thing; // 实例化 Another\thing 类
?>
PHP公共空间
可以简单的理解,没有定义命名空间的方法(函数)、类库(class)、属性(变量)都默认归属于公共空间。这样就解释了为php5.3.0以前版本书写的代码大部分为何在php5.3.0及其以上版本还能正常运行的原因。
另外:公共空间中的代码段被引入到某个命名空间下后,该公共空间中的代码段不属于任何命名空间!
- 1.php
<?php
function fun(){
}
class Demo{
}
?>
- 2.php
<?php
namespace cn\my;
include 'common.php'; //引入当前目录下的文件
$demo = new Demo; //出现致命错误:找不到 cn\my\Demo类
$demo = new \Demo(); //正确的方式 加上 \
var_dump(); // 错误,系统函数在公共空间
\var_dump(); // 正确,使用了 \
?>
说明:调用公共空间的方式是直接在元素名称前面加上 \
就可以了,否则 PHP 解析器会认为用户像调用当前空间下的元素。除了自定义的元素,还包括 PHP 自带的元素,都属于公共空间。其实公共空间的函数和常量不用加\
也可以正常调用,但是为了正确区分元素所在区域,还是建议调用函数的时候加上\
。