Enhanced Symfony2 Autoloader

Ever wondered how to enhance the Symfony2 Autoloader?...

There comes a time when for some reason you will want to or have to modify a class or more from a 3rd party bundle. Sadly, some 3rd party bundles are outdated, discontinued or very rigid, and so you're rendered unable to do this via a configuration option. This makes it so that you will either have to create a new fork of that 3rd party library and maintain it yourself or modify or create a bunch of classes. There are cases when you don't want to do any of this and want to keep the work needed to achieve this a minimum.

If you don't mind getting your hands dirty, the following bit will show you how to achieve your goal, although is quite hackish and should only be relied upon when you're not able to go the standard way of doing things.

The component of the system that can help you achieve this is the autoloader.

The autoloader is an instance of Composer\Autoload\ClassLoader and can be accessed before the system boots from the app/autoload.php in the standard Symfony2 installation.

There are a bunch of methods on the objects $loader that is created inside the app/autoload.php file, that you can use to achieve this:

  • addClassMap(array $classMap)
  • add($prefix, $paths, $prepend = false)
  • addPsr4($prefix, $paths, $prepend = false)
  • set($prefix, $paths)
  • setPsr4($prefix, $paths)

The addClassMap method takes an array as parrameter where the class is the the key and the path is the value. E.g. :

$loader->addClassMap(array(
    'Doctrine\\ORM\\Tools\\Pagination\\Paginator' => __DIR__ . '/../src/SomeVendor/SomeBundle/Pagination/Tools/Paginator.php',
));

The namespace of the new class will stay the same as the namespace of the new class, even though the path is totally now different. This is because in the standard approach the namespace is used to determine the path and the autoloader will try to guess is based on the autoloading config of each bundle (specified in each bundles composer.json) and the PSR autoloading standard.

The only real difference between the add and addPsr4 and between set and setPsr4 is that the $prefix parameters has to end with a backslash (/) when using the PSR4 methods. Other than that everything is the same. So if you'd do something like this for a PSR0 method:

$loader->set('SomeVendor\SomeBundle', $paths);

The PSR4 equivalent would be:

$loader->setPsr4('SomeVendor\SomeBundle\\', $paths);

The add and addPsr4 methods can be used to append or prepend paths for a given namespace while the set and setPsr4 methods will replace the mapping if any exists already. For all four of these methods the $paths parameter can either be a string or an array of strings.

The namespace will stay the same if you're using these methods, but, there's a catch here and is very important: the directory path must exist inside these new locations because the autoloader will try to find the file based on the namespace and class inside these new paths.

For example:

If you wanted to remap the SomeVendor\SomeBundle\Directory\SomeClass which is located in vendor/somevendor/src/SomeVendor/SomeBundle/Directory/SomeClass.php you'd have to create a new file src/MyVendor/MyBundle/Remapped/SomeVendor/SomeBundle/Directory/SomeClass.php and add this to your autoloader:

$loader->set('SomeVendor\SomeBundle', array(
    'src/MyVendor/MyBundle/Remapped',
    'vendor/somevendor/src/SomeVendor/SomeBundle' // this will be the fallback directory to search in, in case the $newDirectory doens't have the file
));

Again, this is non standard and should only be used as a last resort. But, now you know how to do it if you have too.

Let us know what you think.

Want more awesome stuff?

Check us out on our Medium Remote Symfony Team publication.