PHP命名空间(Namespace)的使用详解
对于命名空间,官方文档已经说得很详细[查看],我在这里做了一下实践和总结。
命名空间一个最明确的目的就是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误。这种情况下只要避免命名重复就可以解决,最常见的一种做法是约定一个前缀。
例:项目中有两个模块:article和message board,它们各自有一个处理用户留言的类Comment。之后我可能想要增加对所有用户留言的一些信息统计功能,比如说我想得到所有留言的数量。这时 候调用它们Comment提供的方法是很好的做法,但是同时引入各自的Comment类显然是不行的,代码会出错,在另一个地方重写任何一个 Comment也会降低维护性。那这时只能重构类名,我约定了一个命名规则,在类名前面加上模块名,像这样:Article_Comment、 MessageBoard_Comment
可以看到,名字变得很长,那意味着以后使用Comment的时候会写上更多的代码(至少字符多了)。并且,以后如果要对各个模块增加更多的一些整合 功能,或者是互相调用,发生重名的时候就需要重构名字。当然在项目开始的时候就注意到这个问题,并规定命名规则就能很好的避免这个问题。另一个解决方法可 以考虑使用命名空间。
注明:
本文提到的常量:PHP5.3开始const关键字可以用在类的外部。const和define都是用来声明常量的(它们的区别不详述),但是在命名空间里,define的作用是全局的,而const则作用于当前空间。我在文中提到的常量是指使用const声明的常量。
基础
命名空间将代码划分出不同的空间(区域),每个空间的常量、函数、类(为了偷懒,我下边都将它们称为元素)的名字互不影响, 这个有点类似我们常常提到的‘封装'的概念。
创建一个命名空间需要使用namespace关键字,这样:
//创建一个名为'Article'的命名空间
namespace Article;
?>
要注意的是,当前脚本文件的第一个命名空间前面不能有任何代码,下面的写法都是错误的:
//在脚本前面写了一些逻辑代码
<?php
$path = "/";
class Comment { }
namespace Article;
?>
//例二
//在脚本前面输出了一些字符
<html></html>
<?php
namespace Article;
?>
为什么要说第一个命名空间呢?因为同一脚本文件中可以创建多个命名空间。
下面我创建了两个命名空间,顺便为这两个空间各自添加了一个Comment类元素:
//创建一个名为'Article'的命名空间
namespace Article;
//此Comment属于Article空间的元素
class Comment { }
//创建一个名为'MessageBoard'的命名空间
namespace MessageBoard;
//此Comment属于MessageBoard空间的元素
class Comment { }
?>
在不同空间之间不可以直接调用其它元素,需要使用命名空间的语法:
namespace Article;
class Comment { }
namespace MessageBoard;
class Comment { }
//调用当前空间(MessageBoard)的Comment类
$comment = new Comment();
//调用Article空间的Comment类
$article_comment = new \Article\Comment();
?>
可以看到,在MessageBoard空间中调用article空间里的Comment类时,使用了一种像文件路径的语法: \空间名\元素名
除了类之外,对函数和常量的用法是一样的,下面我为两个空间创建了新的元素,并在MessageBoard空间中输出了它们的值。
namespace Article;
const PATH = '/article';
function getCommentTotal() {
return 100;
}
class Comment { }
namespace MessageBoard;
const PATH = '/message_board';
function getCommentTotal() {
return 300;
}
class Comment { }
//调用当前空间的常量、函数和类
echo PATH; ///message_board
echo getCommentTotal(); //300
$comment = new Comment();
//调用Article空间的常量、函数和类
echo \Article\PATH; ///article
echo \Article\getCommentTotal(); //100
$article_comment = new \Article\Comment();
?>
然后我的确得到了Article空间的元素数据。
子空间
命名空间的调用语法像文件路径一样是有道理的,它允许我们自定义子空间来描述各个空间之间的关系。
抱歉我忘了说,article和message board这两个模块其实都是处于同一个blog项目内。如果用命名空间来表达它们的关系,是这样:
//我用这样的命名空间表示处于blog下的article模块
namespace Blog\Article;
class Comment { }
//我用这样的命名空间表示处于blog下的message board模块
namespace Blog\MessageBoard;
class Comment { }
//调用当前空间的类
$comment = new Comment();
//调用Blog\Article空间的类
$article_comment = new \Blog\Article\Comment();
?>
而且,子空间还可以定义很多层次,比如说 Blog\Article\Archives\Date
公共空间
我有一个common_inc.php脚本文件,里面有一些好用的函数和类:
function getIP() { }
class FilterXSS { }
?>
在一个命名空间里引入这个脚本,脚本里的元素不会归属到这个命名空间。如果这个脚本里没有定义其它命名空间,它的元素就始终处于公共空间中:
namespace Blog\Article;
//引入脚本文件
include './common_inc.php';
$filter_XSS = new FilterXSS(); //出现致命错误:找不到Blog\Article\FilterXSS类
$filter_XSS = new \FilterXSS(); //正确
?>
调用公共空间的方式是直接在元素名称前加 \ 就可以了,否则PHP解析器会认为我想调用当前空间下的元素。除了自定义的元素,还包括PHP自带的元素,都属于公共空间。
要提一下,其实公共空间的函数和常量不用加 \ 也可以正常调用(不明白PHP为什么要这样做),但是为了正确区分元素,还是建议调用函数的时候加上 \
名称术语
在说别名和导入之前,需要知道关于空间三种名称的术语,以及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),这样可能会更容易理解。
我用了几个示例来表示它们:
//创建空间Blog
namespace Blog;
class Comment { }
//非限定名称,表示当前Blog空间
//这个调用将被解析成 Blog\Comment();
$blog_comment = new Comment();
//限定名称,表示相对于Blog空间
//这个调用将被解析成 Blog\Article\Comment();
$article_comment = new Article\Comment(); //类前面没有反斜杆\
//完全限定名称,表示绝对于Blog空间
//这个调用将被解析成 Blog\Comment();
$article_comment = new \Blog\Comment(); //类前面有反斜杆\
//完全限定名称,表示绝对于Blog空间
//这个调用将被解析成 Blog\Article\Comment();
$article_comment = new \Blog\Article\Comment(); //类前面有反斜杆\
//创建Blog的子空间Article
namespace Blog\Article;
class Comment { }
?>
其实之前我就一直在使用非限定名称和完全限定名称,现在它们终于可以叫出它们的名称了。
别名和导入
别名和导入可以看作是调用命名空间元素的一种快捷方式。PHP并不支持导入函数或常量。
它们都是通过使用use操作符来实现:
namespace Blog\Article;
class Comment { }
//创建一个BBS空间(我有打算开个论坛)
namespace BBS;
//导入一个命名空间
use Blog\Article;
//导入命名空间后可使用限定名称调用元素
$article_comment = new Article\Comment();
//为命名空间使用别名
use Blog\Article as Arte;
//使用别名代替空间名
$article_comment = new Arte\Comment();
//导入一个类
use Blog\Article\Comment;
//导入类后可使用非限定名称调用元素
$article_comment = new Comment();
//为类使用别名
use Blog\Article\Comment as Comt;
//使用别名代替空间名
$article_comment = new Comt();
?>
我注意到,如果导入元素的时候,当前空间有相同的名字元素将会怎样?显然结果会发生致命错误。
例:
namespace Blog\Article;
class Comment { }
namespace BBS;
class Comment { }
Class Comt { }
//导入一个类
use Blog\Article\Comment;
$article_comment = new Comment(); //与当前空间的Comment发生冲突,程序产生致命错误
//为类使用别名
use Blog\Article\Comment as Comt;
$article_comment = new Comt(); //与当前空间的Comt发生冲突,程序产生致命错误
?>
动态调用
PHP提供了namespace关键字和__NAMESPACE__魔法常量动态的访问元素,__NAMESPACE__可以通过组合字符串的形式来动态访问:
namespace Blog\Article;
const PATH = '/Blog/article';
class Comment { }
//namespace关键字表示当前空间
echo namespace\PATH; ///Blog/article
$comment = new namespace\Comment();
//魔法常量__NAMESPACE__的值是当前空间名称
echo __NAMESPACE__; //Blog\Article
//可以组合成字符串并调用
$comment_class_name = __NAMESPACE__ . '\Comment';
$comment = new $comment_class_name();
?>
字符串形式调用问题
上面的动态调用的例子中,我们看到了字符串形式的动态调用方式,如果要使用这种方式要注意两个问题。
1. 使用双引号的时候特殊字符可能被转义
namespace Blog\Article;
class name { }
//我是想调用Blog\Article\name
$class_name = __NAMESPACE__ . "\name"; //但是\n将被转义为换行符
$name = new $class_name(); //发生致命错误
?>
2. 不会认为是限定名称
PHP在编译脚本的时候就确定了元素所在的空间,以及导入的情况。而在解析脚本时字符串形式调用只能认为是非限定名称和完全限定名称,而永远不可能是限定名称。
namespace Blog;
//导入Common类
use Blog\Article\Common;
//我想使用非限定名称调用Blog\Article\Common
$common_class_name = 'Common';
//实际会被当作非限定名称,也就表示当前空间的Common类,但我当前类没有创建Common类
$common = new $common_class_name(); //发生致命错误:Common类不存在
//我想使用限定名称调用Blog\Article\Common
$common_class_name = 'Article\Common';
//实际会被当作完全限定名称,也就表示Article空间下的Common类,但我下面只定义了Blog\Article空间而不是Article空间
$common = new $common_class_name(); //发生致命错误:Article\Common类不存在
namespace Blog\Article;
class Common { }
?>
总结
我对PHP的命名空间刚刚接触,也不能随便给一些没有实践的建议。我个人认为命名空
间的作用和功能都很强大,如果要写插件或者通用库的时候再也不用担心重名问题。不过如果项目进行到一定程度,要通过增加命名空间去解决重名问题,我觉得工
作量不会比重构名字少。也不得不承认它的语法会对项目增加一定的复杂度,因此从项目一开始的时候就应该很好的规划它,并制定一个命名规范。
PHP命名空间(Namespace)的使用详解的更多相关文章
- PHP中的命名空间(namespace)及其使用详解
PHP中的命名空间(namespace)及其使用详解 晶晶 2年前 (2014-01-02) 8495次浏览 PHP php自5.3.0开始,引入了一个namespace关键字以及__NAMESPAC ...
- PHP命名空间(Namespace)的使用详解(转)
对于命名空间,官方文档已经说得很详细[查看],我在这里做了一下实践和总结. 命名空间一个最明确的目的就是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误.这种情况下只 ...
- include与__autoload与命名空间namespace与PSR4详解
1. include, require, include_once, require_once include和require是PHP中引入源文件最基本的用法,其他例如__autoload, name ...
- PHP深入浅出之命名空间(Namespace)的使用详解
对于命名空间,官方文档已经说得很详细[查看],我在这里做了一下实践和总结. 命名空间一个最明确的目的就是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误.这种情况下只 ...
- struts.xml 文件中的 namespace 属性图文详解
namespace:名称空间.默认值是""(空字符串). 名称空间+动作名称:构成了动作的访问路径
- python命名空间与闭包函数详解
python命名空间与闭包函数详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客主要介绍的知识点如下: 1>.三元运算 2>.命名空间 3>.globa ...
- PHP命名空间namespace使用小结
1.介绍一下php的命名空间 什么是命名空间?从广义上来说,命名空间是一种封装事物的方法.在很多地方都可以见到这种抽象概念.例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命 ...
- C#解析XML详解(XPath以及带命名空间NameSpace)
<?xml version="1.0" encoding="utf-8" ?> <bookstore> <book> < ...
- [转载]C++之using namespace std 详解与命名空间的使用
来源:https://blog.csdn.net/Bruce_0712/article/details/72824668 所谓namespace,是指标识符的各种可见范围.C++标准程序库中的所有标识 ...
随机推荐
- mysqldump 备份直接至压缩文件,提高备份及压缩时间
1.备份及压缩分开 备份mysqldump --single-transaction -hlocalhost --all-databases --triggers --routines --event ...
- angularjs的directive详解
Directive(指令)笔者认为是AngularJ非常强大而有有用的功能之一.它就相当于为我们写了公共的自定义DOM元素或CLASS属性或ATTR属性,并且它不只是单单如此,你还可以在它的基础上来操 ...
- 【LeetCode】30. Substring with Concatenation of All Words
You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...
- github从上传代码到发布静态网站
1.上传代码 第一步:建立git仓库 cd到你的本地项目根目录下,执行git命令 git init 第二步:将项目的所有文件添加到仓库中 git add . 如果想添加某个特定的文件,只需把.换成特定 ...
- 负载均衡,最理想使用 redis实现session共享
负载均衡在多台php服务器负载均衡的情况下,第一秒请求是a服务器,第二秒请求是b服务器, session必须放在一个公共的服务器,最理想是使用 redis实现session共享.内存的速度比磁盘访问快 ...
- Ubuntu 14.04—无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系 解决办法
在Ubuntu中使用sudo apt-get install安装是有时候会出现: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系 解决办法 这样的错误,这是因为更新源 ...
- 有关C语言学习的调查
有关C语言学习的调查 1.Q:你是怎么学习C语言的?(作业,实验,教材,其他),与你的高超技能相比,C语言的学习有什么经验和教训? A:之间在暑假的之后自己有买了一本C PRIME PLUS 来看基本 ...
- Python基础知识学习_Day6
一.time&datetime模块 常用选项如下: import time print(time.asctime()) #返回时间格式 print(time.localtime())#返回本地 ...
- Weblogic概念与基础
WebLogic Server 1.BEA WebLogic Server 1.Domain:域是个逻辑概念,用来组织管理一系列的应用服务器实例,中心配置文件Config.xml. 2.Cluster ...
- css animation 动画的制作
上效果 效果描述:原来这些图片都绝对定位在最右边,并有一个css3 3D的旋转初始效果.随着动画的开始,依次向左移动,并旋转到0度.(主要用于引导页步骤的描述) 上代码: html布局 <div ...