Web Frameworks: The Laravel bootstrap process


Every developer who respects himself should, sooner or later, understand the tools he/she is working with work. And frameworks are not an exception. For those who don’t have the time to delve into the Laravel internals, here is a list of the main actions that take place every time Laravel is called (based on version 5.7). So, here we go:

1. Instantiate DI container

An “application” object is instantiated.

$app = new Illuminate\Foundation\Application(...);

Ok! You can say that it’s a matter of viewpoint but, in my opinion, $app is just a DI container (extends the Illuminate\Container\Container class). Don’t let the class name (or the variable name) fool you. Yeah, it also has some helpers methods in there that are not related to services but not many. In any case, I suppose that the authors identify this class with the framework and that’s why it also contains a framework version:

const VERSION = '5.8.11';

For some more information about the Laravel DI container, have a look here.

2. Register names for application.

It registers the names ‘app’ and ‘Illuminate\Container\Container’ to the application instance.

3. Register fundamental framework services

It registers short names for some fundamental services.

'events'   ->  Illuminate\Events\Dispatcher
'log'      ->  Illuminate\Log\LogManager
'router'   ->  Illuminate\Routing\Router
'url'      ->  Illuminate\Routing\UrlGenerator
'redirect' ->  Illuminate\Routing\Redirector

Actually, the:

public function register($provider, $force = false)

method that is used delegates the registration to the service provider of each service. The service provider is another class that comes alongside the service class and it handles the service class registration and bootstrapping. That way, the application is decoupled from details related to the service (if the service object should be a singleton, if the service needs to register more than one names, etc).

4. Registers aliases for core framework services.

All aliases (class names, contract names, short names,…) that are available for the core framework services are registered so that they can be used (interchangeably) by the app() helper function:

// use the alias of the component
app('router')->get('/', function () {
   return 'Interfaces are cool!';

// use the class FQN
app('Illuminate\Routing\Router')->get('/', function () {
   return 'Interfaces are cool!';

// use the interface/contract FQN
app('Illuminate\Contracts\Routing\Registrar')->get('/', function () {
   return 'Interfaces are cool!';

5. Register some more core services

Http Kernel Illuminate\Foundation\Http\Kernel
Handles HTTP requests
Console Kernel Illuminate\Foundation\Console\Kernel
Handles a console command (usually artisan commands)
Exception Handler Illuminate\Foundation\Exceptions\Handler
Handles exceptions.

Here is the breaking point between handling an HTTP request and calling an artisan command. Based on which case we have, the relevant kernel will be used in the next step.

6. Instantiate an Http Kernel

An instance of Illuminate\Contracts\Http\Kernel will be instantiated.
For me, the kernel is what I would consider as the real application. The Http Kernel has 4 major components:

$app ( the DI container )

$router ( the router )

$middleware ( the stack of middlewares used by the application )

$bootstrappers ( the classes that bootstrap the application, not the framework )

So, most functionality provided by the framework is brought together by the kernel. The kernel has the main control of what the framework does before the execution flow gets to the code that we have written.

7. Build the request object

$request = Illuminate\Http\Request::capture()

8. Registers the request object as a service.

$this->app->instance('request', $request);

That makes the request object available at any place of our app through the container.

9. Boostraps the application for HTTP requests

(a) loads the environmental variables

(b) loads the configuration variables

(c) setup exception/error handling

(d) register facades

(e) register the service providers

I. A list of service providers is loaded from /bootstrap/cache/services.php (where each provider is also marked as “eager” , “deferred””or “when”) and updated based on the /config/app.php

II. Providers marked as eager are registered to the container (by instantiating the provider and calling its register() method). The ones marked as deferred are added to the container’s deferred services ( a name is mapped to the provider ). By default, providers are loaded eagerly:

protected $defer = false;

(f) boots all the registered service providers

The boot() method of each service provider is called.

Note: routes will be loaded to router during the booting of the RouteServiceProvider

10. Sends the request through the global middleware stack

This stack includes any middleware that should be executed in all cases (independent to which endpoint has been called).

11. Dispatches the request to the router (calling the router’s dispatch() method)

The router will detect a matching route and then it will execute/run this route:

– the request will be passed through the route’s middleware

– the controller’s method (or the callback, assigned to this route) will be executed

Finally, the router will return a response.