php composer机制,PHP Composer类自动加载工作原理

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,找到的话就返回类文件的路径名。

参与评论 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值