新时代PHP:命名空间

前言

命名空间不算新东西了,在PHP5.3.0之后就存在。曾经学次c#的时候接触过命名空间这个概念,后来发现php也出现,但是当时认为\符号来使用命名空间很丑陋,一直不敢兴趣,现在我觉得\符越来越好看,人的眼光总是在进步。

命名空间是新时代PHP不可或缺的一部分,它是现代主流php框架的基石。

什么是命名空间

说白了,命名空间是解决文件、函数、类、常量等的重名问题,它虚拟了类似操作系统中文件系统的目录结构,保证PHP组件和框架的全局唯一。

比如我在在a目录下有个db.php,在b目录下也有个db.php。那么按照古老的方式肯定是通过类名保持和文件一致来区别.

例如a目录下的命名为:

class A_DB{}

b目录下的命名为:

class B_DB{}

编写函数时可能通过每次加上一个function_exist的判断去定义是否重名。
常量的定义同理,都是可以有解决冲突的方案,但是现在composer盛行,PHP生态环境极好,各种组件层出不穷,我们不能保证所有组件的命名都不一样,并且这种通过文件名和类名来做区分十分不优雅,如果目录层级很深,那么类名可能会十分冗长。显然,我们应该选择更先进的方法。

声明命名空间

每个php文件的第一个命名空间必须声明在php的顶部,在<?php标签之后的第一行做声明。命名空间是通过namespace声明,然后跟上一个空格,再然后跟上命名空间的名称,最后用;符号结尾。

例如,声明一个名称为A的命名空间:

<?php
namespace A; 

在这个命名空间后面的所有php类,函数,接口,常量都在这个A命名空间里面。

同一个文件中定义多个命名空间

我们先创建一个app目录:

mkdir app

命名空间是通过namespace来声明的,通过use关键字来引入的。
创建a.php

namespace A;

const NUM = 1;
function output()
{
    echo "A output\n";
}

namespace A2;

const NUM = 2;
function output(){
    echo "A2 output\n";
}

//通过关键字use来引入命名空间
use A;
use A2;

echo "NUM from A:" . A\NUM . "\n";
echo "NUM from A2:" . A2\NUM . "\n";
A\output();
A2\output();

运行结果如下:

NUM from A:1
NUM from A2:2
A output
A2 output

上面我们定义了两个命名空间,分别是A和A2,这两个命名空间下面有常量NUM,函数output,是的,故意命名成一模一样的,用来测试命名空间是否有效。这个时候我们通过use引入后,A\NUM可以根据在A命名空间下找到它的NUM,等于1,同理A2\NUM等于2,而output函数也是通过A\output(),A2\output()找到各自的函数,并且输出各自的内容。

我们初尝命名空间,知道用namespace定义命名空间,用use引入命名空间,通过命名空间\常量,命名空间\函数名调用。

注意:命名空间的定义和文件名乃至目录结构没有任何关系,比如这里的namespace A完全可以换成namespace Apple,没有任何关系,只要调用正确即可。

在同一个文件中声明不建议采用上述写法,建议用大括号包起来,如下:

<?php
namespace A {
const NUM = 1;
function output()
{
    echo "A output\n";
}
}

namespace A2 {
const NUM = 2;
function output(){
    echo "A2 output\n";
}
}

namespace {
use A;
use A2;

echo "NUM from A:" . A\NUM . "\n";
echo "NUM from A2:" . A2\NUM . "\n";
A\output();
A2\output();
}

以上代码输出的结果和上面是一样的,这种写法更佳。

虽然php允许在一个php文件定义多个命名空间,但是违背了“一个文件定义一个类”的良好实践。一个文件定义一个类,只声明一个命名空间,这样更清晰简洁。

在两个文件中使用命名空间

创建a.php:

<php
namespace A;

const NUM = 1;

class DB
{
    public function output()
    {
        echo "DB from A\n";
    }
}

创建b.php:

<?php
namespace B;

use A;

const NUM = 2;

class DB
{
    public function output()
    {
        echo "DB from B\n";
    }
}

include "a.php";//必须引入a.php,才能使用a.php中的命名空间
echo "NUM from namespace A:" . A\NUM . "\n";
echo "NUM from namespace B:" . NUM . "\n";

$aObject = new A\DB();
$aObject->output();

$bObject = new DB();
$bObject->output();

然后执行b.php文件,输出如下:

NUM from namespace A:1
NUM from namespace B:2
DB from A
DB from B

这次没有使用函数,通过类来实践,证明类在命名空间下也是有效的。
常量不多说,它输出还是正常的,这里定义了同名的DB类,但是它们并没有冲突,输出了各自的内容。

有人问为什么调用$bObject = new DB();为什么不需要写成$bObject = new B\DB();,前面为什么不\号也可以调用,因为我们执行的是b.php文件,当前代码是属于B这个命名空间的,默认已经加上了�。

我们可以尝试加上\号,报错如下:

PHP Fatal error: Uncaught Error: Class 'B\B\DB' not found in b.php

所以,我们在当前命名空间是不需要加\B的。

全局空间

如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间。在名称前加上前缀 \ 表示该名称是全局空间中的名称。

创建comon.php

<?php
function test()
{
    echo "test from common.php\n";
}

创建a.php

<php
namespace A;

function test()
{
    echo "test from a.php\n";
}
include "common.php";
test();   //调用a.php
\test(); //调用common.php

执行a.php,结果下:

test from a.php
test from common.php

其实php官方把开头带有\符号的调用叫做完全限定名称,实际上就是当前整个作用于有效的,比如上面引入了common.php,里面有一个test(),那么就应该通过\test()调用。当然这是在重名下的调用,如果common中的test叫做test2,那么可以直接test2();调用,应为在A这个命名空间下没有和它冲突的函数名。

注意:访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 或 \INI_ALL。

例如:

<?php
namespace A;

$str = "123";
function strlen($string){
    return \strlen($string) + 1;
}
echo \strlen($str). "\n";
echo strlen($str) . "\n";

输出结果:

3
4

显然当前a.php文件中strlen和系统函数strlen重名了,我们通过\strlen($str)调用系统的函数, strlen($str)是调用命名空间A的函数,只不过它内部又是通过系统函数strlen实现的。

命名空间的导入和别名

在我们的实际项目中遇到的命名空间的层次会比较深,比如:

<?php
use App\Component\Net\HttpResponsePostTool;
//这里是伪代码
$postobject = new HttpResponsePostTool('http://google.com');
$postobject->go();

这个HttpResponsePostTool很长,可以用as关键字定义别名:

<?php
use App\Component\Net\HttpResponsePostTool as MyPost;
//这里是伪代码
$postobject = new MyPost('http://google.com');
$postobject->go();

这样是否优雅很多。

多重导入

虽然良好实践是在一个php文件中做一个命名空间的声明,但是这不妨碍我们导入多个命名空间,而且这是经常用的。

php允许只使用一次use,通过逗号分割命名空间进行导入,最后一个命名空间加上分号,比如:

<?php
use    A,
       B,
       C;

但是不建议这么写,不利于阅读,最好每行都使用use引入:

<?php
use A;
use B;
use C;

了解以上,基本就能使用命名空间进行开发了。

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

推荐阅读更多精彩内容

  • 提出问题 解决问题--命名空间的使用 PHP的命名空间(namespace)是php5.3之后才有的。这个概念在C...
    洞房花猪阅读 2,834评论 0 3
  • 一、php可以做什么 php是一种可以在服务器端运行的编程语言,可以运行在Web服务器端。 php是一门后台编程语...
    空谷悠阅读 3,086评论 4 97
  • 一、 为什么需要命名空间(问题提出) 命名空间是ANSIC++引入的可以由用户命名的作用域,用来处理程序中 常见的...
    IvanRunning阅读 2,347评论 0 2
  • 作为一种通用开源脚本语言,PHP可以在不同的平台上运行(Windows、LinuxUnix、MacOS X 等),...
    阳明先生1208阅读 941评论 1 4
  • vector的迭代删除 map的迭代删除 vector的这种写法不够直观,for循环中最好不要改变容器妹红告诉我应...
    碎冰op阅读 830评论 0 0