程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> Yii2的深入學習--自動加載機制,yii2深入學習--

Yii2的深入學習--自動加載機制,yii2深入學習--

編輯:關於PHP編程

Yii2的深入學習--自動加載機制,yii2深入學習--


Yii2 的自動加載分兩部分,一部分是 Composer 的自動加載機制,另一部分是 Yii2 框架自身的自動加載機制。

Composer自動加載

對於庫的自動加載信息,Composer 生成了一個 vendor/autoload.php 文件。你可以簡單的引入這個文件,你會得到一個自動加載的支持。

在之前的文章,入口文件的介紹中,我們可以看到如下內容:

// 引入 vendor 中的 autoload.php 文件,會基於 composer 的機制自動加載類
require(__DIR__ . '/../vendor/autoload.php');

因為這個系列主要是關於 Yii2 的,所以有關 Composer 自動加載機制就不在這裡詳細說明了。

可查閱資料:

Yii2 框架的自動加載機制

Yii2 框架的自動加載是通過 spl_autoload_register 方法實現的。

在之前的文章,入口文件的介紹中,我們可以看到如下內容:

// 引入 Yii 框架的文件 Yii.php
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');

Yii.php 裡究竟是什麼內容?如何實現了自動加載?

下面我們來看一下,Yii.php 的內容如下:

<?php
/**
 * Yii bootstrap file.
 *
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

require(__DIR__ . '/BaseYii.php');

/**
 * Yii is a helper class serving common framework functionalities.
 *
 * It extends from [[\yii\BaseYii]] which provides the actual implementation.
 * By writing your own Yii class, you can customize some functionalities of [[\yii\BaseYii]].
 *
 * @author Qiang Xue <[email protected]>
 * @since 2.0
 */
class Yii extends \yii\BaseYii
{
}

/**
 * spl_autoload_register — 注冊給定的函數作為 __autoload 的實現
 *
 * bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
 *
 * 將函數注冊到SPL __autoload函數隊列中。如果該隊列中的函數尚未激活,則激活它們。
 * 如果在你的程序中已經實現了__autoload()函數,它必須顯式注冊到__autoload()隊列中。
 * 因為 spl_autoload_register()函數會將Zend Engine中的__autoload()函數取代為spl_autoload()或spl_autoload_call()。
 * 如果需要多條 autoload 函數,spl_autoload_register() 滿足了此類需求。
 * 它實際上創建了 autoload 函數的隊列,按定義時的順序逐個執行。
 * 相比之下, __autoload() 只可以定義一次。
 *
 * autoload_function
 * 欲注冊的自動裝載函數。如果沒有提供任何參數,則自動注冊 autoload 的默認實現函數spl_autoload()。
 *
 * throw
 * 此參數設置了 autoload_function 無法成功注冊時, spl_autoload_register()是否拋出異常。
 *
 * prepend
 * 如果是 true,spl_autoload_register() 會添加函數到隊列之首,而不是隊列尾部。
 *
 * Yii 注冊了 Yii 的 autoload 函數,實現自動加載, 其實現在 \yii\BaseYii 中
 */
spl_autoload_register(['Yii', 'autoload'], true, true);
// 定義 Yii 核心的 class 的類名與文件地址的 Map
Yii::$classMap = require(__DIR__ . '/classes.php');
// 創建 Yii 的依賴注入的容器
Yii::$container = new yii\di\Container();

其主要內容就是引入了 BaseYii.php 文件,然後聲明了類 Yii,繼承了 BaseYii,然後注冊了 Yii (其實是 BaseYii)的 autoload 方法,去實現自動加載。之後又引入了Yii 核心類名與文件地址一一對應的 Map,存儲到 Yii::$classMap 中。最後創建了一個 yii\di\Container 的實例,存儲到 Yii::$container 中。

可以看出實現自動加載的關鍵代碼是:

spl_autoload_register(['Yii', 'autoload'], true, true);

下面我們來看一下 BaseYii 中 autoload 方法的實現,其內容如下:

    /**
     * Class autoload loader.
     * This method is invoked automatically when PHP sees an unknown class.
     * The method will attempt to include the class file according to the following procedure:
     *
     * 1. Search in [[classMap]];
     * 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt
     *    to include the file associated with the corresponding path alias
     *    (e.g. `@yii/base/Component.php`);
     *
     * This autoloader allows loading classes that follow the [PSR-4 standard](http://www.php-fig.org/psr/psr-4/)
     * and have its top-level namespace or sub-namespaces defined as path aliases.
     *
     * Example: When aliases `@yii` and `@yii/bootstrap` are defined, classes in the `yii\bootstrap` namespace
     * will be loaded using the `@yii/bootstrap` alias which points to the directory where bootstrap extension
     * files are installed and all classes from other `yii` namespaces will be loaded from the yii framework directory.
     *
     * Also the [guide section on autoloading](guide:concept-autoloading).
     *
     * @param string $className the fully qualified class name without a leading backslash "\"
     * @throws UnknownClassException if the class does not exist in the class file
     */
    public static function autoload($className)
    {
        // 自動加載類
        if (isset(static::$classMap[$className])) {
            // 如果 $classMap 中存在該類,就直接使用
            $classFile = static::$classMap[$className];
            // 如果第一個字符串為'@',就意味著對應的文件地址是別名,就將它轉化成真實的文件地址
            if ($classFile[0] === '@') {
                $classFile = static::getAlias($classFile);
            }
        } elseif (strpos($className, '\\') !== false) {
            // 如果存在'\\',就意味著含有 namespace,可以拼成別名,再根據別名獲取真實的文件地址
            $classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false);
            // 沒取到真是文件地址或者獲取的地址不是一個文件,就返回空
            if ($classFile === false || !is_file($classFile)) {
                return;
            }
        } else {
            return;
        }

        // 引入該類的文件
        include($classFile);

        // 如果是調試模式,而且 $className 即不是類,不是接口,也不是 trait,就拋出異常
        if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {
            throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?");
        }
    }

其中,大家可能不太清楚 getAlias 方法,這個方法其實就是將 Yii2 中的別名轉化成真實的文件地址,關於該方法的具體內容,之後會詳細講解。

舉幾個例子,幫助大家理解一下。

如果 Yii::$classMap 的值如下:

Yii::$classMap = [
    'app/test/Test' => '/var/www/basic/webtest/Test.php'
];

當你使用 ‘app/test/Test’ 類時,就會自動引入 '/var/www/basic/webtest/Test.php' 文件,項目中的內容當然不是這個樣子的,這只是個簡單的例子,便於大家理解。

在繼續上面的例子,如果你使用了‘yii\base\Component’ 類,它就會轉變成 ‘@yii/base/Component.php’ 別名,然後在根據別名獲取到它的文件地址,引入進來。

以上就是 Yii2 的自動加載機制的基本內容~~

 

對 Yii2 源碼有興趣的同學可以關注項目 yii2-2.0.3-annotated,現在在上面已經添加了不少關於 Yii2 源碼的注釋,之後還會繼續添加~

有興趣的同學也可以參與進來,提交 Yii2 源碼的注釋。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved