PHP中的命名空間

命名空間

PHP中的命名空間,簡單來說就像是文件夾。在同一個PHP腳本文件中不能引用相同名字的類或者函數,但是由於開發過程中會使用第三方的SDK或者是團隊合作開發,難以避免會有命名重複的可能。

而命名空間的引入就是爲了解決這一問題,命名空間就相當於文件夾,同一個文件夾中不能創建相同名字的文件或者文件夾,但是如果相同名字的文件夾在不同文件中就不存在這一問題。

引用一段來自PHP官方文檔的話。

什麼是命名空間?從廣義上來說,命名空間是一種封裝事物的方法。在很多地方都可以見到這種抽象概念。例如,在操作系統中目錄用來將相關文件分組,對於目錄中的文件來說,它就扮演了命名空間的角色。具體舉個例子,文件 foo.txt 可以同時在目錄/home/greg 和 /home/other 中存在,但在同一個目錄中不能存在兩個 foo.txt 文件。另外,在目錄 /home/greg 外訪問 foo.txt 文件時,我們必須將目錄名以及目錄分隔符放在文件名之前得到 /home/greg/foo.txt。這個原理應用到程序設計領域就是命名空間的概念。

在PHP中,命名空間用來解決在編寫類庫或應用程序時創建可重用的代碼如類或函數時碰到的兩類問題:
1. 用戶編寫的代碼與PHP內部的類/函數/常量或第三方類/函數/常量之間的名字衝突。
2. 爲很長的標識符名稱(通常是爲了緩解第一類問題而定義的)創建一個別名(或簡短)的名稱,提高源代碼的可讀性。


命名空間定義有幾點需要注意的地方


命名空間必須在其它所有代碼之前聲明

命名空間必須在所有代碼之前除了declare關鍵字


允許將同一個命名空間的內容分割存放在不同的文件中

也就說命名空間可以存放在多個文件中


可以在同一個文件中定義多個命名空間

可以在一個PHP腳本中定義多個命名空間,但是這樣不方便管理。同時有兩種定義的方法,推薦使用第二種。

第一種 簡單組合語法

<?php
    namespace MyProject;

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

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

第二種 大括號語法

<?php
    namespace MyProject {

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

    namespace AnotherProject {

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

命名空間的三種使用方式

  1. 非限定名稱 直接引用類
  2. 限定名稱 引用相對路徑
  3. 完全限定名稱 引用絕對路勁

命名空間的優點

命名空間一個很重的作用,就是組件化開發提供了便利和可能,也就是通過命名空間來組織文件,使得某個組件的文件的路徑和命名空間具有一定的關係,最終可以直接通過命名空間找到相應的文件。


Laravel框架中的自動加載

include和require關鍵字是通過手動的方式對相應文件進行包含,實際上PHP提供了更加方便的文件包含方法,即類的自動加載方法。類的自動加載可以通過魔術變量__autoload(string $class)實現,也可以通過函數spl_autoload_register註冊一個自動加載相應實例如下:

<?php
function __atuoload($class){
    require_once($class.".php");
}
?>

當使用一個類名時,如果該類沒有被當前文件包含,則會調用__autoload(class) class 爲使用類的名稱。但在實際應用中,通常spl_autoload_register註冊自定義函數作爲自動加載類的實現,因爲__autoload()魔術函數只可以定義一次,而spl_atutoload_register可以將多個類自動加載方法註冊到隊列中,即創建了autoload函數隊列,在調用時按照定義時的順序逐個執行。
其函數定義如下:

bool spl_autoload_register([callable autoloadfunction[,bool throw=true[,bool $prepend=false]]])

通過spl_autoload_register()函數加載的自動加載函數可以是全局函數,也可以是某個類實例對象的函數,即通過array(“對象名”,“函數名”)註冊。


laravle中的實現方式

在laravel框架中,通過函數spl_autoload_register()實現類的自動加載函數的註冊,其中類的自動加載函數隊列中包含了兩個類的自動加載函數,一個是composer生成的基於PSR規範的自動加載函數,另一個是Laravel框架核心別名的自動加載函數。
其中composer生成的自動加載函數註冊過程如下:

文件 laravel\public\index.php

<?php
    require __DIR__.'/../bootstrap/autoload.php';
?>

文件 laravel\bootstrap\autoload.php

<?php
    define('LARAVEL_STATR',micotime(true));
    requier __DIR__.'/../vendor/autoload.php';
?>

在Laravel框架中,public\index.php文件作爲文件的入口,其中第一句就保護鳥了自動加載文件autoload.php,這個文件中繼續包含位於vendor目錄下的composer的自動加載文件。

文件 laravel\vendor\autoload.php

<?php
    require_once __DIR__ . '/composer' . '/autoload_real.php';
    return ComposerAutoloaderInit9f6ff2d01ba6cb93f0cabaf91359a37c::getLoader();
?>

通過composer工具創建依賴管理時,會在vendor目錄下創建autoload.php文件和一個composer文件夾,其中composer 文件夾中包含了類自動加載函數註冊的相關實現,而autoload.php文件時對外提供接口,通過包含該文件就可以完成類自動加載函數的註冊。通過上述代碼可以看到,autoload.php文件包含了composer目錄下的autoload_real.php文件,而autoload_real.php文件中定義了一個類,這個類末尾有一串數字的方式定義,並且定義了getLoader()函數,該函數首先實例化Composer\Autoload\ClassLoader類,然後通過該實例添加相關的文件路徑配置,包括命名空間(autoload_namespaces.php文件定義)配置,PSR-4規範(autoload_psr4.php文件定義)配置,類映射(autoload_classmap.php文件定義)配置,接着調用$loader->register(true)註冊類自動加載函數,最後加載全局文件,即在autoload_files.php文件中配置的內容。

下面介紹如何註冊類自動加載函數,以及類自動加載函數時如何實現類自動加載的。

文件:laravel\vender\composer\ClassLoader.php

<?php
namespace Composer\Autoload;
class ClassLoader{
    //省略加載文件路徑函數的相關代碼
    public function register($prepend = false){
        spl_autoload_register(array($this,'loadClass'),true,$prepend);
    }

    public function loadClass($class){
        if($file = $this->findFile($class)) {
            includeFile($file);
            return true;
        }
    }
}
//省略了根據加載文件路勁查找文件具體位置的相關代碼
?>

通過前面的介紹,已經瞭解了類的自動加載時在Composer\Autoload\ClassLoader類中實現的,實例化該類並將該類的命名空間和文件路徑的對應關係註冊到對應的屬性中,然後通過實例方法register($prepend = false)註冊一個類自動加載函數,即爲該類實例的loadClass方法,並且將其註冊在類自動加載函數隊列的末尾,當使用一個未包含的類名時,會自動調用loadClass方法並通過參數獲取包含命名空間的類名信息,接着根據類的命名空間與文件路徑的對應關係查找文件路徑,最後通過includeFile()函數包含該文件,實現類的自動加載。

默認Laravel框架包含兩個類的自動加載函數,其中一個時在外觀註冊(Illuminate\Foundation\Bootstrap\RegisterFacades類實現的)過程中實現的,這裏只需要瞭解有這樣一個類自動加載函數被註冊到堆棧中就可以,後面可以在“請求到相應的生命週期”內容,瞭解調用過程,這裏只介紹類的自動加載函數的註冊過程。在註冊過程中也有先實例化後調用的register()函數,進而調用prependToLoaderStack()函數,將load($alias)函數註冊未類的自動加載函數,該函數的作用主要時將外觀別名與外觀名(Facades)對應起來,從而實現對應外觀類的靜態方法調用。對應類的自動加載函數註冊過程實現代碼如下:

文件 Illuminate\Foundation\AliasLoader.php

<?php
namespace Illuminate\Foundation;
class AliasLoader {
    //加載一個類別名,實際上是給外觀類起了一個別名,使兩個對應一個類
    public function load($alias){
        if(isset($this->alliases($alias))){
            return class_alias($this->aliases[$alias],$alias);
        }
    }
    //添加別名到自動加載函數中
    public fucntion alias($class, $alias) {
        $this->aliases[$class] = $alias;
    }

    //註冊自動加載函數到自動加載堆棧中
    public function register (){
        if(!$this->registered) {
            $this->prependToLoaderStack();
            $this->registered = true;
        }
    }

    //將類的自動加載函數添加在自動加載堆棧首部
    protected function prependToLoaderStack(){
        spl_autoload_register([$this,'load'],true,true);
    }

}

?>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章