Laravel composer自动加载原理

程序地址

index.php

require __DIR__.'/../vendor/autoload.php';
跟踪:index.php > /vendor/autoload.php

require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit148a9da910429c7c016377bec97d275e::getLoader();

到达 composer 工作区
在这里插入图片描述

什么是自动加载?

新下载的 laravel5.5 ,App 下默认有 User.php,是 User 模型(Model)

$userModel = new App\User();use App\User;
$userModel = new User();

如此方便得益于 composer 的自动加载。

composer 自动加载的流程

(一)映射归纳 驱动:composer

根据加载标准(files\classmap\psr-4\psr-0),将映射关系分门别类写入不同的文件,以数组保存

当在控制台执行 composer require | update 引入一个组件时包,读取组建包的 composer.json 中的 "autoload"配置,分别按照每个组建包配置的自动加载规则,扫描组件中的文件,将其写入不同加载标准的php文件数组中

那么composer.json 中的自动加载通常有下面几种方式 :(这不是真实的 autoload 配置,这是从 laravel5.5 各个组建包的 composer.json 中拼在一起的)

"autoload": {
		这是 laravel/framework 组建包的 composer.json 中的
		"files": [
            "src/Illuminate/Foundation/helpers.php",
            "src/Illuminate/Support/helpers.php"
        ],
		这是 hamcrest/hamcrest-php 组建包的 composer.json 中的
		"classmap": [
			"hamcrest"
		]
		这是项目的 composer.json 中的
        "psr-4": {
            "App\\": "app/"
        },
		这是 mockery/mockery 组建包的 composer.json 中的
		"psr-0": {
            "Mockery": "library/"
        }
    },

files : 文件预加载,框架启动时便被 include ,通常文件中提供一些函数方法方便我们使用,如经常用的 dd()
"files:["src/Illuminate/Foundation/helpers.php"]" 写入 /composer/autoload_files.php

autoload_files.php >
<?php
return array(
	...
	'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
);

classmap : 直接映射文件真实路径。 这是简单粗暴的,因此这种方式效率是最高的。
"classmap": ["hamcrest"], 写入 /composer/autoload_classmap.php

autoload_classmap.php >
return array(
	'Hamcrest\\Arrays\\IsArray' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArray.php',
    'Hamcrest\\Arrays\\IsArrayContaining' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContaining.php',
    'Hamcrest\\Arrays\\IsArrayContainingInAnyOrder' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingInAnyOrder.php',
	...// hamcrest 文件夹下所有文件
);

use Hamcrest\\Arrays\\IsArray; 时,可直接在 classmap 的数组中找到它,是不是很粗暴呢。

psr-4 :这是最常用的加载标准。"psr-4": {"App\\": "app/"} 写入 autoload_psr4.php

/composer/autoload_psr4.php >
return array(
	...
	'App\\' => array($baseDir . '/app'),// App 称作 prefix
);

只要是 app/ 目录下的文件,且名命空间符合 psr-4 标准的类文件都能被自动加载。如use App\Test => app/Test.php 。而 classmap 方式不可以,这样你可以在 app/ 下自由的增加/删除类文件了。

这里提一下,前面说 classmap 方式是最高效的,而 composer dump-autoload 可以将通过 psr-4 规范加载的文件 “落盘”,即写入 autoload_classmap.php 。从而起到加速的作用,官方建议生产环境时执行 composer dump-autoload --optimize 来优化项目的自动加载速度。

为了说明 composer dump-autoload 的作用,我在 app 目录下建立 Test.php

<?php
namespace App;
class Test{  
}

执行 composer dump-autoload 后,发现在autoload_classmap.phpautoload_static.php(稍后再提及)中找到了它。

/composer/autoload_classmap.php && autoload_static.php >
return array(
	...
	'App\\Test' => __DIR__ . '/../..' . '/app/Test.php',
	'App\\User' => $baseDir . '/app/User.php',
)

composer dump-autoload --optimize 的作用是进行优化(optimize),清理无效索引空间另外在 /composer 生成了 user 缓存文件。

psr-0 :和 psr-4 类似,只是加载规则有所不同。官方已弃用,但 laravel 有的组建包还是在用的,composer 仍然支持向下兼容。"psr-0": {"Mockery": "library/"} 写入 autoload_namespaces.php

/composer/autoload_namespaces.php >
return array(
	...
	'Mockery' => array($vendorDir . '/mockery/mockery/library'),
);

autoload_static.php
回顾 composer/ 目录:

其中四个文件中保存了四种规范的映射,还有一个 autoload_static.php 文件。它把这四个文件中的所有映射集中在一起,通过 getInitializer() 方法注入到 ClassLoader的属性中( ClassLoader 是实现自动加载的类,稍后再提)

autoload_static.php >
<?php
namespace Composer\Autoload;
class ComposerStaticInitccb56ced66f82d50b9e1d3fd28a6ab26{
	public static $files = array(..);
	public static $classMap = array(..);
	public static $prefixLengthsPsr4 = array(..);
	public static $prefixDirsPsr4 = array(..);
	public static $fallbackDirsPsr4 = array(..);
	public static $prefixesPsr0 = array(..);
	public static function getInitializer(ClassLoader $loader){..};
}

(二) 映射提取 驱动:/composer/autoload_real.php

从四个文件中 或 autoload_static.php 中(因它包含了四个文件的全部映射)将全部映射提取到实现自动加载的类(ClassLoader)中,由 ClassLoader 查找类的映射,实现自动加载,

index.php > /vendor/autoload.php > /composer/autoload_real.php >

//重要概念:当前类的任务是把所有映射注入到ClassLoader中,由 ClassLoader 类实现自动加载。为了更好的说明,以下为简化版代码。
class ComposerAutoloaderInitccb56ced66f82d50b9e1d3fd28a6ab26{
	getLoader(){
		----STEP1 实例化 ClassLoader-------
		require __DIR__ . '/ClassLoader.php';
		$loader = new ClassLoader();
		
		----STEP2 提取映射并注入 ClassLoader-------
		if ($useStaticLoader) {
			//若符合运行环境,优先从 autoload_static.php 提取映射(见前面 autoload_static.php 的介绍)
            require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitccb56ced66f82d50b9e1d3fd28a6ab26::getInitializer($loader));
			
        } else {
			// 否则就从分别从三个文件中提取出来并注入 ClassLoader
			//psr-0 
            $map = require __DIR__ . '/autoload_namespaces.php';
            foreach ($map as $namespace => $path) {
                $loader->set($namespace, $path);
            }
			
			//psr-4
            $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);
            }
        }
		
		----STEP3  STEP2完毕,启动 ClassLoader 的自动加载方法-------  
		$loader->register(true); //spl_autoload_register
		
		----STEP4  处理预加载文件-------  
		//处理 autoload_files.php 中的预加载文件,由于这些文件需要立即加载,它和类的自动加载是不同的,只要加载后,文件中的函数就能用。
		//同理,能从 autoload_static.php 提取,优先从这个文件提取。
		if ($useStaticLoader) {
            $includeFiles = Composer\Autoload\ComposerStaticInitccb56ced66f82d50b9e1d3fd28a6ab26::$files;
        } else {
            $includeFiles = require __DIR__ . '/autoload_files.php';
        }
        foreach ($includeFiles as $fileIdentifier => $file) {
            require $file;
        }
		
		//最后一句
		return $loader;
	};
}

(三) 实现自动加载 驱动:/composer/ClassLoader.php

ClassLoader.php >
class ClassLoader{
	/**
     * 将此实例注册为自动加载程序
	 * -------------------------------------------
	 * new App/User();时将 'App/User'类命传递给 loadClass('App/User')方法
	 * 第二参数true debug
     * $prepend为true,将 loadClass() 放在自动加载函数栈的第一个,即优先处理。
	 */
	public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
    }
	
	/**
    * 加载给定的类或接口
    */
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            includeFile($file);
            return true;
        }
    }
	
	/**
    * 查找定义类的文件的路径
	* (二)类文件提取 已将所有映射注入到此类中的数组属性中
	* 从数组中根据 Key 值取出
	* 但并不是那么简单,本人没有太过研究
    */
	public function findFile($class){
		//省略代码
		return $file;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值