Categories


Archives


Recent Posts


Categories


Registering Laravel’s Autoloaders

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

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,

  1. Illuminate\Foundation\AliasLoader::load
  2. Composer\Autoload\ClassLoader::loadClass
  3. Swift::autoload
  4. 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 requires these files into the system.

That is, your webserver routes all incoming HTTP requests to index.php. Then, index.php requires 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 requires 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 requires 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.

  1. Composer\Autoload\ClassLoader::loadClass
  2. Swift::autoload
  3. Illuminate\Support\ClassLoader::load
  4. Illuminate\Foundation\AliasLoader::load

However, if we use spl_autoload_functions() to look at the registered autoloader callbacks we see a different order

  1. Illuminate\Foundation\AliasLoader::load
  2. Composer\Autoload\ClassLoader::loadClass
  3. Swift::autoload
  4. 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.

Originally published February 7, 2015
Series Navigation<< Composer Autoloader Features: Part 2Laravel’s Framework Autoloader >>