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:

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) { }