Architecture Concepts
Request Lifecycle
Lifecycle Overview
The entry point for all requests to a Laravel application is the public/index.php
file. All requests are directed to this file by your web server (Apache / Nginx) configuration. The index.php
file doesn’t contain much code. Rather, it is a starting point for loading the rest of the framework.
The index.php
file loads the Composer generated autoloader definition, and then retrieves an instance of the Laravel application from bootstrap/app.php
script. The first action taken by Laravel itself is to create an instance of the application / service container.
HTTP / Console Kernels
Next, the incoming request is sent to either the HTTP kernel or the console kernel, depending on the type of request that is entering the application. These two kernels serve as the central location that all requests flow through. For now, let’s just focus on the HTTP kernel, which is located in app/Http/Kernel.php
.
The HTTP kernel extends the Illuminate\Foundation\Http\Kernel
class, which defines an array of bootstrappers
that will be run before the request is executed. These bootstrappers configure error handling, configure logging, detect the application environment, and perform other tasks that need to be done before the request is actually handled.
The HTTP kernel also defines a list of HTTP middleware that all requests must pass through before being handled by the application. These middleware handle reading and writing the HTTP session, determining if the application is in maintenance mode, verifying the CSRF token, and more.
The method signature for the HTTP kernel’s handle
method is quite simple: receive a Request
and return a Response
. Think of the Kernel as being a big black box that represents your entire application. Feed it HTTP requests and it will return HTTP responses.
Service Providers
One of the most important Kernel bootstrapping actions is loading the service providers for your application. All of the service providers for the application are configured in the config/app.php
configuration file’s providers
array. First, the register
method will be called on all providers, then, once all providers have been registered, the boot
method will be called.
Service providers are responsible for bootstrapping all of the framework’s various components, such as the database, queue, validation, and routing components. Since they bootstrap and configure every feature offered by the framework, service providers are the most important aspect of the entire Laravel bootstrap process.
Dispatch Request
Once the application has been bootstrapped and all service providers have been registered, the Request
will be handed off to the router for dispatching. The router will dispatch the request to a route or controller, as well as run any route specific middleware.
Focus On Service Providers
Service providers are truly the key to bootstrapping a Laravel application. The application instance is created, the service providers are registered, and the request is handed to the bootstrapped application. It’s really that simple!
Having a firm grasp of how a Laravel application is built and bootstrapped via service providers is very valuable. Your application’s default service providers are stored in the app/Providers
directory.
By default, the AppServiceProvider
is fairly empty. This provider is a great place to add your application’s own bootstrapping and service container bindings. For large applications, you may wish to create several service providers, each with a more granular type of bootstrapping.
Service Container
The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are “injected” into the class via the constructor or, in some cases, “setter” methods.
Since the repository is injected, we are able to easily swap it out with another implementation. We are also able to easily “mock”, or create a dummy implementation when testing our application.
Binding
Binding Basics
Almost all of your service container bindings will be registered within service providers.
However, there is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection.
Simple Bindings
Within a service provider, you always have access to the container via the $this->app property
. We can register a binding using the bind
method, passing the class or interface name that we wish to register along with a Closure
that returns an instance of the class:
$this->app->bind('HelpSpot\API', function ($app) {
return new \HelpSpot\API($app->make('HttpClient'));
});
Note that we receive the container itself as an argument to the resolver. We can then use the container to resolve sub-dependencies of the object we are building.
Binding A Singleton
The singleton
method binds a class or interface into the container that should only be resolved one time. Once a singleton binding is resolved, the same object instance will be returned on subsequent calls into the container:
$this->app->singleton('HelpSpot\API', function ($app) {
return new \HelpSpot\API($app->make('HttpClient'));
});
Binding Instances
You may also bind an existing object instance into the container using the instance
method. The given instance will always be returned on subsequent calls into the container:
$api = new \HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\API', $api);
Binding Interfaces To Implementations
A very powerful feature of the service container is its ability to bind an interface to a given implementation. For example, let’s assume we have an EventPusher
interface and a RedisEventPusher
implementation. Once we have coded our RedisEventPusher
implementation of this interface, we can register it with the service container like so:
$this->app->bind(
'App\Contracts\EventPusher',
'App\Services\RedisEventPusher'
);
This statement tells the container that it should inject the RedisEventPusher
when a class needs an implementation of EventPusher
. Now we can type-hint the EventPusher
interface in a constructor, or any other location where dependencies are injected by the service container:
use App\Contracts\EventPusher;
/**
* Create a new class instance.
*
* @param EventPusher $pusher
* @return void
*/
public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}
Contextual Binding
Sometimes you may have two classes that utilize the same interface, but you wish to inject different implementations into each class. For example, two controllers may depend on different implementations of the Illuminate\Contracts\Filesystem\Filesystem
contract. Laravel provides a simple, fluent interface for defining this behavior:
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\UploadController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
});
$this->app->when([VideoController::class, UploadController::class])
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
Binding Primitives
Sometimes you may have a class that receives some injected classes, but also needs an injected primitive value such as an integer. You may easily use contextual binding to inject any value your class may need:
$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName')
->give($value);
Sometimes a class may depend on an array of tagged instances. Using the giveTagged
method, you may easily inject all of the container bindings with that tag:
$this->app->when(ReportAggregator::class)
->needs('$reports')
->giveTagged('reports');
Binding Typed Variadics
Occasionally you may have a class that receives an array of typed objects using a variadic constructor argument:
class Firewall
{
protected $logger;
protected $filters;
public function __construct(Logger $logger, Filter ...$filters)
{
$this->logger = $logger;
$this->filters = $filters;
}
}
Using contextual binding, you may resolve this dependency by providing the give
method with a Closure that returns an array of resolved Filter
instances:
$this->app->when(Firewall::class)
->needs(Filter::class)
->give(function ($app) {
return [
$app->make(NullFilter::class),
$app->make(ProfanityFilter::class),
$app->make(TooLongFilter::class),
];
});
For convenience, you may also just provide an array of class names to be resolved by the container whenever Firewall
needs Filter
instances:
$this->app->when(Firewall::class)
->needs(Filter::class)
->give([
NullFilter::class,
ProfanityFilter::class,
TooLongFilter::class,
]);
Variadic Tag Dependencies
Sometimes a class may have a variadic dependency that is type-hinted as a given class (Report ...$reports)
. Using the needs
and giveTagged
methods, you may easily inject all of the container bindings with that tag for the given dependency:
$this->app->when(ReportAggregator::class)
->needs(Report::class)
->giveTagged('reports');
Tagging
Occasionally, you may need to resolve all of a certain “category” of binding. For example, perhaps you are building a report aggregator that receives an array of many different Report
interface implementations. After registering the Report
implementations, you can assign them a tag using the tag
method:
$this->app->bind('SpeedReport', function () {
//
});
$this->app->bind('MemoryReport', function () {
//
});
$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');
Once the services have been tagged, you may easily resolve them all via the tagged
method:
$this->app->bind('ReportAggregator', function ($app) {
return new ReportAggregator($app->tagged('reports'));
});
Extending Bindings
The extend
method allows the modification of resolved services. F
or example, when a service is resolved, you may run additional code to decorate or configure the service. The extend
method accepts a Closure, which should return the modified service, as its only argument. The Closure receives the service being resolved and the container instance:
$this->app->extend(Service::class, function ($service, $app) {
return new DecoratedService($service);
});
Resolving
The Make Method
You may use the make
method to resolve a class instance out of the container. The make
method accepts the name of the class or interface you wish to resolve:
$api = $this->app->make('HelpSpot\API');
If you are in a location of your code that does not have access to the $app
variable, you may use the global resolve
helper:
$api = resolve('HelpSpot\API');
If some of your class’ dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the makeWith
method:
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
Automatic Injection
Alternatively, and importantly, you may “type-hint” the dependency in the constructor of a class that is resolved by the container, including controllers, event listeners, middleware, and more. Additionally, you may type-hint dependencies in the handle
method of queued jobs. In practice, this is how most of your objects should be resolved by the container.
For example, you may type-hint a repository defined by your application in a controller’s constructor. The repository will automatically be resolved and injected into the class.
Container Events
The service container fires an event each time it resolves an object. You may listen to this event using the resolving
method:
$this->app->resolving(function ($object, $app) {
// Called when container resolves object of any type...
});
$this->app->resolving(\HelpSpot\API::class, function ($api, $app) {
// Called when container resolves objects of type "HelpSpot\API"...
});
As you can see, the object being resolved will be passed to the callback, allowing you to set any additional properties on the object before it is given to its consumer.
PSR-11
Laravel’s service container implements the PSR-11 interface. Therefore, you may type-hint the PSR-11 container interface to obtain an instance of the Laravel container:
use Psr\Container\ContainerInterface;
Route::get('/', function (ContainerInterface $container) {
$service = $container->get('Service');
//
});
An exception is thrown if the given identifier can’t be resolved. The exception will be an instance of Psr\Container\NotFoundExceptionInterface
if the identifier was never bound. If the identifier was bound but was unable to be resolved, an instance of Psr\Container\ContainerExceptionInterface
will be thrown.
Service Providers
Service providers are the central place of all Laravel application bootstrapping. Your own application, as well as all of Laravel’s core services are bootstrapped via service providers.
But, what do we mean by “bootstrapped”? In general, we mean registering things, including registering service container bindings, event listeners, middleware, and even routes. Service providers are the central place to configure your application.
If you open the config/app.php
file included with Laravel, you will see a providers
array. These are all of the service provider classes that will be loaded for your application. Note that many of these are “deferred” providers, meaning they will not be loaded on every request, but only when the services they provide are actually needed.
Writing Service Providers
All service providers extend the Illuminate\Support\ServiceProvider
class. Most service providers contain a register
and a boot
method. Within the register
method, you should only bind things into the service container.
You should never attempt to register any event listeners, routes, or any other piece of functionality within the register method.
The Artisan CLI can generate a new provider via the make:provider
command:
php artisan make:provider RiakServiceProvider
The Register Method
Within the register
method, you should only bind things into the service container. You should never attempt to register any event listeners, routes, or any other piece of functionality within the register
method. Otherwise, you may accidentally use a service that is provided by a service provider which has not loaded yet.
Within any of your service provider methods, you always have access to the
$app
property which provides access to the service container.
If your service provider registers many simple bindings, you may wish to use the
bindings
andsingletons
properties instead of manually registering each container binding. When the service provider is loaded by the framework, it will automatically check for these properties and register their bindings.
<?php
namespace App\Providers;
use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\ServerToolsProvider;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* All of the container bindings that should be registered.
*
* @var array
*/
public $bindings = [
ServerProvider::class => DigitalOceanServerProvider::class,
];
/**
* All of the container singletons that should be registered.
*
* @var array
*/
public $singletons = [
DowntimeNotifier::class => PingdomDowntimeNotifier::class,
ServerToolsProvider::class => ServerToolsProvider::class,
];
}
The Boot Method
So, what if we need to register a view composer within our service provider? This should be done within the boot
method.
This method is called after all other service providers have been registered, meaning you have access to all other services that have been registered by the framework.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
view()->composer('view', function () {
//
});
}
}
You may type-hint dependencies for your service provider’s
boot
method. The service container will automatically inject any dependencies you need.
use Illuminate\Contracts\Routing\ResponseFactory;
public function boot(ResponseFactory $response)
{
$response->macro('caps', function ($value) {
//
});
}
Registering Providers
All service providers are registered in the config/app.php
configuration file. This file contains a providers
array where you can list the class names of your service providers. By default, a set of Laravel core service providers are listed in this array. These providers bootstrap the core Laravel components, such as the mailer, queue, cache, and others.
Deferred Providers
If your provider is only registering bindings in the service container, you may choose to defer its registration until one of the registered bindings is actually needed. Deferring the loading of such a provider will improve the performance of your application, since it is not loaded from the filesystem on every request.
Laravel compiles and stores a list of all of the services supplied by deferred service providers, along with the name of its service provider class. Then, only when you attempt to resolve one of these services does Laravel load the service provider.
To defer the loading of a provider, implement the \Illuminate\Contracts\Support\DeferrableProvider
interface and define a provides
method. The provides
method should return the service container bindings registered by the provider.
<?php
namespace App\Providers;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use Riak\Connection;
class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection($app['config']['riak']);
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [Connection::class];
}
}
Facades
Facades provide a “static” interface to classes that are available in the application’s service container. Laravel ships with many facades which provide access to almost all of Laravel’s features.
Laravel facades serve as “static proxies” to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.
All of Laravel’s facades are defined in the Illuminate\Support\Facades
namespace. So, we can easily access a facade like so:
Laravel’s facades, and any custom facades you create, will extend the base Illuminate\Support\Facades\Facade
class.
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
If we look at that Illuminate\Support\Facades\Cache
class, you’ll see that there is no static method get
:
Some care must be taken when using facades. The primary danger of facades is class scope creep. Since facades are so easy to use and do not require injection, it can be easy to let your classes continue to grow and use many facades in a single class. Using dependency injection, this potential is mitigated by the visual feedback a large constructor gives you that your class is growing too large. So, when using facades, pay special attention to the size of your class so that its scope of responsibility stays narrow.
Typically, it would not be possible to mock or stub a truly static class method. However, since facades use dynamic methods to proxy method calls to objects resolved from the service container, we actually can test facades just as we would test an injected class instance. For example, given the following route:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
We can write the following test to verify that the Cache::get method was called with the argument we expected:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
In addition to facades, Laravel includes a variety of “helper” functions. Many of these helper functions perform the same function as a corresponding facade. For example, this facade call and helper call are equivalent:
return View::make('profile');
return view('profile');
There is absolutely no practical difference between facades and helper functions. When using helper functions, you may still test them exactly as you would the corresponding facade. For example, given the following route:
Route::get('/cache', function () {
return cache('key');
});
Under the hood, the cache
helper is going to call the get
method on the class underlying the Cache
facade. So, we can write the following test:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Real-Time Facades
Using real-time facades, you may treat any class in your application as if it were a facade.
For example, let’s assume our Podcast
model has a publish
method. However, in order to publish the podcast, we need to inject a Publisher
instance:
<?php
namespace App;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*
* @param Publisher $publisher
* @return void
*/
public function publish(Publisher $publisher)
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
To generate a real-time facade, prefix the namespace of the imported class with Facades
:
<?php
namespace App;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*
* @return void
*/
public function publish()
{
$this->update(['publishing' => now()]);
Publisher::publish($this);
}
}
When the real-time facade is used, the publisher implementation will be resolved out of the service container using the portion of the interface or class name that appears after the Facades
.
When testing, we can use Laravel’s built-in facade testing helpers to mock this method call.
<?php
namespace Tests\Feature;
use App\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PodcastTest extends TestCase
{
use RefreshDatabase;
/**
* A test example.
*
* @return void
*/
public function test_podcast_can_be_published()
{
$podcast = factory(Podcast::class)->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}
Contracts
Laravel’s Contracts are a set of interfaces that define the core services provided by the framework. For example, a Illuminate\Contracts\Queue\Queue
contract defines the methods needed for queueing jobs, while the Illuminate\Contracts\Mail\Mailer
contract defines the methods needed for sending e-mail.
Each contract has a corresponding implementation provided by the framework
All of the Laravel contracts live in their own GitHub repository.
Contracts Vs. Facades and When To Use Contracts
Laravel’s facades and helper functions provide a simple way of utilizing Laravel’s services without needing to type-hint and resolve contracts out of the service container. In most cases, each facade has an equivalent contract.
Unlike facades, which do not require you to require them in your class’ constructor, contracts allow you to define explicit dependencies for your classes. Some developers prefer to explicitly define their dependencies in this way and therefore prefer to use contracts, while other developers enjoy the convenience of facades.
Let’s distill the reasons for using interfaces to the following headings: loose coupling and simplicity.
Loose Coupling
First, let’s review some code that is tightly coupled to a cache implementation. Consider the following
<?php
namespace App\Orders;
class Repository
{
/**
* The cache instance.
*/
protected $cache;
/**
* Create a new repository instance.
*
* @param \SomePackage\Cache\Memcached $cache
* @return void
*/
public function __construct(\SomePackage\Cache\Memcached $cache)
{
$this->cache = $cache;
}
/**
* Retrieve an Order by ID.
*
* @param int $id
* @return Order
*/
public function find($id)
{
if ($this->cache->has($id)) {
//
}
}
}
In this class, the code is tightly coupled to a given cache implementation. It is tightly coupled because we are depending on a concrete Cache class from a package vendor. If the API of that package changes our code must change as well.
Likewise, if we want to replace our underlying cache technology (Memcached) with another technology (Redis), we again will have to modify our repository. Our repository should not have so much knowledge regarding who is providing them data or how they are providing it.
Instead of this approach, we can improve our code by depending on a simple, vendor agnostic interface:
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository
{
/**
* The cache instance.
*/
protected $cache;
/**
* Create a new repository instance.
*
* @param Cache $cache
* @return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
Now the code is not coupled to any specific vendor, or even Laravel. Since the contracts package contains no implementation and no dependencies, you may easily write an alternative implementation of any given contract, allowing you to replace your cache implementation without modifying any of your cache consuming code.
Simplicity
When all of Laravel’s services are neatly defined within simple interfaces, it is very easy to determine the functionality offered by a given service. The contracts serve as succinct documentation to the framework’s features.
In addition, when you depend on simple interfaces, your code is easier to understand and maintain. Rather than tracking down which methods are available to you within a large, complicated class, you can refer to a simple, clean interface.
How To Use Contracts
So, how do you get an implementation of a contract? It’s actually quite simple.
Many types of classes in Laravel are resolved through the service container, including controllers, event listeners, middleware, queued jobs, and even route Closures. So, to get an implementation of a contract, you can just “type-hint” the interface in the constructor of the class being resolved.
For example, take a look at this event listener:
<?php
namespace App\Listeners;
use App\Events\OrderWasPlaced;
use App\User;
use Illuminate\Contracts\Redis\Factory;
class CacheOrderInformation
{
/**
* The Redis factory implementation.
*/
protected $redis;
/**
* Create a new event handler instance.
*
* @param Factory $redis
* @return void
*/
public function __construct(Factory $redis)
{
$this->redis = $redis;
}
/**
* Handle the event.
*
* @param OrderWasPlaced $event
* @return void
*/
public function handle(OrderWasPlaced $event)
{
//
}
}
When the event listener is resolved, the service container will read the type-hints on the constructor of the class, and inject the appropriate value.