【 PHP 學習筆記 】名稱空間

徐俊發表於2017-05-28

【摘錄】:“名稱空間在PHP5.3.0中引入,是一個很重要的工具,其作用是按照一種虛擬的層次結構組織PHP程式碼,這種層次結構類似作業系統中檔案系統的目錄結構,現代的PHP元件和框架都放在各自全域性唯一的廠商名稱空間中,以免與其他廠商使用常見類名衝突”-- O'REILLY《 Modern PHP 》

【意義】:試想下在團隊合作開發中如果專案結構複雜參與人員繁多的情況下,各自的包與擴充套件很可能會發生衝突,名稱空間很好的解決了這一問題,並且通過Composer能夠讓各種第三方元件與專案良好的結合並且無需考慮相容與衝突的問題

【舉例】【一般情況】:

# @ 若宣告類時類名重複,則會產生一個致命錯誤
class test{  }
class test{  }
# @ 類名重複導致一個致命錯誤
( ! ) Fatal error: Cannot redeclare class test in D:\WAMP\www\JustTest\index.php on line 6Call Stack#TimeMemoryFunctionLocation10.0004233528{main}( )...\index.php:0

【舉例】【名稱空間】:

# @ 使用名稱空間
namespace TestSpace_01;
class test{  }
namespace TestSpace_02;
class test{ }

【摘錄】【總結】:PHP名稱空間與作業系統的物理檔案不同,這是一個虛擬的概念,沒必要和檔案系統中的目錄結構完全對應。雖然如此,但是大多數PHP元件為了相容廣泛使用的PSR-4自動載入標準,會把子名稱空間放到檔案系統的子目錄中,從技術層面來看,名稱空間只是PHP中的一種記號,PHP直譯器會將其作為字首新增到類、介面、函式和常量的名稱前面。

【摘錄】【為什麼使用名稱空間】:名稱空間很重要,因為程式碼放在沙盒中,可以和其他開發者編寫的程式碼一起使用。這是現代PHP元件生態系統的基礎。元件和框架的作者編寫了大量的程式碼,供眾多的PHP開發者使用,這些作者不可能知道或控制別人在使用自己的程式碼時還使用了什麼其它類、介面、函式或常量。如果你開發的是小型個人專案,只有少量的依賴,類名衝突可能不是問題。但是如果在團隊中工作,開發有許多第三方依賴的大型專案,就要認真對待命名衝突的問題,因為你無法控制專案依賴在全域性名稱空間中引入的類、介面、函式和常量,這就是為什麼要使用名稱空間的原因

【官方文件】:http://php.net/manual/zh/language.namespaces.rationale.php
宣告名稱空間
【宣告空間】:名稱空間宣告語句以 namespace 開頭,隨後是一個空格,然後是名稱空間的名稱並且以分號結尾(具體可參考上文的DEMO)

【子名稱空間】:子名稱空間的宣告方式和前面的示例完全一樣。唯一的區別是,需要用 \ 把名稱空間和子名稱空間分開:

# @ 名稱空間
namespace TestSpace_01;
# @ 子名稱空間
namespace TestSpace_01\TestSpace_02;

【總結】: 在同一個名稱空間或子名稱空間中的所有類沒必要在同一個PHP檔案中宣告。你可以在PHP檔案的頂部指定一個名稱空間或者子名稱空間,此時這個檔案中的程式碼就是該名稱空間或子名稱空間的一部分。因此我們可以在不同的檔案中編寫屬於同一個名稱空間的多個類
匯入和別名
【使用名稱空間】:名稱空間的預設使用方式只需在例項化物件時加上其名稱空間即可

# @ 名稱空間
namespace TestSpace_01;
class Test
{
    public function TestPrint()
    {
        var_dump('Hellow World');
    }
}
# @ 匯入名稱空間中的類
$Obj_Test = new \TestSpace_01\Test();
$Obj_Test -> TestPrint();

【匯入名稱空間 】:從PHP5.3開始可以匯入PHP類、介面和其它名稱空間,併為其建立別名。從PHP5.6開始可以匯入PHP函式和常量,併為其建立別名。若載入了一些結構複雜的依賴程式碼就會出現一個問題:類名特別長,通過 use 關鍵字告訴PHP想要使用的類

# @ 模擬可能出現的多層級的名稱空間
namespace TestSpace_01\TestSpace_02\TestSpace_03\TestSpace_04\TestSpace_05;
class Test
{
    public function TestPrint()
    {
        var_dump('Hellow World');
    }
}
# @ 使用預設的名稱空間
use TestSpace_01\TestSpace_02\TestSpace_03\TestSpace_04\TestSpace_05;
$Obj_Test = new Test();
$Obj_Test -> TestPrint();

【別名】:如果特別懶,還可以使用別名,只需在匯入名稱空間時加上 as 跟上別名即可

# @ 使用名稱空間,並自定義別名
use TestSpace_01\TestSpace_02\TestSpace_03\TestSpace_04\TestSpace_05\Test as TS;
$Obj_Test = new TS();
$Obj_Test -> TestPrint();

【建議】:應該在PHP檔案的頂部使用 use 關鍵字匯入程式碼,而且要放在<?php標籤或名稱空間宣告語句之後,使用 use 關鍵字匯入程式碼時無需在開頭加上\符號,因為PHP假定匯入的是完全限定的名稱空間。use關鍵字必須出現在全域性作用域中(即不能在類或函式中),因為這個關鍵字在編譯時使用。不過 use 關鍵字可以在名稱空間宣告語句之後使用,匯入其他名稱空間中的程式碼

【匯入函式&常量】:從PHP5.6開始還可以匯入函式和常量,不過要調整 use 關鍵字的句法。如果想匯入函式,要把 use 改成 use func,如果想匯入常量,要把 use 改成 use constant

# @ 定義名稱空間
namespace TestSpace_01;
# @ 定義方法
function Test_Func()
{
    var_dump("AA");
}
# @ 定義常量
const TEST_CONST = 'BB';
# @ 匯入方法與常量,為了區分是通過名稱空間呼叫為其建立別名,並通過別名呼叫
use function TestSpace_01\Test_Func as Spc_Func;
Spc_Func();
use const TestSpace_01\TEST_CONST as Spc_Constant;
echo Spc_Constant;

【注意】:必須注意的是通過名稱空間來引入的常量必須使用 const 關鍵詞來定義,若使用 define 方法來定義則系統會提示使用了未被定義的常量,通過查閱了許多的文件和帖子包括PHP官網,都沒有一個正面的解釋所以我個人猜測是由於名稱空間的作用原理類似於物件導向所以外部使用 define 定義的常量無法被解析,而通過 const 定義的常量則能被正常解析。官網文件:http://php.net/manual/zh/language.constants.syntax.php

# @ 引入通過 define 關鍵詞定義的常量會提示錯誤資訊
Notice: Use of undefined constant TEST_CONST - assumed 'TEST_CONST' in D:\wamp\www\Test\index.php on line 15

實用技巧
【多重匯入】:PHP允許使用簡短的匯入語法,把多個 use 語句寫成一行

# @ 多重匯入名稱空間使用 "," 隔開
use TestSpace_01\Test_01,
    TestSpace_02\Test_02,    
    TestSpace_03\Test_03,    
    TestSpace_04\Test_04;

【建議】:即使支援簡寫,但是還是建議一行一個 use 以便閱讀,並且最好遵循“一個檔案一個名稱空間”的最佳實踐
全域性名稱空間
【說明摘錄】:如果引用類、介面、函式或常量時沒有使用名稱空間,PHP假定引用的類、介面、函式或常量在當前的名稱空間中。如果這個假定不正確,PHP會嘗試解析類、介面、函式或常量。如果需要在名稱空間中引用其他名稱空間中的類、介面、函式或常量,必須使用完全限定的PHP類名(即 名稱空間+類名)。你可以輸入完全限定的PHP類名,也可以使用 use 關鍵字把程式碼匯入當前的名稱空間(參考 "匯入與別名" 一節中的DEMO)-- O'REILLY《 Modern PHP 》
【特殊情況】:有些程式碼可能沒有名稱空間,這些程式碼在全域性名稱空間中。PHP原生的 Exception 類就是如此。在名稱空間中引用全域性名稱空間中的程式碼時,要在類、介面、函式或常量的名稱前加上 \ 符號。

# @ 定義名稱空間
namespace TestSpace_01;
class Foo
{
    public function DoSomeThing()
    {    # @ 嘗試例項化全域性名稱空間中的 Exception 類
        $exception = new Exception();
    }
}
use TestSpace_01;
$Obj_Foo = new Foo();
$Obj_Foo -> DoSomeThing();
# @ 提示錯誤:無法找到 Exception 類
Fatal error: Class 'TestSpace_01\Exception' not found in D:\WAMP\www\JustTest\index.php on line 8 

【正確語法】:

# @ 定義名稱空間
namespace TestSpace_01;
class Foo
{
    public function DoSomeThing()
    {    # @ 新增 "\" 符號呼叫全域性空間中的類
        $exception = new \Exception();
    }
}
use TestSpace_01;
$Obj_Foo = new Foo();
$Obj_Foo -> DoSomeThing();

【補充】:名稱空間還為PHP Framework Interop Group ( PHP-FIG ) 制定的PSR-4自動載入標準奠定了基礎。大多數現代的PHP元件都使用了這種自動載入器模式,並且使用Composer,實現自動載入專案的依賴,如果沒有名稱空間,就不可能出現現代的PHP生態系統和基於元件的新興架構

【例項】:Symfony是一個優秀的知名框架,你可以再其Httpfoundation元件的github站點中檢視其程式碼,你便會發現現代的PHP元件都採用了名稱空間:https://github.com/symfony/http-foundation

相關文章