PHP 命名空间(namespace)
PHP 命名空间(namespace)是在PHP 5.3中加入的,如果你学过C#和Java,那命名空间就不算什么新事物。 不过在PHP当中还是有着相当重要的意义。
PHP 命名空间可以解决以下两类问题:
- 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
- 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
定义命名空间
默认情况下,所有常量、类和函数名都放在全局空间下,就和PHP支持命名空间之前一样。
命名空间通过关键字namespace 来声明。如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间。语法格式如下;
你也可以在同一个文件中定义不同的命名空间代码,如:
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() { }
}
?>
将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用大括号形式的语法。全局代码必须用一个不带名称的 namespace 语句加上大括号括起来,例如:
namespace MyProject {
const CONNECT_OK = 1;
class Connection { }
function connect() { }
}
namespace {
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>
在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的 declare 语句。所有非 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>
namespace MyProject;
?>
子命名空间
与目录和文件的关系很像,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 文件代码
namespace Foo\Bar\subnamespace;
const FOO = 1;
function foo() {}
class foo
{
static function staticmethod() {}
}
?>
file2.php 文件代码
namespace Foo\Bar;
include 'file1.php';
const FOO = 2;
function foo() {}
class foo
{
static function staticmethod() {}
}
foo();
foo::staticmethod();
echo FOO;
subnamespace\foo();
subnamespace\foo::staticmethod();
echo subnamespace\FOO;
\Foo\Bar\foo();
\Foo\Bar\foo::staticmethod();
echo \Foo\Bar\FOO;
?>
注意访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 或 \INI_ALL。
在命名空间内部访问全局类、函数和常量:
namespace Foo;
function strlen() {}
const INI_ALL = 3;
class Exception {}
$a = \strlen('hi');
$b = \INI_ALL;
$c = new \Exception('error');
?>
命名空间和动态语言特征
PHP 命名空间的实现受到其语言自身的动态特征的影响。因此,如果要将下面的代码转换到命名空间中,动态访问元素。
example1.php 文件代码:
class classname
{
function __construct()
{
echo __METHOD__,"\n";
}
}
function funcname()
{
echo __FUNCTION__,"\n";
}
const constname = "global";
$a = 'classname';
$obj = new $a;
$b = 'funcname';
$b();
echo constant('constname'), "\n";
?>
必须使用完全限定名称(包括命名空间前缀的类名称)。注意因为在动态的类名称、函数名称或常量名称中,限定名称和完全限定名称没有区别,因此其前导的反斜杠是不必要的。
动态访问命名空间的元素
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;
$b = 'funcname';
$b();
echo constant('constname'), "\n";
$a = '\namespacename\classname';
$obj = new $a;
$a = 'namespacename\classname';
$obj = new $a;
$b = 'namespacename\funcname';
$b();
$b = '\namespacename\funcname';
$b();
echo constant('\namespacename\constname'), "\n";
echo constant('namespacename\constname'), "\n";
?>
namespace关键字和__NAMESPACE__常量
PHP支持两种抽象的访问当前命名空间内部元素的方法,NAMESPACE 魔术常量和namespace关键字。
常量__NAMESPACE__的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串。
NAMESPACE 示例, 在命名空间中的代码
namespace MyProject;
echo '"', __NAMESPACE__, '"';
?>
NAMESPACE 示例,全局代码
echo '"', __NAMESPACE__, '"';
?>
常量 NAMESPACE 在动态创建名称时很有用,例如:
使用__NAMESPACE__动态创建名称
namespace MyProject;
function get($classname)
{
$a = __NAMESPACE__ . '\\' . $classname;
return new $a;
}
?>
关键字 namespace 可用来显式访问当前命名空间或子命名空间中的元素。它等价于类中的 self 操作符。
namespace操作符,命名空间中的代码
namespace MyProject;
use blah\blah as mine;
mine\mine();
namespace\blah\mine();
namespace\func();
namespace\sub\func();
namespace\cname::method();
$a = new namespace\sub\cname();
$b = namespace\CONSTANT;
?>
namespace操作符, 全局代码
namespace\func();
namespace\sub\func();
namespace\cname::method();
$a = new namespace\sub\cname();
$b = namespace\CONSTANT;
?>
使用命名空间:别名/导入
PHP 命名空间支持 有两种使用别名或导入方式:为类名称使用别名,或为命名空间名称使用别名。
在PHP中,别名是通过操作符 use 来实现的. 下面是一个使用所有可能的三种导入方式的例子:
1、使用use操作符导入/使用别名
namespace foo;
use My\Full\Classname as Another;
use My\Full\NSname;
use \ArrayObject;
$obj = new namespace\Another;
$obj = new Another;
NSname\subns\func();
$a = new ArrayObject(array(1));
?>
2、 一行中包含多个use语句
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another;
NSname\subns\func();
?>
导入操作是在编译执行的,但动态的类名称、函数名称或常量名称则不是。
3、导入和动态名称
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another;
$a = 'Another';
$obj = new $a;
?>
另外,导入操作只影响非限定名称和限定名称。完全限定名称由于是确定的,故不受导入的影响。
4、导入和完全限定名称
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another;
$obj = new \Another;
$obj = new Another\thing;
$obj = new \Another\thing;
?>
使用命名空间:后备全局函数/常量
在一个命名空间中,当 PHP 遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。类名称总是解析到当前命名空间中的名称。因此在访问系统内部或不包含在命名空间中的类名称时,必须使用完全限定名称,例如:
1、在命名空间中访问全局类
namespace A\B\C;
class Exception extends \Exception {}
$a = new Exception('hi');
$b = new \Exception('hi');
$c = new ArrayObject;
?>
对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量。
2、 命名空间中后备的全局函数/常量
namespace A\B\C;
const E_ERROR = 45;
function strlen($str)
{
return \strlen($str) - 1;
}
echo E_ERROR, "\n";
echo INI_ALL, "\n";
echo strlen('hi'), "\n";
if (is_array('hi')) {
echo "is array\n";
} else {
echo "is not array\n";
}
?>
全局空间
如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与 PHP 引入命名空间概念前一样。在名称前加上前缀 \ 表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此。
使用全局空间说明
namespace A\B\C;
function fopen() {
$f = \fopen(...);
return $f;
}
?>
命名空间的顺序
自从有了命名空间之后,最容易出错的该是使用类的时候,这个类的寻找路径是什么样的了。
namespace A;
use B\D, C\E as F;
foo();
\foo();
my\foo();
F();
new B();
new D();
new F();
new \B();
new \D();
new \F();
B\foo();
B::foo();
D::foo();
\B\foo();
\B::foo();
A\B::foo();
\A\B::foo();
?>