前言
平时工作中,一直在使用composer解决一些包依赖管理,自动加载等业务场景,但是一直没有好好看过vendor/composer目录下面的文件,今天看了下源码,也算清楚了内部的文件执行流程。
主要文件:
vendor/autoload.php 入口文件
vendor/composer/autoload_real.php 真正加载文件
vendor/composer/ClassLoader.php 内部加载器文件
vendor/composer/autoload_static.php 当php版本大于等于5.6时,内部加载器会用此文件里面的配置映射信息填充相关数组
vendor/composer/autoload_classmap.php 当php版本小于5.6时,内部加载器会用此文件里面的配置映射信息填充加载器的classMap数组
vendor/composer/autoload_files.php 当php版本小于5.6时,真正加载文件会在内部直接require进autoload_files文件里面的所有文件
vendor/composer/autoload_psr4.php 当php版本小于5.6时,包含符合psr4标准的所有 命名空间:对应查找路径 的映射信息
psr4标准自动加载案例分析:
1、入口文件,我使用了Think\wahaha\Chu这个测试类
<?php require './composer/vendor/autoload.php'; use Think\wahaha\Chu; echo Chu::getName();
2、composer.json中的psr4配置信息
"autoload": {
"psr-4": {
"Think\\": "think/"
}
}
3、目录结构
think
wahaha
Chu.php
vendor
autoload.php
composer
ClassLoader.php
.......
4、自动加载类使用时,内部文件执行过程分析
- 入口文件加载autoload.php,而autoload.php该文件引入autoload_real.php,执行了autoload_real.php文件中类的getLoader()静态方法

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;
}
//注册类本身的loadClassLoader方法为自动加载方法
spl_autoload_register(array('ComposerAutoloaderInitf4cf8dfb98c23a8977f6da4c2c099d38', 'loadClassLoader'), true, true);
//实例化类加载器
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitf4cf8dfb98c23a8977f6da4c2c099d38', 'loadClassLoader'));
//判断运行环境信息
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {//php大于5.6时
/*
加载classmap,files,psr4相关映射配置文件
类加载器相关数组配置信息初始化
prefixLengthsPsr4、classMa、prefixDirsPsr4
*/
require_once __DIR__ . '/autoload_static.php';
//调用返回的回调函数对象,其中回调函数中进行类加载器实例的一些相关数组配置信息初始化
call_user_func(\Composer\Autoload\ComposerStaticInitf4cf8dfb98c23a8977f6da4c2c099d38::getInitializer($loader));
} else {//php小于5.6时
//加载命名空间与查找路径映射配置信息文件,并将类加载器相关数组配置初始化
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
//加载psr4标准的命名空间与查找路径映射配置信息文件,并将类加载器相关数组配置初始化
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
//加载ClassMap映射配置信息文件,并将类加载器相关数组配置初始化
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
//注册类加载器实例的loadClass方法为自动加载方法,实现类的自动加载
$loader->register(true);
//获取files映射信息
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitf4cf8dfb98c23a8977f6da4c2c099d38::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
//加载files数组中的相关文件
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequiref4cf8dfb98c23a8977f6da4c2c099d38($fileIdentifier, $file);
}
return $loader;
}

- 看看类加载器ClassLoader.php文件中的loadclass方法里做了什么

public function loadClass($class)
{
//查找类名对应的实际文件地址,并引入该文件
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
public function findFile($class)
{
// 查找类是否存在classMap数组中,如果在就返回类的文件路径
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
//psr4标准查找
$file = $this->findFileWithExtension($class, '.php');
..............
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;//将类名拼接上文件扩展名 Think\wahaha\Chu.php
$first = $class[0];//获取类名的首字母 T
if (isset($this->prefixLengthsPsr4[$first])) {//通过首字符查找对应的项 例如:[T=>['Think\\'=>6]],详情可查看autoload_static.php文件
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {//将类名拆分,例如 Think\wahaha\Chu => Think\wahaha、Think
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath.'\\'; // Think\ Think\wahaha\
if (isset($this->prefixDirsPsr4[$search])) { //通过search项查找对应的目录数组 例如: ['Think\\'=>[ 0 => __DIR__ . '/../..' . '/think']],,详情可查看autoload_static.php文件
foreach ($this->prefixDirsPsr4[$search] as $dir) {//循环遍历目录数组
$length = $this->prefixLengthsPsr4[$first][$search];//获取命名空间字符长度 例如 Think\ 长度为6
/*
拼接路径:
__DIR__ . '/../..' . '/think' / wahaha\Chu.php
*/
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
..........................
}

结语:
至此,psr4分析差不多就完了,更多细节可以自己参考里面的代码,源码中使用了匿名函数类(autoload_static.php文件中),导致刚开始有点懵,由于自己平时这块使用的比较少,所以需要再这块进行加强,平时看了一些事件机制实现,大部分都是观察者模式进行绑定和通知的。
本文详细解析了Composer在PHP环境下如何利用PSR-4标准进行类的自动加载,从源码层面深入剖析了Composer内部的加载流程,包括入口文件、核心加载文件、类加载器以及不同PHP版本下的配置映射信息。

303

被折叠的 条评论
为什么被折叠?



