PHP Autoloader

Seit PHP 5 gibt es Autoloading. Autoloading ist ein Mechanismus, der bei Bedarf Klassen-, Interface- und Traitsdefinitionen 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

Funktionsweise vom PHP Autoloader

Der PHP Autoloader durchsucht rekursiv definierte Verzeichnisse nach Klassen-, Traits- 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/ClassB.php
<?php

namespace malkusch\autoloader\example;

class 
ClassB implements InterfaceB
{

    use 
ClassConstructorTrait;



echo 
"ClassB loaded.\n";
./classes/packageB/ClassConstructorTrait.php
<?php

namespace malkusch\autoloader\example;

trait 
ClassConstructorTrait
{
    
    static public function 
classConstructor()
    {
        echo 
"calling class constructor for class "__CLASS__".\n";
    }
    
}

echo 
"ClassConstructorTrait loaded.\n";
./classes/packageB/ExceptionB.php
<?php

namespace malkusch\autoloader\example;

class 
ExceptionB extends Exception
{

    use 
ClassConstructorTrait;

}

echo 
"ExceptionB loaded.\n";
./classes/packageB/InterfaceB.php
<?php

namespace malkusch\autoloader\example;

interface 
InterfaceB
{

}

echo 
"InterfaceB loaded.\n";
./classes/ClassA.php
<?php

namespace malkusch\autoloader\example;

class 
ClassA extends ClassB
{
    
}

echo 
"ClassA loaded.\n";
./index.php
#!/bin/env php
<?php

namespace malkusch\autoloader\example;

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. ClassConstructorTrait loaded. ClassB loaded. calling class constructor for class malkusch\autoloader\example\ClassB. ClassA loaded. object(malkusch\autoloader\example\ClassA)#11 (0) { } object(malkusch\autoloader\example\ClassB)#9 (0) { }

Vorausindizierter Projektautoloader

Neben diesem dynamischen Autoloader gibt es noch die Möglichkeit einen kleingewichtigen Autoloader bauen zu lassen. Das Script autoloader/bin/autoloader-build.php indiziert die definierten Klassenpfade vollständig und hinterlegt in einem Verzeichnis einen kleinen Autoloader welcher sämtliche indizierten Klassen finden wird. Der gebaute Autoloader ist zum Einen sehr schnell und zum Anderen besteht er nur aus einer kleinen Klasse. Wenn einer der folgenden Fälle für Sie zutrifft sollten Sie sich für einen gebauten Autoloader entscheiden:

Wenn Sie das Script ohne Parameter aufrufen wird es sich selbst erklären:

autoloader-build.php -c <classpath> {-c <classpath>} [-d <deploypath] [-r] -c, --classpath=KP Sucht nach Klassen im Klassenpfad KP. Man kann mehrere Klassenpfade hinzufügen indem man jeweils eine weitere Option --classpath angibt. -d, --deploypath=DP Liefert den erzeugten Index und den Autoloader in dem Verzeichnis DP aus. -r, --require Autoloading wird nicht verwendet. Alle Klassen aus dem erzeugten Index werden über require_once eingebunden.

Sehen wir uns nochmal obiges Beispiel an. Unterhalb des Ordners classes/ liegen alle Klassen. Wir wollen nun einen Autoloader bauen und ihn im Ordner autoloader/ ablegen.

autoloader-build.php -c examples/classes -d examples/autoloader

Dies hat nun alle Dateien unterhalb des Ordners classes/ indiziert und einen Autoloader im Ordner autoloader/ erzeugt. Um ihn zu nutzen muss man einfach in der index.php ein require_once __DIR__ . "/autoloader/autoloader.php"; machen.