淺析PHP類的自動載入和名稱空間

2020-07-16 10:06:07

淺析PHP類的自動載入和名稱空間

php是使用require(require_once)include(include_once)關鍵字載入類檔案。但是在實際的開發工程中我們基本上不會去使用這些關鍵字去載入類。 因為這樣做會使得程式碼的維護相當的困難。實際的開發中我們會在檔案的開始位置用use關鍵字使用類,然後直接new這個類就可以了. 至於類是怎麼載入的,一般都是框架或者composer去實現的。

<?php
use IlluminateContainerContainer;
$container = new Container();

自動載入

我們可以通過一段虛擬碼來模擬一下在類的範例化工程中類是如何工作的

function instance($class)
{
    // 如果類已載入則返回其範例
    if (class_exists($class, false)) {
        return new $class();
    }
    // 檢視 autoload 函數是否被使用者定義
    if (function_exists('__autoload')) {
        __autoload($class); // 最後一次載入類的機會
    }
    // 再次檢查類是否存在
    if (class_exists($class, false)) {
        return new $class();
    } else { // 系統:我實在沒轍了
        throw new Exception('Class Not Found');
    }
}

php在語言層面提供了**__autoload** 魔術方法給使用者來實現自己的自動載入邏輯。當使用者去new一個類的時候,如果該類沒有被載入,php會在丟擲錯誤前呼叫**__autoload方法去載入類。下面的例子中的__autoload**方法只是簡單的輸出要載入類的名稱, 並沒有去實際的載入對應的類, 所以會丟擲錯誤。

<?php
use IlluminateContainerContainer;
$container = new Container();
function __autoload($class)
{
    /* 具體處理邏輯 */
    echo $class;// 簡單的輸出要載入類的名稱
}
/**
 *

執行結果

IlluminateContainerContainer
Fatal error: Uncaught Error: Class 'IlluminateContainerContainer' not found in D:projectphplaravel_for_ci_cdtestClassLoader.php:5
Stack trace:
#0 {main}
  thrown in D:projectphplaravel_for_ci_cdtestClassLoader.php on line 5
 */

明白了 **__autoload** 函數的工作原理之後,我們來用它去實現一個最簡單自動載入。我們會有index.php和Person.php兩個檔案在同一個目錄下。

//index.php
<?php
function __autoload($class)
{
    // 根據類名確定檔名
    $file = './'.$class . '.php';
    if (file_exists($file)) {
        include $file; // 引入PHP檔案
    }
}
new Person();
/*---------------------分割線-------------------------------------*/
//Person.php
class Person
{
    // 物件範例化時輸出當前類名
    function __construct()
    {
        echo '<h1>' . __CLASS__ . '</h1>';
    }
}
/**執行結果
 * 輸出 <h1>Person</h1>
 */

名稱空間

名稱空間並不是什麼新鮮的事務,很多語言都早就支援了這個特性(只是叫法不相同),它主要解決的一個問題就是命名衝突! 就好像日常生活中很多人都會重名,我們必須要通過一些標識來區分他們的不同。比如說現在我們要用php介紹一個叫張三的人 ,他在財務部門工作。我們可以這樣描述。

namespace 財務部門;
 
class 張三
{
    function __construct()
    {
        echo '財務部門的張三';
    }
}

這就是張三的基本資料 , namespace是他的部門標識,class是他的名稱. 這樣大家就可以知道他是財務部門的張三而不是工程部門的張三。

非限定名稱,限定名稱和完全限定名稱

1.非限定名稱,或不包含字首的類名稱,例如 $comment = new Comment(); 如果當前名稱空間是BlogArticle,Comment將被解析為、BlogArticleComment。如果使用Comment的程式碼不包含在任何名稱空間中的程式碼(全域性空間中),則Comment會被解析為Comment。

注意: 如果檔案的開頭有使用use關鍵字 use onetwoComment; 則Comment會被解析為 **onetwoComment**。

2.限定名稱,或包含字首的名稱,例如 $comment = new ArticleComment(); 如果當前的名稱空間是Blog,則Comment會被解析為BlogArticleComment。如果使用Comment的程式碼不包含在任何名稱空間中的程式碼(全域性空間中),則Comment會被解析為ArticleComment。

3.完全限定名稱,或包含了全域性字首操作符的名稱,例如 $comment = new ArticleComment(); 在這種情況下,Comment總是被解析為ArticleComment。

spl_autoload

接下來讓我們要在含有名稱空間的情況下去實現類的自動載入。我們使用 spl_autoload_register() 函數來實現,這需要你的 PHP 版本號大於 5.12。spl_autoload_register函數的功能就是把傳入的函數(引數可以為回撥函數或函數名稱形式)註冊到 SPL __autoload 函數佇列中,並移除系統預設的 **__autoload()** 函數。一旦呼叫 spl_autoload_register() 函數,當呼叫未定義類時,系統就會按順序呼叫註冊到 spl_autoload_register() 函數的所有函數,而**不是自動呼叫 __autoload()** 函數。

現在, 我們來建立一個 Linux 類,它使用 os 作為它的名稱空間(建議檔名與類名保持一致):

<?php
namespace os; // 名稱空間
 
class Linux // 類名
{
    function __construct()
    {
        echo '<h1>' . __CLASS__ . '</h1>';
    }
}

接著,在同一個目錄下新建一個 index.php檔案,使用 spl_autoload_register 以函數回撥的方式實現自動載入:

<?php
spl_autoload_register(function ($class) { // class = osLinux
 
    /* 限定類名路徑對映 */
    $class_map = array(
        // 限定類名 => 檔案路徑
        'osLinux' => './Linux.php',
    );
    /* 根據類名確定檔案路徑 */
    $file = $class_map[$class];
    /* 引入相關檔案 */
    if (file_exists($file)) {
        include $file;
    }
});
 
new osLinux();

這裡我們使用了一個陣列去儲存類名與檔案路徑的關係,這樣當類名傳入時,自動載入器就知道該引入哪個檔案去載入這個類了。但是一旦檔案多起來的話,對映陣列會變得很長,這樣的話維護起來會相當麻煩。如果命名能遵守統一的約定,就可以讓自動載入器自動解析判斷類檔案所在的路徑。接下來要介紹的PSR-4 就是一種被廣泛採用的約定方式

PSR-4規範

PSR-4 是關於由檔案路徑自動載入對應類的相關規範,規範規定了一個完全限定類名需要具有以下結構:

<頂級名稱空間>(<子名稱空間>)*<類名>

PSR-4 規範中必須要有一個頂級名稱空間,它的意義在於表示某一個特殊的目錄(檔案基目錄)。子名稱空間代表的是類檔案相對於檔案基目錄的這一段路徑(相對路徑),類名則與檔名保持一致(注意大小寫的區別)。

舉個例子:在全限定類名 appviewnewsIndex 中,如果 app 代表 C:Baidu,那麼這個類的路徑則是 C:BaiduviewnewsIndex.php.我們就以解析 appviewnewsIndex 為例,編寫一個簡單的 Demo:

<?php
$class = 'appviewnewsIndex';
 
/* 頂級名稱空間路徑對映 */
$vendor_map = array(
    'app' => 'C:Baidu',
);
 
/* 解析類名為檔案路徑 */
$vendor = substr($class, 0, strpos($class, '')); // 取出頂級名稱空間[app]
$vendor_dir = $vendor_map[$vendor]; // 檔案基目錄[C:Baidu]
$rel_path = dirname(substr($class, strlen($vendor))); // 相對路徑[/view/news]
$file_name = basename($class) . '.php'; // 檔名[Index.php]
 
/* 輸出檔案所在路徑 */
echo $vendor_dir . $rel_path . DIRECTORY_SEPARATOR . $file_name;

以上就是淺析PHP類的自動載入和名稱空間的詳細內容,更多請關注TW511.COM其它相關文章!