Symfony ClassLoader component is a PSR-0 standard compliant PHP class autoloader. It's not only able to load namespaced code but also supports old-school PEAR standards (also used by Zend Framework). It's a perfect class loading tool for most of PHP projects.
Note: Code used in this post is available on github: https://github.com/jakzal/SymfonyComponentsExamples
Installation
You can either install it from the Symfony PEAR channel or grab it directly from github. For the purpose of this article we'll clone the sources to the vendor/ directory of the project.
Note: ClassLoader component uses Symfony\Component\ClassLoader namespace. Therefore we'll put it into Symfony/Component/ClassLoader subdirectory of vendor (see PSR-0 standard).
git clone https://github.com/symfony/ClassLoader.git vendor/Symfony/Component/ClassLoader
Basic Usage
Let's say we have two Acme libraries.
First one is located in the src/Acme/Tools. HelloWorld class uses Acme\Tools _namespace and is declared in the _src/Acme/Tools/HelloWorld.php file:
<?php
// src/Acme/Tools/HelloWorld.php
namespace Acme\Tools;
class HelloWorld
{
public function __construct()
{
echo __METHOD__."\n";
}
}
Second library is stored in the src/Legacy/Acme/Tools. It follows old but well known PEAR naming standards. Legacy_Acme_Tools_HelloWorld class is defined in the src/Legacy/Acme/Tools/HelloWorld.php file:
<?php
// src/Legacy/Acme/Tools/HelloWorld.php
class Legacy_Acme_Tools_HelloWorld
{
public function __construct()
{
echo __METHOD__."\n";
}
}
To make that our classes are automatically loaded we have to register Acme namespace and _Legacy__ prefix:
<?php
// classloader.php
require_once __DIR__.'/vendor/Symfony/Component/ClassLoader/UniversalClassLoader.php';
$loader = new Symfony\Component\ClassLoader\UniversalClassLoader();
$loader->registerNamespaces(array('Acme' => __DIR__ . '/src'));
$loader->registerPrefixes(array('Legacy_' => __DIR__ . '/src'));
$loader->register();
$helloWorld = new Acme\Tools\HelloWorld();
$legacyHelloWorld = new Legacy_Acme_Tools_HelloWorld();
Of course classes are only loaded when needed. Requiring UniversalClassLoader.php file should be the only require statement used in our code. Other classes should be loaded by the class loader.
Note: There's also a way to define paths with registerNamespaceFallbacks() and registerPrefixFallbacks(). Class loader will use them with namespaces or prefixes which weren't listed explicitly.
Increasing performance
Number of class files in a real-world project is rather big. Class loader might have some impact on performance as it checks for file existence before requiring it. To avoid disk operations we can cache results in APC with ApcUniversalClassLoader:
<?php
// classloadercached.php
require_once __DIR__.'/vendor/Symfony/Component/ClassLoader/UniversalClassLoader.php';
require_once __DIR__.'/vendor/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';
$loader = new Symfony\Component\ClassLoader\ApcUniversalClassLoader('ClassLoader');
$loader->registerNamespaces(array('Acme' => __DIR__ . '/src'));
$loader->registerPrefixes(array('Legacy_' => __DIR__ . '/src'));
$loader->register();
$helloWorld = new Acme\Tools\HelloWorld();
$legacyHelloWorld = new Legacy_Acme_Tools_HelloWorld();
Note: Examples are run in a command line. Therefore there's no performance gain from using APC. In fact it can hurt performance as cache is initialized every time our script is run in cli. This is a limitation of APC.