Autoloading in PHP and the PSR-0 Standard

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 SymfonyComponentClassLoaderUniversalClassLoader;

$loader = new UniversalClassLoader();
$loader->registerNamespace("SymfonyComponent" => "/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(
    "SymfonyComponent" => "/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 SymfonyComponentClassLoaderApcUniversalClassLoader;
$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?

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Bryce

    A very informative article, nice job.
    “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!”
    Where exactly does this discussion take place? I don’t know how much I could contribute but I’d love to eavesdrop.

  • http://www.mikehealy.com.au Mike

    I didn’t know __autoload was to be deprecated.

    • http://harikt.com Hari K T

      Thank you for the comments . Yes __autoload is discouraged and may be deprecated or removed in the future.

  • http://www.mario-raspe.de Mario

    Thank you for your very informative article. I think, an other autoloading mechanism is the use of classmaps. For example: “ClassMapAutoloader” in ZF2.

  • zless

    It seems to be a syntax error (array items without array()):
    $loader->registerNamespace(“SymfonyComponent” => “/path/to/symfony/components”);
    $loader->registerNamespace(“Monolog” => “path/to/monolog/src/”);

    • http://harikt.com/blog Hari K T

      Not sure what you mean , see the registerNamespace() and registerNamespaces() methods. Namespaces accepts array .
      https://github.com/symfony/ClassLoader/blob/master/UniversalClassLoader.php#L162
      https://github.com/symfony/ClassLoader/blob/master/UniversalClassLoader.php#L177

      • zless

        Of course it supports, but array declaration in highlighted code is invalid.

        • http://harikt.com/blog Hari K T

          Oh I hear you man.
          Thank you for looking more carefully. I can guess now, you have really gone through this. This is a trick to know how many of them read it carefully ;-).

      • nurikabe

        zless is pointing out that the article is using an associative array syntax in this example:

        $loader->registerNamespace(“SymfonyComponent” => “/path/to/symfony/components”);
        $loader->registerNamespace(“Monolog” => “path/to/monolog/src/”);

        which doesn’t make sense. Note the associative array syntax appearing outside of an array and passed to the (singular) registerNamespace() function.

  • Sebastiaan Stok

    If you use namespaces between double quotes you should escape the backslash.
    “SymfonyComponent” -> “Symfony\Component”

    Or else an “n” will become an newline.
    Even better is to always use single-quotes (”), since your not using any special characters inside the string.

    • http://harikt.com/blog Hari K T

      Hi Sebastiaan Stok,
      Thank you for the comments and I agree with you that we can use single quotes.
      In this case double quotes also works for there is no ‘n’ coming :-) . I will ask to make the correction so there is no doubt regarding it :-) .
      Thank you.

  • Mário Rodrigues

    I tried the approach with spl_autoload and namespaces and I figured out that it doesn’t work properly. At least not yet (PHP 5.3.9). I tried to use classes and namespaces and to do something like this:

    // myClassA.class.php
    namespace app/classes;
    abstract class myClassA { // implementantion }

    // myClassB.class.php
    namespace app/classes;
    class myClassB extends myClassA{ // implementation }

    // myClassC.class.php
    namespace app/classes;
    class myClassC {
    function __construct(myClassA $object){ // code here }
    }

    //test.php
    $myObject = new myClassB();
    $classCItem = new myClassC($myObject);

    And this works with the “old” autoloading method (without namespaces and spl_autoloader) since $myObject is considered a myClassA object type also. Using the namespaces and spl_autoloader I got an error saying that the type of $myObject is not myClassA,
    Any hints on this?
    I read a post on a blog saying that the __autoload() was going to get deprecated soon. So I want to get this thing “fixed” :)
    Thanks in advance! :) (Sorry about the lonnnngggg post :] )

    • http://harikt.com/blog Hari K T

      Now your code

      class myClassC {
      function __construct(myClassA $object){ // code here }
      }
      //test.php
      $myObject = new myClassB();
      $classCItem = new myClassC($myObject);

      Look myClassC constructor , you expects myClassA object , if you want to use any class objects don’t mention about it. Hope this helps !

  • Beanie

    Teaching myself this so that I understand what’s happening I wrote the following which uses php’s include_path to find the correct file…
    class Config {
    static function AutoloadRegister() {
    spl_autoload_register(array(“Config”, “PSR0_AutoLoader”));
    return TRUE;
    }
    // this autoloader requires the folder/file name structure to match the namespace hierarchy.
    // the SPL autoloader will automatically search through the various include_path paths to find the right folder
    static function PSR0_AutoLoader($classname) {
    if (!class_exists($classname)) {
    require_once(str_replace(“\”, DIRECTORY_SEPARATOR, $classname).”.php”);
    }
    return TRUE;
    }
    // use the include path function to add base paths for various namespaces.
    static function Add_Include_Path($path) {
    $include_paths = explode(PATH_SEPARATOR, get_include_path());
    if (!in_array($path, $include_paths)) {
    $include_paths[] = $path;
    set_include_path(implode(PATH_SEPARATOR, $include_paths));
    }
    return TRUE;
    }
    }
    Config::Add_Include_Path(“/path/to/folder/with/packages”);
    Config::AutoloadRegister();

    • Alex Gervasio

      Hey Beanie,
      Autoloaders, in many of its forms and flavors should have state and be instantiable as well, just like any other class out there. While trickily tempting at first, don’t get caught in the trap of using class constructs as namespaces. Make the your autoloader’s methods dynamic, not static, thus opening up the door for exploiting the benefits that Polymorphism provides right out the box. Using classes as fancy wrappers for plain vanilla procedural code doesn’t just mean you’re doing real OOP.

      • Beanie

        Thanks for the comments Alex. Some interesting points you’ve raised. I accept that one shouldn’t be using static methods and one should embrace extensability :). To be fair this was a quick and dirty approach that I created as a proof of concept so that I could understand the autoloader as well as PHP’s include_path. With regard to your point about using objects as wrappers for procedural code, ultimately that is exactly what is happening when you get into the function of an object.
        To be honest, I’ve been looking for information on PSR0 autoloading and was quite excited to see the headline of this article. However, I was disappointed to find that it didn’t really explain how to do it other than to say ‘use my framework’.

        • Alex Gervasio

          Agreed. Feel proud about yourself, as you’ve done quite a nice job with your “firstborn” namespaced autoloader :-). Just implement a few additional methods that add some state to it, and of course, make sure to throw away the static methods once and for all. If you’re interested in playing around with PSR-0 class loaders, and getting your own one up and running with minor hassles, you might want to fork this https://gist.github.com/221634.

          • http://www.7mediaws.org/ Joshua Parker

            I am still trying to wrap my head around namespaces, and I am trying to use the SPL autoload class because I want to conform to PSR-0. However, none of my classes get autoloaded. All of my classes are located in a class directory. And my directory structure to classes is framework/Classes/{Core,Controllers,Models}
            So then I have this:
            $classLoader = new SplClassLoader(‘frameworkClasses’, ‘/home/username/public/framework’);
            $classLoader->register();
            and I add namespace frameworkClasses, but they don’t get autoloaded. What am I doing wrong?

          • http://www.7mediaws.org Joshua Parker

            Nevermind, I can be dense sometime. I figured it out.

        • http://harikt.com/blog Hari K T

          Hi Beanie ,
          I am not sure what made you think that we didn’t cover the PSR-0 autoloader.
          I have shown a simple example of the directory structure of a PSR-0 standard.
          PSR-0 doesn’t think of autoloading as looking through the include path, though some vendors provide it.
          Thank you Alex Gervasio for pointing out @jwage ‘s gist. I didn’t point to it since the link and the sample were available at https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md . Yes it was a mistake that the links were not added, which seem to be lost when moving to wordpress. I will point to Tim regarding this.

  • http://zaemis.blogspot.com Timothy Boronczyk

    Sorry about the links everyone. I don’t know what happened but I’ve got them added back now. Also, Hari asked I add the reference to the gist Gervasio pointed out, which I’ve done. Hopefully this will clear up the confusion some readers have had. And thanks, Hari, for being available to answer the questions!

    • http://harikt.com/blog Hari K T

      Hi Tim ,
      Thank you for taking time to making the corrections. We are sorry about it. Thank you for the questions to make the article much cleaner we hope. Let us know if anything is missed, we will try to resolve it.

  • Raghavendra Karteek

    Please post a sample __autoload example ….

  • http://komelin.com Konstantin Komelin

    Thank you for good article! It’s very useful information.

  • http://php-autoloader.malkusch.de/en/ php-autoloader

    Have a look at http://php-autoloader.malkusch.de/en/. This autoloader supports every autoloadable PHP feature up to PHP 5.4’s traits and find’s every class, interface or traits definition in any situation. No matter what convention is used. The autoloader is index based. So after building the index there should be no performance issue. The building is done dynamically so there is no explicit need of calling a build script. But for the sake of comfortability there exists a script for prebuilding an index.

  • ling

    I prefer Pear naming system over PSR-0 :
    - you can see the full name of the class at a glance
    - it’s easier to type the “_” than the “”
    - you don’t need to use extra keywords like “use” and “namespace”

    Autoloading is very simple :

    //
    //
    // application/vendor/myautoloader.php

    define(‘FS_VENDOR_DIR’, __DIR__);
    function myAutoloader($class) {
    if (‘Vendor1_’ == substr($class, 0, 8)) {
    include FS_VENDOR_DIR . ‘/’ . str_replace(‘_’, DIRECTORY_SEPARATOR, $class) . ‘.php’;
    return;
    }
    elseif (‘Vendor2_’ == substr($class, 0, 8)) {
    include FS_VENDOR_DIR . ‘/class/model/’ . str_replace(‘_’, DIRECTORY_SEPARATOR, $class) . ‘.php’;
    return;
    }
    // add your vendor here…
    }
    spl_autoload_register(‘myAutoloader’, true, false);
    //
    //
    //

    and you add an entry to this function every time you include a new library into your application.
    This is QTN-0 ( or whatever ).
    Now I’m asking you, do you prefer PSR-0 or QTN-0 ?
    Why ?

    • http://harikt.com/blog Hari K T

      @ling PSR-0 do follow the PEAR naming convention, that means it can load Zend_ (zf1) , Solar_ or what ever stuffs.
      Have a look into https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md#underscores-in-namespaces-and-class-names

      • ling

        @Hari K T
        I know. What I meant is I would rather follow a very basic convention, with no extra costs.
        I like PEAR naming convention, I don’t like to use the “use” and “namespace” keywords, I think it’s time wasting.
        Now the PSR-0 wants to autoload PEAR style AND namespace style at the same time !
        That looks overcomplicated to me.
        I’m not sure if I want to switch, I may rather stick to the “Do just one thing and do it well” philosophy.

        • http://harikt.com/blog Hari K T

          So I understood you are confused whether to follow PEAR naming convention or the PSR convention . So one thing I will tell you , pear follows the PSR, so follow PSR. Have a look into the php-fig voting members
          https://github.com/php-fig/fig-standards#voting-members
          If you are still confused checkout the php-fig website http://www.php-fig.org/
          Also you don’t need to think more on how to create your own autoloader . Have a look into one of the PSR-0 autoloader. Eg : The one by Symfony2 or https://github.com/auraphp/Aura.Autoload , which also matches the interface proposed at https://wiki.php.net/rfc/splclassloader.

          • ling

            Thanks, finally I’m getting used to it and founded some advantages :
            you can override an root objects like SplFileInfo just by setting another namespace
            also, typing “” is not so painfull in fact…

  • http://aalaap.com Aalaap Ghag

    This was helpful! :-)

  • http://rajeevphp2011.blogspot.in/ rajeev dhar dwivedi

    helpful and nice , thanks for sharing your knowledge

  • joey

    Thanks Hari! casts light on the topic;)

  • Fra Gadio

    i thought that was more confusing than helpful but thanks anyway