Composer是PHP的基于项目的依赖管理工具,它本身集成一个autoloader,支持PSR-4,PSR-0,classmap,files 四种自动加载方式。
首先介绍一下PSR-4与PSR-0。
介绍这两种自动加载规范之前必须先交代一下什么是PSR:
PSR
是制定的代码规范,简称PSR,是代码开发的事实标准。
PSR共有6个规范,分别是:
1 基础编码规范 PSR-1
2 编码风格规范 PSR-2
3 日志接口规范 PSR-3
4 自动加载规范 PSR-4
4 缓存接口规范 PSR-6
6 HTTP 消息接口规范 PSR-7
还有一个被废弃的规范:自动加载规范 PSR-0。
PSR-0和PSR-4同样是自动加载规范,看起来重复了,但事实上PSR-2和PSR-4是随着PHP版本演变而发展的,在PHP5.3之前,没有命名空间的概念,为了兼容第三方包,都是以下划线来区分类,为了兼容这种情况,PSR-0中会将类名中的下划线解析成目录分隔符,获取相应的目录层级和命名空间,比如加载含有名为Acme_Util_ClassName的类的文件ClassName.php的写法:
{
"name": "acme/util",
"auto" : {
"psr-0": {
"Acme\\Util\\": "src/"
}
}
}
实际目录结构是:
vendor/
acme/
util/
composer.json
src/
Acme/
Util/
ClassName.php
PSR-4与PSR-0的主要区别是前者去掉了对下划线的解析,由于PHP5.2版本已经被抛弃了,就犯不着再将下划线解析成目录分隔符了,同样是ClassName.php:
{
"name": "acme/util",
"auto" : {
"psr-4": {
"Acme\\Util\\": "src/"
}
}
}
实际目录结构是:
vendor/
acme/
util/
composer.json
src/
ClassName.php
还有classmap方式和file方式
classmap
自动加载的最简单形式,有完整的命名空间和文件目录的映射,即将所有的 class 的 namespace + classname 生成成一个 key => value 的 php 数组:
<?php
return [
'App\\Console\\Kernel' => $baseDir . '/app/Console/Kernel.php'
];
?>
file
用于加载全局函数的文件,存放各个全局函数所在的文件路径名,加载helper functions函数库:
{
"files": [
"path/to/file.php"
]
}
接下来看看自动加载的具体实现:
首先是autoload.php:
#引入autoload_real.php文件
require_once __DIR__ . '/composer/autoload_real.php';
/*使用autoload_real.hph中的全局类ComposerAutoloaderInit444e163ae7f2491afbac6c694d5fc5b6 调用静态方法getLoader()
*/
return ComposerAutoloaderInit444e163ae7f2491afbac6c694d5fc5b6::getLoader();
打开autoload_real.php文件看看:
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit444e163ae7f2491afbac6c694d5fc5b6
{
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('ComposerAutoloaderInit444e163ae7f2491afbac6c694d5fc5b6', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit444e163ae7f2491afbac6c694d5fc5b6', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit444e163ae7f2491afbac6c694d5fc5b6::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);
}
}
$loader->register(true);
return $loader;
}
}
找到getLoader函数,可以看到有一次spl_autoload_register调用,加载了核心类ClassLoader,并通过核心类的实例初始化与注册初始化顶级命名空间映射。