Autoloading: Klassen automatisch laden (Thema: PHP Beispiele)

Erläuterung des Prinzips des Autoloadings in PHP und verschiedene Beispiele zur Anwendung

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-Code: Die verschiedenen Möglichkeiten, einen Autoloader zu registrieren
<?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-Code: MyClass1.class.php
<?php
	class MyClass1 {
	}
?>

MyClass2.class.php:

PHP-Code: MyClass2.class.php
<?php
	class MyClass2 {
	}
?>

test.php:

PHP-Code: 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();
?>

HTML-Code: Ausgabe: Fatal Error da MyClass2 nicht eingeladen wurde
<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-Code: MyClass1.class.php
<?php
	class MyClass1 {
	}
?>

MyClass2.class.php:

PHP-Code: MyClass2.class.php
<?php
	class MyClass2 {
	}
?>

test.php:

PHP-Code: 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!");
?>

HTML-Code: Ausgabe
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-Code: 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-Code: 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-Code: Tigerfellsitz.class.php
<?php
	class Tigerfellsitz {
		// irgendwelche Methoden hier
	}
?>

Abstandssensor.class.php:

PHP-Code: Abstandssensor.class.php
<?php
	class Abstandssensor {
		// irgendwelche Methoden hier
	}
?>

GlobeTrotter2000SuperDeluxeNavi.class.php:

PHP-Code: GlobeTrotter2000SuperDeluxeNavi.class.php
<?php
	class GlobeTrotter2000SuperDeluxeNavi {
		// irgendwelche Methoden hier
	}
?>

MyAutoloader.class.php:

PHP-Code: 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-Code: 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);
?>

HTML-Code: Ausgabe
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-Code: NamespaceAutoloader.class.php
<?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-Code: Autoloadtest/Test.php
<?php
	namespace Autoloadtest;
	class Test { }
?>

PHP-Code: test.php
<?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!");
?>

HTML-Code: Ausgabe
ende ohne fatal error erreicht!

Um unsere Webseite für Sie optimal zu gestalten und fortlaufend verbessern zu können, verwenden wir Cookies. Durch die weitere Nutzung der Webseite stimmen Sie der Verwendung von Cookies zu. Weitere Informationen zu Cookies erhalten Sie in unserer Datenschutzerklärung. OK