上文介绍了 HandleExceptions,在 《Laravel源码入门-启动引导过程(五)$kernel->handle($request)》中第五个要载入的是 RegisterProviders,也就是 Foundation\Http\Kernel::bootstrapers[] 的第五个
\Illuminate\Foundation\Bootstrap\RegisterProviders::class, 如下:
// Illuminate\Foundation\Http\Kernel.php 片段
/**
* The bootstrap classes for the application.
* 引导类,起引导作用的类
*
* @var array
*/
protected $bootstrappers = [
// 载入服务器环境变量(.env 文件)
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
// 载入配置信息(config 目录)
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
// 配置如何处理异常
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
// 注册 Facades
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
// 注册 Providers
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
// 启动 Providers
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
我们再直接贴出 RegisterProviders 类的代码,进行分析,非常直观,如下
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Contracts\Foundation\Application;
class RegisterProviders
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
// 非常简单一句话,注册配置的 Providers。
$app->registerConfiguredProviders();
}
}
本来至此,RegisterProviders 就结束了,但再深入一步,我们看看 Application::registerConfiguredProviders() 的源代码:
//来自: Illuminate\Foundation\Application.php
/**
* Register all of the configured providers.
*
* @return void
*/
public function registerConfiguredProviders()
{
// 下面是 注册配置好的 Providers 方法代码,我们注释掉改写一下。
//(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
// ->load($this->config['app.providers']);
// 第一步:创建 Provider 仓库对象
$repository = new ProviderRepository(
$this,
new Filesystem,
$this->getCachedServicesPath()
);
// dump($repository);
// 第二步:获取配置文件 config/app.php 中的 providers 数组中指定的 Providers
// 打印出 $this($app),可以看到 $app 中的 instances 包含 config 实例
// $this->config 是 LoadConfiguration 时 绑定的实例,原来的绑定代码如下:
// Illuminate\Foundation\Bootstrap\LoadConfiguraton.php
// $app->instance('config', $config = new Repository($items));
$providers = $this->config['app.providers'];
// dump($this);
// dump($providers);
// 第三步:Provider 仓库对象载入 Providers
$repository->load($providers);
}
==分析==
1. 第一步,从缓存服务路径(bootstrap/cache/services.php))创建仓库,为什么?深入 ProviderRepository.php 源代码可以看到,services.php 中的 服务提供者是基本的,在载入过程中,需要与 config/app.php 中的 privoders 做对比的,如果不一致,需要重新载入。La让用户添加的自己的 providers 放在 app.php 中。
2. 第二步,就是获取了所有的 config/app.php 中的 providers。
3. 第三步,载入,具体载入时,做了几件事,请看 $reponsitory->load() 源代码。
-- 比对 services.php,判定是否需要重新编译;
-- 注册事件,实际是处理 services.php 中的 when 数组,这个 when 很形象,意思是 什么时候要做什么事(件);
-- 继续注册 eager 数组中的 providers,这里 eager 急切的、热切的,是积极载入的意思,他相对的就是 lazy loading,所以 laravel 让需要先行载入的 providers 放在 eager 数组中;这里 最终实际调用了每个 provider 自己都有的 register(),纵观 Laravel 引导的全过程,实际上就是 找到列表,调用列表中每个的 register()以及必要的 boot()。
-- 最后,处理 标记为 deffered 的 providers,这其实就是 lazy loading 的内容了,这个处理只是 add,不是 register。
下面列出 services.php 大体样子,初学者根据以上描述体会吧。
<?php return array (
'providers' =>
array (
0 => 'Illuminate\\Auth\\AuthServiceProvider',
1 => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
// 略去...2~24...
25 => 'App\\Providers\\EventServiceProvider',
26 => 'App\\Providers\\RouteServiceProvider',
),
'eager' =>
array (
0 => 'Illuminate\\Auth\\AuthServiceProvider',
1 => 'Illuminate\\Cookie\\CookieServiceProvider',
// 略去...2~11...
12 => 'App\\Providers\\EventServiceProvider',
13 => 'App\\Providers\\RouteServiceProvider',
),
'deferred' =>
array (
'Illuminate\\Broadcasting\\BroadcastManager' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
// 略去更多 lazy loading 的
'validation.presence' => 'Illuminate\\Validation\\ValidationServiceProvider',
'command.tinker' => 'Laravel\\Tinker\\TinkerServiceProvider',
),
'when' =>
array (
'Illuminate\\Broadcasting\\BroadcastServiceProvider' =>
array (
),
'Illuminate\\Bus\\BusServiceProvider' =>
array (
),
// 略去一些事件(实际上都是空的,没写)
'Laravel\\Tinker\\TinkerServiceProvider' =>
array (
),
),
);
== 附录 ===
Laravel的核心思想之一是 container 和 provider,应该更精确的说是, service container 和 service provider。粗浅理解,所有的功能(路由、用户验证、缓存、数据库、分页等。。。看看 services.php )都是服务,provider 更容易的理解是 “供应商” ,这些功能都被独立成或收归到供应商那里,需要哪个服务,就把供应商叫来,叫哪里来?叫到app里,都为app提供服务,所以,Application 就是个最大的 container。我们这个供应商,其实也可以叫 vendor,只是 vendor 相对于整个框架,而 provider 还是微观一些。