FAQ:命名空間必知必會(huì)

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

本文分兩節(jié):常見(jiàn)問(wèn)題、有助于完全理解的實(shí)現(xiàn)詳情。

首先,常見(jiàn)問(wèn)題。

  1. 如果我不用命名空間,是否需要關(guān)心它?
  2. 我如何在命名空間內(nèi)使用一個(gè)全局/內(nèi)置的類?
  3. 如何在命名空間內(nèi)訪問(wèn)它自己的類、函數(shù)、常量?
  4. \my\name\name 這樣的名稱是如何解析的?
  5. my\name 這樣的名稱是如何解析的?
  6. name 這樣的非限定類名是如何解析的?
  7. name 這樣的非限定常量和函數(shù)名是如何解析的?

為了幫助理解,我們提供了一些命名空間實(shí)現(xiàn)細(xì)節(jié)。

  1. 在同一個(gè)文件中,導(dǎo)入名稱不能和定義的類名發(fā)生沖突。
  2. 不允許嵌套 namespace。
  3. 動(dòng)態(tài)命名空間名稱(引號(hào)標(biāo)識(shí))應(yīng)該轉(zhuǎn)義反斜線。
  4. 引用一個(gè)未定義的、帶反斜線的常量,會(huì)導(dǎo)致 fatal 錯(cuò)誤并退出
  5. 不能重載特殊常量: NULL、TRUE、FALSE、ZEND_THREAD_SAFE、ZEND_DEBUG_BUILD

如果我不用命名空間,是否需要關(guān)心它?

不需要。命名空間不影響現(xiàn)存的代碼,也不影響即將要寫下的不含命名空間的代碼。 想要的話可以這樣寫:

示例 #1 在命名空間之外訪問(wèn)全局類

<?php
$a 
= new \stdClass;

以上等同于:

示例 #2 在命名空間之外訪問(wèn)全局類

<?php
$a 
= new stdClass;

我如何在命名空間內(nèi)使用一個(gè)全局/內(nèi)置的類?

示例 #3 在命名空間內(nèi)訪問(wèn)內(nèi)置的類

<?php
namespace foo;
$a = new \stdClass;

function 
test(\ArrayObject $parameter_type_example null) {}

$a = \DirectoryIterator::CURRENT_AS_FILEINFO;

// 擴(kuò)展內(nèi)置或全局的 class
class MyException extends \Exception {}
?>

如何在命名空間內(nèi)訪問(wèn)它自己的類、函數(shù)、常量?

示例 #4 在命名空間中訪問(wèn)內(nèi)置的類、函數(shù)、常量

<?php
namespace foo;

class 
MyClass {}

// 以當(dāng)前命名空間中的 class 作為參數(shù)的類型
function test(MyClass $parameter_type_example null) {}
// 以當(dāng)前命名空間中的 class 作為參數(shù)的類型的另一種方式
function test(\foo\MyClass $parameter_type_example null) {}

// 在當(dāng)前命名空間中擴(kuò)展一個(gè)類
class Extended extends MyClass {}

// 訪問(wèn)全局函數(shù)
$a = \globalfunc();

// 訪問(wèn)全局常量
$b = \INI_ALL;
?>

\my\name\name 這樣的名稱是如何解析的?

\ 開頭的名稱總是會(huì)解析成原樣, 因此 \my\name 實(shí)際上是 my\name, 而 \ExceptionException。

示例 #5 完全限定名稱

<?php
namespace foo;
$a = new \my\name(); // class "my\name" 的實(shí)例
echo \strlen('hi'); // 調(diào)用函數(shù) "strlen"
$a = \INI_ALL// $a 的值設(shè)置成常量 "INI_ALL"
?>

my\name 這樣的名稱是如何解析的?

my\name 這樣包含反斜線的名稱,但不以反斜線開頭的名稱, 能夠以兩種不同的方式解析。

如果有個(gè)導(dǎo)入語(yǔ)句,將其他名字設(shè)置別名為 my, 則導(dǎo)入別名會(huì)應(yīng)用到 my\namemy 部分。

如果沒(méi)有導(dǎo)入,就會(huì)追加當(dāng)前的命名空間名稱為 my\name 的前綴。

示例 #6 限定名稱

<?php
namespace foo;
use 
blah\blah as foo;

$a = new my\name(); // class "foo\my\name" 的實(shí)例
foo\bar::name(); // 調(diào)用 class "blah\blah\bar" 的靜態(tài)方法 "name"
my\bar(); // 調(diào)用函數(shù) "foo\my\bar"
$a my\BAR// 設(shè)置 $a 的值為 "foo\my\BAR"
?>

name 這樣的非限定名稱是如何解析的?

name 這樣不包含反斜線的名稱, 能夠以兩種不同的方式解析。

如果有導(dǎo)入語(yǔ)句,設(shè)置別名為 name,就會(huì)應(yīng)用導(dǎo)入別名。

如果沒(méi)有,就會(huì)把當(dāng)前命名空間添加到 name 的前綴。

示例 #7 非限定類名

<?php
namespace foo;
use 
blah\blah as foo;

$a = new name(); // class "foo\name" 的實(shí)例
foo::name(); // 調(diào)用 class "blah\blah" 的靜態(tài)方法 "name"
?>

name 這樣的非限定常量和函數(shù)名是如何解析的?

name 這樣不包含反斜線的常量和函數(shù)名,能以兩種不同的方式解析。

首先,當(dāng)前命名空間會(huì)添加到 name 的前綴。

然后,如果當(dāng)前命名空間不存在函數(shù)和常量 name, 而全局存在,就會(huì)使用全局的函數(shù)和常量 name。

示例 #8 非限定函數(shù)和常量名

<?php
namespace foo;
use 
blah\blah as foo;

const 
FOO 1;

function 
my() {}
function 
foo() {}
function 
sort(&$a)
{
    
sort($a);
    
$a array_flip($a);
    return 
$a;
}

my(); // 調(diào)用 "foo\my"
$a strlen('hi'); // 由于 "foo\strlen" 不存在,所以調(diào)用全局的 "strlen"
$arr = array(1,3,2);
$b sort($arr); // 調(diào)用函數(shù) "foo\sort"
$c foo(); // 未導(dǎo)入,調(diào)用函數(shù) "foo\foo"

$a FOO// 未導(dǎo)入,設(shè)置 $a 為常量 "foo\FOO" 的值
$b INI_ALL// 設(shè)置 $b 為全局常量 "INI_ALL" 的值
?>

在同一個(gè)文件中,導(dǎo)入名稱不能和定義的類名發(fā)生沖突

允許以下腳本中的組合:

file1.php

<?php
namespace my\stuff;
class 
MyClass {}
?>

another.php

<?php
namespace another;
class 
thing {}
?>

file2.php

<?php
namespace my\stuff;
include 
'file1.php';
include 
'another.php';

use 
another\thing as MyClass;
$a = new MyClass//  class "thing" 的實(shí)例來(lái)自于命名空間 another
?>

盡管在 my\stuff 命名空間中存在 MyClass, 因?yàn)轭惗x在了獨(dú)立的文件中,所以不會(huì)發(fā)生名稱沖突。 不過(guò),接下來(lái)的例子中,因?yàn)?MyClass 定義在了 use 語(yǔ)句的同一個(gè)文件中, 所以發(fā)生了名稱沖突,導(dǎo)致了 fatal 錯(cuò)誤。

<?php
namespace my\stuff;
use 
another\thing as MyClass;
class 
MyClass {} // fatal error: MyClass conflicts with import statement
$a = new MyClass;
?>

不允許嵌套 namespace

PHP 不允許嵌套 namespace

<?php
namespace my\stuff {
    namespace 
nested {
        class 
foo {}
    }
}
?>
實(shí)際上,它看上去像是這樣:
<?php
namespace my\stuff\nested {
    class 
foo {}
}
?>

動(dòng)態(tài)命名空間名稱(引號(hào)標(biāo)識(shí))應(yīng)該轉(zhuǎn)義反斜線

重要的是,字符串中反斜線是一個(gè)轉(zhuǎn)義字符,因此在字符串中使用時(shí),必須要寫兩遍。 否則就會(huì)在無(wú)意中造成一些后果:

示例 #9 在雙引號(hào)字符串中使用命名空間的危險(xiǎn)性

<?php
$a 
= new "dangerous\name"// 在雙引號(hào)字符串中,\n 是換行符!
$obj = new $a;

$a = new 'not\at\all\dangerous'// 這里沒(méi)有問(wèn)題
$obj = new $a;
?>
在單引號(hào)字符串中,使用反斜線是安全的。 但在最佳實(shí)踐中,我們?nèi)匀煌扑]為所有字符串統(tǒng)一轉(zhuǎn)義反斜線。

引用一個(gè)未定義的、帶反斜線的常量,會(huì)導(dǎo)致 fatal 錯(cuò)誤并退出

FOO 這樣的非限定名稱常量,如果使用的時(shí)候還沒(méi)定義, 會(huì)產(chǎn)生一個(gè) notice。PHP 會(huì)假設(shè)該常量的值是 FOO。 如果沒(méi)有找到包含反斜線的常量,無(wú)論是完全或者不完全限定的名稱,都會(huì)產(chǎn)生 fatal 錯(cuò)誤。

示例 #10 未定義的常量

<?php
namespace bar;
$a FOO// 產(chǎn)生 notice - undefined constants "FOO" assumed "FOO";
$a = \FOO// fatal error, undefined namespace constant FOO
$a Bar\FOO// fatal error, undefined namespace constant bar\Bar\FOO
$a = \Bar\FOO// fatal error, undefined namespace constant Bar\FOO
?>

不能重載特殊常量:NULL、TRUE、FALSE、ZEND_THREAD_SAFE、ZEND_DEBUG_BUILD

在命名空間內(nèi)定義特殊的內(nèi)置常量,會(huì)導(dǎo)致 fatal 錯(cuò)誤

示例 #11 未定義的常量

<?php
namespace bar;
const 
NULL 0// fatal error;
const true 'stupid'// 也是 fatal error;
// etc.
?>