PHP中的命名空间用来解决大型PHP库(libraries)中作用域的问题,在PHP中,所有的类定义都是全局的,所以,当一个库的作者为这处库创建多种应用或公共API类的时候,他必须清楚其他库中相似功能是否存在的可能性并因而选择唯一的名称,来保证这些库可以被同时使用.通常用唯一的字符串加前缀的方法来解决,如数据库类被冠以My_Library_DB,等等.当这个库增加的时候,前缀也随着增加,这样导致很长的名字.命名空间使开发者在每次引用类的时候不使用长名称即可管理命名范
围.
定义
命名空间在每一个文件的开头以namespace 关键字来声明,如
| <?php namespace MyProject::DB; const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } ?> |
相同的namespace 名称可以在多个文件中使用
namespace 包class,constant和function定义,但无free code
详解:
在命名空间内部,所有定义的class,function和constant名称被自动冠以命名空间名称,类名总是完整的名称,如,以上例子中的类使用MyProject::DB::Connection来调用创建的常量,名称是由命名空间名加上常量名,象类常量、命名空间常量只能为静态常量。
非完整修饰名(如名称不包含::),遵循如下:
非自动加载类在当前命名空间(冠以命名空间名称的)中查找
非自动加载全局命名空间中查找
Autoloading for name in current namespace is attempted.
如前者查找失败,则查找失败
非完整修饰函数名称(如名称不包含::)先在当前命名空间查找再在全局空间中查找
非完整修饰常量名称称在当前命名空间中查找再在全局定义的常量里查找
使用命名空间
每一个命名空间里的类和函数可使用全名来引用,如,MyProject::DB::Connection ,或MyProject::DB::Connection
| <?php require 'MyProject/Db/Connection.php'; $x = new MyProject::DB::Connection; MyProject::DB::connect(); ?> |
| <?php /* ... */ use Some::Name as Othername; // 简化应用 use Foo::Bar; // 与其相同 use Foo::Bar as Bar; ?> |
引入的名称工作如下:每次当解释器遇到局部名称Othername(单一名称或被::分隔的长名称),引入的名称Some::Name被替代
use只能用于全局范围内,非函数或类中,引入的名称从引入开始到当前文件结尾有效,推荐在文件最开始引入以避免冲突
| <?php require 'MyProject/Db/Connection.php'; use MyProject::DB; use MyProject::DB::Connection as DbConnection; $x = new MyProject::DB::Connection(); $y = new DB::connection(); $z = new DbConnection(); DB::connect(); ?> |
全局空间
没有命名空间定义时,所有的类和函数都被放在全局空间内,因为在PHP支持命名空间前就是这样的.
在命名空间上下文中,被冠以::将指定来自全局空间里该名称
| <?php namespace A::B::C; /* 此函数为 A::B::C::fopen */ function fopen() { /* ... */ $f = ::fopen(...); // 全局函数 return $f; } ?> |
__NAMESPACE__
解释期间的常量__NAMESPACE__被定义为当前命名空间的名称,在命名空间外部,这个常量值为空字符串.
| <?php namespace A::B::C; function foo() { // do stuff } set_error_handler(__NAMESPACE__ . "::foo"); ?> |
规则
1.所有完整修饰名通过当前引入规则转化,如A::B::C已引入,C::D::E()被转化为A::B::C::D::e().
2.非完整修饰名通过当前引入规则转化(全名替换简名),如A::B::C已引入,new C()被转化为A::B::C().
3.在命名空间内部,调用当前命名空间内定义的非完整修饰函数名,在解释期间被认为是调用这些命名空间的函数
4.在命名空间内部(A::B),调用在当前命名空间内没有定义的非完整修饰名函数在运行期间被决定,以下是Foo()如何被决定
在当前命名空间内寻找函数A::B::foo().
尝试寻找和调用内部(internal)函数foo()
调用全局空间内用户定义的函数,使用::foo()
5.在命名空间内部(A::B),调用在当前命名空间内没有定义的非完整修饰名类在运行期间被决定,以下是 new C() 如何被决定.
在当前命名空间A::B::C中寻找类
尝试寻找和调用内部类C
尝试自动加载A::B::C
调用全局空间内用户自定义的类,使用new ::c()
6.调用完整修饰名的函数在运行期间被决定.以下是A::B::foo() 如何被决定
在当前命名空间内寻找函数A::B::foo().
调用全局空间内用户定义的函数,使用::foo()
7.调用完整修饰名的类的时候,是在运行的时候才按照想对应的命名空间来加载。比如 new A::B::C() 它是引用 命名空间是A::B下的C() 类
| <?php namespace A; // 函数调用 foo(); // 先尝试调用命名空间A里定义的"foo // 再调用内部(internal)函数 "foo" ::foo(); // 调用全局范围内定义的"foo" // 类引用 new B(); // 先尝试创建在命名空间A定义的B类的对象 // 再创建内部类B对象 new ::B(); //新建全局域定义的B类的对象 // 来自其他命名空间的静态方法/命名空间 B::foo(); //先尝试调用来自命名空间"A::B"的函数foo() // 再调用内部类B的函数foo() ::B::foo(); //先尝试调用来自命名空间B的函数FOO() //再调用全局域里B里的函数FOO() //当前命名空间的静态方法/命名空间 A::foo(); //先调用命名空间"A::A" 里的函数FOO() // 再调用命名空间A里A类的函数foo() // 再尝试调用命名空间A里函数foo() // 再调用内部类A里的函数foo() ::A::foo(); // 先尝试调用命名空间A里函数foo() // 再调用全局域里A类的函数foo() ?> |