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
- Install Composer
- Run
composer require ...
,composer install ...
,composer update ...
-
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
- PSR-0 autoloader
- PSR-4 autoloader
- A generic classmap autoloader
- A “load this include file and do whatever you want to setup the autoloader” method
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 require
s 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.