PHP命名空间

命名空间一个最明确的目的就是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误。

举一个命名空间的例子

在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命名空间的角色。具体举个例子,文件foo.txt 可以同时在目录/home/greg 和 /home/other 中存在,但在同一个目录中不能存在两个 foo.txt 文件。另外,在目录 /home/greg 外访问 foo.txt 文件时,我们必须将目录名以及目录分隔符放在文件名之前得到/home/greg/foo.txt。这个原理应用到程序设计领域就是命名空间的概念

1. 常量:PHP5.3开始const关键字可以用在类的外部。const和define都是用来声明常量的(它们的区别不详述),但是在命名空间里,define的作用是全局的,而const则作用于当前空间。

命名空间将代码划分出不同的空间(区域),每个空间的常量、函数、类(为了偷懒,我下边都将它们称为元素)的名字互不影响。

1. 创建一个命名空间需要使用namespace关键字

1 <?php
2 //创建一个名为'Article'的命名空间
3 namespace Article;
4 ?>

要注意的是,当前脚本文件的第一个命名空间前面不能有任何代码,下面的写法都是错误的

 1 //例一
 2 //在脚本前面写了一些逻辑代码
 3 <?php
 4 $path = "/";
 5 class Comment { }
 6 namespace Article;
 7 ?>
 8  
 9 //例二
10 //在脚本前面输出了一些字符
11 <html></html>
12 <?php
13 namespace Article;
14 ?>

为什么要说第一个命名空间呢?因为同一脚本文件中可以创建多个命名空间。

下面我创建了两个命名空间,顺便为这两个空间各自添加了一个Comment类元素

 1 <?php
 2 //创建一个名为'Article'的命名空间
 3 namespace Article;
 4 //此Comment属于Article空间的元素
 5 class Comment { }
 6 
 7 //创建一个名为'MessageBoard'的命名空间
 8 namespace MessageBoard;
 9 //此Comment属于MessageBoard空间的元素
10 class Comment { }
11 ?>

在不同空间之间不可以直接调用其它元素,需要使用命名空间的语法

 1 <?php
 2 namespace Article;
 3 class Comment { }
 4 
 5 namespace MessageBoard;
 6 class Comment { }
 7 //调用当前空间(MessageBoard)的Comment类
 8 $comment = new Comment();
 9 //调用Article空间的Comment类
10 $article_comment = new \Article\Comment();
11 ?>

可以看到,在MessageBoard空间中调用article空间里的Comment类时,使用了一种像文件路径的语法: \空间名\元素名

除了类之外,对函数和常量的用法是一样的,下面我为两个空间创建了新的元素,并在MessageBoard空间中输出了它们的值。

 1 <?php
 2 namespace Article;
 3 const PATH = '/article';
 4 function getCommentTotal() {
 5     return 100;
 6 }
 7 class Comment { }
 8 
 9 namespace MessageBoard;
10 const PATH = '/message_board';
11 function getCommentTotal() {
12     return 300;
13 }
14 class Comment { }
15 //调用当前空间的常量、函数和类
16 echo PATH; ///message_board
17 echo getCommentTotal(); //300
18 $comment = new Comment();
19 //调用Article空间的常量、函数和类
20 echo \Article\PATH; ///article
21 echo \Article\getCommentTotal(); //100
22 $article_comment = new \Article\Comment();
23 ?>

然后我的确得到了Article空间的元素数据。

2. 子空间
命名空间的调用语法像文件路径一样是有道理的,它允许我们自定义子空间来描述各个空间之间的关系。

 

article和message board这两个模块其实都是处于同一个blog项目内。如果用命名空间来表达它们的关系,是这样

 1 <?php
 2 //我用这样的命名空间表示处于blog下的article模块
 3 namespace Blog\Article;
 4 class Comment { }
 5 
 6 //我用这样的命名空间表示处于blog下的message board模块
 7 namespace Blog\MessageBoard;
 8 class Comment { }
 9 //调用当前空间的类
10 $comment = new Comment();
11 //调用Blog\Article空间的类
12 $article_comment = new \Blog\Article\Comment();
13 ?>

而且,子空间还可以定义很多层次,比如说 Blog\Article\Archives\Date

 

3. 公共空间
我有一个common_inc.php脚本文件,里面有一些好用的函数和类:

 

1 <?php
2 function getIP() { }
3 class FilterXSS { }
4 ?>

在一个命名空间里引入这个脚本,脚本里的元素不会归属到这个命名空间。如果这个脚本里没有定义其它命名空间,它的元素就始终处于公共空间中

1 <?php
2 namespace Blog\Article;
3 //引入脚本文件
4 include './common_inc.php';
5 $filter_XSS = new FilterXSS(); //出现致命错误:找不到Blog\Article\FilterXSS类
6 $filter_XSS = new \FilterXSS(); //正确 \元素名
7 ?>

调用公共空间的方式是直接在元素名称前加 \ 就可以了,否则PHP解析器会认为我想调用当前空间下的元素。除了自定义的元素,还包括PHP自带的元素,都属于公共空间

要提一下,其实公共空间的函数和常量不用加 \ 也可以正常调用(不明白PHP为什么要这样做),但是为了正确区分元素,还是建议调用函数的时候加上 \

 

 

4. 名称术语
在说别名和导入之前,需要知道关于空间三种名称的术语,以及PHP是怎样解析它们的。官方文档说得非常好,我就直接拿来套了。

1.非限定名称,或不包含前缀的类名称,例如 $comment = new Comment();。如果当前命名空间是Blog\Article,Comment将被解析为Blog\Article\Comment。如果使用Comment的代码不包含在任何命名空间中的代码(全局空间中),则Comment会被解析为Comment。

2.限定名称,或包含前缀的名称,例如 $comment = new Article\Comment();。如果当前的命名空间是Blog,则Comment会被解析为Blog\Article\Comment。如果使用Comment的代码不包含在任何命名空间中的代码(全局空间中),则Comment会被解析为Comment。

3.完全限定名称,或包含了全局前缀操作符的名称,例如 $comment = new \Article\Comment();。在这种情况下,Comment总是被解析为代码中的文字名(literal name)Article\Comment。
 

其实可以把这三种名称类比为文件名(例如 comment.php)、相对路径名(例如 ./article/comment.php)、绝对路径名(例如 /blog/article/comment.php),这样可能会更容易理解。

 1 <?php
 2 //创建空间Blog
 3 namespace Blog;
 4 class Comment { }
 5 //非限定名称,表示当前Blog空间
 6 //这个调用将被解析成 Blog\Comment();
 7 $blog_comment = new Comment();
 8 //限定名称,表示相对于Blog空间
 9 //这个调用将被解析成 Blog\Article\Comment();
10 $article_comment = new Article\Comment(); //类前面没有反斜杆\
11 //完全限定名称,表示绝对于Blog空间
12 //这个调用将被解析成 Blog\Comment();
13 $article_comment = new \Blog\Comment(); //类前面有反斜杆\
14 //完全限定名称,表示绝对于Blog空间
15 //这个调用将被解析成 Blog\Article\Comment();
16 $article_comment = new \Blog\Article\Comment(); //类前面有反斜杆\
17 
18 //创建Blog的子空间Article
19 namespace Blog\Article;
20 class Comment { }
21 ?>

 

5. 别名和导入
别名和导入可以看作是调用命名空间元素的一种快捷方式。PHP并不支持导入函数或常量

它们都是通过使用use操作符来实现:

 1 <?php
 2 namespace Blog\Article;
 3 class Comment { }
 4 
 5 //创建一个BBS空间(我有打算开个论坛)
 6 namespace BBS;
 7 //导入一个命名空间
 8 use Blog\Article;
 9 //导入命名空间后可使用限定名称调用元素
10 $article_comment = new Article\Comment();
11 //为命名空间使用别名
12 use Blog\Article as Arte;
13 //使用别名代替空间名
14 $article_comment = new Arte\Comment();
15 //导入一个类
16 use Blog\Article\Comment;
17 //导入类后可使用非限定名称调用元素
18 $article_comment = new Comment();
19 //为类使用别名
20 use Blog\Article\Comment as Comt;
21 //使用别名代替空间名
22 $article_comment = new Comt();
23 ?>

我注意到,如果导入元素的时候,当前空间有相同的名字元素将会怎样?显然结果会发生致命错误

 1 <?php
 2 namespace Blog\Article;
 3 class Comment { }
 4 
 5 namespace BBS;
 6 class Comment { }
 7 Class Comt { }
 8 
 9 //导入一个类
10 use Blog\Article\Comment;
11 $article_comment = new Comment(); //与当前空间的Comment发生冲突,程序产生致命错误
12 //为类使用别名
13 use Blog\Article\Comment as Comt;
14 $article_comment = new Comt(); //与当前空间的Comt发生冲突,程序产生致命错误
15 ?>

 

6. 动态调用
PHP提供了namespace关键字和__NAMESPACE__魔法常量动态的访问元素,__NAMESPACE__可以通过组合字符串的形式来动态访问:

 1 <?php
 2 namespace Blog\Article;
 3 const PATH = '/Blog/article';
 4 class Comment { }
 5 
 6 //namespace关键字表示当前空间
 7 echo namespace\PATH; ///Blog/article
 8 $comment = new namespace\Comment();
 9 //魔法常量__NAMESPACE__的值是当前空间名称
10 echo __NAMESPACE__; //Blog\Article
11 //可以组合成字符串并调用
12 $comment_class_name = __NAMESPACE__ . '\Comment';
13 $comment = new $comment_class_name();
14 ?>

字符串形式调用问题

上面的动态调用的例子中,我们看到了字符串形式的动态调用方式,如果要使用这种方式要注意两个问题。

1. 使用双引号的时候特殊字符可能被转义

 

1 <?php
2 namespace Blog\Article;
3 class name { }
4 //我是想调用Blog\Article\name
5 $class_name = __NAMESPACE__ . "\name"; //但是\n将被转义为换行符
6 $name = new $class_name(); //发生致命错误
7 ?>

2. 不会认为是限定名称

PHP在编译脚本的时候就确定了元素所在的空间,以及导入的情况。而在解析脚本时字符串形式调用只能认为是非限定名称完全限定名称,而永远不可能是限定名称。

 1 <?php
 2 namespace Blog;
 3 //导入Common类
 4 use Blog\Article\Common;
 5 //我想使用非限定名称调用Blog\Article\Common
 6 $common_class_name = 'Common';
 7 //实际会被当作非限定名称,也就表示当前空间的Common类,但我当前类没有创建Common类
 8 $common = new $common_class_name(); //发生致命错误:Common类不存在
 9 //我想使用限定名称调用Blog\Article\Common
10 $common_class_name = 'Article\Common';
11 //实际会被当作完全限定名称,也就表示Article空间下的Common类,但我下面只定义了Blog\Article空间而不是Article空间
12 $common = new $common_class_name(); //发生致命错误:Article\Common类不存在
13 
14 namespace Blog\Article;
15 class Common { }
16 ?>

本文引用自:http://www.jb51.net/article/36389.htm

posted @ 2014-06-13 10:01  科学家会武术  阅读(314)  评论(0编辑  收藏  举报