命名空間用來解決:使用者編寫的代碼與PHP內部的類/函數/常量或第三方類/函數/常量之間的名字衝突。為很長的標識符名稱建立一個別名(或簡短)的名稱,提高原始碼的可讀性。
雖然任意合法的PHP代碼都可以包含在命名空間中,但只有以下類型的代碼受命名空間的影響,它們是:類(包括抽象類別和traits)、介面、函數和常量。
如果一個檔案中包含命名空間,它必須在其它所有代碼之前聲明命名空間,除了一個以外:declare關鍵字,用於定義源檔案編碼方式的 declare 語句。程式碼封裝括空白符都不能出現在命名空間的聲明之前。
與PHP其它的語言特徵不同,同一個命名空間可以定義在多個檔案中,即允許將同一個命名空間的內容分割存放在不同的檔案中。
定義子命名空間
<?phpnamespace MyProject\Sub\Level;const CONNECT_OK = 1;class Connection { /* ... */ }function connect() {/* ... */ }
在同一個檔案中定義多個命名空間
<?phpnamespace MyProject;const CONNECT_OK = 1;class Connection { /* ... */ }function connect() { /* ... */ }namespace AnotherProject;const CONNECT_OK = 1;class Connection { /* ... */ }function connect() { /* ... */ }
不建議使用這種文法在單個檔案中定義多個命名空間。建議使用下面的大括弧形式的文法。
<?phpnamespace MyProject {const CONNECT_OK = 1;class Connection { /* ... */ }function connect() { /* ... */ }}namespace AnotherProject {const CONNECT_OK = 1;class Connection { /* ... */ }function connect() { /* ... */ }}
在實際的編程實踐中,非常不提倡在同一個檔案中定義多個命名空間。這種方式的主要用於將多個 PHP 指令碼合并在同一個檔案中。
將全域的非命名空間中的代碼與命名空間中的程式碼群組合在一起,只能使用大括弧形式的文法。全域代碼必須用一個不帶名稱的 namespace 語句加上大括弧括起來.
類可以通過三種方式引用:
非限定名稱,或不包含首碼的類名稱,例如 $a=new foo(); 或 foo::staticmethod();。如果當前命名空間是 currentnamespace,foo 將被解析為 currentnamespace\foo。如果使用 foo 的代碼是全域的,不包含在任何命名空間中的代碼,則 foo 會被解析為foo。 警告:如果命名空間中的函數或常量未定義,則該非限定的函數名稱或常量名稱會被解析為全域函數名稱或常量名稱。
限定名稱,或包含首碼的名稱,例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果當前的命名空間是 currentnamespace,則 foo 會被解析為 currentnamespace\subnamespace\foo。如果使用 foo 的代碼是全域的,不包含在任何命名空間中的代碼,foo 會被解析為subnamespace\foo。
完全限定名稱,或包含了全域首碼操作符的名稱,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在這種情況下,foo 總是被解析為代碼中的文字名(literal name)currentnamespace\foo。
注意訪問任意全域類、函數或常量,都可以使用完全限定名稱,例如 \strlen() 或 \Exception 或 \INI_ALL。
注意:在父檔案包含的子檔案中又包含的檔案的相對路徑是針對父檔案目錄的 ,而不是子檔案目錄。
PHP支援兩種抽象的訪問當前命名空間內部元素的方法,__NAMESPACE__魔術常量和namespace關鍵字。
__NAMESPACE__可以表示字串,其他的和namesapce一樣。
使用use操作符匯入/使用別名
<?phpnamespace foo;use My\Full\Classname as Another;// 下面的例子與 use My\Full\NSname as NSname 相同use My\Full\NSname;// 匯入一個全域類use ArrayObject;// importing a function (PHP 5.6+)use function My\Full\functionName;// aliasing a function (PHP 5.6+)use function My\Full\functionName as func;// importing a constant (PHP 5.6+)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(); // calls function My\Full\functionNameecho CONSTANT; // echoes the value of My\Full
通過use操作符匯入/使用別名,一行中包含多個use語句
<?phpuse My\Full\Classname as Another, My\Full\NSname;$obj = new Another; // 執行個體化 My\Full\Classname 對象NSname\subns\func(); // 調用函數 My\Full\NSname\subns\func
匯入和動態名稱
匯入操作是在編譯執行的,但動態類名稱、函數名稱或常量名稱則不是。
<?phpuse My\Full\Classname as Another, My\Full\NSname;$obj = new Another; // 執行個體化一個 My\Full\Classname 對象$a = 'Another';$obj = new $a; // 實際化一個 Another 對象
匯入和完全限定名稱
匯入操作隻影響非限定名稱和限定名稱。完全限定名稱由於是確定的,故不受匯入的影響。
<?phpuse My\Full\Classname as Another, My\Full\NSname;$obj = new Another; // instantiates object of class My\Full\Classname$obj = new \Another; // instantiates object of class Another$obj = new Another\thing; // instantiates object of class My\Full\Classname\thing$obj = new \Another\thing; // instantiates object of class Another\thing
使用全域空間說明
<?phpnamespace A\B\C;/* 這個函數是 A\B\C\fopen */function fopen() { /* ... */ $f = \fopen(...); // 調用全域的fopen函數 return $f;}
在一個命名空間中,當 PHP 遇到一個非限定的類、函數或常量名稱時,它使用不同的優先策略來解析該名稱。類名稱總是解析到當前命名空間中的名稱。因此在訪問系統內部或不包含在命名空間中的類名稱時,必須使用完全限定名稱。對於函數和常量來說,如果當前命名空間中不存在該函數或常量,PHP 會退而使用全域空間中的函數或常量。
<?phpnamespace 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 類
<?phpnamespace A\B\C;const E_ERROR = 45;function strlen($str){ return \strlen($str) - 1;}echo E_ERROR, "\n"; // 輸出 "45"echo INI_ALL, "\n"; // 輸出 "7" - 使用全域常量 INI_ALLecho strlen('hi'), "\n"; // 輸出 "1"if (is_array('hi')) { // 輸出 "is not array" echo "is array\n";} else { echo "is not array\n";}
名稱解析遵循下列規則:
對完全限定名稱的函數,類和常量的調用在編譯時間解析。例如 new \A\B 解析為類 A\B。
所有的非限定名稱和限定名稱(非完全限定名稱)根據當前的匯入規則在編譯時間進行轉換。例如,如果命名空間 A\B\C 被匯入為 C,那麼對 C\D\e() 的調用就會被轉換為 A\B\C\D\e()。
在命名空間內部,所有的沒有根據匯入規則轉換的限定名稱均會在其前面加上當前的命名空間名稱。例如,在命名空間 A\B 內部調用 C\D\e(),則 C\D\e() 會被轉換為 A\B\C\D\e() 。
非限定類名根據當前的匯入規則在編譯時間轉換(用全名代替短的匯入名稱)。例如,如果命名空間 A\B\C 匯入為C,則 new C() 被轉換為 new A\B\C() 。
在命名空間內部(例如A\B),對非限定名稱的函數調用是在運行時解析的。例如對函數 foo() 的調用是這樣解析的:
在當前命名空間中尋找名為 A\B\foo() 的函數
嘗試尋找並調用 全域(global) 空間中的函數 foo()。
在命名空間(例如A\B)內部對非限定名稱或限定名稱類(非完全限定名稱)的調用是在運行時解析的。下面是調用 new C() 及 new D\E() 的解析過程: new C()的解析:
new D\E()的解析:
為了引用全域命名空間中的全域類,必須使用完全限定名稱 new \C()。
在類名稱前面加上當前命名空間名稱變成:A\B\D\E,然後尋找該類。
嘗試自動裝載類 A\B\D\E。
在當前命名空間中尋找A\B\C類。
嘗試自動裝載類A\B\C。
<?phpnamespace A;use B\D, C\E as F;// 函數調用foo(); // 首先嘗試調用定義在命名空間"A"中的函數foo() // 再嘗試調用全域函數 "foo"\foo(); // 調用全域空間函數 "foo" my\foo(); // 調用定義在命名空間"A\my"中函數 "foo" F(); // 首先嘗試調用定義在命名空間"A"中的函數 "F" // 再嘗試調用全域函數 "F"// 類引用new B(); // 建立命名空間 "A" 中定義的類 "B" 的一個對象 // 如果未找到,則嘗試自動裝載類 "A\B"new D(); // 使用匯入規則,建立命名空間 "B" 中定義的類 "D" 的一個對象 // 如果未找到,則嘗試自動裝載類 "B\D"new F(); // 使用匯入規則,建立命名空間 "C" 中定義的類 "E" 的一個對象 // 如果未找到,則嘗試自動裝載類 "C\E"new \B(); // 建立定義在全域空間中的類 "B" 的一個對象 // 如果未發現,則嘗試自動裝載類 "B"new \D(); // 建立定義在全域空間中的類 "D" 的一個對象 // 如果未發現,則嘗試自動裝載類 "D"new \F(); // 建立定義在全域空間中的類 "F" 的一個對象 // 如果未發現,則嘗試自動裝載類 "F"// 調用另一個命名空間中的靜態方法或命名空間函數B\foo(); // 調用命名空間 "A\B" 中函數 "foo"B::foo(); // 調用命名空間 "A" 中定義的類 "B" 的 "foo" 方法 // 如果未找到類 "A\B" ,則嘗試自動裝載類 "A\B"D::foo(); // 使用匯入規則,調用命名空間 "B" 中定義的類 "D" 的 "foo" 方法 // 如果類 "B\D" 未找到,則嘗試自動裝載類 "B\D"\B\foo(); // 調用命名空間 "B" 中的函數 "foo" \B::foo(); // 調用全域空間中的類 "B" 的 "foo" 方法 // 如果類 "B" 未找到,則嘗試自動裝載類 "B"// 當前命名空間中的靜態方法或函數A\B::foo(); // 調用命名空間 "A\A" 中定義的類 "B" 的 "foo" 方法 // 如果類 "A\A\B" 未找到,則嘗試自動裝載類 "A\A\B"\A\B::foo(); // 調用命名空間 "A\B" 中定義的類 "B" 的 "foo" 方法 // 如果類 "A\B" 未找到,則嘗試自動裝載類 "A\B"?>