Good news for PHP 5.3 users with namespaced classes:
When you create a subfolder structure matching the namespaces of the containing classes, you will never even have to define an autoloader.
<?php
spl_autoload_extensions(".php"); // comma-separated list
spl_autoload_register();
?>
It is recommended to use only one extension for all classes. PHP (more exactly spl_autoload) does the rest for you and is even quicker than a semantically equal self-defined autoload function like this one:
<?php
function my_autoload ($pClassName) {
include(__DIR__ . "/" . $pClassName . ".php");
}
spl_autoload_register("my_autoload");
?>
I compared them with the following setting: There are 10 folders, each having 10 subfolders, each having 10 subfolders, each containing 10 classes.
To load and instantiate these 1000 classes (parameterless no-action constructor), the user-definded autoload function approach took 50ms longer in average than the spl_autoload function in a series of 10 command-line calls for each approach.
I made this benchmark to ensure that I don't recommend something that could be called "nice, but slow" later.
Best regards,
spl_autoload_register
(PHP 5 >= 5.1.2)
spl_autoload_register — Register given function as __autoload() implementation
Description
Register a function with the spl provided __autoload stack. If the stack is not yet activated it will be activated.
If your code has an existing __autoload function then this function must be explicitly registered on the __autoload stack. This is because spl_autoload_register() will effectively replace the engine cache for the __autoload function by either spl_autoload() or spl_autoload_call().
If there must be multiple autoload functions, spl_autoload_register() allows for this. It effectively creates a queue of autoload functions, and runs through each of them in the order they are defined. By contrast, __autoload() may only be defined once.
Parameters
- autoload_function
-
The autoload function being registered. If no parameter is provided, then the default implementation of spl_autoload() will be registered.
Return Values
Returns TRUE on success or FALSE on failure.
Changelog
| Version | Description |
|---|---|
| 5.3.0 | Namespaces support was introduced. |
Examples
Example #1 spl_autoload_register() example
<?php
namespace Foobar;
class Foo {
static public function test($name) {
print '[['. $name .']]';
}
}
spl_autoload_register(__NAMESPACE__ .'\Foo::test'); // As of PHP 5.3.0
new InexistentClass;
?>
The above example will output something similar to:
[[Foobar\InexistentClass]] Fatal error: Class 'Foobar\InexistentClass' not found in ...
spl_autoload_register
28-Jul-2009 12:05
15-May-2009 02:39
This behaves more like a QUEUE than a STACK, since the registry is accessed in FIFO order. The autoloaders are attempted in the order they are defined.
02-May-2009 09:49
More simple solution:
<?php
function __autoload($className)
{
eval('class '.$className.'{}');
throw new Exception('Class '.$className.' not exists');
}
?>
18-Aug-2008 11:41
This is my version of making it possible to throw exceptions within autoload functions. I liked the functions which used __construct and __callStatic to throw the exceptions, but I did not want to relay on __callStatic because it only exists since PHP version 5.3.0. I tested all three examples below in PHP version 5.2.6. The class named Test and Test_Interface did both not exists.
<?php
new Test();
Test::test();
class Test implements Test_Interface {}
?>
In all three cases it succesfully throwed the exception without any fatal errors.
<?php
function __autoload($sClass)
{
try
{
throw new Exception('It works');
}
catch (Exception $oException)
{
throwAutoloadException($sClass, $oException);
}
}
function throwAutoloadException($sClass, Exception $oException)
{
// The use of eval is risky, because it could be easily exploited. To prefend
// that the use of eval in this method could be exploited we make sure that the
// variable contains only the allowed characters of a class name.
if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $sClass))
{
eval('class '.$sClass.'
{
static public function throwException()
{
throw unserialize(\''.serialize($oException).'\');
}
}
'.$sClass.'::throwException();');
}
}
?>
26-Jun-2008 09:07
The automatic generation of classes can be further improved to cater for files that don't actually contain the class they were supposed to contain:
<?php
// code to set include_path...
class AutoloadException extends Exception { }
class AutoloadClass {
public static function autoload($sClassName) {
@include_once($sClassName . '.class.php');
// does the class requested actually exist now?
if (class_exists($sClassName)) {
// yes, we're done
return;
}
// no, create a new one!
eval("class $sClassName {
function __construct() {
throw new AutoloadException('Class $sClassName not found');
}
static function __callstatic(\$m, \$args) {
throw new AutoloadException('Class $sClassName not found');
}
}");
}
}
spl_autoload_register(array('AutoloadClass', 'autoload'));
?>
You might be able to expand the example above to automatically generate interfaces too...
Enjoy!
DominicC
21-Feb-2008 07:53
For a while I was using the class autoload solution of sandrejev at gmail dot com, 08-Nov-2006 11:23, on this page http://www.php.net/manual/en/language.oop5.autoload.php.
As it appears, this method works fine most of the time, except in two specific cases:
1. It does not handle static classes correctly (also see the note of ostapk, 19-Dec-2007 02:46, same page and the article at onphp (s)he refers to).
2. When you accidentally have a variable in your stack that contains single quotes, PHP will choke with the following cryptic message:
Parse error: parse error, unexpected T_STRING in autoload.inc.php(64) : eval()'d code on line 26
I came up with this solution, which seems to solve these problems:
<?php
// code to set include_path...
class AutoloadException extends Exception { }
class AutoloadClass {
public static function autoload($sClassName) {
$bIsExisting = @include_once($sClassName . '.class.php');
if ($bIsExisting) return;
eval("class $sClassName {
function __construct() {
throw new AutoloadException('Class $sClassName not found');
}
static function __callstatic(\$m, \$args) {
throw new AutoloadException('Class $sClassName not found');
}
}");
}
}
spl_autoload_register(array('AutoloadClass', 'autoload'));
?>
Now, my application, that indeed uses run-time defined (static) classes that are actually allowed not to exist, runs correctly again. The AutoloadException can be caught to handle this case without stopping code execution with a Fatal Error.
28-Sep-2007 03:20
Editorial note: The appropriate PHP bug that requests behavior this function emulates is http://bugs.php.net/bug.php?id=42823 . This function does NOT work if there has been an array($obj, 'nonStaticMethod') registered in the autoload stack--while the autoload will be removed, it will be re-registered incorrectly.
The spl_autoload_register() method registers functions in its stack in the order that spl_autoload_register() was called, and subsequently if you want an autoload function to override previous autoload functions you will either need to unregister the previous ones or change the order of the autoload stack.
For example, say in your default implementation of an autoload function you throw an exception if the class cannot be found, or perhaps a fatal error. Later on in your code you add a second implementation of an autoload function which will load a library that the previous method would fail on. This will not call the second autoloader method first, but rather will continue to error out on the first method.
As previously mentioned, you can unregister the existing autoloader that errors out, or you can create a mechanism for unregistering and re-registering the autoloaders in the order you want.
Here is a sample/example of how you might consider re-registering autoloaders so that the newest autoloader is called first, and the oldest last:
<?php
// Editorial notes: Small bug and compatibility fixes
// added to the function
function spl_autoload_preregister( $autoload ) {
// No functions currently in the stack.
if ( ($funcs = spl_autoload_functions()) === false ) {
spl_autoload_register($autoload);
} else {
// Unregister existing autoloaders...
$compat =
version_compare(PHP_VERSION, '5.1.2', '<=') &&
version_compare(PHP_VERSION, '5.1.0', '>=');
foreach ($funcs as $func) {
if (is_array($func)) {
// :TRICKY: There are some compatibility issues and some
// places where we need to error out
$reflector = new ReflectionMethod($func[0], $func[1]);
if (!$reflector->isStatic()) {
throw new Exception('
This function is not compatible
with non-static object methods due to PHP Bug #44144.
');
}
// Suprisingly, spl_autoload_register supports the
// Class::staticMethod callback format, although call_user_func doesn't
if ($compat) $func = implode('::', $func);
}
spl_autoload_unregister($func);
}
// Register the new one, thus putting it at the front of the stack...
spl_autoload_register($autoload);
// Now, go back and re-register all of our old ones.
foreach ($funcs as $func) {
spl_autoload_register($func);
}
}
}
?>
Note: I have not tested this for overhead, so I am not 100% sure what the performance implication of the above example are.
10-Feb-2007 11:54
This function is smart enough not to add the same loader twice. This seems to work for all of the different loader formats. Example:
<?php
class ALoader
{
static function load($class) { return true; }
}
function anotherLoader($class) {
return true;
}
$F = new ALoader;
spl_autoload_register(array('ALoader', 'load'));
spl_autoload_register(array('ALoader', 'load'));
spl_autoload_register(array($F, 'load'));
spl_autoload_register('anotherLoader');
spl_autoload_register('anotherLoader');
var_dump(spl_autoload_functions());
/*
* Results on PHP5.2 CLI, linux.
* array(2) {
* [0]=>
* array(2) {
* [0]=>
* string(7) "ALoader"
* [1]=>
* string(4) "load"
* }
* [1]=>
* string(13) "anotherLoader"
* }
*/
?>
14-Nov-2006 07:19
If your autoload function is a class method, you can call spl_autoload_register with an array specifying the class and the method to run.
* You can use a static method :
<?php
class MyClass {
public static function autoload($className) {
// ...
}
}
spl_autoload_register(array('MyClass', 'autoload'));
?>
* Or you can use an instance :
<?php
class MyClass {
public function autoload($className) {
// ...
}
}
$instance = new MyClass();
spl_autoload_register(array($instance, 'autoload'));
?>
