PHP命名空间

前言

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

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

什么是命名空间

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

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

例如a目录下的命名为:框架

class A_DB{}

b目录下的命名为:函数

class B_DB{}

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

声明命名空间

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

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

<?php
namespace A;

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

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

咱们先建立一个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;

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

补充:

命名空间的三种访问方式(和相对路径与绝对路径类似)
A. 非限定名称访问方式
B. 限定名称访问方式
C. 彻底限定名称访问方式

<?php
namespace app\get1
function getUser () {
  return $username1;
}
namespace get2
function getUser () {
  return $username2;
}

getUser(); // 非限定名称访问方式
\app\get1\getUser(); //彻底限定名称访问方式,从根路径开始,相似绝对路径
app\get1\getUser(); //限定名称访问方式,不是从根路径开始,相似相对路径

 

命名空间的引入机制

1)空间的引入:关键字use,注意:当移入空间后,必需要用限定名称访问方式访问引入空间里面的函数(或类、常量),不能使用非限定名称方式访问,这样会访问到当前命名空间下的函数(或类、常量)。2)空间类元素的引入:关键字use。注意:只能引入类,而后可使用非限定名称访问。

相关文章
相关标签/搜索