Laden von Klassen

1. Einleitung

Ab Version 2.0 unterstützt das APF die native Nutzung von PHP-Namespaces. Analog zum Namespace-Konzept der Version 1.X werden alle Resourcen absolut und unter Nutzung ihres Namespaces adressiert. Dies betrifft PHP-Klassen, HTML-Templates, Konfigurations-Dateien sowie SQL-Statement-Resourcen gleichermaßen. Je nach Typ kommt eine direkte, absolute Adressierung nach dem PHP-Namespace-Muster (z.B. (Document-)Controller) oder getrennt nach Namespace und Datei (z.B. Templates) zur Anwendung.

Basis für die ClassLoader-Implementierung des APF ist der PHP-Standard PSR-0. Dieser beschreibt den Aufbau von Namespaces für PHP-Klassen, wird jedoch vom APF ebenfalls für die Adressierung von Templates und Konfigurations-Dateien genutzt.

Die folgende Tabelle zeigt auf, welche Komponenten im APF durch welche Bestandteile definiert werden:

Typ Namespace Name
PHP-Klasse (Controller, Tag, ...) \{Hersteller-Name}\{Namespace}\ {Klassen-Name}
HTML-Template \{Hersteller-Name}\{Namespace}\ {Template-Name}
Konfigurations-Datei \{Hersteller-Name}\{Namespace}\ {Konfigurations-Datei-Name}

Die APF-Implementierung des PSR-0 setzt die absolute Adressierung von allen Applikations-Komponenten voraus. Mit dieser Vorschrift lassen sich an Hand des Hersteller-Namens unterschiedliche Basis-Pfade für die Ablage von PHP-Klassen, Templates und Konfigurations-Dateien definieren.

Aus diesem Grund sind PHP-Klassen immmer voll-qualifiziert (z.B. APF\core\loader\StandardClassLoader) anzugeben, andere Komponenten wie beispielsweise Templates benötigen einen voll-qualifizierten Namespace (z.B. APF\modules\usermanagement\pres\templates).

Die folgenden Kapitel stellen Ihnen die mit dem APF ausgelieferten Komponenten vor.

2. RootClassLoader

Der RootClassLoader ist die zentrale Komponente des APF, die sich um die Verwaltung der verschiedenen ClassLoader kümmert. Das Interface der statischen Klasse bietet die Möglichkeit, verschiedene ClassLoader für unterschiedliche Hersteller (Vendor Name) zu registrieren und damit z.B. Klassen, Templates und Konfigurations-Dateien von unterschiedlichen Basis-Pfaden zu laden. Hierdurch lässt sich eine Trennung von Framework- und Applikations-Code sowie die Trennung von produktivem Code und Test-Artefakten pro Hersteller erreichen.

Ein ClassLoader selbst zeichnet sich durch folgendes Interface aus:

PHP-Code
interface ClassLoader { public function load($class); public function getVendorName(); public function getRootPath(); public function getConfigurationRootPath(); }

Wie aus der Signatur des Interfaces zu entnehmen ist, kennt jeder ClassLoader seinen Hersteller-Namen. Dieser ist der erste Bestandteil des Namespaces bzw. des voll-qualifizierten Klassen-Namens einer PHP-Klasse. Auf diese Weise lässt sich zuordnen, welches Template, welche Konfigurations-Datei bzw. welche PHP-Klasse durch welchen ClassLoader geladen werden soll.

Das Laden von Klassen wird vom jeweils zuständigen ClassLoader direkt an Hand des Hersteller-Namen ausgeführt. Die Standard-Implementierung des APF - der StandardClassLoader - läd ausschließlich Klassen, für die er auch zuständig ist. So lassen sich sehr einfach mehrere ClassLoader für unterschiedliche Hersteller-Namen und damit Basis-Pfaden registrieren.

Für das Laden von Templates und Konfigurations-Dateien werden ebenfalls die registrierten ClassLoader genutzt, jedoch stellen Sie in diesem Fall lediglich den Basis-Pfad zur Verfügung. Dieser wird dann genutzt um den vollständigen Datei-Pfad zu erzeugen. Damit können auch Templates (getRootPath()) und Konfigurations-Dateien (getConfigurationRootPath()) aus unterschiedlichen Basis-Pfaden geladen werden.

Der im APF enthaltene Mechnismus zum Laden von Klassen ist voll kompatibel zu anderen Frameworks/Tools, da der RootClassLoader über die Funktion spl_autoload_register() registriert wird und sich damit des PHP-Standards bedient.

3. StandardClassLoader

Der StandardClassLoader ist die Standard-Implementierung des ClassLoader-Interfaces. Er kümmert sich um das Laden von Klassen, für die er mit einem definierten Hersteller-Namen registriert wurde.

Hierzu wird er im Konstruktor mit dem gewünschten Hersteller-Namen und den Basis-Pfaden für Code und Konfiguration initialisiert. Das dritte Argument (Basis-Pfad für Konfigurationen) ist optional und wird ohne explizite Angabe mit dem Inhalt des Basis-Pfads für Code-Dateien initialisiert. Beispiel:

PHP-Code
use APF\core\loader\RootClassLoader; use APF\core\loader\StandardClassLoader; $classLoader = new StandardClassLoader('ACME', '/acme/src'[, '/acme/conf']); RootClassLoader::addLoader($classLoader);

Beabsichtigen Sie Code und Konfiguration von unterschiedlichen Herstellern zu trennen, so können Sie dies mit Hilfe des StandardClassLoader unter Nutzung eines eigenen Hersteller-Namens und Basis-Pfades wie im Code gezeigt konfigurieren.

Umgekehrt lassen sich über diesen Mechanismus auch unterschiedliche Hersteller aus dem selben Basis-Pfad bedienen. Registrieren Sie hierzu einfach mehrere StandardClassLoader mit unterschiedlichen Hersteller-Namen jedoch gleichem Basis-Pfad.

Ein weiteres Szenario ist die Trennung von Code und Konfigurationen eines Herstellers. Mit Hilfe der beiden Basis-Pfade für Klassen und Templates (getRootPath()) sowie Konfigurationen (getConfigurationRootPath()) haben Sie die Möglichkeit, Code-Fragmente von ihren Konfigurationen räumlich zu trennen.

Der Basis-Pfad des StandardClassLoader, der für den Hersteller APF in der Datei APF/core/bootstrap.php registriert wird, kann in der Bootstrap-Datei Ihrer Applikation mit Hilfe der Variable $apfClassLoaderRootPath an Ihre Umgebung angepasst werden.

Weiterhin lassen sich über diesen Mechanismus Code des APF von Ihrer Konfiguration trennen. Setzen Sie hierzu in der Bootstrap-Datei Ihrer Applikation die Variable $apfClassLoaderConfigurationRootPath gemäß der Struktur Ihrer Anwendung vor dem Einbinden der APF/core/bootstrap.php.

Details hierzu entnehmen Sie bitte dem Kapitel 4.

Das Mapping von Hersteller-Name bzw. Namespace und Typ (z.B. PHP-Klasse) auf einen konkreten Datei-Namen übernimmt jeweils die Implementierung des ClassLoader. Das Standard-Setup des APF verhält sich wie folgt:

Namespace/voll-qualifizierter Name Name Datei-Pfad
ACME\application\pres\controller\DoSomethingController - /acme/src/application/pres/controller/DoSomethingController.php
ACME\application\pres\templates something.html /acme/src/application/pres/templates/something.html
ACME\biz\factory goods.ini /acme/src/config/biz/factory/{CONTEXT}/{ENVIRONMENT}_goods.ini

Bitte beachten Sie, dass das Mapping der Namen von Konfigurations-Dateien maßgeblich von der Implementierung des ConfigurationProvider abhängt. Details dazu finden Sie in Kapitel Konfiguration.

4. Anwendung

Die folgenden Kapitel zeigen Ihnen, wie die Sie das Laden von Klassen, Templates und Konfigurations-Dateien anpassen und den ClassLoader-Mechanismus in Ihren Anwendungen einsetzen können.

4.1. Anpassung des Basis-Pfads

Jeder ClassLoader wird mit einem Basis-Pfad initialisiert, der für das Laden von Klassen, Templates und Konfigurations-Dateien des jeweiligen Herstellers genutzt wird. Den StandardClassLoader für den Hersteller APF initialisiert die Datei APF/core/bootstrap.php, die in der Bootstrap-Datei Ihrer Anwendung eingebunden wird. Eine typische index.php hat folgenden Inhalt:

PHP-Code
include('./APF/core/bootstrap.php'); use APF\core\frontcontroller\Frontcontroller; /* @var $fC Frontcontroller */ $fC = Singleton::getInstance(Frontcontroller::class); echo $fC->start('...', '...');

Ohne weitere Konfiguration evaluiert das Framework den Pfad zur Datei bootstrap.php und nutzt den darüber liegenden Ordner - also denjenigen Ordner, in dem sich core, extensions, modules und tools befinden - als Basis-Pfad für die Initialisierung des StandardClassLoader. Als Hersteller-Name für alle APF-Komponenten wird APF verwendet.

Nutzen Sie Symlinks oder wird Ihre Code-Basis für die Auslieferung von mehreren Projekten verwendet, können Sie den Basis-Pfad über die Variable $apfClassLoaderRootPath vor der Einbindung der Datei bootstrap.php selbst definieren. Ihre index.php ändert sich damit wie folgt:

PHP-Code
$apfClassLoaderRootPath = '/path/to/apf/src'; include('./APF/core/bootstrap.php'); use APF\core\frontcontroller\Frontcontroller; /* @var $fC Frontcontroller */ $fC = Singleton::getInstance(Frontcontroller::class); echo $fC->start('...', '...');

PHP-Klassen, Templates und Konfigurations-Dateien für den Hersteller APF werden in Ihrer Applikation anschließend aus dem Pfad /path/to/apf/src geladen.

4.2. Anpassung des Basis-Pfads für Konfigurationen

Neben dem Basis-Pfad für Klassen und Templates kann jeder ClassLoader mit einem Basis-Pfad für Konfigurations-Dateien initialisiert werden. Den StandardClassLoader für den Hersteller APF initialisiert ebenfalls die Datei APF/core/bootstrap.php, die wiederum in der Bootstrap-Datei Ihrer Anwendung eingebunden wird.

Ohne weitere Konfiguration evaluiert das Framework den Pfad zur Datei bootstrap.php und nutzt - wie auch in Kapitel 4.1 - den darüber liegenden Ordner als Basis-Pfad für die Initialisierung des StandardClassLoader. Als Hersteller-Name für alle APF-Komponenten wird APF verwendet.

Möchten Sie neben dem Basis-Pfad für Klassen und Templates auch die Ablage von Konfigurations-Dateien selbst bestimmen, so können Sie dies über die Variable $apfClassLoaderConfigurationRootPath vor der Einbindung der Datei bootstrap.php erledigen. Ihre index.php ändert sich damit wie folgt:

PHP-Code
$apfClassLoaderRootPath = '/path/to/apf/src'; $apfClassLoaderConfigurationRootPath = '/path/to/apf/config' include('./APF/core/bootstrap.php'); use APF\core\frontcontroller\Frontcontroller; /* @var $fC Frontcontroller */ $fC = Singleton::getInstance(Frontcontroller::class); echo $fC->start('...', '...');

PHP-Klassen und Templates für den Hersteller APF werden nun aus dem Pfad /path/to/apf/src geladen, Konfigurations-Dateien suchen die ConfigurationProvider im Ordner /path/to/apf/config.

4.3. Registrieren von weiteren ClassLoadern

Um weitere ClassLoader nutzen zu können, müssen diese zunächst in der Bootstrap-Datei Ihrer Anwendung registriert werden. Hierzu können Sie die Methode RootClassLoader::addLoader() nutzen. Die genannte Methode erwartet eine Instanz vom Typ ClassLoader. Sie können daher sowohl auf eine Implementierung des APF zurück greifen (StandardClassLoader) als auch eigene Implementierung nutzen.

Der folgende Code-Block registriert einen StandardClassLoader für einen eigenständigen Hersteller-Namen:

PHP-Code
include('./APF/core/bootstrap.php'); use APF\core\loader\RootClassLoader; use APF\core\loader\StandardClassLoader; RootClassLoader::addLoader(new StandardClassLoader('ACME', '/acme/src'));

Um Konfiguration und Code zu trennen, lässt sich mit Hilfe des dritten Arguments im Konstruktor des StandardClassLoader ein eigener Basis-Pfad für Konfigurations-Dateien definieren. Dieses lässt sich wie folgt nutzen:

PHP-Code
include('./APF/core/bootstrap.php'); use APF\core\loader\RootClassLoader; use APF\core\loader\StandardClassLoader; RootClassLoader::addLoader(new StandardClassLoader('ACME', '/acme/src', '/acme/config'));

Konfigurations-Dateien des Herstellers ACME werden nun aus dem Ordner /acme/config geladen.

Reicht der Lieferumfang des APF für Ihren Anwendungsfall nicht aus, können Sie jederzeit eigene ClassLoader-Implementierungen zum Einsatz bringen. Das folgende Beispiel zeigt eine Implementierung, die Klassen mit impliziter Angabe von Hersteller, Namespace und Klassen-Namen in der Form ACME_Name_Space_ClassName läd:

PHP-Code
namespace ACME\loader; use APF\core\loader\ClassLoader; class ExplicitClassNameClassLoader implements ClassLoader { private $vendorName; private $rootPath; public function __construct($vendorName, $rootPath) { $this->vendorName = $vendorName; $this->rootPath = $rootPath; } public function load($class) { // loads classes that are including vendor, namespace, and name within // the class name directly, e.g. ACME_Foo_Bar_ClassName. if (strpos($class, $this->vendorName . '_') !== false) { $file = $this->rootPath . '/' . str_replace( $this->vendorName . '_', '', str_replace('_', '/', $class) ); include($file); } } public function getVendorName() { return $this->vendorName; } public function getRootPath() { return $this->rootPath; } public function getConfigurationRootPath() { // Assumes that classes/templates and configuration files // reside within the same folder! May be different within // your implementations. return $this->rootPath; } }

Die Nutzung gestaltet sich wie folgt:

PHP-Code
include('./APF/core/bootstrap.php'); use APF\core\loader\RootClassLoader; use ACME\loader\ExplicitClassNameClassLoader; RootClassLoader::addLoader(new ExplicitClassNameClassLoader('ACME', '/acme/src'));

4.4. Laden von Anwendungskomponenten

Das Laden von Klassen wird direkt vom RootClassLoader übernommen. Möchten Sie Komponenten wie z.B. Schrift-Dateien direkt im Verzeichnisbaum Ihrer Anwendung ablegen und einbinden, können Sie auf die Funktonalitäten des RootClassLoader zurückgreifen.

Das folgende Code-Beispiel zeigt Ihnen, wie Sie eine Schrift-Datei, die parallel zur Klasse ImageHeadlineGenerator abgelegt ist, zur Erzeugung einer grafischen Überschrift nutzen können:

PHP-Code
namespace ACME\components\pres; use APF\core\loader\RootClassLoader; class ImageHeadlineGenerator { private $fontName; private $fontSize; public function __construct($fontName, $fontSize) { $this->fontName = $fontName; $this->fontSize = $fontSize; } public function generateHeadline($text) { $img = imagecreate($this->getWidth($text), $this->getHeight($text)); // Der RootClassLoader kann an Hand des aktuellen Namespace den zuständigen // ClassLoader bestimmen. Dieser wiederum liefert den Basis-Pfad. $loader = RootClassLoader::getLoaderByNamespace(__NAMESPACE__); $rootPath = $loader->getRootPath(); $currentNamespace = RootClassLoader::getNamespace(__NAMESPACE__); // Unter der Annahme, dass die Schrift-Dateien parallel zur aktuellen // Klasse liegen, kann der Pfad der Datei wie folgt definiert werden: $font = $rootPath . '/' . str_replace('\\', '/', $currentNamespace) . '/' . $this->fontName . '.ttf'; imagettftext($img, $this->fontSize, 0, 10, 10, 0, $font, $text); imagepng($img); } private function getWidth($text) { ... } private function getHeight($text) { ... } }

Im Code-Beispiel wird die Methode getLoaderByNamespace() dazu genutzt den für den aktuellen Hersteller zuständigen ClassLoader zu beziehen. Dieser wird dann dazu verwendet, den aktuellen Basis-Pfad des Herstellers für PHP-Klassen, Templates, Konfigurations-Dateien und andere Dateien zu beziehen.

Des Weiteren wird der RootClassLoader in den folgenden Zeilen dazu eingesetzt um aus dem voll-qualifizierten Namespace den relevanten Teil ohne Hersteller-Name zu extrahieren. Die beiden Teile - Basis-Pfad und Namespace-Pfad - ergeben dann den Datei-Pfad der Schrift-Datei.

4.5. Anwendung des RootClassLoader

Neben den in Kapitel 4.4. vorgestellten Methoden verfügt der RootClassLoader über weitere Funktionen, die Sie für das Erzeugen von Datei-Pfaden bzw. zum Laden von Applikations-Inhalten nutzen können.

Die folgende Liste zeigt die vorhandenen Methoden und ihre Anwendungsfälle:

Methode Anwendung
getLoaderByVendor() Gibt die ClassLoader-Instanz zurück, die mit dem übergebenen Hersteller-Namen registriert wurde.
getLoaderByNamespace() Gibt die ClassLoader-Instanz zurück, die zum übergebenen Namespace passt.
getLoaderByClass() Gibt die ClassLoader-Instanz zurück, die zum übergebenen, voll-qualifizierten PHP-Klasse passt.
getClassName() Gibt den Klassen-Namen einer voll-qualifizierten Klasse (z.B. APF\core\loader\RootClassLoader) zurück (z.B. RootClassLoader).
getNamespace() Gibt den Namespace einer voll-qualifizierten Klasse (z.B. APF\core\loader\RootClassLoader) zurück (z.B. APF\core\loader).
getNamespaceWithoutVendor() Arbeitet genau wie getNamespace(), lässt aber den Vendor-Namen aus (dem obigen Beispiel folgend: core\loader).
getVendor() Gibt den Hersteller-Namen einer voll-qualifizierten Klasse (z.B. APF\core\loader\RootClassLoader) zurück (z.B. APF).

Kommentare

Möchten Sie den Artikel eine Anmerkung hinzufügen, oder haben Sie ergänzende Hinweise? Dann können Sie diese hier einfügen. Die bereits verfassten Anmerkungen und Kommentare finden Sie in der untenstehenden Liste.
Für diesen Artikel liegen aktuell keine Kommentare vor.