Laravel $bootstrappers数组加载源码分析(二)
基于laravel10
分析
在上篇文章中分析了前三个,这里继续往后分析
第四个\Illuminate\Foundation\Bootstrap\RegisterFacades::class
类
这个是注册静态代理
我们看一下这个类的 bootstrap
方法做了什么处理
public function bootstrap(Application $app)
{
//清空静态代理已解析实例的数组
Facade::clearResolvedInstances();
//设置容器到静态代理 如果使用静态代理拿不到实例的时候 会先从容器中去拿实例 然后去给静态代理数组
Facade::setFacadeApplication($app);
//别名加载器
①AliasLoader::getInstance(array_merge(
//别名
$app->make('config')->get('app.aliases', []),
//依赖包别名 是从cache/packages.php这个缓存文件里面取aliases
$app->make(PackageManifest::class)->aliases()
))
②->register();
}
①看一下Illuminate\Foundation\AliasLoader
类的getInstance
方法做了什么处理
public static function getInstance(array $aliases = [])
{
//防止重复实例化(单例)
if (is_null(static::$instance)) {
return static::$instance = new static($aliases);
}
//合并
$aliases = array_merge(static::$instance->getAliases(), $aliases);
//重新设置
static::$instance->setAliases($aliases);
//返回当前类实例
return static::$instance;
}
②看一下Illuminate\Foundation\AliasLoader
类的register
方法做了什么处理
public function register()
{
//判断是有已经注册
if (! $this->registered) {
//为自动加载程序堆栈准备加载方法
$this->prependToLoaderStack();
//标记注册
$this->registered = true;
}
}
protected function prependToLoaderStack()
{
//自动加载 当类未被定义时会触发
③ spl_autoload_register([$this, 'load'], true, true);
}
③看一下Illuminate\Foundation\AliasLoader
类的load
方法做了什么处理
public function load($alias)
{
//这里是判断空间命名是否是以Facades开头
//如果是以Facades开头 代表是实时静态代理(可以看一下文档介绍https://learnku.com/docs/laravel/10.x/facades/14844#real-time-facades)
if (static::$facadeNamespace && str_starts_with($alias, static::$facadeNamespace)) {
④$this->loadFacade($alias);
return true;
}
...
}
④看一下Illuminate\Foundation\AliasLoader
类的loadFacade
方法做了什么处理
protected function loadFacade($alias)
{
//这里引入文件
require $this->ensureFacadeExists($alias);
}
protected function ensureFacadeExists($alias)
{
//判断缓存实时静态代理文件是否存在
if (is_file($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) {
return $path;
}
//如果不存在就创建文件
file_put_contents($path, $this->formatFacadeStub(
$alias, file_get_contents(__DIR__.'/stubs/facade.stub')
));
return $path;
}
再看一下Illuminate\Foundation\AliasLoader
类的load
方法后续做了什么处理
public function load($alias)
{
...
//如果存在別名
if (isset($this->aliases[$alias])) {
//为这个类创建一个别名 use 别名 等同与 use 这个类
return class_alias($this->aliases[$alias], $alias);
}
}
第五个\Illuminate\Foundation\Bootstrap\RegisterProviders::class
类
这个是执行所有服务提供者的Register方法
我们看一下这个类的 bootstrap
方法做了什么处理
public function bootstrap(Application $app)
{
//这里是调用app容器的方法
①$app->registerConfiguredProviders();
}
①我们看一下app容器的registerConfiguredProviders
方法做了什么处理
public function registerConfiguredProviders()
{
//这里是把官方自带的服务提供者和其他的分开成两个数组
$providers = Collection::make($this->make('config')->get('app.providers'))
->partition(fn ($provider) => str_starts_with($provider, 'Illuminate\\'));
//这里是把安装的依赖包的服务提供者放到自带的后面,app文件夹下的服务提供者前面,应该是为了防止我们在服务提供者里面使用依赖包的类,防止报错
$providers->splice(1, 0, [①$this->make(PackageManifest::class)->providers()]);
...
}
①看一下PackageManifest
类的providers
方法做了什么处理
public function providers()
{
return $this->config('providers');
}
public function config($key)
{
//这里是拿到getManifest方法返回值然后用flatMap处理成一维数组 每个value是 服务提供者
return collect(②$this->getManifest())->flatMap(function ($configuration) use ($key) {
return (array) ($configuration[$key] ?? []);
})->filter()->all();
}
②看一下PackageManifest
类的getManifest
方法做了什么处理
protected function getManifest()
{
//这里是防止重复调用
if (! is_null($this->manifest)) {
return $this->manifest;
}
//这个文件路径是bootstarp/cache/packages.php 如果不存在的时候就会去生成一个
if (! is_file($this->manifestPath)) {
//这里就是去生成
③$this->build();
}
//这里就是加载文件拿到文件中的返回值
return $this->manifest = is_file($this->manifestPath) ?
④$this->files->getRequire($this->manifestPath) : [];
}
③看一下PackageManifest
类的build
方法做了什么处理
public function build()
{
$packages = [];
//这里是判断installed.json文件是否存在
if ($this->files->exists($path = $this->vendorPath.'/composer/installed.json')) {
//解析json
$installed = json_decode($this->files->get($path), true);
//拿到packages字段 如下图
$packages = $installed['packages'] ?? $installed;
}
//这里是拿到忽略的包(不需要自动发现)
$ignoreAll = in_array('*', $ignore = $this->packagesToIgnore());
//这里是写到bootstarp/cache/packages.php文字
$this->write(collect($packages)->mapWithKeys(function ($package) {
return [$this->format($package['name']) => $package['extra']['laravel'] ?? []];
})->each(function ($configuration) use (&$ignore) {
$ignore = array_merge($ignore, $configuration['dont-discover'] ?? []);
})->reject(function ($configuration, $package) use ($ignore, $ignoreAll) {
return $ignoreAll || in_array($package, $ignore);
})->filter()->all());
}
④看一下Illuminate\Filesystem\Filesystem
类的getRequire
方法做了什么处理
public function getRequire($path, array $data = [])
{
if ($this->isFile($path)) {
$__path = $path;
$__data = $data;
//这里用闭包的作用是防止用extract创建出来的变量会用冲突被污染
//静态闭包的作用应该是和创建一个单独的函数一样
return (static function () use ($__path, $__data) {
//这里是把$__data数组中的key变成变量给$__path文件里面去用
extract($__data, EXTR_SKIP);
return require $__path;
})();
}
throw new FileNotFoundException("File does not exist at path {$path}.");
}
再回到app容器的registerConfiguredProviders
方法,看后续做了什么处理
public function registerConfiguredProviders()
{
...
//这里就是去加载拿到的服务提供者
(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
⑤->load($providers->collapse()->toArray());
}
⑤看一下Illuminate\Foundation\ProviderRepository
类的load
方法做了什么处理
public function load(array $providers)
{
⑥$manifest = $this->loadManifest();
...
}
⑥看一下Illuminate\Foundation\ProviderRepository
类的loadManifest
方法做了什么处理
public function loadManifest()
{
//这里就是去加载文件 bootstrap/cache/services.php
if ($this->files->exists($this->manifestPath)) {
//引入文件拿到返回值,getRequire方法上面有分析
$manifest = $this->files->getRequire($this->manifestPath);
if ($manifest) {
return array_merge(['when' => []], $manifest);
}
}
}
再回到Illuminate\Foundation\ProviderRepository
类的load
方法,看后续做了什么处理
public function load(array $providers)
{
...
//是否应该重新生成bootstrap/cache/services.php文件
if ($this->shouldRecompile($manifest, $providers)) {
//这里就是去重新生成文件
⑦$manifest = $this->compileManifest($providers);
}
...
}
public function shouldRecompile($manifest, $providers)
{
return
//表示文件不存在 或者文件无返回值
is_null($manifest) ||
//这里是以上面获取到的服务提供者为准 不管获取到的是否完整
$manifest['providers'] != $providers;
}
⑦看一下Illuminate\Foundation\ProviderRepository
类的compileManifest
方法做了什么处理
protected function compileManifest($providers)
{
//创建新的服务数据结构
$manifest = $this->freshManifest($providers);
foreach ($providers as $provider) {
//实例化服务提供者
$instance = $this->createProvider($provider);
//判断是否延迟加载
if ($instance->isDeferred()) {
//拿到provides定义需要延迟加载的服务
//在这个服务提供者里面定义的服务都需要定义到provides方法中,不然就会找不到服务(http模式下,console模式下会直接去加载延迟服务)
foreach ($instance->provides() as $service) {
//放入到延迟加载服务数组里面
$manifest['deferred'][$service] = $provider;
}
//这里是绑定事件,通过触发事件加载服务提供者
$manifest['when'][$provider] = $instance->when();
} else {
//放到急切加载的提供者数组中
$manifest['eager'][] = $provider;
}
}
//写到bootstrap/cache/services.php文件中
return $this->writeManifest($manifest);
}
protected function freshManifest(array $providers)
{
return ['providers' => $providers, 'eager' => [], 'deferred' => []];
}
再回到Illuminate\Foundation\ProviderRepository
类的load
方法,看后续做了什么处理
public function load(array $providers)
{
...
foreach ($manifest['when'] as $provider => $events) {
//这里就是去注册事件监听绑定 当触发这个事件的时候会执行注册服务提供者
$this->registerLoadEvents($provider, $events);
}
foreach ($manifest['eager'] as $provider) {
//注册服务提供者
⑧$this->app->register($provider);
}
//把延迟服务提供者 放入容器的deferredServices数组中
$this->app->addDeferredServices($manifest['deferred']);
}
protected function registerLoadEvents($provider, array $events)
{
if (count($events) < 1) {
return;
}
$this->app->make('events')->listen($events, fn () => $this->app->register($provider));
}
⑧看一下app容器的register
方法做了什么处理
public function register($provider, $force = false)
{
//判断服务提供者是否已经注册 并且不强制加载
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
if (is_string($provider)) {
//初始化服务提供者实例
$provider = $this->resolveProvider($provider);
}
//去注册服务
$provider->register();
//这里是判断是否有bindings属性 这种就是每次去make的时候都会去重新实例化
if (property_exists($provider, 'bindings')) {
foreach ($provider->bindings as $key => $value) {
$this->bind($key, $value);
}
}
//这里是判断是否有singletons(单例)属性
if (property_exists($provider, 'singletons')) {
foreach ($provider->singletons as $key => $value) {
$key = is_int($key) ? $value : $key;
$this->singleton($key, $value);
}
}
//去标记已经注册
$this->markAsRegistered($provider);
//这里是boot是否已经被加载
//其中一个目的启动(boot)延迟加载服务提供者(就是去调用它的boot方法)
if ($this->isBooted()) {
//启动(boot)服务提供者
$this->bootProvider($provider);
}
return $provider;
}
protected function bootProvider(ServiceProvider $provider)
{
//启动前执行
$provider->callBootingCallbacks();
//启动
if (method_exists($provider, 'boot')) {
$this->call([$provider, 'boot']);
}
//启动后执行
$provider->callBootedCallbacks();
}
第六个\Illuminate\Foundation\Bootstrap\BootProviders::class
类
这个是执行所有服务提供者的boot方法
我们看一下这个类的 bootstrap
方法做了什么处理
public function bootstrap(Application $app)
{
//这里是调用app容器的方法
①$app->boot();
}
①我们看一下app容器的boot
方法做了什么处理
public function boot()
{
//如果已经启动了
if ($this->isBooted()) {
return;
}
//调用app容器的启动前执行回调
$this->fireAppCallbacks($this->bootingCallbacks);
//这里就是去遍历把每个元素给到闭包参数里面
array_walk($this->serviceProviders, function ($p) {
//这个放到上面有分析
$this->bootProvider($p);
});
//标记已启动
$this->booted = true;
//调用app容器的启动后执行回调
$this->fireAppCallbacks($this->bootedCallbacks);
}
以上就是 $bootstrappers 数组后三个的执行流程了,如果有写的有误的地方,请大佬们指正
本作品采用《CC 协议》,转载必须注明作者和本文链接