1. Einleitung
In PHP gibt es das sogenannte Konzept des Autoloadings. Im Rahmen dessen kann ein Autoloader — eine Funktion oder eine Methode einer Klasse — definiert werden, welcher immer dann aktiv wird, wenn irgendwo eine noch nicht eingeladene Klasse benötigt wird. An den Autoloader wird der Name dieser Klasse übergeben. Entsprechend kann er auf Basis des Namens die zur Klasse passende Datei includen. Der Vorteil davon ist, dass man zum einen nur an einer zentralen Stelle die Zuordnung zwischen Datei und Klasse speichern muss. Zum anderen braucht sich der restliche Code nicht mehr darum kümmern, Klassen zur gegebenen Zeit zu includen. Sie sind einfach da, wenn man sie braucht. Dieses Vorgehen ist auch wesentlich effizienter als sämtliche eventuell benötigten Klassen am Anfang einer Datei zu includen, da ein Großteil von diesen in der Regel ohnehin nicht verwendet wird.
Eine Funktion kann über spl_autoload_register($callback) als Autoloader definiert werden:
<?php // Mit Closure spl_autoload_register(function ($className) { // $className enthält die Klasse, Datei muss hier entsprechend included werden }); // Mit Funktionsname function myAutoload($className) { } spl_autoload_register('myAutoload'); // Statische Methode einer Klasse class Autoloader { public static function autoload($className) { } } spl_autoload_register(array('Autoloader', 'autoload')); // Methode einer Instanz class Autoloader2 { public function autoload($className) { } } spl_autoload_register(array(new Autoloader2(), 'autoload')); ?>
Es sollte kein Fehler am Ende der autoloading-Funktion geworfen werden, wenn kein passender Eintrag für die Klasse gefunden wurde, denn es können durchaus mehrere verschiedene Autoloader registriert werden, die nacheinander durchgegangen werden, bis einer von diesen die Klasse included.
2. Beispiel ohne Autoloading
In diesem Beispiel wird zunächst das Verhalten ohne Autoloader vorgestellt. Es werden zwei Klassen „MyClass1” und „MyClass2” definiert, welche sich in den Dateien MyClass1.class.php und entsprechend MyClass2.class.php befinden. Der Code aus der Datei test.php wird aufgerufen, welcher die Klassen MyClass1 und MyClass2 benötigt. Es wird explizit die Datei MyClass1.class.php included, aber nicht MyClass2.class.php. So kommt es zu einem Fatal Error, da PHP die Klasse MyClass2 nicht finden kann und — mangels Autoloader — auch nicht automatisch einlädt. In komplexen System mit vielen Klassen und ohne Autoloader sind solche vergessenen includes nichts ungewöhnliches und fallen erst bei der Ausführung auf (und auch dann nur wenn der entsprechende Zweig des Codes ausgeführt wird).
MyClass1.class.php:
<?php class MyClass1 { } ?>
MyClass2.class.php:
<?php class MyClass2 { } ?>
test.php:
<?php include_once("MyClass1.class.php"); // funktioniert, da die Klasse explizit included wurde $myClass1 = new MyClass1(); // funktioniert NICHT, da die Klasse nicht included wurde $myClass2 = new MyClass2(); ?>
<br /> <b>Fatal error</b>: Class 'MyClass2' not found in <b>...\test.php</b> on line <b>8</b><br />
3. Beispiel mit Autoloading
Das nachfolgende Beispiel ist identisch zum vorherigen, nur wird diesmal zusätzlich eine Funktion als Autoloader definiert und registriert. Sobald die beiden Klassen benötigt werden, wird der Autoloader mit deren Namen aufgerufen, welcher die zugehörigen Dateien einlädt.
MyClass1.class.php:
<?php class MyClass1 { } ?>
MyClass2.class.php:
<?php class MyClass2 { } ?>
test.php:
<?php // Hier wird eine Funktion definiert, welche zu MyClass1 und MyClass2 jeweils die passende // Datei included spl_autoload_register(function($className) { switch ($className) { case 'MyClass1': include_once('MyClass1.class.php'); break; case 'MyClass2': include_once('MyClass2.class.php'); break; } }); // kein explizites include für beide Klassen, trotzdem werden eingeladen $myClass1 = new MyClass1(); $myClass2 = new MyClass2(); echo("fertig ohne fatal error!"); ?>
fertig ohne fatal error!
4. Beispiel mit Autoloading 2
Das nächste Beispiel ist vom Prinzip her gleich, aber dafür etwas umfangreicher. Es gibt die folgenden Klassen:
- "Auto" in Datei Auto.class.php: Abstrakte Oberklasse aller Autos.
- "Audi" in Audi.class.php: Eine beispielhafte Implementierung einer Klasse, die sich von „Auto” ableitet. In diesem Fall bekommt der Wagen gleich eine Standardausstattung aus Sitzen, Abstandssensor und Navigationsgerät, welche jeweils durch eigene Klassen abgebildet werden.
- "Tigerfellsitz" in Tigerfellsitz.class.php: Ein möglicher Sitz, der in ein Auto eingebaut werden kann.
- "Abstandssensor" in Abstandssensor.class.php: Ein Standardsensor zur Abstandsmessung.
- "GlobeTrotter2000SuperDeluxeNavi" in GlobeTrotter2000SuperDeluxeNavi.class.php: Ein sehr exklusives Navigationsgerät.
- "MyAutoloader" in MyAutoloader.class.php: Die Klasse, die die Autoloading-Funktion enthält.
Die Klasse „Audi” ist hier das „Zentrum” und benötigt die Klassen „Auto”, „Tigerfellsitz”, „Abstandssensor” und „GlobeTrotter2000SuperDeluxeNavi”. Diese sollen automatisch eingeladen werden, ohne dass „Audi” wissen muss, woher diese genau kommen. Dafür ist der Autoloader aus der gleichnamigen Klasse zuständig, welcher in der Datei test.php einmalig included und registriert wird.
Auto.class.php:
<?php abstract class Auto { protected $sitze = null; protected $abstandssensor = null; protected $navi = null; // irgendwelche Methoden hier... abstract public function hupen(); } ?>
Audi.class.php:
<?php class Audi extends Auto { public function __construct() { // Standardausstattung // Man beachte, dass hier nirgendwo ein include steht und trotzdem erwartet wird, // dass diese Klassen vorhanden sind $this->sitze = array(new Tigerfellsitz(), new Tigerfellsitz(), new Tigerfellsitz(), new Tigerfellsitz()); $this->abstandssensor = new Abstandssensor(); $this->navi = new GlobeTrotter2000SuperDeluxeNavi(); } public function hupen() { echo("Huup huup!"); } } ?>
Tigerfellsitz.class.php:
<?php class Tigerfellsitz { // irgendwelche Methoden hier } ?>
Abstandssensor.class.php:
<?php class Abstandssensor { // irgendwelche Methoden hier } ?>
GlobeTrotter2000SuperDeluxeNavi.class.php:
<?php class GlobeTrotter2000SuperDeluxeNavi { // irgendwelche Methoden hier } ?>
MyAutoloader.class.php:
<?php class MyAutoloader { // Diese Methode wird immer dann aufgerufen, wenn eine noch nicht // eingeladene Klasse entdeckt wird public static function autoload($className) { switch ($className) { case 'Auto': case 'Audi': case 'Tigerfellsitz': case 'Abstandssensor': case 'GlobeTrotter2000SuperDeluxeNavi': include_once($className . '.class.php'); break; } } } ?>
test.php:
<?php // Autoloader einladen und registrieren include_once('MyAutoloader.class.php'); spl_autoload_register(array('MyAutoloader', 'autoload')); $auto = new Audi(); $auto->hupen(); echo("\n"); // aktuellen Zustand des Objekts ausgeben var_dump($auto); ?>
Huup huup! object(Audi)#1 (3) { ["sitze":protected]=> array(4) { [0]=> object(Tigerfellsitz)#2 (0) { } [1]=> object(Tigerfellsitz)#3 (0) { } [2]=> object(Tigerfellsitz)#4 (0) { } [3]=> object(Tigerfellsitz)#5 (0) { } } ["abstandssensor":protected]=> object(Abstandssensor)#6 (0) { } ["navi":protected]=> object(GlobeTrotter2000SuperDeluxeNavi)#7 (0) { } }
5. Vollautomatischer Autoloader mit Namespaces
Da das Pflegen einer Autoloader-Datei aufwendig und störend sein kann, wird nun ein Autoloader definiert, welcher anhand der Namespaces des Klassennamens automatisch die passende Datei heraussucht. Voraussetzung dabei ist es, dass die einzuladenden Dateien in Ordnern liegen, die wiederum alle in einem gemeinsamen Basisverzeichnis liegen, welches dann im Autoloader konfiguriert werden kann. Zudem müssen die Namen der Verzeichnisse und Unterverzeichnisse exakt mit denen in den Namespaces übereinstimmen — einschließlich Groß- und Kleinschreibung. So müsste etwa die Datei „Autoloadertest\Test” unter „Basisverzeichnis/Autoloadertest/Test.class.php” liegen, wobei „Basisverzeichnis” ein Verzeichnis ist, das konfiguriert werden kann.
<?php class NamespaceAutoloader { // Basisverzeichnis in dem wiederum die nach Namespaces benannten Verzeichnisse liegen // Hier wird angenommen, dass sie im selben Verzeichnis wie der Autoloader liegen // Der Wert soll nicht auf einem "/" enden const BASE_DIR = __DIR__; // Hier wird die Dateierweiterung bestimmt, die jede Datei mit einer PHP-Klasse haben muss. // (Üblich ist zumeist eher nur ".php".) const FILE_EXTENSION = '.class.php'; public static function autoload($className) { // $className enthält die Namespaces (hier zum Beispiel "Autoloadertest\Test") // Nur unter Windows ist "\" ein erlaubtes Trennzeichen für Verzeichnisse, daher muss // es an den Systemstandard angeglichen werden (unter Linux etwa zu "Autoloadertest/Test") $className = str_replace('\\', DIRECTORY_SEPARATOR, $className); $filePath = NamespaceAutoloader::BASE_DIR . DIRECTORY_SEPARATOR . $className . NamespaceAutoloader::FILE_EXTENSION; if (file_exists($filePath)) { // Datei zur Klasse includen, falls sie denn existiert include_once($filePath); } } } ?>
<?php namespace Autoloadtest; class Test { } ?>
<?php // Autoloader includen und registrieren include_once('NamespaceAutoloader.class.php'); spl_autoload_register(array('NamespaceAutoloader', 'autoload')); // Eine Klasse einladen, zu der der Pfad im Autoloader nicht explizit angegeben wurde. // Der Pfad zur Datei wird dynamisch generiert. new Autoloadtest\Test(); echo("ende ohne fatal error erreicht!"); ?>
ende ohne fatal error erreicht!
Kommentare (1)
Von neu nach altWir bitten um ihr Verständnis.