composer.json
首先需要了解下Composer的配置文件,在composer.json中,与类加载相关的指令有:
require
require-dev(root-only)
autoload
autoload-dev(root-only)
include-path
target-dir
root-only指在我们正在开发的项目中定义了composer.json(里面声明了项目依赖的包等),那么这个项目就是一个根包。require-dev和autoload-dev只在根包起作用。
require定义了项目依赖的包,这些包会被安装到与composer.json同级的vendor目录下。例如我们的项目A->B、C,而B->D,那么A是根包,B、C、D都会被安装到vendor下。
autoload为自动加载器定义了类名到类文件的映射关系。
1、PSR-4:命名空间到类文件路径的映射。
假如根包的composer.json配置如下:
{……"require":{"foo/bar":"2.*"},"autoload":{"psr-4":{"App\\":"app/"}},……}
foo/bar包的composer.json配置如下:
{……"autoload":{"psr-4":{"TTD\\":"src/TTD"}},……}
那么类目录结构会是:
/*注释为根包项目中使用的类名*/|-composer.json|-app||-Takk.php/*App\Takk*/||-BAA|||-Uk.php/*App\BAA\Uk*/||-vendor||-foo|||-bar||||-composer.json||||-src|||||-TTD||||||-Kok.php/*TTD\Kok*/
2、PSR-0:命名空间到类文件路径的映射、带_的类名到类文件路径的映射,相比psr-4,多了以命名空间作为层级目录(目录层级更深),以及对_的支持。
假如根包的composer.json配置如下:
{……"require":{"foo/bar":"2.*"},"autoload":{"psr-0":{"Aaa\\Bbb\\":"src/","Ccc_Ddd_":"tsrc/"}},……}
foo/bar包的composer.json配置如下:
{……"autoload":{"psr-0":{"TTD\\":"src/TTD","Eee_Fff_":"src/EF"}},……}
那么类目录结构会是:
/*注释为根包项目中使用的类名*/|-composer.json|-src||-Aaa|||-Bbb||||-Jkd.php/*Aaa\Bbb\Jkd*/||-Ccc|||-Ddd||||-Jkd.php/*Ccc_Ddd_Jkd*/|-vendor||-foo|||-bar||||-src|||||-TTD||||||-TTD|||||||-Ipl.php/*TTD\Ipl*/||||-src|||||-EF||||||-Eee|||||||-Fff||||||||-Jud.php/*Eee_Fff_Jud*/
3、classmap:扫描目录下所有.php或.inc文件中的类,建立类名和类文件的映射,以路径层级作为命名空间。
假如根包的composer.json配置如下:
{……"require":{"foo/bar":"2.*"},"autoload":{"classmap":["App"]},……}
foo/bar包的composer.json配置如下:
{……"autoload":{"classmap":["Uuu/Xxx"]},……}
那么类目录结构会是:
/*注释为根包项目中使用的类名*/|-composer.json|-App||-Htt|||-Koo.php/*Htt\Koo*/|-vendor||-foo|||-bar||||-Uuu|||||-Xxx|||||-Ppp|||||||-Kuu.php/*Ppp\Kuu*/
4、files:用于加载一些php函数。
假如根包的composer.json配置如下:
{……"require":{"foo/bar":"2.*"},"autoload":{"files":["src/Aaa/Hhh/K.php"]},……}
foo/bar包的composer.json配置如下:
{……"autoload":{"files":["src/Yyy/Fd.php"]},……}
那么类目录结构会是:
/*注释为根包项目中使用的类名*/|-composer.json|-src||-Aaa|||-Hhh||||-K.php|-vendor||-foo|||-bar||||-src|||||-Yyy||||||-Fd.php
autoload.php
在根包composer.json所在目录执行composer install或composer dumpautoload后会在vendor目录下产生autoload.php和composer目录,那么我们的项目中只需要require这个文件autoload.php,就可以使用到上文分析出来的各种加载方式产生的类。
vendor目录结构:
|-vendor|-autoload.php||-composer|||-autoload_psr4.php|||-autoload_namespaces.php|||-autoload_classmap.php|||-autoload_files.php|||-autoload_real.php|||-autoload_static.php|||-ClassLoader.php|||-installed.json|||-LICENSE/*其他包*/
直接跟踪autoload.php中的代码执行,可以了解到其流程主要就是实例化一个单例的\Composer\Autoload\ClassLoader,然后初始化其私有成员变量(根据PHP版本和是否为HHVM其初始化方式不同),之后调用spl_autoload_register注册\Composer\Autoload\ClassLoader::loadClass()方法到SPL __autoload函数队列中,最后逐个包含composer.json.autoload.files配置的文件。
其实看到autoload_real.php这个文件中ComposerAutoloaderInit***::getLoader()方法,对其代码有些疑惑的:
1、为啥 \Composer\Autoload\ClassLoader的实例化不直接require __DIR__ . '/ClassLoader.php';?
public static function loadClassLoader($class)
{
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('ComposerAutoloaderInitcdf2e5379301041a36605f84abb3abc4', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitcdf2e5379301041a36605f84abb3abc4', 'loadClassLoader'));
……
}
}
2、根据PHP版本和是否为HHVM,对\Composer\Autoload\ClassLoader私有成员变量的初始化方式不同,其实看到autoload_static.php中的内容就想知道为何不在composer产生这些文件的时候,直接把结果赋值给\Composer\Autoload\ClassLoader私有成员变量?
……
public static function getLoader()
{
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitcdf2e5379301041a36605f84abb3abc4::getInitializer($loader));
} else {
$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);
}
}
……
}
……
类的查找
根据已注册的SPL __autoload函数,调用了\Composer\Autoload\ClassLoader的loadClass()->findFile()。在findFile中,查找顺序是:classmap->psr4->psr0,找到的话就返回类文件的路径名。