yii2框架-yii2自身的自动加载(三)

时间:2022-10-04 21:17:59
上一节说完了composer的自动加载,下面我们来说一下yii2自身的自动加载。

在我们的入口文件,例如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 <qiang.xue@gmail.com>      * @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()函数,按照函数定义的方式一直往下找,知道找到为止.如果确实找不到会返回错误。

原文链接 :http://blog.csdn.net/u012979009/article/details/51479504