PSR-0 and PSR-4 autoloading of classes in PHP

The PHP Framework Interop Group is about interoperability standards between frameworks so that code can be reused between projects. To facilitate this way of working there is “Composer” (a package manager) and “Packagist” (a package repository) made available. The SCM system used by the packages is mainly Git and the code repository resides most of the times on Github. Symfony2 is heavily using this system already. For class autoloading (the most important interoperability problem) the following standards apply:

The following PHP file can be put into the vendor directory (and required by your project) to do autoloading of classes:

class Loader
{
    protected static $parentPath = null;
    protected static $paths = null;
    protected static $nsChar = '\\';
    protected static $initialized = false;

    protected static function initialize()
    {
        if (static::$initialized) return;
        static::$initialized = true;
        static::$parentPath = __FILE__;
        for ($i=substr_count(get_class(), static::$nsChar);$i>=0;$i--) {
            static::$parentPath = dirname(static::$parentPath);
        }
        static::$paths = array();
        static::$files = array(__FILE__);
    }

    public static function register($path,$namespace) {
        if (!static::$initialized) static::initialize();
        static::$paths[$namespace] = trim($path,DIRECTORY_SEPARATOR);
    }

    public static function load($class) {
        if (class_exists($class,false)) return;
        if (!static::$initialized) static::initialize();

        foreach (static::$paths as $namespace => $path) {
            if (!$namespace || $namespace.static::$nsChar === substr($class, 0, strlen($namespace.static::$nsChar))) {

                $fileName = substr($class,strlen($namespace.static::$nsChar)-1);
                $fileName = str_replace(static::$nsChar, DIRECTORY_SEPARATOR, ltrim($fileName,static::$nsChar));
                $fileName = static::$parentPath.DIRECTORY_SEPARATOR.$path.DIRECTORY_SEPARATOR.$fileName.'.php';

                if (file_exists($fileName)) {
                  include $fileName;
                  return true;
                }
            }
        }
        return false;
    }
}

spl_autoload_register(array('Loader', 'load'));

If you clone the guzzle library into “vendor/guzzle” then you can use the snippet below to load the Loader class and register the library. After that you can just use the Guzzle Client class, because the Loader takes care of the autoloading.

require 'vendor/autoload.php';
Loader::register('guzzle','GuzzleHttp');
use GuzzleHttp\Client;
$client = new Client();
// do something with $client

Alternatively, you can also use the autoloading file generated by Composer. I recommend doing this in a production environment. Using Composer allows you to manage your dependencies and automatically update them when new versions are released. This post should show you how the autoloading mechanism works in PHP. It also shows you how to build your own standards compliant autoloading mechanism, something you probably only need when building your own PHP framework.

3 Responses to “PSR-0 and PSR-4 autoloading of classes in PHP”

  • according to http://stackoverflow.com/a/6807615/1829460
    it’s best practice to replace self:: with static::
    just saying.

  • if you’re using other include paths (php.ini)
    you could replace if (file_exists($fileName)) {
    with if(stream_resolve_include_path($fileName) !== false) {

    and if you would like a PHP_ERROR replace include with require. (_once)

  • Maurits van der Schee (Innovation Engineer):

    @Arnold: Thank you for the excellent remarks! I updated the code, it now has static instead of self. I think using stream_resolve_include_path makes sense, but in the code I am already having absolute pathnames. I think in that case it does not make sense, right? Maybe I should rewrite the code to use the include paths.

Leave a Reply