PHP5.3中的新特性之:命名空间
我在上一篇文章中曾经提到过,PHP 5.3版本将会在2008年的早些时候发布, 现在我想应该是时候谈一谈这个版本的新特性了。在这些特性中,重量级的特性包括 命名空间, 静态延迟绑定(late static binding) 以及 mysqlnd,当然还有其他的一些有趣的改进, 比如 __callStatic和dynamic static calls。这一节我们先来分析一下“命名空间”的细节。
PHP对命名空间的支持是一项人们期待已久的特性了。加入这一特性的主要目的是为了解决PHP中的超长类名问题。如果你准备开发一个大一点的库,那就不得不使用比较长的类名来避免命名冲突的问题,
比如下面这种怪物名字: Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive.
从5.3开始你可以使用命名空间来组织你的代码了。 不同的命名空间内可以包含相同名字的类、函数以及常量。 声明一个命名空间非常简单,只需要在文件的开始处加上namespace 语句,像下面这样
/** classes/my/foo/MyClass.php */
namespace my::foo;
class MyClass {}//定义一个类
// 当然,也可以定义函数和常量
function myFunc() { }
const MY_CONST = ‘foo’;
?>
有多种方法来访问
/** test.php */
include(‘classes/my/foo/MyClass.php’);
// 可以随时通过完整的名称来访问一个类
$foo = new my::foo::MyClass();
// 还可以使用use语句来导入一个命名空间
use my::foo;
// 然后,通过foo来引用 my::foo这个命名空间
$foo = new foo::MyClass();
// 也可以只导入一个类
use my::foo::MyClass;
$foo = new MyClass;
// 可以为命名空间或者命名空间中的类创建别名
use my::foo as MyFoo;
use my::foo::MyClass as MyFooClass;
$foo = new MyFoo::MyClass();
$foo = new MyFooClass();
// 注意, 下面的两种写法是等价的:
use my::foo;
use my::foo as foo;
// 也可以用同样的方法来访问函数和常量
my::foo::myFunc();
myFoo::myFunc();
my::foo::MY_CONST;
myFoo::MY_CONST;
?>
use语句只能导入命名空间和类,下面这种写法就不行: use my::foo::MY_CONST;。一个use语句的有效范围是从它被定义开始直到文件的结尾,你可以在全局范围内任何地方使用它。 你可以在多个文件中使用相同的命名空间。但是一个文件只应该包含一个命名空间 (这一行为可能会在最终版本中被改变,也可能用package来替换namespace关键字
)。 尽管不能import一个函数或者常量, 但是仍然可以使用一些前缀来从命名空间中访问他们。在PHP 5.3的早期版本中,import 关键字被用来代替 use, 现在这一点已经被改变了。
“空”命名空间 (::)
如果你在函数或常量名前面使用 :: 前缀, 他们将会被从当前的引用规则中独立出来,这在命名空间中十分有用。
Life with namesapces (porting code to PHP 5.3)
如果你打算使用命名空间,那么下面几处缺陷是你应该注意的:
类的命名规则
首先,要避免在类名中使用PHP的保留字,仔细看下面的代码:
/** classes/my/form/element/static.php */
class MyFormElementStatic {}
?>
而在使用命名空间后,代码会变成这样:
/** classes/my/form/element/static.php */
namespace my::form::element;
class Static {}
?>
但是因为Static 是一个保留字,所以这段代码最终会导致一个致命错误。如果你有很多类似名字的类,那么对你来说这确实是一个坏消息。
Autoloading
从5.3版本开始, __autoload()将使用类的全路径名称,这就意味着如果你已经使用了__autoload(),就要对它做一下适当的修改,举个简单的例子
/** test.php */
function __autoload($className)
{
require ‘classes/’.str_replace(‘::’, DIRECTORY_SEPARATOR, $className).‘.php’;
}
$foo = new my::foo::MyClass();
?>
或者选择 SPL的形式 来完成类似的功能
/** classes/my/core/classloader.php */
namespace my::core;
function classLoader($className)
{
require ‘classes/’.str_replace(‘::’, DIRECTORY_SEPARATOR, $className).‘.php’;
}
spl_autoload_register(‘my::core::classLoader’);
?>
/** test.php */
require ‘classes/my/core/classLoader.php’;
$foo = new my::foo::MyClass();
?>
get_class(), get_parent_class(), 及其他.
当你使用这类函数的时候也要注意,他们也同样返回一个类的全路径名称。
反射 API
反射 API 同样也要反映出这种新的语言特性,这部分的计划是这样的:
* 创建一个包含下面这些方法的ReflectionNamespace类: getName(), getClasses(), getFunctions(), getFiles()
* 扩展ReflectionClass 和ReflectionFunction 类并加入getNamespace() 这一新方法。
下面这些知识也是你应该了解的:
常量: __NAMESPACE__
在这一版本中我们引入了一个新的常量: __NAMESPACE__,它包含了当前引用的命名空间的名字,比如你可以使用下面的方式来实现一个SPL风格的loader
/** classes/my/core/classloader.php */
namespace my::core;
function classLoader($className)
{
require ‘classes/’.str_replace(‘::’, DIRECTORY_SEPARATOR, $className).‘.php’;
}
spl_autoload_register(__NAMESPACE__.‘::classLoader’);
?>
PHP对命名空间内的元素的解析流程
我不想对这部分内容作过多的解释,你可以在 PHP namespaces README 这里阅读到详细的规则(fidy注:作者对这部分的解释确实比较“简略”,建议还是读一下那个readme,其中的关键部分我会在后面贴出来)。 记住一个要点,就是PHP会首先参照当前的命名空间来解析一个元素, 让我们来看例子
namespace my::foo;
…
mysql_connect();
…
::mysql_connect();
?>
当你调用mysql_connect()的时候,PHP会首先到my::foo 下面去找这个函数。如果找到了就调用它,没找到的话再去调用PHP内部集成的mysql_connect()函数,而其他命名空间中定义的mysql_connect()函数你是没办法访问到的。而使用::bar() 这种写法则可以访问到全局命名空间中的函数,它可能是PHP的内部函数,也可能是用户自定义的。
namespace my::foo;
core::bar();
::core::bar();
?>
在core::bar()这种情况下, PHP 会首先尝试调用my::foo::core 下面的函数bar(),如果没有找到,则尝试调用my::foo命名空间下的类core(如果存在的话)的静态方法bar(),最后尝试调用PHP的内置类core的bar()方法。
::core::bar()这种情况下,PHP会首先尝试调用core命名空间下的 函数 bar() ,然后尝试调用全局范围内类core的静态方法bar()。
现在PHP对命名空间的支持还没有完成,很可能将来会做一些必要的改动。