2017_01_02_1_類自動加載

類自動加載(Autoloading)
Yii 依靠類自動加載機制來定位和包含所需的類文件。
它提供一個高性能且完美支持PSR-4 標準 (中文漢化)的自動加載器。
該自動加載器會在引入框架文件 Yii.php 時安裝好。



使用 Yii 自動加載器
要使用 Yii 的類自動加載器,你需要在創建和命名類的時候遵循兩個簡單的規則:
每個類都必須置於命名空間之下 (比如 foo\bar\MyClass)。
每個類都必須保存爲單獨文件,且其完整路徑能用以下算法取得:
// $className 是一個開頭包含反斜槓的完整類名
$classFile = Yii::getAlias('@' . str_replace('\\', '/', $className) . '.php');
舉例來說,若某個類名爲 foo\bar\MyClass,對應類的文件路徑別名會是 @foo/bar/MyClass.php。
爲了讓該別名能被正確解析爲文件路徑,@foo 或 @foo/bar 中的一個必須是根別名。




在我們的入口文件,例如index.PHP,代碼如下:
    <?php  
      
    // comment out the following two lines when deployed to production  
      
    defined('YII_DEBUG') or define('YII_DEBUG', true);  
    defined('YII_ENV') or define('YII_ENV', 'dev');  
      
    require(__DIR__ . '/../vendor/autoload.php');  
      
    require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');  
      
    $config = require(__DIR__ . '/../config/web.php');  
      
    (new yii\web\Application($config))->run();  

我們可以注意到require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'),將yii2的核心的函數包含進去,下面看看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(['Yii', 'autoload'], true, true);  
    Yii::$classMap = require(__DIR__ . '/classes.php');  
    Yii::$container = new yii\di\Container();  

首先包含BaseYii.php這個基礎的yii文件,然後類Yii繼承包含的Yii文件,命名空間是\yii\BaseYii,同時通過spl_autoload_register註冊一個自動加載函數,這個函數是本類的autoload()函數,這些函數是在\yii\BaseYii中定義,而Yii類又繼承\yii\BaseYii,所以Yii類相當於定義這些屬性和方法。

而Yii::$classMap = require(__DIR__ . '/classes.php')這一句定義了命名空間與實際路徑的映射關係,將返回一個數組的形式,保存在一個靜態的變量中,那麼這個變量有什麼用呢?

下面我們再來看看spl_autoload_register註冊的autoload()函數,在BaseYii.php中定義如下:


    public static function autoload($className)  
        {  
            if (isset(static::$classMap[$className])) {  
                $classFile = static::$classMap[$className];  
                if ($classFile[0] === '@') {  
                    $classFile = static::getAlias($classFile);  
                }  
            } elseif (strpos($className, '\\') !== false) {  
                $classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false);  
                if ($classFile === false || !is_file($classFile)) {  
                    return;  
                }  
            } else {  
                return;  
            }  
      
            include($classFile);  
      
            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?");  
            }  
        }  


其中參數$className代表的是命名空間的類名,這個函數首先根據類名$className從$classMap是否可以找到對應設置的映射關係,如果找到對應的映射關係,就把文件對應的路徑解釋出來,賦值變量$classFile,如果找不到對應的映射關係,則將這個命名空間類執行:
$classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php'
這句代碼就是通過別名的方式來映射定義路徑,比如:new \app\controllers\User,這個在$classMap肯定是找不到對應的關係的,所以這句代碼會把\app\controllers\User先替換成@app/controllers/User,然後再通過static::getAlias('@app/controllers/User')解釋別名,找到對應的實際文件路徑,因爲@app在配置文件中已經定義或者默認定義別名,getAlias()會負責解釋,當然需要在@app下建立controllers的文件夾等,並將解釋路徑的值賦值變量$classFile,最後將這個路徑包含進來。

所以總結一點:
我們可以在定義了別名的文件夾下@xxx,再建立文件夾aaa,然後定義文件bbb.php,那麼在bbb.php的文件的命名空間就是xxx\aaa,這個文件的類名就是bbb。那麼我們就可以在任何的地方直接用 new \xxx\aaa\bbb()的形式類創建實例。發生的過程大概就是這樣子:
a)因爲我們在入口文件首先註冊composer的自動加載函數,放在spl的堆棧中

b)接着,我們又註冊yii2自身的自動加載函數,放在spl的堆棧中,根據棧的後入先出的原則,yii2註冊的函數將放在composer註冊函數的前面,如果要用自動加載函數的話,首先用yii2自身註冊的。

c)所以當我們 new \xxx\aaa\bbb 的時候,首先會從堆棧中調用yii2自身註冊的autoload()函數,查找在yii2的本身是否存在對應的映射關係,如果找到,就直接包含文件進來,並實例化。如果找不到,再到yii2定義的別名的文件夾下面找,如果找到,就直接包含文件進來,並實例化。如果在yii註冊的函數裏都找不到,則接着調用spl堆棧的第二個自動函數,即composer自定義的autload()函數,按照函數定義的方式一直往下找,知道找到爲止.如果確實找不到會返回錯誤。
        

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