1. 程式人生 > >php 名稱空間(namespace)

php 名稱空間(namespace)

php名稱空間

版本(PHP 5 >= 5.3.0, PHP 7)

 

一. 名稱空間概述

什麼是名稱空間?從廣義上來說,名稱空間是一種封裝事物的方法。在很多地方都可以見到這種抽象概念。例如,在作業系統中目錄用來將相關檔案分組,對於目錄中的檔案來說,它就扮演了名稱空間的角色。具體舉個例子,檔案 foo.txt 可以同時在目錄/home/greg 和 /home/other 中存在,但在同一個目錄中不能存在兩個 foo.txt 檔案。另外,在目錄 /home/greg 外訪問 foo.txt

 檔案時,我們必須將目錄名以及目錄分隔符放在檔名之前得到 /home/greg/foo.txt。這個原理應用到程式設計領域就是名稱空間的概念。

在PHP中,名稱空間用來解決在編寫類庫或應用程式時建立可重用的程式碼如類或函式時碰到的兩類問題:

  1. 使用者編寫的程式碼與PHP內部的類/函式/常量或第三方類/函式/常量之間的名字衝突。
  2. 為很長的識別符號名稱(通常是為了緩解第一類問題而定義的)建立一個別名(或簡短)的名稱,提高原始碼的可讀性。

PHP 名稱空間提供了一種將相關的類、函式和常量組合到一起的途徑。下面是一個說明 PHP 名稱空間語法的示例:

Example #1 名稱空間語法示例

<?php
namespace my\name; // 參考 "定義名稱空間" 小節

class MyClass {}
function myfunction() {}
const MYCONST = 1;

$a = new MyClass;
$c = new \my\name\MyClass; // 參考 "全域性空間" 小節

$a = strlen('hi'); // 參考 "使用名稱空間:後備全域性函式/常量" 小節

$d = namespace\MYCONST; // 參考 "namespace操作符和__NAMESPACE__常量” 小節

$d = __NAMESPACE__ . '\MYCONST';
echo constant($d); // 參考 "名稱空間和動態語言特徵" 小節
?>

Note:

名為PHPphp的名稱空間,以及以這些名字開頭的名稱空間(例如PHP\Classes)被保留用作語言核心使用,而不應該在使用者空間的程式碼中使用。

 

二. 定義名稱空間

雖然任意合法的PHP程式碼都可以包含在名稱空間中,但只有以下型別的程式碼受名稱空間的影響,它們是:類(包括抽象類和traits)、介面、函式和常量。

名稱空間通過關鍵字namespace 來宣告。如果一個檔案中包含名稱空間,它必須在其它所有程式碼之前宣告名稱空間,除了一個以外:declare關鍵字。

Example #1 宣告單個名稱空間

<?php
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

?>

在宣告名稱空間之前唯一合法的程式碼是用於定義原始檔編碼方式的 declare 語句。另外,所有非 PHP 程式碼包括空白符都不能出現在名稱空間的宣告之前:

Example #2 宣告單個名稱空間

<html>
<?php
namespace MyProject; // 致命錯誤 - 名稱空間必須是程式指令碼的第一條語句
?>

另外,與PHP其它的語言特徵不同,同一個名稱空間可以定義在多個檔案中,即允許將同一個名稱空間的內容分割存放在不同的檔案中。

 

三. 定義子名稱空間

與目錄和檔案的關係很象,PHP 名稱空間也允許指定層次化的名稱空間的名稱。因此,名稱空間的名字可以使用分層次的方式定義:

Example #1 宣告分層次的單個名稱空間

<?php
namespace MyProject\Sub\Level;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

?>

上面的例子建立了常量MyProject\Sub\Level\CONNECT_OK,類 MyProject\Sub\Level\Connection和函式 MyProject\Sub\Level\connect

 

四. 在同一個檔案中定義多個名稱空間

也可以在同一個檔案中定義多個名稱空間。在同一個檔案中定義多個名稱空間有兩種語法形式。

Example #1 定義多個名稱空間,簡單組合語法

<?php
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

namespace AnotherProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
?>

不建議使用這種語法在單個檔案中定義多個名稱空間。建議使用下面的大括號形式的語法。

Example #2 定義多個名稱空間,大括號語法

<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace AnotherProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}
?>

在實際的程式設計實踐中,非常不提倡在同一個檔案中定義多個名稱空間。這種方式的主要用於將多個 PHP 指令碼合併在同一個檔案中。

將全域性的非名稱空間中的程式碼與名稱空間中的程式碼組合在一起,只能使用大括號形式的語法。全域性程式碼必須用一個不帶名稱的 namespace 語句加上大括號括起來,例如:

Example #3 定義多個名稱空間和不包含在名稱空間中的程式碼

<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace { // global code
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

除了開始的declare語句外,名稱空間的括號外不得有任何PHP程式碼。

Example #4 定義多個名稱空間和不包含在名稱空間中的程式碼

<?php
declare(encoding='UTF-8');
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace { // 全域性程式碼
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

 

五. 使用名稱空間:基礎

在討論如何使用名稱空間之前,必須瞭解 PHP 是如何知道要使用哪一個名稱空間中的元素的。可以將 PHP 名稱空間與檔案系統作一個簡單的類比。在檔案系統中訪問一個檔案有三種方式:

  1. 相對檔名形式如foo.txt。它會被解析為 currentdirectory/foo.txt,其中 currentdirectory 表示當前目錄。因此如果當前目錄是 /home/foo,則該檔名被解析為/home/foo/foo.txt
  2. 相對路徑名形式如subdirectory/foo.txt。它會被解析為 currentdirectory/subdirectory/foo.txt
  3. 絕對路徑名形式如/main/foo.txt。它會被解析為/main/foo.txt

PHP 名稱空間中的元素使用同樣的原理。例如,類名可以通過三種方式引用:

  1. 非限定名稱,或不包含字首的類名稱,例如 $a=new foo(); 或 foo::staticmethod();。如果當前名稱空間是 currentnamespace,foo 將被解析為currentnamespace\foo。如果使用 foo 的程式碼是全域性的,不包含在任何名稱空間中的程式碼,則 foo 會被解析為foo。 警告:如果名稱空間中的函式或常量未定義,則該非限定的函式名稱或常量名稱會被解析為全域性函式名稱或常量名稱。詳情參見 使用名稱空間:後備全域性函式名稱/常量名稱
  2. 限定名稱,或包含字首的名稱,例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果當前的名稱空間是currentnamespace,則 foo 會被解析為 currentnamespace\subnamespace\foo。如果使用 foo 的程式碼是全域性的,不包含在任何名稱空間中的程式碼,foo 會被解析為subnamespace\foo
  3. 完全限定名稱,或包含了全域性字首操作符的名稱,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在這種情況下,foo 總是被解析為程式碼中的文字名(literal name)currentnamespace\foo

下面是一個使用這三種方式的例項:

file1.php

<?php
namespace Foo\Bar\subnamespace;

const FOO = 1;
function foo() {}
class foo
{
    static function staticmethod() {}
}
?>

file2.php

<?php
namespace Foo\Bar;
include 'file1.php';

const FOO = 2;
function foo() {}
class foo
{
    static function staticmethod() {}
}

/* 非限定名稱 */
foo(); // 解析為 Foo\Bar\foo resolves to function Foo\Bar\foo
foo::staticmethod(); // 解析為類 Foo\Bar\foo的靜態方法staticmethod。resolves to class Foo\Bar\foo, method staticmethod
echo FOO; // resolves to constant Foo\Bar\FOO

/* 限定名稱 */
subnamespace\foo(); // 解析為函式 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // 解析為類 Foo\Bar\subnamespace\foo,
                                  // 以及類的方法 staticmethod
echo subnamespace\FOO; // 解析為常量 Foo\Bar\subnamespace\FOO
                                  
/* 完全限定名稱 */
\Foo\Bar\foo(); // 解析為函式 Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // 解析為類 Foo\Bar\foo, 以及類的方法 staticmethod
echo \Foo\Bar\FOO; // 解析為常量 Foo\Bar\FOO
?>

注意訪問任意全域性類、函式或常量,都可以使用完全限定名稱,例如 \strlen() 或 \Exception 或 \INI_ALL

Example #1 在名稱空間內部訪問全域性類、函式和常量

<?php
namespace Foo;

function strlen() {}
const INI_ALL = 3;
class Exception {}

$a = \strlen('hi'); // 呼叫全域性函式strlen
$b = \INI_ALL; // 訪問全域性常量 INI_ALL
$c = new \Exception('error'); // 例項化全域性類 Exception
?>

 

六. 名稱空間和動態語言特徵

PHP 名稱空間的實現受到其語言自身的動態特徵的影響。因此,如果要將下面的程式碼轉換到名稱空間中:

Example #1 動態訪問元素

example1.php:

<?php
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}
function funcname()
{
    echo __FUNCTION__,"\n";
}
const constname = "global";

$a = 'classname';
$obj = new $a; // prints classname::__construct
$b = 'funcname';
$b(); // prints funcname
echo constant('constname'), "\n"; // prints global
?>

必須使用完全限定名稱(包括名稱空間字首的類名稱)。注意因為在動態的類名稱、函式名稱或常量名稱中,限定名稱和完全限定名稱沒有區別,因此其前導的反斜槓是不必要的。

Example #2 動態訪問名稱空間的元素

<?php
namespace namespacename;
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}
function funcname()
{
    echo __FUNCTION__,"\n";
}
const constname = "namespaced";

include 'example1.php';

$a = 'classname';
$obj = new $a; // prints classname::__construct
$b = 'funcname';
$b(); // prints funcname
echo constant('constname'), "\n"; // prints global

/* note that if using double quotes, "\\namespacename\\classname" must be used */
$a = '\namespacename\classname';
$obj = new $a; // prints namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // also prints namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // prints namespacename\funcname
$b = '\namespacename\funcname';
$b(); // also prints namespacename\funcname
echo constant('\namespacename\constname'), "\n"; // prints namespaced
echo constant('namespacename\constname'), "\n"; // also prints namespaced
?>

 

七. namespace關鍵字和__NAMESPACE__常量

PHP支援兩種抽象的訪問當前名稱空間內部元素的方法,__NAMESPACE__ 魔術常量和namespace關鍵字。

常量__NAMESPACE__的值是包含當前名稱空間名稱的字串。在全域性的,不包括在任何名稱空間中的程式碼,它包含一個空的字串。

Example #1 __NAMESPACE__ 示例, 在名稱空間中的程式碼

<?php
namespace MyProject;

echo '"', __NAMESPACE__, '"'; // 輸出 "MyProject"
?>

Example #2 __NAMESPACE__ 示例,全域性程式碼

<?php

echo '"', __NAMESPACE__, '"'; // 輸出 ""
?>

常量 __NAMESPACE__ 在動態建立名稱時很有用,例如:

Example #3 使用__NAMESPACE__動態建立名稱

<?php
namespace MyProject;

function get($classname)
{
    $a = __NAMESPACE__ . '\\' . $classname;
    return new $a;
}
?>

關鍵字 namespace 可用來顯式訪問當前名稱空間或子名稱空間中的元素。它等價於類中的 self 操作符。

Example #4 namespace操作符,名稱空間中的程式碼

<?php
namespace MyProject;

use blah\blah as mine; // see "Using namespaces: importing/aliasing"

blah\mine(); // calls function MyProject\blah\mine()
namespace\blah\mine(); // calls function MyProject\blah\mine()

namespace\func(); // calls function MyProject\func()
namespace\sub\func(); // calls function MyProject\sub\func()
namespace\cname::method(); // calls static method "method" of class MyProject\cname
$a = new namespace\sub\cname(); // instantiates object of class MyProject\sub\cname
$b = namespace\CONSTANT; // assigns value of constant MyProject\CONSTANT to $b
?>

Example #5 namespace操作符, 全域性程式碼

<?php

namespace\func(); // calls function func()
namespace\sub\func(); // calls function sub\func()
namespace\cname::method(); // calls static method "method" of class cname
$a = new namespace\sub\cname(); // instantiates object of class sub\cname
$b = namespace\CONSTANT; // assigns value of constant CONSTANT to $b
?>

 

八. 使用名稱空間:別名/匯入

允許通過別名引用或匯入外部的完全限定名稱,是名稱空間的一個重要特徵。這有點類似於在類 unix 檔案系統中可以建立對其它的檔案或目錄的符號連線。

所有支援名稱空間的PHP版本支援三種別名或匯入方式:為類名稱使用別名、為介面使用別名或為名稱空間名稱使用別名。PHP 5.6開始允許匯入函式或常量或者為它們設定別名。

在PHP中,別名是通過操作符 use 來實現的. 下面是一個使用所有可能的五種匯入方式的例子:

Example #1 使用use操作符匯入/使用別名

<?php
namespace 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\functionName
echo CONSTANT; // echoes the value of My\Full\CONSTANT
?>

注意對名稱空間中的名稱(包含名稱空間分隔符的完全限定名稱如 Foo\Bar以及相對的不包含名稱空間分隔符的全域性名稱如 FooBar)來說,前導的反斜槓是不必要的也不推薦的,因為匯入的名稱必須是完全限定的,不會根據當前的名稱空間作相對解析。

為了簡化操作,PHP還支援在一行中使用多個use語句

Example #2 通過use操作符匯入/使用別名,一行中包含多個use語句

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 例項化 My\Full\Classname 物件
NSname\subns\func(); // 呼叫函式 My\Full\NSname\subns\func
?>

匯入操作是在編譯執行的,但動態的類名稱、函式名稱或常量名稱則不是。

Example #3 匯入和動態名稱

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 例項化一個 My\Full\Classname 物件
$a = 'Another';
$obj = new $a;      // 實際化一個 Another 物件
?>

另外,匯入操作隻影響非限定名稱和限定名稱。完全限定名稱由於是確定的,故不受匯入的影響。

Example #4 匯入和完全限定名稱

<?php
use 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
?>

匯入範圍規則

The use keyword must be declared in the outermost scope of a file (the global scope) or inside namespace declarations. This is because the importing is done at compile time and not runtime, so it cannot be block scoped. The following example will show an illegal use of the usekeyword:

必須在檔案(全域性範圍)或名稱空間宣告的最外層範圍內宣告use關鍵字。這是因為匯入是在編譯時完成的,而不是執行時完成的,因此它不能是塊作用域。下面的示例將顯示非法使用use關鍵字:

Example #5 非法引入示例

<?php
namespace Languages;

class Greenlandic
{
    use Languages\Danish;

    ...
}
?>

Note:

匯入規則是每個檔案的基礎,意味著包含的檔案將不會繼承父檔案的匯入規則。

請一定別忘了閱讀 對字串中的名稱空間名稱轉義的註解.

九. 全域性空間

如果沒有定義任何名稱空間,所有的類與函式的定義都是在全域性空間,與 PHP 引入名稱空間概念前一樣。在名稱前加上字首 \ 表示該名稱是全域性空間中的名稱,即使該名稱位於其它的名稱空間中時也是如此。

Example #1 使用全域性空間說明

<?php
namespace A\B\C;

/* 這個函式是 A\B\C\fopen */
function fopen() { 
     /* ... */
     $f = \fopen(...); // 呼叫全域性的fopen函式
     return $f;
} 
?>

 

十. 使用名稱空間:後備全域性函式/常量

在一個名稱空間中,當 PHP 遇到一個非限定的類、函式或常量名稱時,它使用不同的優先策略來解析該名稱。類名稱總是解析到當前名稱空間中的名稱。因此在訪問系統內部或不包含在名稱空間中的類名稱時,必須使用完全限定名稱,例如:

Example #1 在名稱空間中訪問全域性類

<?php
namespace 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 類
?>

對於函式和常量來說,如果當前名稱空間中不存在該函式或常量,PHP 會退而使用全域性空間中的函式或常量。 For functions and constants, PHP will fall back to global functions or constants if a namespaced function or constant does not exist.

Example #2 名稱空間中後備的全域性函式/常量

<?php
namespace 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_ALL

echo strlen('hi'), "\n"; // 輸出 "1"
if (is_array('hi')) { // 輸出 "is not array"
    echo "is array\n";
} else {
    echo "is not array\n";
}
?>

 

轉載:http://php.net/manual/zh/language.namespaces.rationale.php