Autoloading in PHP and the PSR-0 Standard

Hari K T
Share

Let’s say you have the file Rectangle.php which contains the definition for a Rectangle class. Before you can create an instance of the object elsewhere in your code, you first need to pull in the Rectangle.php file, perhaps by writing something like this:

<?php
require "Rectangle.php";
$rect = new Rectangle(42, 25);

Normally we put each class’ definition in its own file for better organization, and so you need to require/include each of the class files you want to use. If there are only a few files then it isn’t too much of a problem, but oftentimes that’s not the case. It can be very cumbersome to load a large library including all of its dependencies like this.

In this article I’ll walk you through the “history of autoloading,” from the older to the current PSR-0 standard autoloader approach found in many PHP frameworks such as Lithium, Symfony, Zend, etc. Then I will introduce you to the ClassLoader component from the Symfony2 project for PHP 5.3 which follows the PSR-0 standard.

Warning: Most of the code samples in the beginning of this article demonstrate deprecated approaches. It would be unwise to use them in production. I recommend you use one of the PSR-0 standard autoloaders instead.

Autoloading in the “Olden Days”

PHP 5 introduced the magic function __autoload() which is automatically called when your code references a class or interface that hasn’t been loaded yet. This provides the runtime one last chance to load the definition before PHP fails with an error.

Here’s an example of an extremely basic __autoload() implementation:

<?php
function __autoload($className) {
    $filename = $className . ".php";
    if (is_readable($filename)) {
        require $filename;
    }
}

It’s a good idea to make sure a file exists before you try to include it, but sometimes the file may be there but will not have sufficient read permissions so it’s better to use is_readable() over file_exists() which will for test both conditions.

The major drawback to the __autoload() function is that you can only provide one autoloader with it. PHP 5.1.2 introduced spl_autoload() which allows you to register multiple autoloader functions, and in the future the __autoload() function will be deprecated.

The introduction of spl_autoload_register() gave programmers the ability to create an autoload chain, a series of functions that can be called to try and load a class or interface. For example:

<?php
function autoloadModel($className) {
    $filename = "models/" . $className . ".php";
    if (is_readable($filename)) {
        require $filename;
    }
}

function autoloadController($className) {
    $filename = "controllers/" . $className . ".php";
    if (is_readable($filename)) {
        require $filename;
    }
}

spl_autoload_register("autoloadModel");
spl_autoload_register("autoloadController");

Generally the functions are called in the order they’re registered, but the order can also be affected by additional arguments passed to spl_autoload_register().

It’s important to remember that once a function has been registered with spl_autoload_register(), the __autoload() function will no longer be called. If you have an __autoload() function you want to run as part of your autoloader chain, then you’ll have to register it with spl_autoload_register().

Of course, the implementations of the autoloading functions I’ve shown this far have been rather simple. Real-world autoloaders are more complex.

Before real namespace support was introduced in PHP 5.3, developers devised their own approaches to prevent naming collisions. The PEAR Coding Standard used underscores to prefix class names with their directory path; the class Zend_Translate for example would be defined in the file Zend/Translate.php. The autoloader needed to replace the underscores with directory separators to locate the definition.

Also, different developers adopted different conventions when it came to naming their class files, for example the files might end in .php, .class.php, .inc, etc. Some libraries may be installed in different paths as well. The loader needed to look in various places for them, so now the loader begins to look like this:

<?php
function __autoload($className) {
    $extensions = array(".php", ".class.php", ".inc");
    $paths = explode(PATH_SEPARATOR, get_include_path());
    $className = str_replace("_" , DIRECTORY_SEPARATOR, $className);
    foreach ($paths as $path) {
        $filename = $path . DIRECTORY_SEPARATOR . $className;
        foreach ($extensions as $ext) {
            if (is_readable($filename . $ext)) {
                require_once $filename . $ext;
                break;
           }
       }
    }
}

Autoloading is a useful idea, but was an idea that desperately needed some standardization.

PSR-0 Standard

After PHP 5.3’s introduction of true namespace support, a group of people from the PHP community decided to create the PHP Standards Working Group in 2009 (later renamed to the Framework Interoperatability Group) and establish the PSR-0 standard which outlines various practices and constraints that must be followed for autoloader interoperability. Below are the requirements for PSR-0 compliance:

  • A fully-qualified namespace and class must have the following structure <Vendor Name>(<Namespace>)*<Class Name>.
  • Each namespace must have a top-level namespace (“Vendor Name”).
  • Each namespace can have as many sub-namespaces as it wishes.
  • Each namespace separator is converted to a DIRECTORY_SEPARATOR when loading from the file system.
  • Each underscore in the class name is converted to a DIRECTORY_SEPARATOR. The underscore has no special meaning in the namespace.
  • The fully-qualified namespace and class is suffixed with .php when loading from the file system.
  • Alphabetic characters in vendor names, namespaces, and class names may be of any combination of lower case and upper case.

According to the PSR-0 standard, there should be a top level directory with the vendor’s name and then the package name, so the directory tree will look like this:

example component path

The classes would then be namespaced accordingly:

<?php
namespace VendorPackage;
class Example
{
}

Thus, the class definition for DoctrineCommonConnections would be found at /path/to/project/lib/Doctrine/Common/Connections.php, and SymfonyCoreRequest at /path/to/project/lib/Symfony/Core/Request.php. The PSR-0 standard does not mandate what the base /path/to/project/lib portion of the path is, and conforming autoloaders offer different methods for its resolution. Some will allow you to register the directory, some will search PHP’s include_path, and some offer you both. Below is an example taken from the accepted PSR-0 standard.

<?php
function autoload($className)
{
    $className = ltrim($className, '\');
    $fileName  = '';
    $namespace = '';
    if ($lastNsPos = strripos($className, '\')) {
        $namespace = substr($className, 0, $lastNsPos);
        $className = substr($className, $lastNsPos + 1);
        $fileName  = str_replace('\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
    }
    $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';

    require $fileName;
}

This gist by Jonathan Wage is a sample SplClassLoader implementation that can load your classes if you follow the autoloader interoperability standards. It is the current recommended way to load PHP 5.3 classes that follow these standards.

You can use any one of the PSR-0 compliant autoloaders from frameworks such as Symfony, Pear2, AuraPHP (which is for PHP 5.4+), etc. and adhere to the rules above with your own code to take advantage of autoloading without the uncertainties I discussed previously.

Using Symfony’s Autoloader

The Symfony2 project is a component-based framework for PHP 5.3 and greater which you can use as a component library or as a full-stack framework. You can download Symfony’s Autoloader, the ClassLoader component, via different means — pear.symfony.com, packagist, or from GitHub.

Here’s the directory structure of Symfony’s ClassLoader component:

Symfony ClassLoader Component

Using the component then looks like this:

<?php
require_once "/path/to/Symfony/Component/ClassLoader/UniversalClassLoader.php";
use Symfony\Component\ClassLoader\UniversalClassLoader;

$loader = new UniversalClassLoader();
$loader->registerNamespace("Symfony\Component" => "/path/to/symfony/components");
$loader->registerNamespace("Monolog" => "path/to/monolog/src/");
$loader->registerPrefix("Zend_", "path/to/zend/library");
$loader->register();

The registerNamespace() method is used to inform the autoloader where the given namespace’s base directory maps to on the file system and accepts a namespace as its first argument and path as its second value. You can also register multiple namespaces in a single call with the registerNamespaces() method.

<?php
$loader->registerNamespaces(array(
    "Symfony\Component" => "/path/to/symfony/components",
    "Monolog' => "path/to/monolog/src"));

The registerPrefix() method is used to register pseudo-namespaces which was used by Pear, Zend, and other libraries and frameworks before real namespace support was implemented in PHP as we have already covered above. You can also register mulitple ones with the registerPrefixes() method and passing it as an associative array.

<?php
$loader->registerPrefixes(array(
    "Zend_" => "/path/to/zend/library",
    "Twig_" => "path/to/twig/library"));

If you are using the Alternative PHP Cache (APC), a free and open source opcode cache for PHP, then you can may want to consider using the ApcUniversalClassLoader class. The ApcUniversalClassLoader extends the UniversalClassLoader but uses apc_store() and apc_fetch() to store lookup information in APC’s cache. The standard UniversalClassLoader will of course work with APC, but the additional behavior offered by the ApcUniversalClassLoader class afford extra performance benefit.

<?php
require_once "path/to/Symfony/Component/ClassLoader/UniversalClassLoader.php";
require_once "path/to/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php";
use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
$loader = new ApcUniversalClassLoader("apc.prefix.");

The ApcUniversalClassLoader accepts a prefix with its constructor. For more information on APC, I suggest reading the APC documentation.

Summary

In this article we have discussed autoloading, from its early days to the current PSR-0 standard which has become widely adopted across many PHP frameworks. Recently, David Coallier tried to push the SplClassloader class into PHP 5.4 to offer native PSR-0 compliant autoloading functionality, but for various reasons it didn’t happen. Maybe in the future we will see it added. (See the C extension at gist.github.com/1310352.)

Now the current hot discussion in the working group is focused on caching. If it’s something you’d like to be part of, feel free to join the discussion!

Image via Matyas Szabo / Shutterstock

And if you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like Jump Start PHP.

Comments on this article are closed. Have a question about PHP? Why not ask it on our forums?