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。注意:只能引入類,而後可使用非限定名稱訪問。

相關文章
相關標籤/搜索