Background

今天在網路上看到一篇對 Namespaces 解釋的還蠻簡單的文章. 剛好最近有在研究Laravel. 常用到 Namespaces 的觀念. 所以簡單的翻譯這篇文章, 做個紀錄. (原文出處)

內文

namespaces 是在 PHP 5.3 版本中才加入的新功能. 那本文的目的是為了瞭解為什麼在開發時需要用到 namespaces.

文中提到. 在 PHP 中你是不能同時擁有兩個相同的類別名稱. 舉個例子來說, 假設你今天有用了一個Third party的套件, 當中有用到class named user. 不巧的是你在自己的程式碼中也用到了相同名稱的class也叫 user. 這樣就會產生出一些問題.

那 PHP 的 namespaces 可以解決這個問題. 我們可以利用 namespace 將類似的代碼包含在整齊的 package 中,甚至顯示所有權.

Global Namespace

首先, 先來看一個簡單的範例.

<?php

// app/models/Eddard.php

class Eddard
{

}

沒什麼特別, 如果你想要用它的話. 使用如下.

<?php

// app/routes.php

$eddard = new Eddard();

在上述範例中, 雖然你沒用到 namespace 的情況. 但其實你可以假設你用了 global namespace (default). 這樣你可能會比較好理解. (但其實沒有 global namespace這個說法, 是作者自己創造一個比較能接受的說法)

Simple Namespacing

ok, 那現在在建立一個新的 Class也叫 Eddard.

<?php

namespace Stark;

// app/models/another.php

class Eddard
{

}

不太一樣的是, 這邊我們加上了 namespace Stark, 透過這樣 PHP 就會知道我們做的一切都跟 Stark namespace 相關. 同時也表示在這個檔案中的所有 Class 都跟 Stark 這個 namespace 有關.

<?php

// app/routes.php

$eddard = new Stark\Eddard();

現在我們可以利用 Stark namespace. 來建立一個 instance. 那規則其實很簡單. namespace\class

The Theory of Relativity

ok, 接下來我們來看一下下面這例子

<?php

namespace Stark;

// app/routes.php

$eddard = new Eddard();

透過這行指令 namespace Stark, 我們將PHP腳本的執行移動到Stark命名空間. 那現在有個問題. 我們如何創造出一個原來的 Eddard Instance? 那幸運的是, PHP 中可以透過 backward () slash, 來解決這個問題ㄝ.

<?php

// app/routes.php

$eddard = new \Eddard();

照上述的例子我們可以知道 以 \ 開頭, 前面不加任何的 namespace name. 則表示這個 Eddard 的 instance 是屬於 global namespace.

接下來用一點點想像, 我們有另一個名為Tully\Edmure的 namespace 的 class. 現在我們要在 Stark 框架內使用這個 class. 我們該怎麼做?

<?php

namespace Stark;

// app/routes.php

$edmure = new \Tully\Edmure();

那上面這個例子稍微解釋一下 \Tully\Edmure, 首先吃到 backward () slash, 所以先回到 global namespace, 接著再進入到 Tully 的 namespace.

那這樣寫其實蠻麻煩的, 所以可以透過 use 這個指令.

<?php

namespace Stark;

use Tully\Edmure;

// app/routes.php

$edmure = new Edmure();

使用use語句, 我們可以將一個 class 從另一個 namespace 引入當前的 namespace. 那還有一個額外的小技巧, 請看下面的範例

<?php

namespace Stark;

use Tully\Brynden as Blackfish;

// app/routes.php

$brynden = new Blackfish();

通過使用as關鍵字,我們給 Tully/Brynden class 提供了 Blackfish 的暱稱, 允許我們使用新的暱稱在當前的 namespace 中進行識別.

那如果您需要在同一個 namespace 中使用兩個類似命名的 class,那麼它也非常方便,例如:

<?php

namespace Targaryen;

use Dothraki\Daenerys as Khaleesi;

// app/routes.php

class Daenerys
{

}

// Targaryen\Daenerys
$daenerys = new Daenerys();

// Dothraki\Daenerys
$khaleesi = new Khaleesi();

通過在Dothraki namespace 中將 Daenerys 命名為 Khaleesi 的暱稱,我們可以通過名稱使用兩個Daenerys 的 class.

Structure

namespace 不僅僅是為了避免衝突, 我們也可以將它們用於組織和所有權. 我們再看以下的例子:

假設我想創建一個 open source library. 我想要別人使用我的代碼,這將是很棒的! 但麻煩的是,我不想對使用我的代碼的人造成任何有問題的 class 命名衝突, 這將是非常不便的. 那以下是我如何避免這個問題的做法.

Dayle\Blog\Content\Post
Dayle\Blog\Content\Page
Dayle\Blog\Tag

在這裡,我們使用了我的名字來表示我創建了原始代碼,並將我的代碼與使用我的 library 的人的代碼分開. 在基礎的 namespace 中,我創建了一些 sub-namespaces, 以通過其內部結構組織我的應用程序.

Limitations

這不是bug, 這只是作者單純的抱怨. 在其他語言中, 你可以很容易地看到下面類似的寫法, 直接匯入檔案中所有的 class.

import dayle.blog.*;

但是抱歉, 在 PHP 中你必須一個一個的 import. 這點是要特別注意的.

Reference