Starting with Laravel, we couldn’t get around the concept of ServiceProvider. Serviceproviders are everywhere in the Laravel framework AppServiceProvider, AuthServiceProvider, BroadcastServiceProvider, EventServiceProvider, RouteServiceProvider, etc.

Add ****ServiceProvider to the providers array of config/app.php.

Don’t we want to know how Laravel loads these ServiceProviders?

So today from the source code run to see how the implementation of the load?

Copy the code

The Application class

We all know that Laravel’s entry file is in public/index.php


      .require __DIR__.'/.. /vendor/autoload.php'; . $app =require_once __DIR__.'/.. /bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);

Copy the code

Require_once __DIR__.’/.. /bootstrap/app.php’ to create the app object.


      

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/.. / '));/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
|
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.
|
*/

$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;
Copy the code

New Illuminate\Foundation\Application(realPath (__DIR__.’/.. /’) Application object.

This object is Laravel’s “container.” Let’s start by looking at how Does Application discover 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();
}
Copy the code

It is mainly to accomplish these four methods. The first and last methods are not listed; We mainly look at:

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

registerBaseBindings()

/**
 * Register the basic bindings into the container.
 *
 * @return void
 */
protected function registerBaseBindings(a)
{
    static::setInstance($this);

    $this->instance('app'.$this);

    $this->instance(Container::class, $this);

    $this->instance(PackageManifest::class, new PackageManifest(
        new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
    ));
}
Copy the code

The first two mainly bind Application objects and Container objects. $this->getCachedPackagesPath() $this->getCachedPackagesPath()

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

This is our bootstrap/cache/packages. PHP file, we look at the contents of this file:


       return array (
  'fideloper/proxy'= >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',),),);Copy the code

Through analysis, it can be seen that this file mainly contains ServiceProviders and aliases introduced by ourselves from a third party. We can verify this by comparing the project root path with the composer. Json:

"Require" : {" PHP ":" > = 7.0.0 ", "encore/laravel - admin" : "1.5. *", "fanly/log2dingding" : "^ hundreds", "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" : "^ @ dev 1.0"},Copy the code

As for the bootstrap/cache/packages. PHP file contents how to produce, we will say to the later.

New PackageManifest() : new PackageManifest();

/** * This function combines the providers of the package. PHP plugin array into a Collection output */
public function providers(a) {}
    
/** * plug-in 'aliases' into a Collection output. * *@return array
 */
public function aliases(a) {}

/ * * * this is the key, from verdor/composer/installed. The json file for all through the composer plug-in installed array, and then by using ` name ` binding corresponding ` ServiceProvider `, constitute an array, I then exclude the 'dont-Discover' of each plugin and the 'dont-Discover' of project composer. Json. This is also at the heart of automatic discovery in the Laravel package. * * /
public function build(a)
{
    $packages = [];

    if ($this->files->exists($path = $this->vendorPath.'/composer/installed.json')) {
        $packages = json_decode($this->files->get($path), true);
    }

    $ignoreAll = in_array(The '*', $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());
}

/ * * * finally put the above meet ServiceProvider written to the file, is we said above ` bootstrap/cache/packages. PHP ` * /
protected function write(array $manifest) {}
Copy the code

So far, we’ve found the third-party ServiceProvider we need to load.

registerBaseServiceProviders()

Let’s take a look at this registerBaseServiceProviders () method.

/**
 * Register all of the base service providers.
 *
 * @return void
 */
protected function registerBaseServiceProviders(a)
{
    $this->register(new EventServiceProvider($this));

    $this->register(new LogServiceProvider($this));

    $this->register(new RoutingServiceProvider($this));
}
Copy the code

There are three service Providers registered in this section.

Kernel

After a quick look at the New Application, let’s go back to index.php and continue:

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();
Copy the code

The $kernel is Laravel’s “kernel”, and the $kernel->handle() method is Laravel’s “kernel of a kernel” — that is, output a response based on an input Request. Complete the request-to-response process.

We go to the $kernel->handle() method.

/**
 * 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;
}
Copy the code

To eliminate other distractions, focus on this line of code:

$response = $this->sendRequestThroughRouter($request);
Copy the code

This also exposes the three elements of network requests: Request, Router, and 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());
}
Copy the code

$this->bootstrap(); $this->bootstrap(); Initializing information such as ServiceProviders

/**
 * Bootstrap the application for HTTP requests.
 *
 * @return void
 */
public function bootstrap(a)
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers()); }}/ / Application class:

/**
 * 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]); }}Copy the code

$bootstrappers->bootstrap($this)

Now let’s look at $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,
    ];
Copy the code

These six classes are used to load environment variables, config, exception handling, register Facades, and finally, register and boot for ServiceProvider.

Let’s take a look at each.

RegisterProviders

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

Is actually calling Application registerConfiguredProviders () :

/**
 * Register all of the configured providers.
 *
 * @return void
 */
public function registerConfiguredProviders(a)
{
    $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());
}
Copy the code

This is awesome. Load all configured ServiceProviders, including providers in the config/app.php configuration file. And in the boostrap/cached/service. All will in PHP.

Finally, execute ProviderRepository:: Load to traverse 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 (a) use ($provider) {
        $this->app->register($provider);
    });
}
Copy the code

After register, we can look at the boot method.

BootProviders

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

We follow the blueprint:

/**
 * Boot the application's service providers.
 *
 * @return void
 */
public function boot(a)
{
    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']); }}Copy the code

This means iterating through the Boot () of all ServiceProviders (provided the ServiceProvider defines the method).

conclusion

PHP is used to discover ServiceProviders, while $kernel is used to discover ServiceProviders before processing Request requests. Register all ServiceProviders and boot them. Load all ServiceProviders into system memory for handling various requests.

Each ServiceProvider performs its own functions to meet the requirements of the Laravel system.

We’ll focus on what these ServiceProviders mean and what they do. Stay tuned!

To be continued