PHP Namespaces and Autoloaders, Reviewed

Autoloading

Autoloading is a “trick” that helps PHP to load scripts that define classes.

In the old style PHP, you needed to use include to bring in class scripts.

include "lib/Database.php";

$db = new Database();

In the new style PHP, you don’t include classes. Instead, you use an autoloader function that knows the file location of a class’ script, and loads the class definition for you.

Here’s how it works.

When you create an instance of a class with “new”, PHP looks for the class’ definition. If it’s not defined, it calls the autoloader function with the class name, including the namespace.

Here’s the standard PSR-0 autoloader:

function autoload($className)
{
    $className = ltrim($className, '\\');
    $fileName  = '';
    $namespace = '';
    if ($lastNsPos = strrpos($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;
}
spl_autoload_register('autoload');

It does a little string manipulation in there. Suppose we were using namespaces, and our code looked like this:

namespace Foo;

use RB\Core\Database;

$x = new Database();

When the code runs, the definition for the class Rb\Database doesn’t exist, so PHP calls autoload(“RB\Core\Database”). That is the full name of the class – it includes the RB namespace.

autoload() splits off the namespace, “RB\Core”, from the class name “Database”. Then it replaces the “\” with “/”, and creates the path “RB/Core/”.

Then, it creates the filename “RB/Core/Database.php”, and “require”s it. (Note that it does a little replacement of _ with / as well. Our class name doesn’t contain underscores.)

If the file RB/Core/Database.php exists, we’re almost done. We load it.

(If it doesn’t exist, it’s an error.)

Database.php should look like this:

<?php
namespace RB\Core;

class Database
{ ...

That defines the RB\Core\Database class. We’re golden!

If the namespace is wrong… then, the code will be loaded, but it won’t define RB\Core\Database. It’ll be defining something else, and we are screwed.

So the namespace needs to match up with the filepaths.

Use?

Remember this?

namespace Foo;

use RB\Core\Database;

$x = new Database();

The above example elided a little fact. The use keyword brings things from another namespace into the current namespace.

When I wrote $x = new Database(), how did I know whether it was Foo\Database or RB\Core\Database?

The use keyword caused the local ‘Database’ name to be aliased to ‘RB\Core\Database’.

That’s another way to say, Foo\Database = RB\Core\Database, (for the remainder of the code, until the next namespace.)

Leave a Reply