PHP Autoloader
Autoloading was introduced in PHP 5. Autoloading is a mechanism which requires class, interface and trait definitions on demand.
PHP offers two ways to realize this mechanism.
You can either implement the function __autoload()
or register a callback with
spl_autoload_register().
spl_autoload_register() should be prefered over __autoload(),
as __autoload() can only be implemented once.
spl_autoload_register() can register many Autoloaders.
That's important if you're using several libraries with own Autoloaders.
A convenient Autoloader makes these orgy of require_once in each file superfluous.
An even more convenient Autoload implementation lets moving of class definitions happen
without any additional effort.
As files are required on demand, you don't have to care about unnecessary file operations.
Before Autoloading you had to require every possibly used class definition in advance.
That led PHP to parse files which are actually never used during runtime, like
unreached Type Hints in Catch-Blocks:
<?php
try {
exit();
} catch (MyException $e) {
/* This block will never be reached. Without Autoloading
* it's necessary to require the definition of MyException.
* With Autoloading PHP is able to omit this definition.
*/
}
Beside the comfort aspect, there exists the need to declare classes before deserialization. That's especially important if you cannot predict the content of a session. Without an autoloader you have to include every possible class definition or you risk the instantiation of incomplete __PHP_Incomplete_Class objects. Any method call on these objects leed to a fatal error.
PHP Autoloader features
- Traits
- Namespaces
- Index based class search
- Optimized file search
- Class constructor
- Multiple usage
- Deployable autoloader
How PHP Autoloader works
The PHP Autoloader searches recursively in defined directories for class, trait and interface definitions. Without any further configuration the directory in which the requiring file resides will be used as default class path.
File names don't need to obey any convention. All files are searched for class definitions. Files which are similar to the class name or end with .php or .inc are preferred. If supported, PHP Tokenizer will be used for reliable class definition discovery.
A found class won't be searched again. The location of its class definition is stored in a PHP Autoloader Index.
After requiring a class definition PHP Autoloader tries to call the optional existing
class constructor
public static function classConstructor().
PHP Autoloader usage
The PHP Autoloader runs without any further configuration out of the box. To use it there is nothing more to do than require the file autoloader/autoloader.php:
<?php
require __DIR__ . '/autoloader/autoloader.php';
This example registers an Autoloader with the class path of the directory which contains that code. You could also register an Autoloader with an arbitrary class path. Following example does that by registering an equivalent Autoloader:
<?php
require __DIR__ . '/autoloader/autoloader.php';
$autoloader = new Autoloader(__DIR__);
$autoloader->register();
You may noticed the require instead of require_once.
That's because you might use other components which already use this autoloader
implementation. So you really need to require the Autoloader again, to make sure
all class paths are registered.
PHP Autoloader example application
In the directory autoloader/docs/examples/ exists this example application:
- ./classes/packageB/ClassB.php
<?php
namespace malkusch\autoloader\example;
class ClassB implements InterfaceB
{
use ClassConstructorTrait;
}
echo "ClassB loaded.\n";
- ./classes/packageB/ClassConstructorTrait.php
<?php
namespace malkusch\autoloader\example;
trait ClassConstructorTrait
{
static public function classConstructor()
{
echo "calling class constructor for class ", __CLASS__, ".\n";
}
}
echo "ClassConstructorTrait loaded.\n";- ./classes/packageB/ExceptionB.php
<?php
namespace malkusch\autoloader\example;
class ExceptionB extends Exception
{
use ClassConstructorTrait;
}
echo "ExceptionB loaded.\n";- ./classes/packageB/InterfaceB.php
<?php
namespace malkusch\autoloader\example;
interface InterfaceB
{
}
echo "InterfaceB loaded.\n";- ./classes/ClassA.php
<?php
namespace malkusch\autoloader\example;
class ClassA extends ClassB
{
}
echo "ClassA loaded.\n";- ./index.php
#!/bin/env php
<?php
namespace malkusch\autoloader\example;
try {
require __DIR__ . "/../../autoloader.php";
$a = new ClassA();
$b = new ClassB();
var_dump($a, $b);
} catch (ExceptionB $e) {
}
The output of the script index.php shows that the class ExceptionB wasn't required:
InterfaceB loaded. ClassConstructorTrait loaded. ClassB loaded. calling class constructor for class malkusch\autoloader\example\ClassB. ClassA loaded. object(malkusch\autoloader\example\ClassA)#11 (0) { } object(malkusch\autoloader\example\ClassB)#9 (0) { }Deployable Autoloader
Beside the dynamic autoloader exists a small deployable autoloader.
This autoloader must be build by the script
autoloader/bin/autoloader-build.php. The script builds
an index of all classes and deploys a small autoloader. The deployed
autoloader is fast and with one class very compact. You should prefer
this autoloader if:
- Your component should not have any external dependency.
- You need a lot of performance.
The script explaines itself if you call it without arguments:
autoloader-build.php -c <classpath> {-c <classpath>} [-d <deploypath] [-r] -c, --classpath=CP Search for classes in classpath CP. You can add more classpaths by adding more --classpath options. -d, --deploypath=DP Deploy the generated index and the Autoloader in the path DP. -r, --require Don't use autoloading. All classes of the generated index will be included.Let's have a look at the upper example project. Below the directory classes/ lie all classes. We want to build an autoloader and deploy it at the directory autoloader/.
autoloader-build.php -c examples/classes -d examples/autoloader
To use the deployed autoloader you have to require it in the index.php:
require_once __DIR__ . "/autoloader/autoloader.php";