For and by top PHP developers

Autoload in complex systems

It's very tedious keeping track of code modules, and the PHP 5 ability to autoload them is a great boon. But in a complex system, such as a CMS, multiple software applications have to coexist, and this places demands on autoload techniques. In some environments, the system autoloader must come last.

We'll fix that in a moment, but first let's review where we are. The first implementation of autoload in PHP 5 was a simple function with a special name "__autoload". That proved valuable but was inflexible because only a single autoload system could be implemented since functions are global and so there can be only one __autoload function.

With PHP 5.1.2 came the spl_autoload set of functions that provided for a whole bunch of autoload functions to be registered, each with an arbitrary name and the possibility of being a method, static or not, as an alternative to a function. The manual refers to them as a "stack" but that can be misleading. The first to be registered will be called first when a class is not found, and so on up to the last to be registered, or until the class is found and the answer "true" given by an autoload function.

So far so good. Some systems (such as Joomla 1.5) use the old __autoload function. But any use of spl_autoload_register will disable the __autoload function. People have got round this by a somewhat standard technique of adding (if present) the __autoload function. So, some typical code might be:

Now multiple applications may be doing this, and if the order in which the autoloaders are called does not matter, then the code above works fine. But in a CMS like Joomla, it is important that the system autoloader be called last. This is because other autoloaders may perform actions that do not load the class, but provide information to the CMS to enable the CMS autoloader to do the actual loading. That technique will not work unless the CMS autoloader is called last.

Reflecting on the code above and the way spl_autoload_register works tells us that if the code is used more than once, the CMS autoloader will not remain last. The first use of the code will work. But the second time it is called, the new autoloader is added to the end of the list. Because the CMS autoloader is already in the list, the second call to spl_autoload_register does not do anything, since the PHP function is smart enough to ignore requests to add functions already registered.

And if a CMS like that is modernised (as is happening with Joomla 1.6) so that it uses spl_autoload_register itself, then the code above will fail to keep the CMS autoloader at the end of the list, even the first time it is used.

With PHP 5.3 we could easily get round the problem because an additional parameter to spl_autoload_register will tell it to put the new autoloader at the start of the list, instead of the default action of adding it to the end. But we don't all have the luxury of being able to assume our code will run with PHP 5.3.

So for an environment where the original autoloader needs to be kept at the end, slightly more elaborate code is needed:

$myautoloader = array('myClass', 'myAutoloadMethod');
// Ensure that the system autoload function remains the last in the "stack"
// Find registered autoload functions and unregister them
$already = spl_autoload_functions();
foreach ($already as $func) spl_autoload_unregister($func);
// Register my autoload function as the first and then re-register the others
spl_autoload_register($myautoloader);
foreach ($already as $func) spl_autoload_register($func);
// and then use original __autoload if it exists
// - if already registered it will be ignored
if(function_exists("__autoload")) {
spl_autoload_register("__autoload");
}

Apart from the first line, which defines the autoloader to be added, the rest of the code is standard and can be used unaltered. (Provided the variables $already and $func do not clash with anything in the existing code!)