首頁  >  文章  >  後端開發  >  什麼是命名空間?詳解PHP中的命名空間

什麼是命名空間?詳解PHP中的命名空間

怪我咯
怪我咯轉載
2017-06-14 11:49:586007瀏覽

什麼是命名空間?這篇文章帶大家深入了解PHP中的命名空間(namespace),希望對大家有幫助。

什麼是命名空間?詳解PHP中的命名空間

什麼是命名空間?

從廣義來說,命名空間是一種封裝事物的方法,在許多地方都可以見到這個抽象概念。例如,在作業系統中目錄用來將相關檔案分組,而對於目錄中的檔案來說,它就扮演了命名空間的角色。

舉個簡單的例子,檔案 foo.txt 可以同時在目錄 /home/greg 和 /home/other 中存在,但在同一個目錄中不能存在兩個 foo.txt 檔案。另外,在目錄 /home/greg 外存取 foo.txt 檔案時,我們必須將目錄名稱以及目錄分隔符號放在檔案名稱之前,例如 /home/greg/foo.txt。這個原理應用在程式設計領域就是命名空間的概念。

命名空間的定義

PHP 中命名空間(namespace)是在PHP5.3 中加入的,如果你了解C++ 的話,那命名空間就不算是什麼新事物了。不過命名空間在 PHP 當中還是相當重要的。

PHP 命名空間可以解決以下兩類問題:

  • 使用者寫的程式碼與PHP 內部的類別/函數/常數或第三方類別/函數/常數之間的命名衝突;

  • 為很長的標識符名稱(通常是為了緩解第一類問題而定義的)創建一個別名(或簡短)的名稱,以提高來源代碼的可讀性。

1) 定義命名空間(使用關鍵字namespace)

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

命名空間的定義需要透過關鍵字namespace 來聲明,語法格式如下:

namespace 命名空间名;

【範例】下面我們來示範如何定義了命名空間:

<?php
    namespace MyProject;    // 定义名为 MyProject 的命名空间。
    const CONNECT_OK = 1;
    class Myclass {
        /* ... */
    }
    function Myfunc() {
        /* ... */
    }
?>

在聲明命名空間之前除了用於定義原始檔案編碼方式的declare 語句外,所有非PHP 程式碼(包括空白符號)都不能出現在命名空間聲明之前。

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

2) 定義子命名空間

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

namespace App\Model;
namespace App\Controller\Home;

【範例】定義一個子命名空間:

<?php
    namespace MyProject\Controller\Home;    // 定义名为 MyProject 的命名空间。
    const CONNECT_OK = 1;
    class Myclass {
        /* ... */
    }
    function Myfunc() {
        /* ... */
    }
?>

3) 在同一個檔案中定義多個命名空間

在一個檔案中也可以定義多個命名空間,在同一檔案中定義多個命名空間有兩種語法形式,下面透過範例示範:

# 【範例】定義多個命名空間-簡單組合語法。

<?php
    namespace MyProject;
    const CONNECT_OK = 1;
    class className {
        /* ... */
    }
    namespace AnotherProject;
    const CONNECT_OK = 1;
    class className {
        /* ... */
    }
?>

【範例】定義多個命名空間-大括號{ }語法。

<?php
    namespace MyProject{
        const CONNECT_OK = 1;
        class className {
            /* ... */
        }
    }
    namespace AnotherProject{
        const CONNECT_OK = 1;
        class className {
            /* ... */
        }
    }
?>

在實際的程式設計實踐中,並不提倡在同一個檔案中定義多個命名空間。定義多個命名空間主要用於將多個 PHP 腳本合併在同一個檔案中。在定義多個命名空間時建議使用大括號形式的語法。

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

<?php
    namespace MyProject{        // 命名空间中的代码
        const CONNECT_OK = 1;
        class className {
            /* ... */
        }
    }
    namespace {                 // 命名空间外的全局代码
        $obj = new MyProject\className();
    }
?>

使用命名空間:基礎

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

  • 相對檔案名稱形式如 foo.txt。它會被解析為 currentdirectory/foo.txt,其中 currentdirectory 表示目前目錄。因此如果目前目錄是 /home/foo,則該檔案名稱被解析為 /home/foo/foo.txt;

  • 相對路徑名稱形式如 subdirectory/foo.txt。它會被解析為 currentdirectory/subdirectory/foo.txt;

  • #絕對路徑名稱形式如 /main/foo.txt。它會被解析為 /main/foo.txt。

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

  • 非限定名稱,或不包含前綴的類別名稱,例如$a=new foo() ;foo::staticmethod();。如果目前命名空間是 currentnamespace,那麼 foo 將會解析為 currentnamespace\foo。如果使用 foo 的程式碼是全域的,不包含在任何命名空間中的程式碼,則 foo 會被解析為 foo。

  • 限定名称,或包含前缀的名称,例如$a = new subnamespace\foo();subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为 subnamespace\foo。

  • 完全限定名称,或包含了全局前缀操作符的名称,例如$a = new \currentnamespace\foo();\currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名 currentnamespace\foo。

警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。

下面是一个使用这三种方式的实例,我们需要两个 PHP 源文件,分别是 demo.php 和 index.php,示例代码如下:

1) demo.php

<?php
    namespace Foo\Bar\Demo;
    const FOO = 1;
    function foo() {}
    class foo
    {
        public function demo() {
            echo &#39;命名空间为:Foo\Bar\Demo&#39;;
        }
    }
?>

2) index.php

<?php
    namespace Foo\Bar;
    include &#39;Demo.php&#39;;
    const FOO = 2;
    function foo() {
        echo &#39;Foo\Bar 命名空间下的 foo 函数<br>&#39;;
    }
    class foo
    {
        static function demo(){
            echo &#39;命名空间为:Foo\Bar<br>&#39;;
        }
    }
    /* 非限定名称 */
    foo();                  // 解析为 Foo\Bar\foo resolves to function Foo\Bar\foo
    foo::demo();            // 解析为类 Foo\Bar\foo 的静态方法 staticmethod。
    echo FOO.&#39;<br>&#39;;        // 解析为常量 Foo\Bar\FOO
    /* 限定名称 */
    Demo\foo();             // 解析为函数 Foo\Bar\Demo\foo
    Demo\foo::demo();       // 解析为类 Foo\Bar\Demo\foo,
                            // 以及类的方法 demo
    echo Demo\FOO.&#39;<br>&#39;;   // 解析为常量 Foo\Bar\Demo\FOO
                                     
    /* 完全限定名称 */
    \Foo\Bar\foo();         // 解析为函数 Foo\Bar\foo
    \Foo\Bar\foo::demo();   // 解析为类 Foo\Bar\foo, 以及类的方法 demo
    echo \Foo\Bar\FOO.&#39;<br>&#39;; // 解析为常量 Foo\Bar\FOO
?>

运行结果如下:

Foo\Bar 命名空间下的 foo 函数
命名空间为:Foo\Bar
2
Foo\Bar\Demo 命名空间下的 foo 函数
命名空间为:Foo\Bar\Demo
1
Foo\Bar 命名空间下的 foo 函数
命名空间为:Foo\Bar
2

注意:访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 等。

使用命名空间:别名/导入

PHP 允许通过别名引用或导入的方式来使用外部的命名空间,这是命名空间的一个重要特征。这有点类似于在类 unix 文件系统中可以创建对其它的文件或目录的符号连接。

使用 use 关键字可以实现命名空间的导入,从 PHP5.6 开始允许导入函数和常量,并为它们设置别名。语法格式如下:

use namespace;

在 PHP 中,别名是通过操作符 use 与 as 来实现的,语法格式如下:

use 命名空间 as 别名;

【示例】使用 use 操作符导入和使用别名。

<?php
    namespace foo;
    use My\Full\Classname as Another;
    // 下面的例子与 use My\Full\NSname as NSname 相同
    use My\Full\NSname;
    // 导入一个全局类
    use ArrayObject;
    // 导入一个函数
    use function My\Full\functionName;
    // 导入一个函数并定义别名
    use function My\Full\functionName as func;
    // 导入一个常量
    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();                         // 调用 My\Full\functionName 函数
    echo CONSTANT;                  // 打印 My\Full\CONSTANT 常量
?>

注意:对命名空间中的名称(包含命名空间分隔符的完全限定名称,如 Foo\Bar ,以及相对的不包含命名空间分隔符的全局名称,如 FooBar)来说,前导的反斜杠是不必要的也是不推荐的,因为导入的名称必须是完全限定的,不会根据当前的命名空间作相对解析。

为了简化操作,PHP 还支持在一行中导入多个命名空间,中间使用,隔开,示例代码如下:

<?php
    use My\Full\Classname as Another, My\Full\NSname;
    $obj = new Another;     // 实例化 My\Full\Classname 对象
    NSname\subns\func();    // 调用 My\Full\NSname\subns\func 函数
?>

导入操作是编译执行的,但动态的类名称、函数名称或常量名称则不是。

<?php
    use My\Full\Classname as Another, My\Full\NSname;
    $obj = new Another; // 实例化一个 My\Full\Classname 对象
    $a = &#39;Another&#39;;
    $obj = new $a;      // 实际化一个 Another 对象
?>

另外,导入操作只影响非限定名称和限定名称。完全限定名称由于是确定的,故不受导入的影响。

namespace 关键字和 __NAMESPACE__ 常量

PHP 支持两种抽象的访问当前命名空间内部元素的方法,__NAMESPACE__ 魔术常量和 namespace 关键字。

__NAMESPACE__ 常量的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它是一个空的字符串。示例代码如下:

<?php
    namespace App\Controller\Home;
    echo __NAMESPACE__;
?>

运行结果如下:

App\Controller\Home

namespace 关键字可用来显式访问当前命名空间或子命名空间中的元素,它等价于类中的 self 操作符。示例代码如下:

<?php
    namespace MyProject;
    use Foo\Bar\Demo as demo;       // 导入 Foo\Bar\Demo 命名空间
    blah\mine();                    // 调用 MyProject\blah\mine() 函数
    namespace\blah\mine();          // 调用 MyProject\blah\mine() 函数
    namespace\func();               // 调用 MyProject\func() 函数
    namespace\sub\func();           // 调用 MyProject\sub\func() 函数
    namespace\cname::method();      // 调用 MyProject\cname 类的静态方法 method
    $a = new namespace\sub\cname(); // 实例化 MyProject\sub\cname 对象
    $b = namespace\CONSTANT;        // 将常量 MyProject\CONSTANT 的值赋给 $b
?>

命名空间名称解析规则

在说明名称解析规则之前,我们先来看看命名空间名称的定义:

  • 非限定名称:名称中不包含命名空间分隔符的标识符,例如 Foo;

  • 限定名称:名称中含有命名空间分隔符的标识符,例如 Foo\Bar;

  • 完全限定名称:名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如 \Foo\Bar。namespace\Foo 也是一个完全限定名称。

名称解析遵循下列规则:

  • 對完全限定名稱的函數,類別和常數的呼叫在編譯時解析。例如 new \A\B 解析為類別 A\B;
  • 所有的非限定名稱和限定名稱(非完全限定名稱)根據目前的導入規則在編譯時進行轉換。例如,如果命名空間A\B\C 被導入為C,那麼對C\D\e() 的呼叫就會被轉換為A\B\C\D\e();
  • 在命名空間內部,所有的沒有根據導入規則轉換的限定名稱均會在其前面加上目前的命名空間名稱。例如,在命名空間A\B 內部呼叫C\D\e(),則C\D\e() 會轉換為A\B\C\D\e();
  • 非限定類別名稱根據目前的導入規則在編譯時轉換(以全名取代短的導入名稱)。例如,如果命名空間A\B\C 導入為C,則new C() 轉換為new A\B\C();
  • 在命名空間內部(例如A\B),對非限定名稱的函數呼叫是在執行時間解析的。例如對函數foo() 的呼叫是這樣解析的:
    • 在目前命名空間中尋找名為A\B\foo() 的函數;

    • 嘗試尋找並呼叫全域空間中的foo() 函數。

  • 在命名空間(例如 A\B)內部對非限定名稱或限定名稱類別(非完全限定名稱)的呼叫是在執行時間解析的。以下是呼叫new C() 及new D\E() 的解析過程:
    • new C() 的解析:

      • 在目前命名空間中尋找A \B\C 類別;
      • 嘗試自動裝載類別A\B\C。
    • new D\E() 的解析:

      • #在類別名稱前面加上目前命名空間名稱變成:A\B\ D\E,然後尋找該類別;
      • 嘗試自動裝載類別A\B\D\E。
    • 為了引用全域命名空間中的全域類,必須使用完全限定名稱 new \C()。

推薦學習:《PHP影片教學

以上是什麼是命名空間?詳解PHP中的命名空間的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:biancheng.net。如有侵權,請聯絡admin@php.cn刪除