Pragmatism in the real world

Using Zend\Loader\Autoloader

Autoloading is the process in PHP whereby the system attempts to load a class when it is first encountered (via new or via class_exists) if it hasn’t already been loaded via a require or include. Autoload works by looking for a method called __autoload or walking through any method registered with spl_autoload_register.

Zend Framework 2 provides the Zend\Loader\Autoloader component for autoloading of Zend Framework and your own classes.

What does it provide?

The ZF2 autoloader provides the following:

  • PSR-0 compliant include_path autoloading
  • PSR-0 compliant per-prefix or namespace autoloading
  • Class map autoloading, including class map generation
  • Autoloader factory for loading several autoloader strategies at once

It provides this functionality through two classes: StandardAutoloader and ClassMapAutoloader.

Zend\Loader\StandardAutoloader

StandardAutoloader implements loading of classes by inspecting their classname and finding the file on disk. It supports three strategies:

  • Searching the include_path
  • Loading from a list of specific namespace / directory pairs
  • Loading from a list of specific vendor prefix / directory pairs

These can be combined for complete flexibility.

Example usage:


require_once ZF2_PATH . '/Loader/StandardAutoloader.php';

$autoLoader = new Zend\Loader\StandardAutoloader(array(
'prefixes' => array(
'MyVendor' => __DIR__ . '/MyVendor',
),
'namespaces' => array(
'MyNamespace' => __DIR__ . '/MyNamespace',
),
'fallback_autoloader' => true,
));

// register our StandardAutoloader with the SPL autoloader
$autoLoader->register();

Searching the include_path is the slowest and similar to how ZF1’s autoloader works and is known as the fallback autoloader as it’s the last resort :)

Having configured our StandardAutoloader and registered it, we can then use it:


$test1 = new MyNamespaceTest();
$test2 = new MyVendor_Test();

What’s the difference between a prefix and a namespace?

A prefix system is used for non-namespaced classes. i.e. those classes where the directories are separated by underscores. e.g. Zend_Config_Ini. A namespace is a PHP 5.3 namespace where the directories are separated by the namespace separator or underscores. For example: the class MyNamespaceSub_Test would be stored in /path/to/MyNamespace/Sub/Test.php.

Programmatic interface

You can also use a Programmatic interface rather than configuring in the constructor:


require_once ZF2_PATH . '/Loader/StandardAutoloader.php';

$loader = new ZendLoaderStandardAutoloader();

$loader->registerPrefix('MyVendor', __DIR__ . '/MyVendor')
->registerNamespace('MyNamespace', __DIR__ . '/MyNamespace')
->setFallbackAutoloader(true);

$loader->register();

If you have multiple prefixes or namespaces, you can use `registerPrefixes` and `registerNamespaces` which take arrays.

Zend\Loader\ClassMapAutoloader

The class map autoloader is a high performance autoloader. It uses class maps, which are simply associative arrays of each classname to the name of the file disk that contains that class. As a result, it is very fast as the work done is an array key lookup. In fact the entire autoload method is simply:


public function autoload($class)
{
if (isset($this->map[$class])) {
include $this->map[$class];
}
}

You can’t get much faster than that! It also works well with PHP opcode cache and realpath caches.

To use the class map autoloader, you need a class map file, autoload_classmap.php such as this:


<?php
return array(
'MyNamespaceTest' => __DIR__ . '/MyNamespace/Test.php',
'MyVendor_Test' => __DIR__ . '/MyVendor/Test.php',
);

You can the use the class map autoloader like this:


require_once ZF2_PATH . '/Loader/ClassMapAutoloader.php';

$autoLoader = new ZendLoaderClassMapAutoloader(
array(__DIR__ . '/autoload_classmap.php'));

// register with the SPL autoloader
$autoLoader->register();

In this case, you can only load those classes that are listed in the supplied file(s). Note that, by convention, the class map file is called autoload_classmap.php, but can be any file that returns an array.

Note that the constructor allows you to pass in multiple map files:


$loader = new Zend\Loader\ClassMapAutoloader(array(
__DIR__ . '/../library/autoload_classmap.php',
__DIR__ . '/../application/autoload_classmap.php',
));

There’s also a method called registerAutoloadMap() that does the same thing.


$loader = new Zend\Loader\ClassMapAutoloader();
$loader->registerAutoloadMap(array(
__DIR__ . '/../library/autoload_classmap.php',
__DIR__ . '/../application/autoload_classmap.php',
));

Note that new maps are merged with the maps that are already registered. The last definition for a class wins. Hence, you can override the location of class if required.

Creating class maps

As you can imagine, creating class maps manually would quickly get tiresome. To alleviate this, Zend Framework 2 provides a PHP script, classmap_generator.php in the `bin` directory that will do this for you. This tool will scan the entire directory from the current directory (or that specified via an option) and create a class map file for every class that it finds. It is used like this:

    prompt> php path/to/zf2/bin/classmap_generator.php -w
    Creating class file map for library in '/var/www/project/library'...
    Wrote classmap file to '/var/www/project/library/autoload_classmap.php'

I expect that ZF2 will be distributed with an autoload_classmap.php when it is released.

Combining autoloading strategies

ZF2 also provides an AutoloaderFactory that allows you to combine ClassMapAutoloaders and StandardAutoloaders. This is especially helpful in development where you may want to use a ClassMapAutoloader for ZF2 files, but a StandardAutoloader for your own files. Usage is like this:


require_once ZF2_PATH . '/Loader/AutoloaderFactory.php';
Zend\Loader\AutoloaderFactory::factory(array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/../library/Zend/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'prefixes' => array(
'MyVendor' => __DIR__ . '/MyVendor',
),
'namespaces' => array(
'MyNamespace' => __DIR__ . '/MyNamespace',
),
'fallback_autoloader' => true,
),
));

When the factory method executes, it will load and initialise each autoloader in turn and then call the register() method to register with the spl_autoload system. As spl_autoload uses as queue, you should put the class map autoloaders first as they return really quickly on failure.

Note, that if you subsequently use the factory to add more files to the class map autoloader, for instance, then it will reuse the same ClassMapAutoloader instance that it created earlier.