命名空间

命名空间是一种封装事物的方法,在php中,可以看做是组织代码的一种形式。例如,在操作系统中用目录来把不同的文件分组,这样在不同的目录下可以存在相同文件名,目录就相当于“命名空间”。

命名空间解决的问题

1、用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。比如:用户定义了一个函数 is_numeric(),如果在代码中定义一个命名空间namespace user,用户在调用时加上命名空间\user\is_numeric()就可以调用自己的函数了。
2、为很长的标识符名称创建一个简短的别名,提高源代码的可读性。栗子:随着项目的扩大,类名可能会很长,例如A_B_C_D,如果使用命名空间,就可以这样用,在D的类里面定义命名空间:namespace \A_B_C_,在定义D的时候,类名就可以直接写D。

定义命名空间

用关键字namespace关键字声明,如果一个文件中包含命名空间,它必须在其他所有代码之前声明,declare关键字除外。同一个命名空间可以定义在多个文件中,即允许将同一个命名空间的内容分割存放在不同的文件中。
tips:如果用define()定义命名空间的常量,需要在变量名称前加上命名空间,栗子:define('test\HELLO', 'Hello world!');表示在命名空间test下定义常量HELLO,如果不加命名空间,表示在全局范围内定义常量。
在使用一个变量时,如果变量名前不添加任何命名空间,表示该变量是当前文件命名空间的变量;变量名前加\,表示全局范围的变量,栗子:
<pre>
namespace NS;
define(NAMESPACE .'\foo','111’); //在当前命名空间定义常量foo
define('foo','222’);//全局范围定义常量foo
echo foo; // 111,默认取当前命名空间的常量
echo \foo; // 222,取全局范围的常量
echo \NS\foo // 111. 去NS命名空间的常量
echo NS\foo // 报错,因为不是以\开头,表示取\NS\NS\foo,这个常量没有被定义。
</pre>

定义子命名空间

类似文件系统中的文件子目录,命名空间也可以分层,栗子:namespace A\B\C,子命名空间的好处只是更好的表达代码的层级关系,用法和普通的命名空间没有区别,和所谓的“父空间”并没有关系,\A\B\C和\A\B没有关系。

同一个文件中定义多个命名空间:
简单组合方式,在命名空间后直接写代码,栗子:
<pre>
namespace MyProject;

const CONNECT_OK = 1;
class Connection { }
function connect() { }

namespace AnotherProject;

const CONNECT_OK = 1;
class Connection { }
function connect() { }
</pre>
大括号方式,栗子:
<pre>
namespace MyProject {

const CONNECT_OK = 1;
class Connection { }
function connect() { }
}

namespace AnotherProject {

const CONNECT_OK = 1;
class Connection { }
function connect() { }
}
</pre>

使用命名空间

1、不包含前缀,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。
2、包含前缀: $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。
3、包含全局前缀:例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。

命名空间和动态语言的关系

php中有一些魔术方法,例如:METHODCLASS等,这些方法的输出结果和代码所在的位置有关,如果想用这些魔术方法输出类所在的命名空间,定义类的实例的时候要把命名空间前缀加上,否则不会打印出命名空间的信息。栗子:
<pre>
namespace namespacename;
class classname
{
function __construct()
{
echo METHOD,"\n";
}
}
function funcname()
{
echo FUNCTION,"\n";
}
const constname = "namespaced";

$a = 'classname';
$obj = new $a; //输出classname::__construct,因为定义的时候没有加上namespacename前缀
$b = 'funcname';
$b(); // 同理,只输出funcname
echo constant('constname'), "\n"; // 只输出global

$a = '\namespacename\classname';
$obj = new $a; // 定义的时候加上前缀,因此打印出 namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // 同样打印出namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // 输出 namespacename\funcname
$b = '\namespacename\funcname';
$b(); // 同样输出 namespacename\funcname
echo constant('\namespacename\constname'), "\n"; //输出常量的时候不会输出命名空间信息
echo constant('namespacename\constname'), "\n"; //输出 namespaced
</pre>

namespace关键字

关键字 namespace 可用来显式访问当前命名空间或子命名空间中的元素。它等价于类中的 self 操作符。栗子:
namespace MyProject;//定义当前命名空间是MyProject
namespace\func(); // 调用函数MyProject\func()

__NAMESPACE__常量

常量NAMESPACE的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串。

使用命名空间:别名/导入

如果我们想在当前文件引用另外一个文件的命名空间,而另外一个命名空间很长,如果每用一次那个空间类都要写很长的类名,这样就会使代码很难看,命名空间有个特征:允许通过别名引用或导入外部的完全限定名称,类似于类unix文件系统中可以创建对其他的文件或目录的软连接。

所有支持命名空间的PHP版本支持三种别名或导入方式:为类名称使用别名、为接口使用别名或为命名空间名称使用别名。PHP 5.6开始允许导入函数或常量或者为它们设置别名。

别名是通过操作符 use 来实现的,栗子:
<pre>
namespace foo;
use My\Full\Classname as Another; //引入My\Full\Classname并起个别名Another

use My\Full\NSname;//相当于use My\Full\NSname as NSname

use ArrayObject;// 导入一个全局类

use function My\Full\functionName;//导入一个方法

use function My\Full\functionName as func;//导入一个方法并起个别名

use const My\Full\CONSTANT;//导入一个常量

$obj = new namespace\Another; // 实例化 foo\Another 对象
$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象,如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象
func(); //调用方法My\Full\functionName
echo CONSTANT; // 输出常量My\Full\CONSTANT
</pre>

后备全局函数/常量

在一个命名空间中,当 PHP 遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。类名称总是解析到当前命名空间中的名称。栗子:
<pre>
namespace A\B\C;
class Exception extends \Exception {}

$a = new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象
$b = new \Exception('hi'); // $b 是类 Exception 的一个对象

$c = new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类
</pre>
对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量。

名称解析的规则

1、对完全限定名称的函数,类和常量的调用在编译时解析。例如 new \A\B 解析为类 A\B。
2、所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间 A\B\C 被导入为 C,那么对 C\D\e() 的调用就会被转换为 A\B\C\D\e()。
3、在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间 A\B 内部调用 C\D\e(),则 C\D\e() 会被转换为 A\B\C\D\e() 。
4、非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间 A\B\C 导入为C,则 new C() 被转换为 new A\B\C() 。
5、在命名空间内部(例如A\B),对非限定名称的函数调用是在运行时解析的。例如对函数 foo() 的调用是这样解析的:在当前命名空间中查找名为 A\B\foo() 的函数尝试查找并调用 全局(global) 空间中的函数 foo()。
6、在命名空间(例如A\B)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用 new C() 及 new D\E() 的解析过程: new C()的解析:1、在当前命名空间中查找A\B\C类。2、尝试自动装载类A\B\C。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容