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.
*/
}
Neben dem Komfortaspekt, gibt es noch die Notwendigkeit Klassendefinitionen vor dem Deserialisieren bereitzustellen. Dies wird insbesondere bei einem nicht vorhersehbaren Sessioninhalt notwendig. Ohne Autoloader muss man entweder sämtliche Klassendefinitionen laden oder man riskiert die Existenz unvollständiger __PHP_Incomplete_Class-Objekte, welche letztendlich jeden Methodenaufruf zu einem Fatal Error führen.
Eigenschaften vom PHP Autoloader
- Namespaces
- Indexbasierte Klassensuche
- Optimierte Dateisuche
- Klassenkonstruktor
- Mehrfachverwendung
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.
Nach dem Einbinden der Klassendefinition wird falls vorhanden der
Klassenkonstruktor
public static function classConstructor()
aufgerufen.
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 __DIR__ . '/autoloader/Autoloader.php';
Dieses Beispiel registriert einen Autoloader mit dem Klassenpfad von dem Verzeichnis in dem die Datei mit obigen Code liegt. Man kann auch explizit einen beliebigen Klassenpfad registrieren. Folgendes Beispiel wäre äquivalent zu Obigem:
<?php
require __DIR__ . '/autoloader/Autoloader.php';
$autoloader = new Autoloader(__DIR__);
$autoloader->register();
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/docs/examples/ befindet sich folgende Beispielanwendung:
- ./classes/packageB/ExceptionB.php
<?php
class ExceptionB extends Exception
{
static public function classConstructor()
{
echo __CLASS__, " loaded.\n";
}
}- ./classes/packageB/InterfaceB.php
<?php
echo "InterfaceB loaded.\n";
interface InterfaceB
{
}- ./classes/packageB/ClassB.php
<?php
class ClassB implements InterfaceB
{
static public function classConstructor()
{
echo __CLASS__, " loaded.\n";
}
}- ./classes/ClassA.php
<?php
class ClassA extends ClassB
{
static public function classConstructor()
{
echo __CLASS__, " loaded.\n";
}
}- ./index.php
#!/bin/env php
<?php
try {
require __DIR__ . "/../../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)#2 (0) { } object(ClassB)#9 (0) { }