yii2采用的基于namespace的autoload机制,我们从初始化来参看yii2的autoload机制的整个过程,详解yii的初始化过程
1.在入口文件index.php我们可以看到代码:
require(__DIR__ . '/../../vendor/autoload.php');
2.我们打开这个文件:
<?php // autoload.php @generated by Composer require_once __DIR__ . '/composer' . '/autoload_real.php'; return ComposerAutoloaderInit90def245ed1c6f870abec3fefcc03f88::getLoader();
可以看到加载了文件/vendor/composer/autoload_real.php,打开这个文件,我们可以发现,里面定义了一个php的class类:
ComposerAutoloaderInit90def245ed1c6f870abec3fefcc03f88
也就是调用了这个类的getLoader()方法。
3.找到这个方法getLoader()方法:下面是所有的代码:
<?php // autoload_real.php @generated by Composer class ComposerAutoloaderInit90def245ed1c6f870abec3fefcc03f88 { private static $loader; public static function loadClassLoader($class) { if ('Composer\Autoload\ClassLoader' === $class) { require __DIR__ . '/ClassLoader.php'; } } public static function getLoader() { if (null !== self::$loader) { return self::$loader; } spl_autoload_register(array('ComposerAutoloaderInit90def245ed1c6f870abec3fefcc03f88', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInit90def245ed1c6f870abec3fefcc03f88', 'loadClassLoader')); $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } $loader->register(true); $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { composerRequire90def245ed1c6f870abec3fefcc03f88($file); } return $loader; } } function composerRequire90def245ed1c6f870abec3fefcc03f88($file) { require $file; }
首先我们看到的是spl_autoload_register这个方法,这个方法的作用是,在找不到类的情况下,通过这个函数定义的类方法去找,通过传递的参数,返回加载的类的路径。也就是说,当找不到类的时候,就通过这个方法找:
public static function loadClassLoader($class) { if ('Composer\Autoload\ClassLoader' === $class) { require __DIR__ . '/ClassLoader.php'; } }
因此这段代码 self::$loader = $loader = new \Composer\Autoload\ClassLoader();
加载的文件是vendor/composer/ClassLoader.php
4.然后通过这个类的方法,加载很多初始路径:
$map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } $loader->register(true); $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { composerRequire90def245ed1c6f870abec3fefcc03f88($file); } return $loader;
4.1通过set setPsr4 addClassMap等方法进行namespace路径初始化。 这个对应的文件是/autoload_psr4.php , 这个文件里面是对yii2的插件的namespace的定义:
<?php // autoload_psr4.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( 'yii\\swiftmailer\\' => array($vendorDir . '/yiisoft/yii2-swiftmailer'), 'yii\\redis\\' => array($vendorDir . '/yiisoft/yii2-redis'), 'yii\\gii\\' => array($vendorDir . '/yiisoft/yii2-gii'), 'yii\\faker\\' => array($vendorDir . '/yiisoft/yii2-faker'), 'yii\\debug\\' => array($vendorDir . '/yiisoft/yii2-debug'), 'yii\\composer\\' => array($vendorDir . '/yiisoft/yii2-composer'), 'yii\\codeception\\' => array($vendorDir . '/yiisoft/yii2-codeception'), 'yii\\bootstrap\\' => array($vendorDir . '/yiisoft/yii2-bootstrap'), 'yii\\' => array($vendorDir . '/yiisoft/yii2'), 'fecadmin\\' => array($vendorDir . '/fancyecommerce/fec_admin'), 'fec\\' => array($vendorDir . '/fancyecommerce/fec'), 'cebe\\markdown\\' => array($vendorDir . '/cebe/markdown'), 'Faker\\' => array($vendorDir . '/fzaninotto/faker/src/Faker'), );
定义各个插件的根路径。
4.2autoload_classmap.php 这个目前为空,没有细致研究具体内部的存放
4.3/autoload_files.php
<?php // autoload_files.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', '2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php', );
4.4 这是非yii2插件的包库 autoload_namespaces.php
<?php // autoload_namespaces.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( 'PHPExcel' => array($vendorDir . '/phpoffice/phpexcel/Classes'), 'Imagine' => array($vendorDir . '/imagine/imagine/lib'), 'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'), 'Diff' => array($vendorDir . '/phpspec/php-diff/lib'), );
譬如我的yii2 – fec 插件中加入的PHPExcel 和Imagine 库包,就会在这里被标注namespace的对应关系。
对于composer安装的库包,有的是include的方式加入的,譬如Excel,安装库包后,不需要再程序中require,包管理器在autoload_namespaces.php 会加入路径,自动加载进来。
还有的是基于namespaces的,譬如Imagine。
5. 通过上面的配置,去找到对应文件路径,加载文件。
也就是说,对于 https://packagist.org/ 这里的php的库包,我们都可以通过composer加载到我们的系统中,在线安装。
譬如:我的fec插件的 composer.json的配置。
"require": { "php": ">=5.4.0", "yiisoft/yii2": ">=2.0.6", "imagine/imagine": "0.5.*", "phpoffice/phpexcel": "1.8.*" }, "autoload": { "psr-4": { "fec\\": "" } },
其中require代表的需要下载的包
autoload psr-4 里面添加了信息后,会在 vendor/yiisoft/extensions.php 文件中加入别名。
'fancyecommerce/fec' => array ( 'name' => 'fancyecommerce/fec', 'version' => '1.1.2.4', 'alias' => array ( '@fec' => $vendorDir . '/fancyecommerce/fec', ), ),
vendor/composer/autoload_psr4.php 中加入namespace信息:
'fecadmin\\' => array($vendorDir . '/fancyecommerce/fec_admin'), 'fec\\' => array($vendorDir . '/fancyecommerce/fec'),