以下是我对命名空间的一些体会和理解,最好先看下php的官方文档再来看此文,会更好一点。由于水平有限,文中如有纰漏,希望指出
前言
在php5.3以上的版本中,php引入了命名空间(以下称呼为namespace)这个性能。个人认为,这是非常重要的一项改变。之所以说重要,并不是说namespace本身有多么牛逼或者多高的技术含量。而是由于命名空间的引入,引起了一些列的连锁反应,这些连锁反应给我们现在phper开发者提供了另一种可能。
现在的开发方式
我们在以往开发应用的时候,即使是非常简单的应用,也经常使用php的那些框架,他们通常很重、很大、很难学,更要命的是,还很多,什么Yii2、Thinkphp、CI、ZendFrame、Laravel......好吧我不想说了。
另一种可能
现在呢,我们似乎有了另外一种选择。依靠composer包管理工具,加载packagist上面那些优秀的组件,然后通过composer提供的自动加载机制,将组建应用到项目中去。我们的开发似乎变成了摆积木,我们要做的事情只是将组件恰当的组合起来,怎么样,听着是不是很心动是么?全世界优秀的轮子随我用,这个feel倍爽!!!哦,离题有点远了,那么这和本文的主题namespacey又有什么关系呢?当然有关系了,自动加载的基石namespace
实现另外一种可能很重要的一步就是,将packagist组件自动加载到应用中。由于php的_autoload有各种各样的实现方式,为此php-fig(php framework interop group一个php组织)提出了psr-0(已经废弃)和psr-4,这两个专门自动加载的解决方案,其核心思想就是:将命名空间和实际的磁盘物理路径建立映射
(ps : 到目前为止spr-0、spr-4只是推荐的自动加载解决方案,并没有强制,但事实上,基本都采用了这种自动加载方式。除了spr-和spr-4外,composer还支持file 和classmap的方式)
为什么会有命名空间,它解决了什么问题?
命名空间出现的根本原因是为了解决命名冲突的问题,个人认为主要是为了解决以下命名冲突
- 用户自定义的接口/类/trait/函数/常量 同 php系统提供的 接口/类/trait/函数/常量命名冲突
- 用户自定义的接口/类/trait/函数/常量 同 第三方框架提供的 接口/类/trait/函数/常量命名冲突
举个例子
我们经常使用var_dump进行断点调试,这个是系统提供给我们的内置函数。但是呢,有的程序员对var_dump输出的数据格式不是很满意,希望自己写一个var_dump函数(var_dump用时间长了,叫其他的名字不习惯),自己格式化其输出信息,但是报错!
<?php
//namespace mydubug;
//如果将上面一行的注释打开就不会再报错,只不过调用的时候需要使用mydebug\var_dump的形式了
function var_dump($message)
{
echo "<br/>========================================</br>"
echo $message;
echo "<br/>========================================</br>"
}
/**
* 报错信息:PHP Fatal error: Cannot redeclare var_dump()
*/
命名空间的一些基础概念?
我将采用类比的方法来说明这些高深的概念,尽量能让大家清楚。
全局命名空间
接触过linux系统的同学都应该知道根目录"/"吧,我们可以将全局命名空间想象为linux的根目录,在该目录下存放的都是php本身提供的各种接口/类/trait/函数/常量。
完全限定名称
以全局命名空间为前缀的名称,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();我们可以将其类比为绝对路径
限定名称
不以全局命名空间为前缀的名称,例如,$a = new subnamespace\foo();或subnamespace\foo::staticmethod();我们可以将其类比为相对路径
非限定名称
不包含任何前缀的名称,例如, $a=new foo(); 或 foo::staticmethod();我们可以将其类比一个问文件名称或者目录名称
php中的那些要素会受到命名空间的影响?
首先肯定一点,并不是所有的元素都受到命名空间的影响,比如说变量就不会,只有以下php要素会受到影响:
- 类(class)
- 接口(interface)
- trait
- 常量(constant)
- 方法名(function,我认为object里面的方法是method)
什么是别名/导入?命名空间的解析规则是什么?两者又有什么关系?
什么是别名/导入?
我们可以认为别名/导入,类似是windows下的快捷方式或者是linux下的软链,通过该快捷方式我们可以链接到,别的空间下的class、interface、trait、constant、function,甚至可以是单纯的连接到别的命名空间而不特指该空间下的任何元素。下面举个例子
<?php
namespace foo;
use My\Full\Classname as Another;
// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;
$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
?>
当我们的代码中有以别名打头的情况时,php就会尝试链接到别名对应的命名空间中的元素,或是别名对应的命名空间,然后拼接剩余的名称,如果有的话。例子中NSname\subns\func(); 检查到NSname是别名,所以先链接到MyFullNSname, 然后拼接剩余名称My\Full\NSname\subns\func();
(ps:导入的名称必须是完全限定的,不会根据当前的命名空间作相对解析,前导的反斜杠是不必要的也不推荐的)
命名空间的解析规则是什么?
一图胜千言,还是上图吧
从图中,可以看出主要是从完全限定名和非完全限定名这两块进行分析的。
两者的关系是什么?
由上图,我们可以知道,限定名称和非限定名称的解析都会受到别名的影响。
而且,别名的优先级比当前命名空间的优先级高(很多地方都没有说)。
也就是说,在一个命名空间下,限定名称和非限定名称会先尝试寻找别名替换,如果找不到对应的别名,才会将当前的命名空间追加到现在的名字前面组成完全限定名
<?php
//b.php文件
namespace Test\Top;
class B
{
public function Test()
{
echo __FILE__, "\r\n";
}
}
<?php
//a.php
namespace Top;
use Test\Top as Top;
require __DIR__ . DIRECTORY_SEPARATOR . 'b.php';
Class B
{
function Test()
{
echo __FILE__,"\r\n";
}
}
(new Top\B())->Test();
a.php和b.php两个文件在同一个目录下:在b.php文件中,在namespace Top下面,拥有别名Test\Top as Top,所以new Top\B() 会被解释成 new Test\Top\B(),也就是将别名进行了替换; 而并不是new Top\Top\B(),将当前空间名添加到现在的名字前面。所以此时的运行结果是:xxxx/b.php
命名空间的一些易错点,仅个人体会
1、class 、interface 、traits和const 、function尝试执行的加载策略不同
2、一个文件中,只有第一个namespace声明命名空间前不能有任何字符,特别是bom头(看不见但是存在),其他的namespace不做限制。
3、namespace本身并不分区大小写,相同名字的namespace认为是同一个空间
<?php
namespace Test{
function showName()
{
echo "This is a test in namespace Test";
}
}
namespace test{
function showName()
{
echo "This is a test in namespace test";
}
}
//报错:PHP Fatal error: Cannot redeclare test\showName()
参考资料
http://php.net/manual/zh/lang...
《Modern php》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。