In some cases, Laravel needs to return the same object instance every time it is requested. For this, it uses the Singleton pattern in multiple places.
In the case of authentication, you want to avoid fetching the user from the database or any other source every time you need to access the authenticated user.
Instead, you create a Singleton instance and store the authenticated user in it:
Illuminate/Auth/AuthServiceProvider.php
protected function registerAuthenticator(){ $this->app->singleton('auth', fn ($app) => new AuthManager($app)); $this->app->singleton('auth.driver', fn ($app) => $app['auth']->guard());}
This registers the auth
(Auth Facade) and auth.driver
services as singletons. Inside, it creates new instances of AuthManager
and Guard
, respectively.
Inside the AuthManager
class, it will create a User resolver that will be used to resolve the authenticated user:
Illuminate/Auth/AuthManager.php
/** * Create a new Auth manager instance. * * @param \Illuminate\Contracts\Foundation\Application $app * @return void */public function __construct($app){ $this->app = $app; $this->userResolver = fn ($guard = null) => $this->guard($guard)->user();}
And this then allows us to retrieve the authenticated user from the auth
Singleton:
Auth::user()auth()->user()
Which will always return the same instance of the authenticated user, even if the data in the database changes.
This allows us to be sure that the user has the correct information for that specific request.
Note: Singletons are per request, so they are not shared between requests. Reloading the page will create a new instance of the Singleton.
Another example we can find is the mailer
singleton (Mail facade):
Illuminate/Mail/MailServiceProvider.php
/** * Register the Illuminate mailer instance. * * @return void */protected function registerIlluminateMailer(){ $this->app->singleton('mail.manager', function ($app) { return new MailManager($app); }); $this->app->bind('mailer', function ($app) { return $app->make('mail.manager')->mailer(); });}
This registers the mail.manager
and mailer
services as singletons. And if you look at the mailer
Singleton creation, we can see that it calls a function:
Illuminate/Mail/MailManager.php
/** * Get a mailer instance by name. * * @param string|null $name * @return \Illuminate\Contracts\Mail\Mailer */public function mailer($name = null){ $name = $name ?: $this->getDefaultDriver(); return $this->mailers[$name] = $this->get($name);}
This will create a new instance of the configured mailer and store it as a Singleton for later usage. This saves us from creating a new instance of the mailer whenever we need to send an email.
Then we can look at another typical Singleton - the cache
:
Illuminate/Cache/CacheServiceProvider.php
/** * Register the service provider. * * @return void */public function register(){ $this->app->singleton('cache', function ($app) { return new CacheManager($app); }); $this->app->singleton('cache.store', function ($app) { return $app['cache']->driver(); }); // ...}
This registers the cache
and cache.store
services as singletons. And if you look at the cache.store
Singleton creation, we can see that it calls a function:
Illuminate/Cache/CacheManager.php
/** * Get a cache store instance by name, wrapped in a repository. * * @param string|null $name * @return \Illuminate\Contracts\Cache\Repository */public function store($name = null){ $name = $name ?: $this->getDefaultDriver(); return $this->stores[$name] ??= $this->resolve($name);}
All the cache configuration is now stored in a Singleton instance, and we can access it by calling:
Cache::put()Cache::get()
To retrieve a Singleton instance, you can use the app()
helper function or the resolve()
method:
app('auth')app('mailer')app('cache')
But at the same time, Laravel uses Facades to access these Singletons:
Auth::user()Mail::send()Cache::get()
And this happens behind the scenes with a clever Facade implementation. Most of the time, they look like this:
Illuminate/Auth/AuthManager.php
class Cache extends Facade{ /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'cache'; }}
Once called, Laravel knows you want to access the cache
Singleton and will return the instance of the CacheManager
class.
If you are interested in how this technically works, you can check one of stackoverflow questions or dive into the Laravel source code!