前言
命名空間不算新東西了,在PHP5.3.0之后就存在。曾經(jīng)學(xué)次c#的時(shí)候接觸過(guò)命名空間這個(gè)概念剑刑,后來(lái)發(fā)現(xiàn)php也出現(xiàn)墨吓,但是當(dāng)時(shí)認(rèn)為\符號(hào)來(lái)使用命名空間很丑陋球匕,一直不敢興趣,現(xiàn)在我覺(jué)得\符越來(lái)越好看帖烘,人的眼光總是在進(jìn)步亮曹。
命名空間是新時(shí)代PHP不可或缺的一部分,它是現(xiàn)代主流php框架的基石秘症。
什么是命名空間
說(shuō)白了照卦,命名空間是解決文件、函數(shù)乡摹、類(lèi)役耕、常量等的重名問(wèn)題,它虛擬了類(lèi)似操作系統(tǒng)中文件系統(tǒng)的目錄結(jié)構(gòu)聪廉,保證PHP組件和框架的全局唯一瞬痘。
比如我在在a目錄下有個(gè)db.php,在b目錄下也有個(gè)db.php板熊。那么按照古老的方式肯定是通過(guò)類(lèi)名保持和文件一致來(lái)區(qū)別.
例如a目錄下的命名為:
class A_DB{}
b目錄下的命名為:
class B_DB{}
編寫(xiě)函數(shù)時(shí)可能通過(guò)每次加上一個(gè)function_exist的判斷去定義是否重名框全。
常量的定義同理,都是可以有解決沖突的方案干签,但是現(xiàn)在composer盛行津辩,PHP生態(tài)環(huán)境極好,各種組件層出不窮,我們不能保證所有組件的命名都不一樣喘沿,并且這種通過(guò)文件名和類(lèi)名來(lái)做區(qū)分十分不優(yōu)雅情萤,如果目錄層級(jí)很深,那么類(lèi)名可能會(huì)十分冗長(zhǎng)摹恨。顯然筋岛,我們應(yīng)該選擇更先進(jìn)的方法。
聲明命名空間
每個(gè)php文件的第一個(gè)命名空間必須聲明在php的頂部晒哄,在<?php標(biāo)簽之后的第一行做聲明睁宰。命名空間是通過(guò)namespace
聲明,然后跟上一個(gè)空格寝凌,再然后跟上命名空間的名稱(chēng)柒傻,最后用;符號(hào)結(jié)尾。
例如较木,聲明一個(gè)名稱(chēng)為A的命名空間:
<?php
namespace A;
在這個(gè)命名空間后面的所有php類(lèi)红符,函數(shù),接口伐债,常量都在這個(gè)A命名空間里面预侯。
同一個(gè)文件中定義多個(gè)命名空間
我們先創(chuàng)建一個(gè)app目錄:
mkdir app
命名空間是通過(guò)namespace
來(lái)聲明的,通過(guò)use
關(guān)鍵字來(lái)引入的峰锁。
創(chuàng)建a.php
namespace A;
const NUM = 1;
function output()
{
echo "A output\n";
}
namespace A2;
const NUM = 2;
function output(){
echo "A2 output\n";
}
//通過(guò)關(guān)鍵字use來(lái)引入命名空間
use A;
use A2;
echo "NUM from A:" . A\NUM . "\n";
echo "NUM from A2:" . A2\NUM . "\n";
A\output();
A2\output();
運(yùn)行結(jié)果如下:
NUM from A:1
NUM from A2:2
A output
A2 output
上面我們定義了兩個(gè)命名空間萎馅,分別是A和A2,這兩個(gè)命名空間下面有常量NUM虹蒋,函數(shù)output糜芳,是的,故意命名成一模一樣的魄衅,用來(lái)測(cè)試命名空間是否有效峭竣。這個(gè)時(shí)候我們通過(guò)use引入后,A\NUM可以根據(jù)在A命名空間下找到它的NUM晃虫,等于1皆撩,同理A2\NUM等于2,而output函數(shù)也是通過(guò)A\output()傲茄,A2\output()找到各自的函數(shù)毅访,并且輸出各自的內(nèi)容沮榜。
我們初嘗命名空間盘榨,知道用namespace
定義命名空間,用use
引入命名空間蟆融,通過(guò)命名空間\常量
,命名空間\函數(shù)名
調(diào)用草巡。
注意:命名空間的定義和文件名乃至目錄結(jié)構(gòu)沒(méi)有任何關(guān)系,比如這里的namespace A完全可以換成namespace Apple型酥,沒(méi)有任何關(guān)系山憨,只要調(diào)用正確即可查乒。
在同一個(gè)文件中聲明不建議采用上述寫(xiě)法,建議用大括號(hào)包起來(lái)郁竟,如下:
<?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();
}
以上代碼輸出的結(jié)果和上面是一樣的玛迄,這種寫(xiě)法更佳。
雖然php允許在一個(gè)php文件定義多個(gè)命名空間棚亩,但是違背了“一個(gè)文件定義一個(gè)類(lèi)”的良好實(shí)踐蓖议。一個(gè)文件定義一個(gè)類(lèi),只聲明一個(gè)命名空間讥蟆,這樣更清晰簡(jiǎn)潔勒虾。
在兩個(gè)文件中使用命名空間
創(chuàng)建a.php:
<php
namespace A;
const NUM = 1;
class DB
{
public function output()
{
echo "DB from A\n";
}
}
創(chuàng)建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();
然后執(zhí)行b.php文件,輸出如下:
NUM from namespace A:1
NUM from namespace B:2
DB from A
DB from B
這次沒(méi)有使用函數(shù)瘸彤,通過(guò)類(lèi)來(lái)實(shí)踐修然,證明類(lèi)在命名空間下也是有效的。
常量不多說(shuō)质况,它輸出還是正常的愕宋,這里定義了同名的DB類(lèi),但是它們并沒(méi)有沖突结榄,輸出了各自的內(nèi)容掏婶。
有人問(wèn)為什么調(diào)用$bObject = new DB();
為什么不需要寫(xiě)成$bObject = new B\DB();
,前面為什么不\號(hào)也可以調(diào)用潭陪,因?yàn)槲覀儓?zhí)行的是b.php文件雄妥,當(dāng)前代碼是屬于B這個(gè)命名空間的,默認(rèn)已經(jīng)加上了?依溯。
我們可以嘗試加上\號(hào)老厌,報(bào)錯(cuò)如下:
PHP Fatal error: Uncaught Error: Class 'B\B\DB' not found in b.php
所以,我們?cè)诋?dāng)前命名空間是不需要加\B的黎炉。
全局空間
如果沒(méi)有定義任何命名空間枝秤,所有的類(lèi)與函數(shù)的定義都是在全局空間。在名稱(chēng)前加上前綴 \ 表示該名稱(chēng)是全局空間中的名稱(chēng)慷嗜。
創(chuàng)建comon.php
<?php
function test()
{
echo "test from common.php\n";
}
創(chuàng)建a.php
<php
namespace A;
function test()
{
echo "test from a.php\n";
}
include "common.php";
test(); //調(diào)用a.php
\test(); //調(diào)用common.php
執(zhí)行a.php淀弹,結(jié)果下:
test from a.php
test from common.php
其實(shí)php官方把開(kāi)頭帶有\(zhòng)符號(hào)的調(diào)用叫做完全限定名稱(chēng),實(shí)際上就是當(dāng)前整個(gè)作用于有效的庆械,比如上面引入了common.php薇溃,里面有一個(gè)test(),那么就應(yīng)該通過(guò)\test()調(diào)用缭乘。當(dāng)然這是在重名下的調(diào)用沐序,如果common中的test叫做test2,那么可以直接test2();調(diào)用,應(yīng)為在A這個(gè)命名空間下沒(méi)有和它沖突的函數(shù)名策幼。
注意:訪問(wèn)任意全局類(lèi)邑时、函數(shù)或常量,都可以使用完全限定名稱(chēng)特姐,例如 \strlen() 或 \Exception 或 \INI_ALL晶丘。
例如:
<?php
namespace A;
$str = "123";
function strlen($string){
return \strlen($string) + 1;
}
echo \strlen($str). "\n";
echo strlen($str) . "\n";
輸出結(jié)果:
3
4
顯然當(dāng)前a.php文件中strlen和系統(tǒng)函數(shù)strlen重名了,我們通過(guò)\strlen($str)調(diào)用系統(tǒng)的函數(shù)唐含, strlen($str)是調(diào)用命名空間A的函數(shù)铣口,只不過(guò)它內(nèi)部又是通過(guò)系統(tǒng)函數(shù)strlen實(shí)現(xiàn)的。
命名空間的導(dǎo)入和別名
在我們的實(shí)際項(xiàng)目中遇到的命名空間的層次會(huì)比較深觉壶,比如:
<?php
use App\Component\Net\HttpResponsePostTool;
//這里是偽代碼
$postobject = new HttpResponsePostTool('http://google.com');
$postobject->go();
這個(gè)HttpResponsePostTool很長(zhǎng)脑题,可以用as關(guān)鍵字定義別名:
<?php
use App\Component\Net\HttpResponsePostTool as MyPost;
//這里是偽代碼
$postobject = new MyPost('http://google.com');
$postobject->go();
這樣是否優(yōu)雅很多。
多重導(dǎo)入
雖然良好實(shí)踐是在一個(gè)php文件中做一個(gè)命名空間的聲明铜靶,但是這不妨礙我們導(dǎo)入多個(gè)命名空間叔遂,而且這是經(jīng)常用的。
php允許只使用一次use争剿,通過(guò)逗號(hào)分割命名空間進(jìn)行導(dǎo)入已艰,最后一個(gè)命名空間加上分號(hào),比如:
<?php
use A,
B,
C;
但是不建議這么寫(xiě)蚕苇,不利于閱讀哩掺,最好每行都使用use引入:
<?php
use A;
use B;
use C;
了解以上,基本就能使用命名空間進(jìn)行開(kāi)發(fā)了涩笤。