Namespacing in PHP (php 中使用命名空間)

  最近學習一下php的命名空間,中文文檔很少,搜到一篇英文的,講的還蠻系統的,特此翻譯一下,以備之後查閱,你們有什麼高見或更深入或者 更悟透的看法但願能不吝賜教,晚輩感激涕零。 php

原文: http://code.tutsplus.com/tutorials/namespacing-in-php--net-27203mysql

關於PHP對命名空間的支持,這走過了一段崎嶇的歷程。感謝的是,從PHP5.3起,加入了對命名空間。php的代碼結構也所以提高了了許多。可是,咱們究竟該怎樣使用命名空間呢?sql

1、什麼是命名空間

  「別玩了要去掉前面的反斜槓,當你將一個命名空間存儲到一個字符串變量中的時候。」數據庫

 

       把命名空間想象成一個抽屜,在這個抽屜裏面你能夠放各類各樣的東西:想一支鉛筆、一張紙、等等,這些是屬於你的東西。在你的抽屜下面呢是別人的抽屜,他也能夠把各類東西放在他的抽屜裏面。爲了不互相取錯對方的東西,你決定給抽屜貼上標籤,這樣就很清晰哪一個抽屜式屬於誰的了。編程

   之前,沒有引入命名空間的時候,開發者們在他們的類中、函數中、常量中使用 下劃線 「_」來區分開。這其實等同於給各自的物品貼上標籤而後把他們放到同一個大的抽屜中。固然,這至少已經看上去有點結構,不太那麼亂了,可是這很是的不高效。markdown

  命名空間能夠拯救這一切。你能夠在各自的命名空間內定義同名的函數、類、接口、常量,只要他們是在不一樣的命名空間中,這就不會報錯。本質上來講,一個命名空間僅僅是一個有層級的,來標識常規的php代碼塊的東西。app

  「你已經在使用命名空間了」編程語言

  有一點很重要你應該記住的,其實你間接的在使用命名空間了,php5.3之後,全部的代碼定義,若是沒有顯示的定義在用戶命名的命名空間中,都是默認定義在一個全局的命名空間中的。全局命名空間一樣也包含了php的許多內置的函數定義,想echo(),mysql_connect(),和Exception類。由於全局命名空間沒有一個獨立的標識名字,它一般指的是 全局空間。函數

  記住,你沒有必要必定要使用命名空間。你的php腳本一樣能夠執行的很好即便沒有命名空間,並且加入命名空間也可能不會發生太快。學習

2、怎樣定義一個命名空間

在一個php文件中,一個命名空間的定義應該是一個最前面的聲明(php解釋器應該第一個遇到的)。注:惟一一個能夠在聲明一個命名空間以前聲明的東西是 declare 聲明,僅僅當declare聲明瞭這個腳本文件的編碼。

  聲明一個命名空間就是使用一個簡單的 namespace關鍵字,命名空間的名字和php 中其餘的標識符的命名規範同樣。就是隻能有字母、數字、下劃線組成,並且不能以數字開頭。

<?php 
namespace MyProject {
    // Regular PHP code goes here, anything goes!
    function run() 
    {
        echo 'Running from a namespace!';
    }
}

若是你想分配一個代碼塊給全局空間,你使用namespace關鍵字,可是後面不帶名字。

 

If you want to assign a code block to the global space, you use the namespace keyword without appending a name.  

<?php
namespace{
  //Global Space!  
}

你能夠在同一個文件中定義多個命名空間。

<?php
namespace MyProject{
  //  
}

namespace MySecondProject{
  //  
}

namespace {
  //  
}

你也能夠在多個文件中使用同一個命名空間;文件的包含 處理過程會自動合併他們。所以,一個好的編程實踐是在每一文件中只使用一個命名空間,就像你只在一個文件中定義一個類同樣。

  命名空間的使用是用來避免命名(定義)衝突的,和進入了更多的靈活性,更好地組織你的代碼。

注意一點,命名空間的用來圍繞代碼塊大括號「{}」徹底是可選的,你能夠不要它。實際上,堅持一個文件一個命名空間的原則,而後省略大括號使你的代碼更簡潔多了--不須要代碼縮進了,代碼嵌套。

  子命名空間

命名空間能夠遵循一個肯定層級(等級),就像你電腦的文件系統中的目錄結構同樣。子命名空間是很是有用的,來組織你的代碼結構。例如:若是你的項目須要訪問數據庫,你可能想把全部的數據庫相關的代碼,如數據庫異常和連接句柄都放到一個叫作 database的子命名空間中去。

爲了維護上的靈活性,將一個子命名空間放到一個子目錄中是明智的。這促使了你的代碼更加結構化,更容易使用 autoloader自動加載。PHP使用反斜槓 "\」來做爲命名空間的分隔符。

// myproject/database/connection.php
<?php 
namespace MyProject\Database
 
class Connection {
    // Handling database connections
}

 你可使用不少的子命名空間,只要你喜歡

<?php 
namespace MyProject\Blog\Auth\Handler\Social;
 
class Twitter {
    // Handles Twitter authentification
}

定義嵌套的子命名空間是不支持的。下面的例子會拋出一個嚴重的錯誤「命名空間的聲明不能嵌套!」。 

<?php
namespace MyProject {
    namespace Database {
        class Connection { }
    }
}

3、調用命名空間內的代碼

若是你實例化一個別的命名空間中的對象,或者調用一個函數,或者使用一個常量,你要使用反斜槓的語法。他們能夠從如下三種方式中解析出來。

  • 非限制的名稱
  • 限制的名稱
  • 徹底限制的名稱

這是一個類的名字,函數的名字,或者常量的名字,沒有包含對任何命名空間的引用。若是你是剛剛接觸命名空間,這是常用它們的方式。

<?php
namespace MyProject;
 
class MyClass {
    static function static_method()
    {
        echo 'Hello, world!';
    }
}
 
// Unqualified name, resolves to the namespace you are currently in (MyProject\MyClass)
MyClass:static_method()

 (2)限制的名稱

這是咱們訪問子命名空間層級的方式,這種方式要使用反斜槓語法。

<?php
namespace MyProject;
 
require 'myproject/database/connection.php';
 
// Qualified name, instantiating a class from a sub-namespace of MyProject
$connection = new Database\Connection();

下面的例子拋出了一個嚴重的錯誤:「Fatal error: Class 'MyProject\Database\MyProject\FileAccess\Input' not found" .」 

這是由於 Myproject\FileAccess\Input  訪問的是相對於你當前所在的命名空間的。

<?php
namespace MyProject\Database;
 
require 'myproject/fileaccess/input.php';
 
// Trying to access the MyProject\FileAccess\Input class
$input = new MyProject\FileAccess\Input()

 (3)徹底限制的命名空間 

非限制的命名空間和限制的命名空間都是相對於你當前所在的命名空間-----相對路徑。他們僅僅用於訪問他們同一級的或者子級的命名空間。

若是你想訪問一個函數、類、或常量,而他們是位於更高一級的命名空間中,這時候你就要使用徹底限制的命名空間----絕對路徑而不是相對路徑。這就須要在你的調用前面加上反斜槓「\」.這就會讓PHP知道調用時要從全局空間開始解析而不是相對路徑。 

<?php
namespace MyProject\Database;
 
require 'myproject/fileaccess/input.php';
 
// Trying to access the MyProject\FileAccess\Input class
// This time it will work because we use the fully qualified name, note the leading backslash
$input = new \MyProject\FileAccess\Input();

沒有要求說要在php 的函數內部使用徹底限制的名稱,經過 一個不限制的名稱來調用一個不存在與當前命名空間中的常量或函數時,PHP會自動去全局空間中尋找他們。這個是內置的向後查找,可是這不適用於不限制名稱的方式去調用類(也就是說以不限制名稱的方式去調用一個不存在與當前命名空間中的類時,PHP不會自動去全局空間中尋找)。在腦子裏記住這一點,咱們如今能夠重載內部的php函數,同時也仍然能夠調用原始的函數(或常量)。  

<?php
namespace MyProject;
 
var_dump($query); // Overloaded
\var_dump($query); // Internal
 
// We want to access the global Exception class
// The following will not work because there's no class called Exception in the MyProject\Database namespace and unqualified class names do not have a fallback to global space
// throw new Exception('Query failed!');
 
// Instead, we use a single backslash to indicate we want to resolve from global space
throw new \Exception('ailed!');
 
function var_dump() {
    echo 'Overloaded global var_dump()!<br />'
}

 動態調用

PHP 是一種動態的編程語言;所以也能夠應用這種功能於命名空間的調用上。這本質上相似於實例化變量類 或者 包含變量文件。PHP命名空間的分隔符也是一個元字符。別忘了當你把命名空間存儲於一個自動串變量中的時候去掉前面的反斜槓。

<?php
namespace OtherProject;
 
$project_name = 'MyProject';
$package_name = 'Database';
$class_name = 'Connection';
 
// Include a variable file
require strtolower($project_name . '/'. $package_name .  '/' . $class_name) . '.php';
 
// Name of a variable class in a variable namespace. Note how the backslash is escaped to use it properly
$fully_qualified_name = $project_name . '\\' . $package_name . '\\' . $class_name;
 
$connection = new $fully_qualified_name();

4、namespace 關鍵字

<?php
namespace MyProject;
 
function run() 
{
    echo 'Running from a namespace!';
}
 
// Resolves to MyProject\run
run();
// Explicitly resolves to MyProject\run
namespace\run();

5、__NAMESPACE_常量

很像self關鍵字,不能用於決定當前所在的類是哪一個類,namespace關鍵字也不能用於決定當前的命名空間是什麼命名空間,這就是咱們爲何要使用__NAMESPACE__常量。

<?php
namespace MyProject\Database;
 
// 'MyProject\Database'
echo __NAMESPACE__;

這個常量很是的有用,若是你是剛剛開始命名空間的學習,它對於調試也頗有幫助。做爲一個字符串,它還能夠用在動態的代碼調用,咱們以前說過了。

6、別名或者導入

    「沒有必要必定要使用命名空間」

PHP中的命名空間支持導入 importing .導入也叫作 別名。只用類、接口、命名空間能夠取別名或者被導入。

導入是很是有用的,也是命名空間很是重要的一方面。它使你有能力去使用外部的包代碼,像類庫,不須要擔憂命名衝突。導入是經過 使用 use 關鍵字來支持的。制定一個自定義的別名 跟着 as 關鍵字。

use [name of class, interface or namespace] as [optional_custom_alias]

導入時怎樣實現的呢  

一個徹底限制的名稱 能夠取一個短一點的非限制的名稱,這樣你當你想用它的時候不用每次都寫一個長長的徹底限制的名稱。別名或者導入應該發生(或者叫使用)於最高層級的做用域或者是全局空間中。 嘗試在一個方法或是函數的做用域中使用導入時不合法(不和語法的)的。

<?php
namespace OtherProject;
 
// This holds the MyProject\Database namespace with a Connection class in it
require 'myproject/database/connection.php';
 
// If we want to access the database connection of MyProject, we need to use its fully qualified name as we're in a different name space
$connection = new \MyProject\Database\Connection();
 
// Import the Connection class (it works exactly the same with interfaces)
use MyProject\Database\Connection;
 
// Now this works too! Before the Connection class was aliased PHP would not have found an OtherProject\Connection class
$connection = new Connection();
 
// Import the MyProject\Database namespace
use MyProject\Database;
 
$connection = new Database\Connection()

 或者,你能夠取一個不一樣的名字做爲別名。

<?php
namespace OtherProject;
 
require 'myproject/database/connection.php';
 
use MyProject\Database\Connection as MyConnection;
 
$connection = new MyConnection();
 
use MyProject\Database as MyDatabase;
 
$connection = new MyDatabase\Connection();

你也能夠容許導入全局的類,像 Exception類。當導入以後呢,你就能夠不再用寫他的長長的徹底限制的名稱了。

注意,導入的名稱不是被解析成相對於當前命名空間的路徑,而是絕對路徑,從全區空間開始。這意味着 前導的反斜槓付是沒必要要的也是不推薦的。

<?php
namespace MyProject;
 
// Fatal error: Class 'SomeProject\Exception' not found
throw new Exception('An exception!');
 
// OK!
throw new \Exception('An exception!');
 
// Import global Exception. 'Exception' is resolved from an absolute standpoint, the leading backslash is unnecessary
use Exception;
 
// OK!
throw new Exception('An exception!');

儘管能夠動態調用命名空間的代碼,  可是動態的導入是不支持的。

<?php
namespace OtherProject;
 
$parser = 'markdown';
 
// This is valid PHP
require 'myproject/blog/parser/' . $parser . '.php';
 
// This is not
use MyProject\Blog\Parser\$parser;

結論 

命名空間是用來避免定義衝突的和引入了更多的靈活性,更好的組織你的代碼的。記住你沒有必要說非得使用命名空間。它的使用是結合面向對象的工做流中會常見。 然而,仍是但願,你將考慮將你的PHP項目帶入到下一個層次中,經過使用命名空間。你決定這樣作了嗎?

相關文章
相關標籤/搜索