命名空間是一種封裝事物的方法焙蚓,在php中鳖轰,可以看做是組織代碼的一種形式遣疯。例如雄可,在操作系統(tǒng)中用目錄來把不同的文件分組,這樣在不同的目錄下可以存在相同文件名缠犀,目錄就相當(dāng)于“命名空間”数苫。
命名空間解決的問題
1、用戶編寫的代碼與PHP內(nèi)部的類/函數(shù)/常量或第三方類/函數(shù)/常量之間的名字沖突辨液。比如:用戶定義了一個函數(shù) is_numeric(),如果在代碼中定義一個命名空間namespace user,用戶在調(diào)用時加上命名空間\user\is_numeric()就可以調(diào)用自己的函數(shù)了虐急。
2、為很長的標(biāo)識符名稱創(chuàng)建一個簡短的別名滔迈,提高源代碼的可讀性止吁。栗子:隨著項(xiàng)目的擴(kuò)大,類名可能會很長燎悍,例如A_B_C_D敬惦,如果使用命名空間,就可以這樣用间涵,在D的類里面定義命名空間:namespace \A_B_C_,在定義D的時候仁热,類名就可以直接寫D。
定義命名空間
用關(guān)鍵字namespace關(guān)鍵字聲明勾哩,如果一個文件中包含命名空間抗蠢,它必須在其他所有代碼之前聲明,declare關(guān)鍵字除外思劳。同一個命名空間可以定義在多個文件中迅矛,即允許將同一個命名空間的內(nèi)容分割存放在不同的文件中。
tips:如果用define()定義命名空間的常量潜叛,需要在變量名稱前加上命名空間秽褒,栗子:define('test\HELLO', 'Hello world!');表示在命名空間test下定義常量HELLO,如果不加命名空間,表示在全局范圍內(nèi)定義常量威兜。
在使用一個變量時销斟,如果變量名前不添加任何命名空間,表示該變量是當(dāng)前文件命名空間的變量椒舵;變量名前加\蚂踊,表示全局范圍的變量,栗子:
<pre>
namespace NS;
define(NAMESPACE .'\foo','111’); //在當(dāng)前命名空間定義常量foo
define('foo','222’);//全局范圍定義常量foo
echo foo; // 111笔宿,默認(rèn)取當(dāng)前命名空間的常量
echo \foo; // 222犁钟,取全局范圍的常量
echo \NS\foo // 111. 去NS命名空間的常量
echo NS\foo // 報(bào)錯棱诱,因?yàn)椴皇且診開頭,表示取\NS\NS\foo涝动,這個常量沒有被定義迈勋。
</pre>
定義子命名空間
類似文件系統(tǒng)中的文件子目錄,命名空間也可以分層醋粟,栗子:namespace A\B\C,子命名空間的好處只是更好的表達(dá)代碼的層級關(guān)系靡菇,用法和普通的命名空間沒有區(qū)別,和所謂的“父空間”并沒有關(guān)系昔穴,\A\B\C和\A\B沒有關(guān)系镰官。
同一個文件中定義多個命名空間:
簡單組合方式,在命名空間后直接寫代碼吗货,栗子:
<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();笨腥。如果當(dāng)前命名空間是 currentnamespace,foo 將被解析為 currentnamespace\foo勇垛。
2脖母、包含前綴: $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果當(dāng)前的命名空間是 currentnamespace闲孤,則 foo 會被解析為 currentnamespace\subnamespace\foo谆级。
3、包含全局前綴:例如讼积, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();肥照。
命名空間和動態(tài)語言的關(guān)系
php中有一些魔術(shù)方法,例如:METHOD勤众、CLASS等舆绎,這些方法的輸出結(jié)果和代碼所在的位置有關(guān),如果想用這些魔術(shù)方法輸出類所在的命名空間们颜,定義類的實(shí)例的時候要把命名空間前綴加上吕朵,否則不會打印出命名空間的信息。栗子:
<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窥突,因?yàn)槎x的時候沒有加上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關(guān)鍵字
關(guān)鍵字 namespace 可用來顯式訪問當(dāng)前命名空間或子命名空間中的元素阻问。它等價(jià)于類中的 self 操作符茅坛。栗子:
namespace MyProject;//定義當(dāng)前命名空間是MyProject
namespace\func(); // 調(diào)用函數(shù)MyProject\func()
__NAMESPACE__常量
常量NAMESPACE的值是包含當(dāng)前命名空間名稱的字符串。在全局的则拷,不包括在任何命名空間中的代碼贡蓖,它包含一個空的字符串。
使用命名空間:別名/導(dǎo)入
如果我們想在當(dāng)前文件引用另外一個文件的命名空間煌茬,而另外一個命名空間很長斥铺,如果每用一次那個空間類都要寫很長的類名,這樣就會使代碼很難看坛善,命名空間有個特征:允許通過別名引用或?qū)胪獠康耐耆薅Q晾蜘,類似于類unix文件系統(tǒng)中可以創(chuàng)建對其他的文件或目錄的軟連接。
所有支持命名空間的PHP版本支持三種別名或?qū)敕绞剑簽轭惷Q使用別名眠屎、為接口使用別名或?yàn)槊臻g名稱使用別名剔交。PHP 5.6開始允許導(dǎo)入函數(shù)或常量或者為它們設(shè)置別名。
別名是通過操作符 use 來實(shí)現(xiàn)的改衩,栗子:
<pre>
namespace foo;
use My\Full\Classname as Another; //引入My\Full\Classname并起個別名Another
use My\Full\NSname;//相當(dāng)于use My\Full\NSname as NSname
use ArrayObject;// 導(dǎo)入一個全局類
use function My\Full\functionName;//導(dǎo)入一個方法
use function My\Full\functionName as func;//導(dǎo)入一個方法并起個別名
use const My\Full\CONSTANT;//導(dǎo)入一個常量
$obj = new namespace\Another; // 實(shí)例化 foo\Another 對象
$obj = new Another; // 實(shí)例化 My\Full\Classname 對象
NSname\subns\func(); // 調(diào)用函數(shù) My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // 實(shí)例化 ArrayObject 對象岖常,如果不使用 "use \ArrayObject" ,則實(shí)例化一個 foo\ArrayObject 對象
func(); //調(diào)用方法My\Full\functionName
echo CONSTANT; // 輸出常量My\Full\CONSTANT
</pre>
后備全局函數(shù)/常量
在一個命名空間中葫督,當(dāng) PHP 遇到一個非限定的類竭鞍、函數(shù)或常量名稱時,它使用不同的優(yōu)先策略來解析該名稱橄镜。類名稱總是解析到當(dāng)前命名空間中的名稱偎快。栗子:
<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>
對于函數(shù)和常量來說,如果當(dāng)前命名空間中不存在該函數(shù)或常量洽胶,PHP 會退而使用全局空間中的函數(shù)或常量晒夹。
名稱解析的規(guī)則
1、對完全限定名稱的函數(shù)姊氓,類和常量的調(diào)用在編譯時解析丐怯。例如 new \A\B 解析為類 A\B。
2他膳、所有的非限定名稱和限定名稱(非完全限定名稱)根據(jù)當(dāng)前的導(dǎo)入規(guī)則在編譯時進(jìn)行轉(zhuǎn)換响逢。例如,如果命名空間 A\B\C 被導(dǎo)入為 C棕孙,那么對 C\D\e() 的調(diào)用就會被轉(zhuǎn)換為 A\B\C\D\e()舔亭。
3、在命名空間內(nèi)部蟀俊,所有的沒有根據(jù)導(dǎo)入規(guī)則轉(zhuǎn)換的限定名稱均會在其前面加上當(dāng)前的命名空間名稱钦铺。例如,在命名空間 A\B 內(nèi)部調(diào)用 C\D\e()肢预,則 C\D\e() 會被轉(zhuǎn)換為 A\B\C\D\e() 矛洞。
4、非限定類名根據(jù)當(dāng)前的導(dǎo)入規(guī)則在編譯時轉(zhuǎn)換(用全名代替短的導(dǎo)入名稱)。例如沼本,如果命名空間 A\B\C 導(dǎo)入為C噩峦,則 new C() 被轉(zhuǎn)換為 new A\B\C() 。
5抽兆、在命名空間內(nèi)部(例如A\B)识补,對非限定名稱的函數(shù)調(diào)用是在運(yùn)行時解析的。例如對函數(shù) foo() 的調(diào)用是這樣解析的:在當(dāng)前命名空間中查找名為 A\B\foo() 的函數(shù)嘗試查找并調(diào)用 全局(global) 空間中的函數(shù) foo()辫红。
6凭涂、在命名空間(例如A\B)內(nèi)部對非限定名稱或限定名稱類(非完全限定名稱)的調(diào)用是在運(yùn)行時解析的。下面是調(diào)用 new C() 及 new D\E() 的解析過程: new C()的解析:1贴妻、在當(dāng)前命名空間中查找A\B\C類切油。2、嘗試自動裝載類A\B\C名惩。