PHP Autoloader
Seit PHP 5 gibt es Autoloading. Autoloading ist ein Mechanismus, der bei Bedarf eine Klassendefinition lädt.
PHP stellt dafür nicht weniger als zwei Mechanismen zur Verfügung. Zum Einen kann man die Funktion
__autoload() implementieren oder man registriert einen Callback mit
spl_autoload_register().
spl_autoload_register() ist __autoload() vorzuziehen, da man nur einmalig die
Funktion __autoload() implementieren kann. Mit spl_autoload_register()
kann man beliebig viele Autoloader registrieren. Das wird insbesondere beim Einbinden weiterer
Bibliotheken mit eigenen Autoloadern relevant.
Mit einem bequemen Autoloader entfallen die require_once-Orgien am Anfang jeder
Klassendefinition. Mit einem sehr bequemen Autoloader ist ein Verschieben der Klassendefinition
ohne zusätzlichem Aufwand verbunden.
Da Dateien nun erst bei Bedarf geladen werden, muss man sich auch keine Sorgen um unnötige
Dateioperationen machen. Bislang hat man jede evtl. benötigte Klassendefinition am Anfang
der Datei eingebunden. So war PHP beschäftigt Dateien zu verarbeiten, die überhaupt nicht
benötigt werden, wie etwa unerreichte
Type Hints in Catch-Blöcken:
<?php
try {
exit();
} catch (MyException $e) {
/* Dieser Block wird niemals erreicht. Ohne Autoloading
* ist es zwingend erforderlich die Klassendefinition für
* MyException einzubinden. Mit Autoloading verzichtet
* PHP auf die unnötige Klassendefinition.
*/
}
Funktionsweise vom PHP Autoloader
Der PHP Autoloader durchsucht rekursiv definierte Verzeichnisse nach Klassen- und Interfacedefinitionen. Ohne weitere Konfiguration wird als Standard das Verzeichnis aus dem er eingebunden wurde als Klassenverzeichnis angenommen.
Beim Durchsuchen muss der Dateiname keiner Konvention folgen. Es werden sämtliche Dateien nach Klassendefinitionen durchsucht. Dabei werden Dateien die dem Klassennamen ähneln sowie mit .php oder .inc enden bevorzugt. Falls vorhanden wird zum zuverlässigen Entdecken einer Klassendefinition der PHP Tokenizer verwendet.
Eine einmal gefundene Klassendefinition muss nicht erneut gesucht werden. Sie wird in einem PHP Autoloader Index hinterlegt. Folgende Indexstrukturen werden unterstützt:
- Komprimierte serialisierte Hashtabelle (Standard)
- Serialisierte Hashtabelle
- PDO
Klassenkonstruktor
Nach dem Einbinden der Klassendefinition wird falls vorhanden der Klassenkonstruktor
__static() aufgerufen. PHP selbst kennt keinen
Klassenkonstruktor. Ein Klassenkonstruktor wird
anders als der Objektkonstruktor __construct() nur einmalig für eine Klasse aufgerufen.
Er befindet sich mit der Definition
public static function __static()
im statischen Klassenkontext. Dies kann z.B. nützlich sein um Klassenattribute
zu initialisieren:
<?php
class Singleton {
/**
* @var Singleton
*/
private static $instance;
public static function __static() {
self::$instance = new self();
}
/**
* @return Singleton
*/
public static function getInstance() {
return self::$instance;
}
private function __construct() {
}
private function __clone() {
}
}
Anwendung vom PHP Autoloader
Der PHP Autoloader läuft so wie er ist ohne weitere Konfiguration. Um ihn zu benutzen reicht ein einmaliges Einbinden der Datei autoloader/Autoloader.php:
<?php
require dirname(__FILE__) . '/autoloader/Autoloader.php';
Es fällt auf, dass require anstatt require_once verwendet wird.
Das liegt daran, dass man vielleicht andere Komponenten verwendet, die ebenfalls
diese Autoloader Implementierung verwenden. Mit einem erneuten require
sorgt man dafür dass alle Klassenpfade registriert werden.
PHP Autoloader Beispielanwendung
Im Verzeichnis autoloader/example/ befindet sich folgende Beispielanwendung:
- ./classes/packageB/InterfaceB.php
<?php
echo "InterfaceB loaded.\n";
interface InterfaceB {
}- ./classes/packageB/ClassB.php
<?php
class ClassB implements InterfaceB {
static public function __static() {
echo __CLASS__, " loaded.\n";
}
}- ./classes/packageB/ExceptionB.php
<?php
class ExceptionB extends Exception {
static public function __static() {
echo __CLASS__, " loaded.\n";
}
}- ./classes/ClassA.php
<?php
class ClassA extends ClassB {
static public function __static() {
echo __CLASS__, " loaded.\n";
}
}- ./index.php
#!/usr/bin/php
<?php
try {
require dirname(__FILE__) . '/../Autoloader.php';
$a = new ClassA();
$b = new ClassB();
var_dump($a, $b);
} catch (ExceptionB $e) {
}
Die Ausgabe vom Script index.php zeigt, dass die Klasse ExceptionB nicht geladen wurde:
InterfaceB loaded. ClassB loaded. ClassA loaded. object(ClassA)#7 (0) { } object(ClassB)#6 (0) { }