博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
看 Laravel 源代码了解 ServiceProvider 的加载
阅读量:6411 次
发布时间:2019-06-23

本文共 13710 字,大约阅读时间需要 45 分钟。

使用 Laravel 开始,我们总是绕不开 ServiceProvider 这个概念。在 Laravel 框架里到处充斥着 ServiceProvider —— AppServiceProvider、AuthServiceProvider、BroadcastServiceProvider、EventServiceProvider 和 RouteServiceProvider 等等。

还有我们在安装一些第三方插件时,都时不时有这么句话,将****ServiceProvider 加入到 config/app.php 的 providers 数组中。

难道咱们就不想知道 Laravel 是如何加载这些 ServiceProviders 的吗?

所以今天从源代码的运行来看看是怎么实现加载的?

复制代码

看 Application 类

我们都知道 Laravel 的入口文件在 public/index.php

make(Illuminate\Contracts\Http\Kernel::class);$response = $kernel->handle( $request = Illuminate\Http\Request::capture());$response->send();$kernel->terminate($request, $response);复制代码

这里先看载入 require_once __DIR__.'/../bootstrap/app.php',创建 app 对象。

singleton( Illuminate\Contracts\Http\Kernel::class, App\Http\Kernel::class);$app->singleton( Illuminate\Contracts\Console\Kernel::class, App\Console\Kernel::class);$app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\Handler::class);/*|--------------------------------------------------------------------------| Return The Application|--------------------------------------------------------------------------|| This script returns the application instance. The instance is given to| the calling script so we can separate the building of the instances| from the actual running of the application and sending responses.|*/return $app;复制代码

直接返回的是 new Illuminate\Foundation\Application( realpath(__DIR__.'/../') Application 对象。

这个对象就是 Laravel 的「容器」。我们开始看看 Application 是怎么发现 ServiceProvider 的?

/** * Create a new Illuminate application instance. * * @param  string|null  $basePath * @return void */public function __construct($basePath = null){    if ($basePath) {        $this->setBasePath($basePath);    }    $this->registerBaseBindings();    $this->registerBaseServiceProviders();    $this->registerCoreContainerAliases();}复制代码

主要是完成这四个方法。第一个和最后一个方法暂且不表;我们主要看:

$this->registerBaseBindings(); $this->registerBaseServiceProviders();

registerBaseBindings()

/** * Register the basic bindings into the container. * * @return void */protected function registerBaseBindings(){    static::setInstance($this);    $this->instance('app', $this);    $this->instance(Container::class, $this);    $this->instance(PackageManifest::class, new PackageManifest(        new Filesystem, $this->basePath(), $this->getCachedPackagesPath()    ));}复制代码

前两个主要是绑定 Application 对象和 Container 对象。重点分析 PackageManifest 对象之前,我们看看 $this->getCachedPackagesPath()这个函数:

/** * Get the path to the cached packages.php file. * * @return string */public function getCachedPackagesPath(){    return $this->bootstrapPath().'/cache/packages.php';}复制代码

这个就是我们 bootstrap/cache/packages.php文件,这个文件的内容我们看看:

array ( 'providers' => array ( 0 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider', ), ), 'encore/laravel-admin' => array ( 'providers' => array ( 0 => 'Encore\\Admin\\AdminServiceProvider', ), 'aliases' => array ( 'Admin' => 'Encore\\Admin\\Facades\\Admin', ), ), 'laravel/tinker' => array ( 'providers' => array ( 0 => 'Laravel\\Tinker\\TinkerServiceProvider', ), ), 'rebing/graphql-laravel' => array ( 'providers' => array ( 0 => 'Rebing\\GraphQL\\GraphQLServiceProvider', ), 'aliases' => array ( 'GraphQL' => 'Rebing\\GraphQL\\Support\\Facades\\GraphQL', ), ), 'tymon/jwt-auth' => array ( 'aliases' => array ( 'JWTAuth' => 'Tymon\\JWTAuth\\Facades\\JWTAuth', 'JWTFactory' => 'Tymon\\JWTAuth\\Facades\\JWTFactory', ), 'providers' => array ( 0 => 'Tymon\\JWTAuth\\Providers\\LaravelServiceProvider', ), ), 'noh4ck/graphiql' => array ( 'providers' => array ( 0 => 'Graphiql\\GraphiqlServiceProvider', ), ), 'rollbar/rollbar-laravel' => array ( 'providers' => array ( 0 => 'Rollbar\\Laravel\\RollbarServiceProvider', ), 'aliases' => array ( 'Rollbar' => 'Rollbar\\Laravel\\Facades\\Rollbar', ), ), 'fanly/log2dingding' => array ( 'providers' => array ( 0 => 'Fanly\\Log2dingding\\FanlyLog2dingdingServiceProvider', ), ),);复制代码

通过分析,可以看出这个文件主要是放着我们自己引入第三方的 ServiceProvidersaliases,我们对照项目根路径的 composer.json 你就可以证实了:

"require": {    "php": ">=7.0.0",    "encore/laravel-admin": "1.5.*",    "fanly/log2dingding": "^0.0.2",    "fideloper/proxy": "~3.3",    "guzzlehttp/guzzle": "^6.3",    "laravel/framework": "5.5.*",    "laravel/tinker": "~1.0",    "noh4ck/graphiql": "@dev",    "overtrue/phplint": "^1.1",    "rebing/graphql-laravel": "^1.10",    "rollbar/rollbar-laravel": "^2.3",    "tymon/jwt-auth": "^1.0@dev"},复制代码

至于这个 bootstrap/cache/packages.php 文件内容怎么产生的,我们后面会说到。

我们回来分析 new PackageManifest(),类中的几个函数的作用,显而易见:

/** * 这个函数是将 package.php 文件的插件数组的 `providers`整合成一个 Collection 输出 */public function providers() {}    /** * 插件中的 `aliases` 整合成 Collection 输出。 * * @return array */public function aliases() {}/** * 这个是关键,从 verdor/composer/installed.json 文件中获取所有通过 composer 安装的插件数组,然后再通过用 `name` 绑定对应的 `ServiceProvider`,构成数组,然后再排除每个插件的 `dont-discover` 和项目 composer.json 填入的 `dont-discover`。 * 这也是 Laravel 包自动发现的核心所在。 *  */public function build(){    $packages = [];    if ($this->files->exists($path = $this->vendorPath.'/composer/installed.json')) {        $packages = json_decode($this->files->get($path), true);    }    $ignoreAll = in_array('*', $ignore = $this->packagesToIgnore());    $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());}/** * 最后就把上面的满足的 ServiceProvider 写入到文件中,就是上文我们说的 `bootstrap/cache/packages.php` */protected function write(array $manifest) {}复制代码

到目前为止,我们找到了需要加载的第三方的 ServiceProvider 了。

registerBaseServiceProviders()

接下来我们看看这个 registerBaseServiceProviders() 方法了。

/** * Register all of the base service providers. * * @return void */protected function registerBaseServiceProviders(){    $this->register(new EventServiceProvider($this));    $this->register(new LogServiceProvider($this));    $this->register(new RoutingServiceProvider($this));}复制代码

这里主要注册三个 ServiceProvider,具体功能后面详聊。

Kernel

我们简单过了一遍 new Application,我们回到 index.php 继续往下看:

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);$response = $kernel->handle(    $request = Illuminate\Http\Request::capture());$response->send();复制代码

这个 $kernel 就是 Laravel 的「核」,而 $kernel->handle() 方法就是 Laravel 的「核中之核」了 —— 即,根据输入的 Request,输出 response。完成请求到响应的过程。

我们进入 $kernel->handle() 方法。

/** * Handle an incoming HTTP request. * * @param  \Illuminate\Http\Request  $request * @return \Illuminate\Http\Response */public function handle($request){    try {        $request->enableHttpMethodParameterOverride();        $response = $this->sendRequestThroughRouter($request);    } catch (Exception $e) {        $this->reportException($e);        $response = $this->renderException($request, $e);    } catch (Throwable $e) {        $this->reportException($e = new FatalThrowableError($e));        $response = $this->renderException($request, $e);    }    $this->app['events']->dispatch(        new Events\RequestHandled($request, $response)    );    return $response;}复制代码

排除其它「干扰」东西,眼睛关注到这行代码:

$response = $this->sendRequestThroughRouter($request);复制代码

这也暴露了,网络请求的三要素:request、router 和 response。

/** * Send the given request through the middleware / router. * * @param  \Illuminate\Http\Request  $request * @return \Illuminate\Http\Response */protected function sendRequestThroughRouter($request){    $this->app->instance('request', $request);    Facade::clearResolvedInstance('request');    $this->bootstrap();    return (new Pipeline($this->app))                ->send($request)                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)                ->then($this->dispatchToRouter());}复制代码

但今天我们说的不是考虑执行的问题,我们需要知道什么时候加载我们的 ServiceProviders 所以在 return 执行之前的代码 ($this->bootstrap();) 就是初始化 ServiceProviders等信息的过程

/** * Bootstrap the application for HTTP requests. * * @return void */public function bootstrap(){    if (! $this->app->hasBeenBootstrapped()) {        $this->app->bootstrapWith($this->bootstrappers());    }}    // Application 类:/** * Run the given array of bootstrap classes. * * @param  array  $bootstrappers * @return void */public function bootstrapWith(array $bootstrappers){    $this->hasBeenBootstrapped = true;    foreach ($bootstrappers as $bootstrapper) {        $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);        $this->make($bootstrapper)->bootstrap($this);        $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);    }}复制代码

到此我们知道,实际上遍历执行 $bootstrappers->bootstrap($this)

此时我们看看 $bootstrappers

protected $bootstrappers = [    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,    \Illuminate\Foundation\Bootstrap\HandleExceptions::class,    \Illuminate\Foundation\Bootstrap\RegisterFacades::class,    \Illuminate\Foundation\Bootstrap\RegisterProviders::class,    \Illuminate\Foundation\Bootstrap\BootProviders::class,    ];复制代码

这六个类的作用主要是:加载环境变量、config、异常处理、注册 facades、和最后我们关注的 ServiceProviderregisterboot

我们分别来看看。

RegisterProviders

class RegisterProviders{    /**     * Bootstrap the given application.     *     * @param  \Illuminate\Contracts\Foundation\Application  $app     * @return void     */    public function bootstrap(Application $app)    {        $app->registerConfiguredProviders();    }}复制代码

实际上是调用 ApplicationregisterConfiguredProviders()

/** * Register all of the configured providers. * * @return void */public function registerConfiguredProviders(){    $providers = Collection::make($this->config['app.providers'])                    ->partition(function ($provider) {                        return Str::startsWith($provider, 'Illuminate\\');                    });    $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);    (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))                ->load($providers->collapse()->toArray());}复制代码

这个就厉害了。加载所有已配置的 ServiceProvider,主要包含了在配置文件 config/app.php 里的 providers,上文讲述的第三方所有满足的 ServiceProviders,以及在 boostrap/cached/service.php 中的所有 Providers

最后执行 ProviderRepository::load 方法,进行遍历 register

/** * Register the application service providers. * * @param  array  $providers * @return void */public function load(array $providers){    $manifest = $this->loadManifest();    // First we will load the service manifest, which contains information on all    // service providers registered with the application and which services it    // provides. This is used to know which services are "deferred" loaders.    if ($this->shouldRecompile($manifest, $providers)) {        $manifest = $this->compileManifest($providers);    }    // Next, we will register events to load the providers for each of the events    // that it has requested. This allows the service provider to defer itself    // while still getting automatically loaded when a certain event occurs.    foreach ($manifest['when'] as $provider => $events) {        $this->registerLoadEvents($provider, $events);    }    // We will go ahead and register all of the eagerly loaded providers with the    // application so their services can be registered with the application as    // a provided service. Then we will set the deferred service list on it.    foreach ($manifest['eager'] as $provider) {        $this->app->register($provider);    }    $this->app->addDeferredServices($manifest['deferred']);}/** * Register the load events for the given provider. * * @param  string  $provider * @param  array  $events * @return void */protected function registerLoadEvents($provider, array $events){    if (count($events) < 1) {        return;    }    $this->app->make('events')->listen($events, function () use ($provider) {        $this->app->register($provider);    });}复制代码

register 之后,我们可以看看 boot 方法了。

BootProviders

class BootProviders{    /**     * Bootstrap the given application.     *     * @param  \Illuminate\Contracts\Foundation\Application  $app     * @return void     */    public function bootstrap(Application $app)    {        $app->boot();    }}复制代码

我们按图索骥:

/** * Boot the application's service providers. * * @return void */public function boot(){    if ($this->booted) {        return;    }    // Once the application has booted we will also fire some "booted" callbacks    // for any listeners that need to do work after this initial booting gets    // finished. This is useful when ordering the boot-up processes we run.    $this->fireAppCallbacks($this->bootingCallbacks);    array_walk($this->serviceProviders, function ($p) {        $this->bootProvider($p);    });    $this->booted = true;    $this->fireAppCallbacks($this->bootedCallbacks);}.../** * Boot the given service provider. * * @param  \Illuminate\Support\ServiceProvider  $provider * @return mixed */protected function bootProvider(ServiceProvider $provider){    if (method_exists($provider, 'boot')) {        return $this->call([$provider, 'boot']);    }}复制代码

也就是说遍历执行所有 ServiceProvidersboot() (前提是该 ServiceProvider 有定义该方法)。

总结

通过分析 index.php 执行过程,发现 Application 主要是发现各种 ServiceProviders,而 $kernel 更多的是在处理 Request请求之前,把所有的 ServiceProvider进行注册 (register),然后再 boot。把所有的 ServiceProviders装载进系统内存中,供处理各式各样的 Request 使用。

而每一个 ServiceProvider 各司其职,负责各自不同的职能,来满足 Laravel 系统的各种需要。

下文我们将重点说说这些 ServiceProvider 的含义和作用。敬请期待!

未完待续

转载地址:http://rzkra.baihongyu.com/

你可能感兴趣的文章
centos7.x搭建svn server
查看>>
原码编译安装openssh6.7p1
查看>>
项目实战:自定义监控项--监控CPU信息
查看>>
easyui-datetimebox设置默认时分秒00:00:00
查看>>
蚂蚁分类信息系统5.8多城市UTF8开源优化版
查看>>
在django1.2+python2.7环境中使用send_mail发送邮件
查看>>
“Metro”,移动设备视觉语言的新新人类
查看>>
PHP源代码下载(本代码供初学者使用)
查看>>
Disruptor-NET和内存栅栏
查看>>
Windows平台ipod touch/iphone等共享笔记本无线上网设置大全
查看>>
播放加密DVD
查看>>
分享Silverlight新鲜事 - Silverlight Firestarter全球会议
查看>>
产品设计体会(3013)项目的“敏捷沟通”实践
查看>>
RHEL6.3基本网络配置(1)ifconfig命令
查看>>
网络诊断工具之—路由追踪tracert命令
查看>>
Java模拟HTTP的Get和Post请求(增强)
查看>>
php 环境搭建(windows php+apache)
查看>>
让虚拟机的软盘盘符不显示(适用于所有windows系统包括Windows Server)
查看>>
Cygwin不好用
查看>>
jQuery插件之验证控件jquery.validate.js
查看>>