php Namespaces and Class loading

I want to autoload php classes and functions. Ideally, I want a lazy loader (include class files only when they are needed, never including more files than necessary). I’d also like namespace protection (if running PHP 5.3 or later). And of course I want to do this easily without having to keep track of hierarchical dependency chains.

PHP provides a magic function called __autoload() which can be leveraged to this end. You can implement __autoload() directly, OR you can use the spl_autoload functions. There is a default spl_autload() that provides namespace support (mapping namespaces to lowercase directories, classname to lowercase filename, and extension to .inc or .php)

Given two namespaces ‘foobar’ and ‘snafoo’; and lowercase filenames representing the class name (e.g., foobar\Foo() class would be located in foobar/foo.php) you could simply use the default spl_autoload() function as follows:

<?php
  spl_autoload_register();

  $foo1 = foobar\Foo();
  $foo2 = snafoo\Foo();
?>

However, I would like my files to match the class names directly, e.g., “class FooBar” should be in a file named “FooBar.php” rather than “foobar.php”.

Also, to support legacy code written in PHP 5.2 and before, I would like the autoloader to search for underscore-based long class names, e.g., “class legacy_snafoo_FooBar” would be located in “legacy/snafoo/FooBar.php”. Prefixing a classname with an underscored-namespace has been an unofficial solution to the lack of PHP namespaces for years.

Fortunately, spl_autoload_register allows you to chain multiple autoload functions, and you can keep the default spl_autoload() at the top of the chain to maintain much faster performance autoloading namespace-based code.

Here is a custom autoload implementation with namespace support, legacy long-classname suppport, as well as honoring multiple file extensions. Note the use of a closure requires PHP 5.3

<?php
/*
 * Custom autoload callback
 * * maintains spl_autoload_extensions
 */
function delimiter_autoload ($delimiter) {
  return function($className) use ($delimiter) {
    $extensions = explode(',', spl_autoload_extensions());
    foreach($extensions as $ext) {
      $classFile = str_replace($delimiter,DIRECTORY_SEPARATOR,$className) . $ext;
      if (is_file($classFile)) {
        include_once($classFile);
        break;
      }
    }
  };
}
/*
 * register autload functions
 */
spl_autoload_extensions('.php,.inc,.class.php,.lib,.lib.php');
spl_autoload_register();
spl_autoload_register(delimiter_autoload('\\'));
spl_autoload_register(delimiter_autoload('_'));

?>

The actual application code is now free to instantiate based on any namespace or legacy classname (using the underscore-approach), e.g.,

<?php
  $foo1 = foobar\Foo();
  $foo2 = snafoo\Foo();
  $oldfoo = legacy_foobar_Foo();
?>