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
$autoload_function
[, bool $throw = true
[, bool $prepend = false
]]] )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.
-
throw -
This parameter specifies whether spl_autoload_register() should throw exceptions when the
autoload_functioncannot be registered. -
prepend -
If true, spl_autoload_register() will prepend the autoloader on the autoload stack instead of appending it.
Return Values
Returns TRUE on success or FALSE on failure.
Changelog
| Version | Description |
|---|---|
| 5.3.0 | Namespaces support was introduced. |
| 5.3.0 |
The prepend parameter was added.
|
Examples
Example #1 spl_autoload_register() as a replacement for an __autoload() function
<?php
// function __autoload($class) {
// include 'classes/' . $class . '.class.php';
// }
function my_autoloader($class) {
include 'classes/' . $class . '.class.php';
}
spl_autoload_register('my_autoloader');
// Or, using an anonymous function as of PHP 5.3.0
spl_autoload_register(function ($class) {
include 'classes/' . $class . '.class.php';
});
?>
Example #2 spl_autoload_register() example where the class is not loaded
<?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 ...
What I said here previously is only true on Windows. The built-in default autoloader that is registered when you call spl_autoload_register() without any arguments simply adds the qualified class name plus the registered file extension (.php) to each of the include paths and tries to include that file.
Example (on Windows):
include paths:
- "."
- "d:/projects/phplib"
qualified class name to load:
network\http\rest\Resource
Here's what happens:
PHP tries to load
'.\\network\\http\\rest\\Resource.php'
-> file not found
PHP tries to load
'd:/projects/phplib\\network\\http\\rest\\Resource.php'
-> file found and included
Note the slashes and backslashes in the file path. On Windows this works perfectly, but on a Linux machine, the backslashes won't work and additionally the file names are case-sensitive.
That's why on Linux the quick-and-easy way would be to convert these qualified class names to slashes and to lowercase and pass them to the built-in autoloader like so:
<?php
spl_autoload_register(
function ($pClassName) {
spl_autoload(strtolower(str_replace("\\", "/", $pClassName)));
}
);
?>
But this means, you have to save all your classes with lowercase file names. Otherwise, if you omit the strtolower call, you have to use the class names exactly as specified by the file name, which can be annoying for class names that are defined with non-straightforward case like e. g. XMLHttpRequest.
I prefer the lowercase approach, because it is easier to use and the file name conversion can be done automatically on deploying.
When using spl_autoload_register() with class methods, it might seem that it can use only public methods, though it can use private/protected methods as well, if registered from inside the class:
<?php
class ClassAutoloader {
public function __construct() {
spl_autoload_register(array($this, 'loader'));
}
private function loader($className) {
echo 'Trying to load ', $className, ' via ', __METHOD__, "()\n";
include $className . '.php';
}
}
$autoloader = new ClassAutoloader();
$obj = new Class1();
$obj = new Class2();
?>
Output:
--------
Trying to load Class1 via ClassAutoloader::loader()
Class1::__construct()
Trying to load Class2 via ClassAutoloader::loader()
Class2::__construct()
Think twice about throwing an exception from a registered autoloader.
If you have multiple autoloaders registered, and one (or more) throws an exception before a later autoloader loads the class, stacked exceptions are thrown (and must be caught) even though the class was loaded successfully.
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'));
?>
Cleverly - and usefully - I have noticed that (on PHP 5.3 at least) these autoloaders "kick in" even when you call a public static method of an as-yet-unloaded all static class.
An example to allow autoloading classes with namespace:
<?php
define('MY_BASE_PATH', (string) (__DIR__ . '/'));
// Set include path
$path = (string) get_include_path();
$path .= (string) (PATH_SEPARATOR . MY_BASE_PATH . 'php_class/');
$path .= (string) (PATH_SEPARATOR . MY_BASE_PATH . 'php_global_class/');
// $path .= (string) (PATH_SEPARATOR . 'additional/path/');
set_include_path($path);
spl_autoload_register(function ($className) {
$className = (string) str_replace('\\', DIRECTORY_SEPARATOR, $className);
include_once($className . '.class.php');
});
?>
It is never a good idea and a unconscienable concept to create the classes in the autoload function via eval.
It should be a nice feature with these Exception, but i think anyone is able to handle it without this method although. Atm i dont realize for what this is good for...
As i might note, class_exists() will ever define the classes u only want to check for existance, and will therefor ever return true:
<?php
function EvalIsEvil($class) {
eval('class '.$className.'{}');
}
spl_autoload_register('EvalIsEvil');
if (class_exists($s="IsMyModuleHere")) {
// this is no module, but get there with eval()...
return new $s();
}
?>
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.
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"
* }
*/
?>
It seems, that spl_autoload tests, if the class exists, after calling every registered loader. So it breaks the chain, if the class exists and will not call the other loaders
<?php
function a ($c) {
echo "a\n";
class Bla {} // Usually "include 'path/to/file.php';"
}
function b ($c) {
echo "b\n";
}
spl_autoload_register('a');
spl_autoload_register('b');
$c = new Bla();
?>
Note that when specifying the third parameter (prepend), the function will fail badly in PHP 5.2
Be careful using this function on case sensitive file systems.
<?php
spl_autoload_extensions('.php');
spl_autoload_register();
?>
I develop on OS X and everything was working fine. But when releasing to my linux server, none of my class files were loading. I had to lowercase all my filenames, because calling a class "DatabaseObject" would try including "databaseobject.php", instead of "DatabaseObject.php"
I think i'll go back to using the slower __autoload() function, just so i can keep my class files readable
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.
