composer的自动加载机制解读
按照composer文档的说法,如果是composer项目,只需要在开始的时候require 'vendor/autoload.php'即可享受类的自动加载特性。可是这是如何实现的呢?
vendor/autoload.php
以Laravel 5.1项目为例,vendor/autoload.php文件只做了两件事情:
- include
vendor/composer/autoload_real.php - 调用
ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b::getLoader()
vendor/composer/autoload_real.php仅仅定义了ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b类和composerRequireb6d254015e39cf5090fb84fdb1ed664b函数。(b6d254015e39cf5090fb84fdb1ed664b应该是类似id一样的东西,确保每次不同) 接下来我们关注下ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b::getLoader()做了哪些事情。
ComposerAutoloaderInit<id>::getLoader()
首先,这个类的loader只会初始化一次,第二次是直接返回已经存在的loader了:
1 | |
如果是第一次调用,先注册['ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b', 'loadClassLoader'],然后new一个\Composer\Autoload\ClassLoader 作为loader,然后立马取消注册loadClassLoader。 接下来就一步一步处理各种autoload了。
autoload_namespaces.php
直接从vendor/composer/autoload_namespaces.php中返回的就是$namespace到$path的一个数组。通过\Composer\Autoload\ClassLoader::set方法来记录这些信息:
1 | |
autoload_psr4.php
直接从该文件的返回值是$namespace到$path的数组。通过\Composer\Autoload\ClassLoader::setPsr4来记录此信息:
1 | |
autoload_classmap.php
直接从该文件的返回值是$classname到$path的数组。通过\Composer\Autoload\ClassLoader::addClassMap来记录此信息:
1 | |
这些都处理完之后,\Composer\Autoload\ClassLoader::register将[$this, 'loadClass']注册为autoload函数(通过spl_autoload_register)。
autoload_files.php
直接从该文件的返回值是$fileIdentifier到文件路径的映射,通过之前定义的composerRequireb6d254015e39cf5090fb84fdb1ed664b函数来require每个文件。 这个函数在require文件的时候同时也设置了$GLOBALS['__composer_autoload_files'][$fileIdentifier]的值为true。
\Composer\Autoload\ClassLoader::loadClass
这个函数的作用是装载一个类。
1 | |
\Composer\Autoload\ClassLoader::findFile
这个函数是composer装载类的重点。
首先是从
classMap里面找$class对应的文件,如果有,直接返回。然后从
prefixDirsPsr4找到前缀符合的文件,如果找到,直接返回。(那个prefixLengthsPsr4就是用来判断需要从$class去掉的前缀长度)接下来,直接从
fallbackDirsPsr4对应的目录中查找文件。PSR-0加载
- namespaced class name变换规则:
\A_namespace\a_class=>/A_namespace/a/class.php - PEAR-like class name变换规则:
\A_namespace\a_class=>/A/namespace/a/class.php
在
prefixesPsr0中查找(和prefixDirsPsr4类似),直接找到对应的文件,返回。- namespaced class name变换规则:
如果没有,直接从
fallbackDirsPsr0中尝试加载。