So far in this series, whenever we’ve wanted to use a service, we’ve fetched Laravel’s application/service-container object with the global app
function
$app = app();
and then used either make
or ArrayAccess
syntax to fetch a service
$service = $app['db'];
$service = $app->make('db');
While this is a perfectly valid use of the framework, it’s not ideal for all situations and all developers. This is especially true for people just getting into Laravel and PHP programming.
PHP’s ArrayAccess
feature, while useful, is advanced functionality that can confuse programmers just learning PHP. Most meta-programming falls under this category, as most new programmers start by assuming their programming language follows a strict set of rules. Changing the rules under them tends to confuse and frustrate people until they make the “programming the programming language” connection.
Calling make
is also problematic. It’s not that calling make
itself is incorrect, it’s that there are numerous other methods on the application object that can instantiate or return an object/service. Here’s some off the top of my head.
$app = app();
$object = $app->make(...);
$object = $app->register(...);
$object = $app->getRegistered(...);
$object = $app->resolveProviderClass(...);
$object = $app->extend(...);
$object = $app->db;
$object = $app->offsetGet(...);
...
There’s also methods like $app->singleton
and $app->instance
which might look like they’re for instantiating objects and services, but aren’t (or aren’t directly)
Despite their public
access level, many of these methods are meant to be used by the application object’s internal logic, (or programmers familiar with that logic). One of the disadvantages of combining the global application object and the service container object is it becomes a lot less obvious which methods client programmers use to interact with the service container, and which methods are meant for internal framework use.
This is where Laravel facades enter the picture.
Laravel Facades
Let’s jump in with some code. Calling a service method with a Laravel facade looks like this
DB::select('SELECT * FROM table');
The above is equivalent to
$app = app();
$app->make('db')->select('SELECT * FROM table');
How it’s equivalent is not a straightforward story, but it’s the story we’re going to tell today.
First, let’s define a facade’s job in Laravel
A Laravel facade provides access to a shared/singleton service without requiring the end client-programmer to use the application container.
That is, the point of facades are to provide access to services without the container. A Laravel application is still structured around using services, but for many applications a client programmer may be completely unaware of the application service container, and will only access services through facades.
A facade looks like a static method call (and ultimately, behind the scenes, is a static method call). A less experienced PHP programmer might look at
DB::select('SELECT * FROM table');
and assume there’s a class in the global namespace named DB
, and that this class has a static method named select
. However, you can search your Laravel code base up, down, left, and right, and you won’t find a global class DB
anywhere in the system.
Facades are a classic example of meta-programming. Roughly defined, meta-programming is changing a language/platform’s behavior to build new features into the language. Meta-programming allows system developers to come up with new language constructs and ideas without needing to change the underlying language implementation. Meta-programming knowledge is an important part of any modern day developer’s toolkit, but it’s like ginger in cooking. A little goes a long way.
Today we’re going to unravel how Laravel facades work. One important warning: If you’re a classically trained object oriented programmer, Laravel’s facades are not the Gang of Four’s facades. We’ll get to that eventually, but for now assume you’re learning a whole new design pattern.
Facade Classes are Aliases
Let’s consider our previous facade call
DB::select('SELECT * FROM table');
This looks like a call to the static method select
on the class DB
. Earlier we said Laravel has no class DB
defined. Lets use the Reflection API to figure out where PHP thinks this class is. As in previous articles, we’ll drop our code in a testbed
route.
Route::get('testbed', function(){
$r = new ReflectionClass('DB');
var_dump(
$r->getFilename()
);
var_dump(
$r->getName()
);
});
Above we’ve used the Reflection API to lookup both the class’s definition file, and the class’s name. If you run the above by loading the route testbed
in your browser
http://laravel.example.com/index.php/testbed
You’ll see the following
string '/path/to/laravel/vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php' (length=115)
string 'Illuminate\Support\Facades\DB' (length=29)
For some reason, PHP thinks the class’s name is Illuminate\Support\Facades\DB
. If you’ve read last week’s article on PHP’s class aliases, you probably know what’s going on. The class DB
is actually a class_alias
. This alias allows a service developer to provide a short, easy to remember-and-type facade name for their service.
It’s beyond the scope of this article, but if you’re curious, these aliases are setup automatically by the Laravel autoloader
Illuminate\Foundation\AliasLoader::alias()
This autoloader class reads through the alises defined in app/config/app.php
. You can see the DB
alias here
#File: app/config/app.php
'aliases' => array(
//...
'DB' => 'Illuminate\Support\Facades\DB',
/...
By using an autoloader to define an alias, the Laravel core team ensures no aliases are loaded until they’re actually needed.
Facade Classes
Now that we know the real class Laravel uses when we type DB
we can go look for the select
method. However, if we look at the Illuminate/Support/Facades/DB
class, we’ll be disappointed.
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php
class DB extends Facade {
protected static function getFacadeAccessor() {
return 'db';
}
}
There’s no select
method defined. You might think there’s a select
method in the parent Illuminate\Support\Facades\Facade
class (defined in vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
), but it’s not there either. What we do have is a getFacadeAccessor
method that returns a string — db
.
Unless you’re already familiar with some of PHP’s meta-programming features, this entire state of affairs is pretty confusing and confounding. However, even if you don’t know about things like __callStatic
, think back to how we defined a facade’s responsibilities
A Laravel facade provides access to a shared/singleton service without requiring the end client-programmer to use the application container.
A facade class has one responsibility — and that’s to define a getFacadeAccessor method, and have this method return a service identifier. In the DB
facade above that identifier is the string db
. This means a call to
DB::select(...)
Is equivalent to each of the following
app()->make('db')->select(...)
app()->db->select(...)
app()['db']->select(...)
If the accessor had been written
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php
protected static function getFacadeAccessor() {
return 'my_crazy_service';
}
Then the static call DB::select
would have been equivalent to
app()->make('my_crazy_service')->select(...)
app()->my_crazy_service->select(...)
app()['my_crazy_service']->select(...)
Of course, you’re probably wondering how a static call is somehow transformed into a method call to a service. To understand that, we’ll need to look more deeply into the definition of the Facade
base class — the abstract class which all Laravel facades inherit from.
Facades and Magic Methods
If you read last week’s article, you already know about __callStatic
. In brief, if you call a method that doesn’t exist on a PHP object, PHP will issue a fatal error unless that PHP object has a magic __call
or __callStatic
method defined. So, when a programmer calls
DB:select(...)
PHP says
Hmm, no
select
method is defined onDB
or its parent classes, I better crash with a fata— wait, there’s a__callStatic
on the parentFacade
class, I’ll call that instead.
Let’s take a look at __callStatic
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
switch (count($args))
{
case 0:
return $instance->$method();
#... snipped for brevity ...#
default:
return call_user_func_array(array($instance, $method), $args);
}
}
What __callStatic
does is get an instance of an object with the getFacadeRoot
method, and then pass on the static method name (passed in as $method
) as a call to a regular method on the object.
It’s worth repeating that step by step. First, we have a call to a static method
DB::select('SELECT * FROM table');
PHP can’t find a select
method to call, so instead PHP calls the __callStatic
method, passing in the method name and arguments as parameters.
DB::__callStatic('select', array('SELECT * FROM table'));
In __callStatic
, the Laravel framework code fetches a service object with a call to getFacadeRoot
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot('select', array('SELECT * FROM table'));
//...
}
Finally, __callStatic
passes the method call on to the object in $instance
using PHP’s dynamic method calling feature, and returns the value
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
return $instance->select('SELECT * FROM table');
All of this explains the mystery of how a static looking method call is actually a call to an instance method on a service object, but it doesn’t explain how the facade class knows which service to instantiate.
This is where getFacadeAccessor
comes into the picture.
The Facade Accessor
If we take a look at the method call that fetches the service object
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
$instance = static::getFacadeRoot('select', array('SELECT * FROM
We see the special static
keyword. The static
keyword is similar to self
. Both are a way to refer to the current class without without using the full classname. You can read more in the PHP manual’s Late Static Binding section, but for now all you need to know is getFacadeRoot
is defined on the same base Facade
class. (Also, the keyword static
is something completely different if used in a function context, so Drupal developers beware)
If we take a look at the getFacadeRoot
method, we’ll see the following
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(
static::getFacadeAccessor()
);
}
Ah ha! Here’s that getFacadeAccesssor
method we saw defined in the specific Facade
class. You’ll remember that looks like this
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php
protected static function getFacadeAccessor() {
return 'db';
}
So, putting our x-ray variable specs back on, for our method call, getFacadeRoot
actually looks like
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
return static::resolveFacadeInstance(
'db'
);
This means our next stop is the resolveFacadeInstance
method
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) return $name;
if (isset(static::$resolvedInstance[$name]))
{
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
This short method is the final key to understanding a facade. It’s the last line that’s most important
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
return static::$resolvedInstance[$name] = static::$app[$name];
Here the facade code refers to the static property $app
. The static property $app
contains a reference to the application container object. In other words, the above could be rewritten to
//get an instance of the global appliaction objcet
$app = app();
//set a copy of the service we fetch on the $resolvedInstance property
static::$resolvedInstance[$name] = $app['db'];
//return the instance
return static::$resolvedInstance[$name];
The Facade
is, ultimately, using the same code to fetch a service reference as a normal Laravel developer would. There’s one small wrinkle to be aware of, and that’s the conditional
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
if (isset(static::$resolvedInstance[$name]))
{
return static::$resolvedInstance[$name];
}
If the facade has already fetched (or “resolved”) an instance of the service, it will return that instance. You’ll remember from our definition
A Laravel facade provides access to a shared/singleton service without requiring the end client-programmer to use the application container.
that a facade is meant to provide access to a shared/singleton service — well it turns out facades will work with unshared services as well, but because of the above conditional, theses unshared services will be treated as shared/singleton by the facade.
This might give an object system developer reason to pause, as it introduces a second level of “singleton” into the system. A facade might not catch a shared service that’s changed in the application object at runtime.
This (admittedly rare) edge case must have seemed like an acceptable tradeoff to the Laravel core team, and of all the complaints about facades this is a relatively minor one.
Facade Controversy
If you’ve spent anytime lurking in various programmer communities, you’ve probably gotten a whiff of the displeasure some programmers have for Laravel’s facades. Among these complaints
- Laravel uses the word facade, but the influential “Gang of Four” book Design Patterns defines a facade in a completely different way
- Facades end up creating a number of “God Objects” in the application, which in turn creates hard coded dependencies, which in turn makes writing unit tests difficult or impossible (which Laravel addresses by giving Facades test mocking abilities)
-
A general distrust of static method calls, dating back to the earliest days of object oriented programming
-
A willingness of some Laravel core team members to defend their decisions by pouring gasoline on the couch, setting it on fire, and spreading jello on the floor.
I can only speak for myself, but I’ve found it more useful to not have strong opinions about these sorts of things. It’s good to know and understand programming/computer science theory, and the story of how we got here. It’s also good to be able to drop yourself into the mindset of a programmer who does have strong opinions, and be able to talk coherently about your own decisions, but ultimately it’s a lot easier to swim with the flow of a framework’s design. The ability to work with multiple programming systems, warts and all, is the most important skill a 21st century developer needs.
That said, I have my own personal list of gripes about facades, which we’ll cover next time in the facade troubleshooting article.