yii2 自动加载

1.基本知识

1. include, require

2.如何实现类的自动加载 
spl_autoload_register() 
__autoload() 
 
3.自动加载规范
1.PSR-0:https://github.com/PizzaLiu/P...
2.PSR-4:https://github.com/PizzaLiu/P...

2.Yii2中代码自动加载的机制

在Yii2.0的运行过程中主要由以下两个方法来实现代码的自动加载:
1.path_to_your_project/vendor/composer/ClassLoader.php中的ClassLoader::loadClass()方法,这个方法是由composer提供的。

2.类\yii\BaseYii的autoload()方法,这个方法是由Yii2框架提供的

Yii2中是如何实现代码的自动加载的?
入口脚本两行代码:

require(__DIR__ . '/../vendor/autoload.php');
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');

 

3.关于autoload.php:

1.注册ComposerAutoloaderInit06ca19902d5e5679bb4a73b919aadb2a::loadClassLoader($class)为自动加载函数。这个loader负责引入了一个类:ClassLoader.php中的\Composer\Autoload\ClassLoader(),随后立即解除注册。

2.注册vendor/composer/ClassLoader.php中的ClassLoader::loadClass($class)为自动加载函数,并利用配置文件(即vendor/composer目录下的autoload_*.php文件)对这个自动加载函数进行了初始化。这个函数实现了PSR-0,PSR-4,classmap等方式来自动加载。

3.Require “vendor/composer/autoload_static.php”中的$files(作为全局函数使用) ??

 

autoload_*.php的解释:

1. autoload_static 主要是为了提高效率,相当于缓存

当满足以下条件:
1.PHP_VERSION_ID >= 50600
2.&& !defined('HHVM_VERSION')
3.&& (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
则直接使用autoload_static文件而不采用上面的4个文件。

2.autoload_namespaces.php 对应的是一些符合PSR-0的目录或文件

3. autoload_psr4.php 包含的命名空间目录:vendor/yiisoft下满足psr-4的目录(包括yii命名空间,即yii api 中包含的类)

4.autoload_classmap.php

命令"composer dump-autoload -o"会生成这么一个文件,通过classmap方式,可以提高自动加载的效率
(相比使用PSR-0或PSR-4自动加载,可以减少计算量和IO

5.autoload_files.php 主要包括了需要立即require的文件( 通常是库文件,也可以是自定义的,引入作为全局函数使用)

上面关于Closure::bind()的使用参考http://www.cnblogs.com/iforev...

 

使用ComposerAutoloadClassLoader::loadClass()加载文件的顺序:

  • 1.先从classMap中找(时间复杂度O(1))
  • 2.查看是否文件之前已经查找过,证实不存在($missingClasses)
  • 3.如果有使用apc缓存的话从缓存中取
  • 4.查找文件名后缀为”.php”的文件

    - a)PSR-4 lookup:格式化类名,通过prefixLengthsPsr4,prefixDirsPsr4找到文件的绝对路径
    - b)PSR-4 fallback:根据$fallbackDirsPsr4查找根命名空间下的目录
    - c)PSR-0 lookup:分纯pear格式,pear+命名空间格式,根据prefixesPsr0找到文件的绝对路径
    - d)PSR-0 lookup:根据fallbackDirsPsr0查找根命名空间下的目录
    - e)PSR-0 include:如果允许使用include path方式的话,使用stream_resolve_include_path()返回绝对路径
    - f)找不到,返回false
  • 5.如果有使用HHVM的话,找后缀为”.hh”的文件,回到4下的具体查找(即a,b,c..)
  • 6.如果有使用apc缓存的话,将找到的文件的绝对路径存储到apc缓存中

注意:
1.在ClassLoader::findFileWithExtension($class, $ext)中实现了PSR-0和PSR-4的自动加载,其时间复杂度均为O(n2),相比于classmap的方式而言(时间复杂度为O(1))是低效的,因此在生产环境中可以采用composer命令"composer dump-autoload -o"进行优化

 

4.其中Yii.php 的作用:

1.定义类Yii(需要手动引入其父类的文件,而不是靠自动加载)
2.注册Yii::autoload()为自动加载函数
3.赋值Yii::$classMap (其值即yii2 api 中介绍的所有类,对应文件vendor\yiisoft\yii2\classes.php)
4.生成依赖注入容器:Yii::$container = new yiidiContainer();

相对于ComposerAutoloadClassLoader::loadClass(),Yii.php所做的就简单明了许多了,如果所需加载的类在Yii::$classMap中有定义则直接通过它加载,没有的话就解析别名,然后加载。如果解析别名后依然找不到相应的文件路径,则使用composer提供的自动加载函数来加载(即ClassLoader::loadClass($class))

Yii::autoload()主要能引入什么类?
1.Yii::$classMap中定义的yii2核心类
2.引入我们的应用中自己创建的类(依靠命名空间和别名,遵循PSR-4)

最终注册了的自动加载方法以及顺序:
1.Yii::autoload()
2.vendor\composer\ClassLoader.php中的ClassLoader::loadClass($class)

.自动加载器YiiBase::autoload()

 

 

  • a)先从静态变量$classMap中找(从代码中看,这个貌似貌似没有用到)
  • b)再从静态变量$_coreClasses中找 (这个写死在YiiBase文件中了,包含了所有api)
  • c)如果允许 include_path,则直接include
  • d)不允许 include_path,根据self::$_includePaths 逐一查找文件,找到的话就include
  • e)按照类psr-4的方式查找( 与 .)(注意:这里这里需要用到一些常见的别名)
  • /**
         * Class autoload loader.
         * This method is provided to be invoked within an __autoload() magic method.
         * @param string $className class name
         * @return boolean whether the class has been loaded successfully
         */
        public static function autoload($className)
        {
            // use include so that the error PHP file may appear
            if(isset(self::$classMap[$className]))
                include(self::$classMap[$className]);
            elseif(isset(self::$_coreClasses[$className]))//这个数组是写死在YiiBase中的,包含所有yii api
                include(YII_PATH.self::$_coreClasses[$className]);
            else
            {
                // include class file relying on include_path
                if(strpos($className,'\\')===false)  // class without namespace
                {
                    if(self::$enableIncludePath===false)
                    {
                        foreach(self::$_includePaths as $path)
                        {
                            $classFile=$path.DIRECTORY_SEPARATOR.$className.'.php';
                            if(is_file($classFile))
                            {
                                include($classFile);
                                if(YII_DEBUG && basename(realpath($classFile))!==$className.'.php')
                                    throw new CException(Yii::t('yii','Class name "{class}" does not match class file "{file}".', array(
                                        '{class}'=>$className,
                                        '{file}'=>$classFile,
                                    )));
                                break;
                            }
                        }
                    }
                    else
                        include($className.'.php');
                }
                else  // class name with namespace in PHP 5.3
                {
                    $namespace=str_replace('\\','.',ltrim($className,'\\'));
                    if(($path=self::getPathOfAlias($namespace))!==false)
                        include($path.'.php');
                    else
                        return false;
                }
                return class_exists($className,false) || interface_exists($className,false);
            }
            return true;
        }

     

 

参考: https://segmentfault.com/a/1190000010508018

 

posted @ 2019-12-10 01:39  wayne_99  阅读(398)  评论(0编辑  收藏  举报