Namespacing in PHP (php 中使用命名空间)
最近学习一下php的命名空间,中文文档不多,搜到一篇英文的,讲的还蛮系统的,特此翻译一下,以备以后查阅,大家有什么高见或更深刻或者 更悟透的见解希望能不吝赐教,晚辈感激不尽。
原文: http://code.tutsplus.com/tutorials/namespacing-in-php--net-27203
关于PHP对命名空间的支持,这走过了一段崎岖的历程。感谢的是,从PHP5.3起,加入了对命名空间。php的代码结构也因此提升了了许多。但是,我们究竟该怎样使用命名空间呢?
一、什么是命名空间
“别玩了要去掉前面的反斜杠,当你将一个命名空间存储到一个字符串变量中的时候。”
把命名空间想象成一个抽屉,在这个抽屉里面你可以放各种各样的东西:想一支铅笔、一张纸、等等,这些是属于你的东西。在你的抽屉下面呢是别人的抽屉,他也可以把各种东西放在他的抽屉里面。为了避免互相取错对方的东西,你决定给抽屉贴上标签,这样就很清晰哪个抽屉式属于谁的了。
以前,没有引入命名空间的时候,开发者们在他们的类中、函数中、常量中使用 下划线 “_”来区分开。这其实等同于给各自的物品贴上标签然后把他们放到同一个大的抽屉中。当然,这至少已经看上去有点结构,不太那么乱了,但是这非常的不高效。
命名空间可以拯救这一切。你可以在各自的命名空间内定义同名的函数、类、接口、常量,只要他们是在不同的命名空间中,这就不会报错。本质上来说,一个命名空间仅仅是一个有层级的,来标识常规的php代码块的东西。
“你已经在使用命名空间了”
有一点很重要你应该记住的,其实你间接的在使用命名空间了,php5.3以后,所有的代码定义,如果没有显示的定义在用户命名的命名空间中,都是默认定义在一个全局的命名空间中的。全局命名空间同样也包含了php的许多内置的函数定义,想echo(),mysql_connect(),和Exception类。因为全局命名空间没有一个独立的标识名字,它通常指的是 全局空间。
记住,你没有必要一定要使用命名空间。你的php脚本同样可以执行的很好即使没有命名空间,而且加入命名空间也可能不会发生太快。
二、怎样定义一个命名空间
在一个php文件中,一个命名空间的定义应该是一个最前面的声明(php解释器应该第一个遇到的)。注:唯一一个可以在声明一个命名空间之前声明的东西是 declare 声明,仅仅当declare声明了这个脚本文件的编码。
声明一个命名空间就是使用一个简单的 namespace关键字,命名空间的名字和php 中其他的标识符的命名规范一样。就是只能有字母、数字、下划线组成,而且不能以数字开头。
<?php namespace MyProject { // Regular PHP code goes here, anything goes! function run() { echo ‘Running from a namespace!‘; } }
如果你想分配一个代码块给全局空间,你使用namespace关键字,但是后面不带名字。
If you want to assign a code block to the global space, you use the namespace
keyword without appending a name.
<?php namespace{ //Global Space! }
你可以在同一个文件中定义多个命名空间。
<?php namespace MyProject{ // } namespace MySecondProject{ // } namespace { // }
你也可以在多个文件中使用同一个命名空间;文件的包含 处理过程会自动合并他们。因此,一个好的编程实践是在每一文件中只使用一个命名空间,就像你只在一个文件中定义一个类一样。
命名空间的使用是用来避免命名(定义)冲突的,和进入了更多的灵活性,更好地组织你的代码。
注意一点,命名空间的用来围绕代码块大括号“{}”完全是可选的,你可以不要它。实际上,坚持一个文件一个命名空间的原则,然后省略大括号使你的代码更简洁多了--不需要代码缩进了,代码嵌套。
子命名空间
命名空间可以遵循一个确定层级(等级),就像你电脑的文件系统中的目录结构一样。子命名空间是非常有用的,来组织你的代码结构。例如:如果你的项目需要访问数据库,你可能想把所有的数据库相关的代码,如数据库异常和链接句柄都放到一个叫做 database的子命名空间中去。
为了维护上的灵活性,将一个子命名空间放到一个子目录中是明智的。这促使了你的代码更加结构化,更容易使用 autoloader自动加载。PHP使用反斜杠 "\”来作为命名空间的分隔符。
// myproject/database/connection.php <?php namespace MyProject\Database class Connection { // Handling database connections }
你可以使用很多的子命名空间,只要你喜欢
<?php namespace MyProject\Blog\Auth\Handler\Social; class Twitter { // Handles Twitter authentification }
定义嵌套的子命名空间是不支持的。下面的例子会抛出一个严重的错误“命名空间的声明不能嵌套!”。
<?php namespace MyProject { namespace Database { class Connection { } } }
三、调用命名空间内的代码
如果你实例化一个别的命名空间中的对象,或者调用一个函数,或者使用一个常量,你要使用反斜杠的语法。他们可以从以下三种方式中解析出来。
- 非限制的名称
- 限制的名称
- 完全限制的名称
(1)非限制的名称
这是一个类的名字,函数的名字,或者常量的名字,没有包含对任何命名空间的引用。如果你是刚刚接触命名空间,这是经常使用它们的方式。
<?php namespace MyProject; class MyClass { static function static_method() { echo ‘Hello, world!‘; } } // Unqualified name, resolves to the namespace you are currently in (MyProject\MyClass) MyClass:static_method()
(2)限制的名称
这是我们访问子命名空间层级的方式,这种方式要使用反斜杠语法。
<?php namespace MyProject; require ‘myproject/database/connection.php‘; // Qualified name, instantiating a class from a sub-namespace of MyProject $connection = new Database\Connection();
下面的例子抛出了一个严重的错误:“Fatal error: Class ‘MyProject\Database\MyProject\FileAccess\Input‘ not found" .”
这是因为 Myproject\FileAccess\Input 访问的是相对于你当前所在的命名空间的。
<?php namespace MyProject\Database; require ‘myproject/fileaccess/input.php‘; // Trying to access the MyProject\FileAccess\Input class $input = new MyProject\FileAccess\Input()
(3)完全限制的命名空间
非限制的命名空间和限制的命名空间都是相对于你当前所在的命名空间-----相对路径。他们仅仅用于访问他们同一级的或者子级的命名空间。
如果你想访问一个函数、类、或常量,而他们是位于更高一级的命名空间中,这时候你就要使用完全限制的命名空间----绝对路径而不是相对路径。这就需要在你的调用前面加上反斜杠“\”.这就会让PHP知道调用时要从全局空间开始解析而不是相对路径。
<?php namespace MyProject\Database; require ‘myproject/fileaccess/input.php‘; // Trying to access the MyProject\FileAccess\Input class // This time it will work because we use the fully qualified name, note the leading backslash $input = new \MyProject\FileAccess\Input();
没有要求说要在php 的函数内部使用完全限制的名称,通过 一个不限制的名称来调用一个不存在与当前命名空间中的常量或函数时,PHP会自动去全局空间中寻找他们。这个是内置的向后查找,但是这不适用于不限制名称的方式去调用类(也就是说以不限制名称的方式去调用一个不存在与当前命名空间中的类时,PHP不会自动去全局空间中寻找)。在脑子里记住这一点,我们现在可以重载内部的php函数,同时也仍然可以调用原始的函数(或常量)。
<?php namespace MyProject; var_dump($query); // Overloaded \var_dump($query); // Internal // We want to access the global Exception class // The following will not work because there‘s no class called Exception in the MyProject\Database namespace and unqualified class names do not have a fallback to global space // throw new Exception(‘Query failed!‘); // Instead, we use a single backslash to indicate we want to resolve from global space throw new \Exception(‘ailed!‘); function var_dump() { echo ‘Overloaded global var_dump()!<br />‘
}
动态调用
PHP 是一种动态的编程语言;因此也可以应用这种功能于命名空间的调用上。这本质上类似于实例化变量类 或者 包含变量文件。PHP命名空间的分隔符也是一个元字符。别忘了当你把命名空间存储于一个自动串变量中的时候去掉前面的反斜杠。
<?php namespace OtherProject; $project_name = ‘MyProject‘; $package_name = ‘Database‘; $class_name = ‘Connection‘; // Include a variable file require strtolower($project_name . ‘/‘. $package_name . ‘/‘ . $class_name) . ‘.php‘; // Name of a variable class in a variable namespace. Note how the backslash is escaped to use it properly $fully_qualified_name = $project_name . ‘\\‘ . $package_name . ‘\\‘ . $class_name; $connection = new $fully_qualified_name();
四、namespace 关键字
namespace关键字不仅仅用于定义一个命名空间,它也可以用来解析当前的命名空间,功能上类似于 类中的关键字 self.
<?php namespace MyProject; function run() { echo ‘Running from a namespace!‘; } // Resolves to MyProject\run run(); // Explicitly resolves to MyProject\run namespace\run();
五、__NAMESPACE_常量
很像self关键字,不能用于决定当前所在的类是哪个类,namespace关键字也不能用于决定当前的命名空间是什么命名空间,这就是我们为什么要使用__NAMESPACE__常量。
<?php namespace MyProject\Database; // ‘MyProject\Database‘ echo __NAMESPACE__;
这个常量非常的有用,如果你是刚刚开始命名空间的学习,它对于调试也很有帮助。作为一个字符串,它还可以用在动态的代码调用,我们之前说过了。
六、别名或者导入
“没有必要一定要使用命名空间”
PHP中的命名空间支持导入 importing .导入也叫做 别名。只用类、接口、命名空间可以取别名或者被导入。
导入是非常有用的,也是命名空间非常重要的一方面。它使你有能力去使用外部的包代码,像类库,不需要担心命名冲突。导入是通过 使用 use 关键字来支持的。制定一个自定义的别名 跟着 as 关键字。
use [name of class, interface or namespace] as [optional_custom_alias]
导入时怎样实现的呢
How it‘s Done
一个完全限制的名称 可以取一个短一点的非限制的名称,这样你当你想用它的时候不用每次都写一个长长的完全限制的名称。别名或者导入应该发生(或者叫使用)于最高层级的作用域或者是全局空间中。 尝试在一个方法或是函数的作用域中使用导入时不合法(不和语法的)的。
<?php namespace OtherProject; // This holds the MyProject\Database namespace with a Connection class in it require ‘myproject/database/connection.php‘; // If we want to access the database connection of MyProject, we need to use its fully qualified name as we‘re in a different name space $connection = new \MyProject\Database\Connection(); // Import the Connection class (it works exactly the same with interfaces) use MyProject\Database\Connection; // Now this works too! Before the Connection class was aliased PHP would not have found an OtherProject\Connection class $connection = new Connection(); // Import the MyProject\Database namespace use MyProject\Database; $connection = new Database\Connection()
或者,你可以取一个不同的名字作为别名。
<?php namespace OtherProject; require ‘myproject/database/connection.php‘; use MyProject\Database\Connection as MyConnection; $connection = new MyConnection(); use MyProject\Database as MyDatabase; $connection = new MyDatabase\Connection();
你也可以允许导入全局的类,像 Exception类。当导入之后呢,你就可以再也不用写他的长长的完全限制的名称了。
注意,导入的名称不是被解析成相对于当前命名空间的路径,而是绝对路径,从全区空间开始。这意味着 前导的反斜杠付是不必要的也是不推荐的。
<?php namespace MyProject; // Fatal error: Class ‘SomeProject\Exception‘ not found throw new Exception(‘An exception!‘); // OK! throw new \Exception(‘An exception!‘); // Import global Exception. ‘Exception‘ is resolved from an absolute standpoint, the leading backslash is unnecessary use Exception; // OK! throw new Exception(‘An exception!‘);
尽管可以动态调用命名空间的代码, 但是动态的导入是不支持的。
<?php namespace OtherProject; $parser = ‘markdown‘; // This is valid PHP require ‘myproject/blog/parser/‘ . $parser . ‘.php‘; // This is not use MyProject\Blog\Parser\$parser;
结论
命名空间是用来避免定义冲突的和引入了更多的灵活性,更好的组织你的代码的。记住你没有必要说非得使用命名空间。它的使用是结合面向对象的工作流中会常见。 然而,还是希望,你将考虑将你的PHP项目带入到下一个层次中,通过使用命名空间。你决定这样做了吗?
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。