Categories


Archives


Recent Posts


Categories


Composer Autoloader Features: Part 1

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.

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

Today we’re going to veer slightly away from Laravel, and take a look at Composer’s autoloading features. While Composer bills itself as a “Dependency Manager”, its real strength is that it gives PHP developers a standard environment to build their applications in.

That is, if you want to develop a PHP program that uses third party components, all you need to do is

  1. Install Composer

  2. Run composer require ..., composer install ..., composer update ...

  3. Include or require Composer’s autoloader in your program

For developers coming from other platforms, platforms where this sort of setup is handled by the language (Ruby/Python modules, C header files and linking, etc.), this may seem like a minor thing, but until Composer came along writing a non-trivial program in PHP meant learning each third party library’s autoloader system, include/require hell, or figuring out what sort of compromises the previous project developers made with regards to the same.

It’s not that Composer prevents third party libraries from creating the autoload hell of the past — it’s that Composer offers an autoloader system that’s good enough, and there’s little reason not to adopt it for greenfield development projects.

All that said — there’s a lot of legacy code out there, and adoption of the latest Composer and PSR autoloading standards is uneven. Add to that the fact that Composer development has been the red hot burning center of the PHP autoloading debate, and there’s plenty of misleading legacy debris lying around. As a PHP developer, you’ll often find yourself in situations where autoloader interaction is the root cause of a bug in your build system or framework of choice.

Understanding what Composer’s autoloader does is an important bit of knowledge to keep in your toolkit, whether you’re a developer interested in contributing to Composer, or just someone trying to get a job done using Composer managed code.

Composer’s Four Autoloaders and Code Generation

The first thing you’ll need to know about Composer autoloading is there’s actually four different autoloader methodologies. They are

There’s also two different modalities to consider with the Composer autoloader. First, there’s using the Composer’s autoloader features as a package developer. Second, there’s using the autoloader features in a specific project not meant for redistribution. This second scenario is often called the “root” package.

Put another way, the Doctrine core team has a composer.json file for Doctrine, which includes configuration for autoloading Doctrine classes. However, you, as a developer building a business application using Laravel also have your own composer.json file, which you can configure to take advantage of Composer’s autoloader features for your own code.

While the syntax is identical for these two modalities, there’s differences to the paths generated. The first scenario needs to load from a package’s vendor/namespace/package-name folder, and the second needs to load from the root project folder.

Composer’s autoloader also relies heavily on code generation. Rather than search the entire directory tree for a class file every time, Composer auto-generates include files that contain all the information needed to include a class definition file. These files are mostly located and generated in the Composer vendor folder,

vendor/composer/autoload_classmap.php
vendor/composer/autoload_files.php
vendor/composer/autoload_namespaces.php
vendor/composer/autoload_psr4.php
vendor/composer/autoload_real.php
vendor/composer/include_paths.php

with the root autoload.php file located in the vendor folder

vendor/autoload.php

Many developers are surprised to learn Composer generates autoload files — that’s because this usually happens behind the scenes. When you add a new package to your project or update a version, Composer regenerates these files behind the scenes. However, you can (are are sometimes required) to rebuild the generated files by running the

$ composer dumpautoload

command. We’ll talk more about dumpautoloader later.

Using the Composer Autoloader

If you want to use Composer’s autoloader in your project, all you need to do is include the base Composer autoloader file.

include 'vendor/autoload.php';
#require 'vendor/autoload.php';
#include_once 'vendor/autoload.php'
#require_once 'vendor/autoload.php'

If you’re using a framework like Symfony, Zend, or Laravel, the framework may handle this for you. If not, you’ll need to use include, require, include_once, or require_once to load this file into your program.

If you take a look at this file’s content, you’ll see something like this

#File: vendor/autoload.php
// autoload.php @generated by Composer

require_once __DIR__ . '/composer' . '/autoload_real.php';

return ComposerAutoloaderInit1d454b6a6f83c27dc0634c7142f32df1::getLoader();

The first two lines are pretty self explanatory

#File: vendor/autoload.php    
// autoload.php @generated by Composer

require_once __DIR__ . '/composer' . '/autoload_real.php';

The comment notifies anyone looking at this file that it’s an auto-generated file. The implication here is “don’t edit this file and expect your changes to stick”. The second line requires in the generated autoload_real.php file.

The third line is a little weird —

#File: vendor/autoload.php    
return ComposerAutoloaderInit1d454b6a6f83c27dc0634c7142f32df1::getLoader();

This line calla the static getLoader method on the the ComposerAutoloaderInit1d454b6a6f83c27dc0634c7142f32df1 class. Why the goofy class name? Because this is a generated class file — you’ll find its definition in the autoload_real.php file

#File: vendor/composer/autoload_real.php
// autoload_real.php @generated by Composer

class ComposerAutoloaderInit1d454b6a6f83c27dc0634c7142f32df1
{
    //...
}

Whenever Composer generates a class file, it appends a unique identifying hash string to the end of the name (1d454b6a6f83c27dc0634c7142f32df1 above). This helps Composer developers when they’re working with the autoloader code, and also has the side benefit of making sure no library developers try calling these methods on their own (since the hash isn’t easily guessable). In some ways, a generated class file with a per-generation unique name is the ultimate private method.

The getLoader method in this class is where Composer instantiates its autoloader. We’ll talk more about this later, but for now let’s move on to Composer’s actual autoloader implementations.

Autoloader Include Files

We’re going to start with the simplest autoloader type in Composer, the file autoloader.

The file autoloader isn’t, strictly speaking, an autoloader. Instead, it’s intended as a legacy mechanism for libraries and frameworks with their own autoloading functionality that can’t (or won’t) be converted into a classmap or PSR autoloader.

To take advantage of the files autoloader, simply add an array of file names to your composer.json.

#File: composer.json    
{
    //...
    "autoload": {
        "files": ["path/to/file.php","path/to/second/file.php"]
    },
    //...        
}

When you run composer dumpautoload, (or when Composer runs it automatically during an update or install), Composer will look through all the packages composer.json files, as well as your main project composer.json files for the files autoloaders, and use them to generate the autoload_files.php file.

A generated autoload_files.php might look like this.

#File: vendor/composer/autoload_files.php
<?php    
// autoload_files.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    $vendorDir . '/ircmaxell/password-compat/lib/password.php',
    $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
    $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
    $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php',
);

As previously mentioned, in order to take advantage of Composer’s autoloader, a PHP program or framework needs to include/require the vendor/autoload.php file.

#File: vendor/autoload.php
<?php    
// autoload.php @generated by Composer

require_once __DIR__ . '/composer' . '/autoload_real.php';

return ComposerAutoloaderInit58ab3186241c31e404c0020afdf07dd0::getLoader();

If we take a look at the definition of getLoader, we’ll see the following near the end of the method

#File: vendor/composer/autoload_real.php
//...
public static function getLoader()
{
    //...
    $includeFiles = require __DIR__ . '/autoload_files.php';
    foreach ($includeFiles as $file) {
        composerRequire58ab3186241c31e404c0020afdf07dd0($file);
    }        
    //...
}    
//...
function composerRequire58ab3186241c31e404c0020afdf07dd0($file)
{
    require $file;
}

Here Composer loads its generated autoload_files.php files, and simply requires each file from the array.

While not exactly forward thinking, the files autoloaders are an important part of Composer’s adoption strategy. Convincing existing framework developers to convert years of code over to a PSR or classmap autoloader is going to be, in many cases, a non-starter. By creating this files mechanism, the Composer team gives existing library developers a clear path forward for using their autoloaders in a Composer based project. We’ll have more to say about this in a future article, when we look at the implementation of the SwiftMailer’s autoloader.

There are, of course, unintended consequences. When many system system and library developers discover the files autoloader mechanism, they see it as a perfect opportunity for some additional framework bootstrapping. Consider this section of Laravel’s composer.json

#File: composer.json    
"autoload": {
    //...
    "files": [
        "src/Illuminate/Support/helpers.php"
    ],
    //...
},

Here we see Laravel 4.2 uses the files autoloader to automatically include the src/Illuminate/Support/helpers.php file. The helpers.php file has nothing to do with class autoloading — it’s the file where Laravel defines its global helper functions (like array_add, e, link_to, etc.). Laravel isn’t alone in this — many packages overload the files autoloader for other purposes. At this point the Composer documentation embraces this use of the files autoloader, and it can be a little confusing to an experienced PHP developer who thinks “autoload == classes”.

As a systems developer, it’s important to remember that creative developers are going to use your features for things you never intended.

Classmap Autoloader

Next up the chain is the classmap autoloader. This autoloader is easy to understand. If you take a look at the generated file autoload_classmap.php

#File: vendor/composer/autoload_classmap.php

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    //...
    'PHPUnit_Extensions_GroupTestSuite' => $vendorDir . '/phpunit/phpunit/src/Extensions/GroupTestSuite.php',
    'PHPUnit_Extensions_PhptTestCase' => $vendorDir . '/phpunit/phpunit/src/Extensions/PhptTestCase.php',
    'PHPUnit_Extensions_PhptTestSuite' => $vendorDir . '/phpunit/phpunit/src/Extensions/PhptTestSuite.php',
    'PHPUnit_Extensions_RepeatedTest' => $vendorDir . '/phpunit/phpunit/src/Extensions/RepeatedTest.php',
    'PHPUnit_Extensions_TestDecorator' => $vendorDir . '/phpunit/phpunit/src/Extensions/TestDecorator.php',
    'PHPUnit_Extensions_TicketListener' => $vendorDir . '/phpunit/phpunit/src/Extensions/TicketListener.php',
    //...
);

you’ll see a PHP array where each key is a class name, and each value is a full file path to the class definition. Remember the findFile method from last time?

#File: vendor/composer/ClassLoader.php
public function findFile($class)
{
    //...

    // class map lookup
    if (isset($this->classmap[$class])) {
        return $this->classmap[$class];
    }

    //...
}

The classmap object property is the same array that’s in autoload_classmap.php. The Composer autoloader loads this map in the getLoader method.

#File: vendor/composer/autoload_real.php
public static function getLoader()
{
    //...
    $classmap = require __DIR__ . '/autoload_classmap.php';
    if ($classmap) {
        $loader->addClassMap($classmap);
    }
    //...
}

You can find the addClassMap method (called above) in the base ClassLoader class.

#File: vendor/composer/ClassLoader.php
public function addClassMap(array $classmap)
{
    if ($this->classmap) {
        $this->classmap = array_merge($this->classmap, $classmap);
    } else {
        $this->classmap = $classmap;
    }
}

The classmap is another popular PHP autoloader pattern. Rather than dynamically generate a class name each time a program instantiates a new class (which will include several string concat, replace, etc. combinations), with a classmap you can be explicit about the location of each class definition.

If you want to use a classmap autoloader in your Composer package or Composer based project, you can add a classmap node to the autoload node in your composer.json file.

#File: composer.json
"autoload": {
    "classmap": [
        "path/to/class/File_With_Class.php",
    ]
},

and then run

$ composer dumpautoload

Composer will generate a classmap array something like the following (for a package)

#File: vendor/composer/autoload_classmap.php
return array(
    //...
    'File_With_Class' => $vendorDir . '/vendor-name/package-name/path/to/class/File_With_Class.php',
    //...
);

or something like this (for a project)

#File: vendor/composer/autoload_classmap.php    
return array(
    //...
    'File_With_Class' => $baseDir . '/path/to/class/File_With_Class.php',
    //...
); 

In other words, Composer will insert your specified class definition file with the package folder, or project folder, prepended with the base vendor directory, or the base project directory, depending on the context of the composer.json file.

Important: Composer does not generate the array key (i.e. the class name, File_With_Class above) from the class’s file name. Instead, the Composer core code actually scans and reg-ex parses the file for any class names, interface names, or trait names.

One downside of a classmap is, you need to build and maintain the classmap. Having an additional step for a programmer to complete anytime they need to create a new class can be a block to productivity. Fortunately, Composer has a solution for this.

#File: composer.json
"autoload": {
    "classmap": [
        "path/to/class/folder",
    ]
},

That is, the classmap autoloader type also supports adding folders. If you add a folder, and then run

$ composer dumpautoload

Composer will scan the entire directory hierarchy of the configured path for class definition files. Specifically, Composer will search for files with the .php, .inc, or .hh file extension, and then search them for class definitions using the same findClasses method we linked to above. If you’re curious where the scanning happens in Composer’s source, checkout the createMap method.

Important: In case its not obvious from above — the code that generates these Composer autoloader include fiels is not a part of your vendor tree. It lives in the Composer program (phar) itself. This means Composer’s autoloader is useless without the Composer program itself, and that can throw some PHP developers for a bit of a loop.

All in all, Composer’s classmap feature is a powerful one, and a popular choice when a project that started without Composer finally decides to take the plunge. The ability to point Composer at at a class directory and have it magically find all your classes for you is a huge time saver.

With both the include files and classmap autoloader types, Composer seems well armed to handle any autoloading situation. However, recent development in the larger PHP community have led Composer developers to implement full support for both the PSR-0 and PSR-4 autoloading standards. Composer’s implementation of these standards will be our topic next time, when we continue to explore Composer’s autoloader implementations.

Originally published January 26, 2015
Series Navigation<< Laravel, Composer, and the State of AutoloadingComposer Autoloader Features: Part 2 >>

Copyright © Alana Storm 1975 – 2023 All Rights Reserved

Originally Posted: 26th January 2015

email hidden; JavaScript is required