This one’s a quick article to transition us from Composer back into Laravel. Also, in a rare bit of timely news, the Laravel core team just released Laravel 5.0. This release features some really big new features (a task runner!), promotion of stuff that’s lived under the hood for a while (Application object middlewares!), and a new application architecture. We’re going to finish up this series from a Laravel 4.2 point of view, but then come back with one final article that covers the autoloading changes in Laravel 5.
Our topic today, however, is how Laravel’s four autoloaders are registered into the system. Let’s get to it.
Laravel’s Four Autoloaders
If you run the following code from a Laravel bootstrapped environment
var_dump(spl_autoload_functions());
PHP will show us the four callbacks registered as autoloaders
array (size=4)
0 =>
array (size=2)
0 =>
object(Illuminate\Foundation\AliasLoader)[46]
protected 'aliases' =>
array (size=38)
...
protected 'registered' => boolean true
1 => string 'load' (length=4)
1 =>
array (size=2)
0 =>
object(Composer\Autoload\ClassLoader)[1]
private 'prefixLengthsPsr4' =>
array (size=1)
...
private 'prefixDirsPsr4' =>
array (size=1)
...
private 'fallbackDirsPsr4' =>
array (size=0)
...
private 'prefixesPsr0' =>
array (size=11)
...
private 'fallbackDirsPsr0' =>
array (size=0)
...
private 'useIncludePath' => boolean false
private 'classMap' =>
array (size=2107)
...
1 => string 'loadClass' (length=9)
2 =>
array (size=2)
0 => string 'Swift' (length=5)
1 => string 'autoload' (length=8)
3 =>
array (size=2)
0 => string 'Illuminate\Support\ClassLoader' (length=30)
1 => string 'load' (length=4)
Or, more succinctly,
Illuminate\Foundation\AliasLoader::load
Composer\Autoload\ClassLoader::loadClass
Swift::autoload
Illuminate\Support\ClassLoader::load
What PHP can’t tell us is where Laravel registered each of these. Registration order matters for autoloaders — the first autoloader on the stack gets first crack at trying to load a class file.
It’s rarely necessary to know where an autoloader enters the system, but it can help you when you’re dealing with an unfamiliar system.
Laravel Bootstrap
Bootstrapping is a generic systems programming term that means, roughly,
Us system developers are going to run all our code that sets up the system for you client programmers
A framework’s bootstrapping is often complicated to follow, and there’s many different layers of magnification you could examine it with.
Fortunately for us, today we can look at Laravel’s bootstrapping from a very high level. We only need to concern outselves with four files
public/index.php
bootstrap/autoload.php
bootstrap/start.php
vendor/laravel/framework/src/Illuminate/Foundation/start.php
Notice the indentation pattern? That represents how Laravel require
s these files into the system.
That is, your webserver routes all incoming HTTP requests to index.php
. Then, index.php
require
s in bootstrap/autoload.php
. Then, index.php
requires in bootstrap/start.php
. Before bootstrap/start.php
is done loading, it will have included in vendor/laravel/framework/src/Illuminate/Foundation/start.php
. There’s lots more going on behind the scenes, but today we’re only interested in these four files.
Registering the Composer Autoloader
We’ll start with the Composer autoloader. Laravel require
s in the Composer autoloader file in bootstrap/start.php
#File: bootstrap/start.php
require __DIR__.'/../vendor/autoload.php';
As discussed previously, this autoloader includes some generated code
#File: vendor/autoload.php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit0bb5663af67b1bf0d39c096213f1a0cf::getLoader();
and the getLoader
method
#File: vendor/composer/autoload_real.php
public static function getLoader()
{
///
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
//...
$loader->register(true);
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $file) {
composerRequire0bb5663af67b1bf0d39c096213f1a0cf($file);
}
return $loader;
}
calls the register
method
#File: vendor/composer/ClassLoader.php
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
and the register
method is where Composer registers the autoloader with the spl_autoload_register
function.
Loading the Swift Autoloader
Next up is the SwiftMailer autoloader. You’ll remember SwiftMailer’s composer.json
file contains a files
autoloader
#File: vendor/swiftmailer/swiftmailer/composer.json
"autoload": {
"files": ["lib/swift_required.php"]
},
If we jump back to Composer’s getLoader
method
#File: vendor/composer/autoload_real.php
public static function getLoader()
{
///
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
//...
$loader->register(true);
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $file) {
composerRequire0bb5663af67b1bf0d39c096213f1a0cf($file);
}
return $loader;
}
//...
function composerRequire0bb5663af67b1bf0d39c096213f1a0cf($file)
{
require $file;
}
You’ll remember that Composer require
s in these files
paths in the foreach
loop that follows the register
method. If we take a look at swift_required.php
#File: vendor/swiftmailer/swiftmailer/lib/swift_required.php
//...
Swift::registerAutoload('_swiftmailer_init');
we’ll see a call to the static registerAutoload
method,
#File: swiftmailer/swiftmailer/lib/classes/Swift.php
public static function registerAutoload($callable = null)
{
if (null !== $callable) {
self::$inits[] = $callable;
}
spl_autoload_register(array('Swift', 'autoload'));
}
which is where SwiftMailer registers its autoloader with the spl_autoload_register
function.
The main takeaway here is to notice that loading the Composer autoloader may, in turn, register additional autoloaders. Depending on your Composer package list, your Laravel project may have more than 4 autoloaders.
Laravel Autoloader
Once the Composer files
autoloader is finished, we return to the bootstrap/autoload.php
file, and see the following
#File: bootstrap/start.php
require __DIR__.'/../vendor/autoload.php';
//...
Illuminate\Support\ClassLoader::register();
That is, Laravel calls the static register
method on the Illuminate\Support\ClassLoader
class, and the register
method
#File: vendor/classpreloader/classpreloader/src/ClassPreloader/ClassLoader.php
public function register()
{
spl_autoload_register(array($this, 'loadClass'), true, true);
}
is where Laravel registers its autoloader.
Laravel Alias Autoloader
Finally, when PHP finishes with the bootstrap/autoload.php
file, it returns to its standard bootstrap. When it gets to the Laravel package start.php
file you’ll see
#File: vendor/laravel/framework/src/Illuminate/Foundation/start.php
$config = $app['config']['app'];
//...
$aliases = $config['aliases'];
AliasLoader::getInstance($aliases)->register();
Laravel calls the register
method on an instantiated AliasLoader
object.
#File: vendor/laravel/framework/src/Illuminate/Foundation/AliasLoader.php
public function register()
{
if ( ! $this->registered)
{
$this->prependToLoaderStack();
$this->registered = true;
}
}
which in turn calls the prependToLoaderStack
method
#File: vendor/laravel/framework/src/Illuminate/Foundation/AliasLoader.php
protected function prependToLoaderStack()
{
spl_autoload_register(array($this, 'load'), true, true);
}
which is where Laravel adds the alias loader with spl_autoload_register
.
Autoloader Order
That covers how and where Laravel actually registers its autoloaders. There is, however, one last thing to cover.
Above we described, in order, the registering of each autoloader.
Composer\Autoload\ClassLoader::loadClass
Swift::autoload
Illuminate\Support\ClassLoader::load
Illuminate\Foundation\AliasLoader::load
However, if we use spl_autoload_functions()
to look at the registered autoloader callbacks we see a different order
Illuminate\Foundation\AliasLoader::load
Composer\Autoload\ClassLoader::loadClass
Swift::autoload
Illuminate\Support\ClassLoader::load
Why the discrepancy? By default, the spl_autoload_register
function adds each autoloader as though it were pushing the items onto the end of an array/stack. However, if spl_autoload_register
‘s third argument is true
spl_autoload_register(array($this, 'load'), true, true);
spl_autoload_register(array($this, 'loadClass'), true, true);
then PHP will prepend the autoloader. In other words, when you use this parameter you’re telling PHP
Hey, use my autoloader before all those other autoloaders you already know about.
The PHP core team added the prepend parameter in PHP 5.3.
Both the Composer autoloader and Laravel alias autoloader use prepend, and it’s why they’re the first two autoloaders on the stack even though they’re not the first two autoloaders registered.
This order will become important next time, when we (hopefully!) finish up our Laravel 4.2 autoloader series with a look at how the autoloaders interact when autoloading Laravel’s six MVC class types.