给力星

Web Developer

重温PHP手册 – 命名空间

命名空间概述

PHP 在 5.3.0 以后开始支持的命名空间提供了一种将相关的类、函数和常量组合到一起的途径。解决了如下两类问题:

  1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
  2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名,提高源代码的可读性。

命名空间用法示例;

namespace my\name; // 参考 "定义命名空间" 小节

class MyClass {}
function myfunction() {}
const MYCONST = 1;

$a = new MyClass;
$c = new \my\name\MyClass; // 参考 "全局空间" 小节

$a = strlen('hi'); // 参考 "使用命名空间:后备全局函数/常量" 小节

$d = namespace\MYCONST; // 参考 "namespace操作符和__NAMESPACE__常量” 小节

$d = __NAMESPACE__ . '\MYCONST';
echo constant($d); // 参考 "命名空间和动态语言特征" 小节

定义命名空间

虽然任意合法的PHP代码都可以包含在命名空间中,但只有兕种类型的代码受命名空间的影响,它们是:类,接口、函数和常量。

  • 命名空间通过关键字namespace 来声明
  • 命名空间必须是程序脚本的第一条语句。
  • 同一个命名空间可以定义在多个文件中,即允许将同一个命名空间的内容分割存放在不同的文件中。
  • 命名空间不能使用 . 和 /。

使用 define() 来定义命名空间中的常量

namespace NS;

define(__NAMESPACE__ .'\foo','111');
define('foo','222');

echo foo;   // 111.
echo \foo;  // 222.
echo \NS\foo  // 111.
echo NS\foo   // fatal error. assumes \NS\NS\foo.

定义子命名空间

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

namespace MyProject\Sub\Level;

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

在同一个文件中定义多个命名空间

也可以在同一个文件中定义多个命名空间。

简单组合语法:

namespace MyProject;

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

namespace AnotherProject;

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

建议使用大括号语法形式:

namespace MyProject {

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

namespace AnotherProject {

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

在实际的编程实践中,非常不提倡在同一个文件中定义多个命名空间。这种方式的主要用于将多个 PHP 脚本合并在同一个文件中。

使用命名空间

注意命名空间和文件地址类似,有相对地址、绝对地址的类似概念。类名可以通过三种方式引用:

  1. 非限定名称: 例如 $a = new foo();,如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。
  2. 限定名称: 例如 $a = new subnamespace\foo();,如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo。
  3. 完全限定名称: 例如$a = new \currentnamespace\foo();,则总是被解析为代码中的文字名(literal name)currentnamespace\foo。

另外,可以给命名空间取别名:

namespace com\rsumilang\util; 
use com\rsumlang\common as Common; 

class String extends Common\Object 
{ 
   // ... code ... 
} 

namespace关键字和__NAMESPACE__常量

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

常量 __NAMESPACE__ 的值是包含当前命名空间名称的字符串。在全局中则返回空字符串。

常量 __NAMESPACE__ 在动态创建名称时很有用:

namespace MyProject;

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

关键字 namespace 可用来显式访问当前命名空间或子命名空间中的元素。它等价于类中的 self 操作符。

namespace MyProject;

namespace\func(); // calls function MyProject\func()

use 控制命名空间的一个例子:

namespace bar;
use foo\Xyz;    // 命名空间 foo 是存在的
// 如果命名空间 bar 中没有定义类 Xyz, 则产生 \foo\Xyz 的实例
// 否则产生 \bar\Xyz 的实例
new Xyz();

使用命名空间:别名/导入

PHP 命名空间支持使用别名或导入方式:为类名称使用别名,或为命名空间名称使用别名。注意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 对象

注意对命名空间中的名称(包含命名空间分隔符的完全限定名称如 Foo\Bar 以及相对的不包含命名空间分隔符的全局名称如 FooBar)来说,前导的反斜杠是不必要的也不允许有反斜杠,因为导入的名称必须是完全限定的,不会根据当前的命名空间作相对解析。

全局空间

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

namespace A\B\C;

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

使用命名空间:后备全局函数/常量

对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量。

1条评论

  1. "namespace关键字和__NAMESPACE__常量"这样小节,有误。

    namespace bar;
    use fooXyz; // 命名空间 foo 是存在的
    // 如果命名空间 bar 中没有定义类 Xyz, 则产生 fooXyz 的实例
    // 否则产生 barXyz 的实例
    new Xyz();

    我测试发现,不管命名空间 bar有没有定义类Xyz,都是用fooXyz类的。

发表评论

电子邮件地址不会被公开。