Als ich das erste Mal das noch zu unbekannte Framework Laravel vor der Nase hatte, sind mir sehr schnell die statischen Zugriffe der API aufgefallen. Wo sich viele qualitätsbewusste Entwickler heutzutage die „und wie soll man das testen“ Frage stellen, stand ich hier vor verschlossenen Türen.
THE PHP FRAMEWORK FOR WEB ARTISANS.
Hier scheint sich Laravel 4 eines uns sehr gut bekannten Design Patterns zu bedienen. Die Rede ist von einer Facade. Jede Klasse welche scheinbar statische Methoden bereitstellt, erbt von einer „Facade“-Klasse. Wenn wir also „Config::get(‚database‘)“ aufrufen, wird eine fast leere Facade benutzt. Diese kennt lediglich ihren Accessor (facadeAccessor = ‚config‘) und erweitert entsprechend die Laravel Facade. Die eigentliche Implementierung der genutzten Methode ist dort also gar nicht enthalten.
Das führt uns zu zwei Fragen:
- Welche Klasse wird bei dem Genannten nun wirklich aufgerufen?
- Und woher kennt die Facade augenscheinlich diese Klasse?
Nun dann schauen wir mal genauer hin.
Die Facade kann uns mit „Config::getFacadeRoot()“ die eigentlich aufgerufene Klasse als String ausgeben. In diesem Fall ist das die „Illuminate\Config\Repository„-Klasse welches scheinbar im Hintergrund initialisiert wurde. Sie hat keine statischen Methoden, aber sie besitzt dynamisch die von uns genutzte „get„-Methode.
Okay, wir haben die Funktionalität scheinbar gefunden welche tatsächlich hinter der Facade steckt.
- Wie also weiss die Facade nun, welche Klasse sie initialisieren muss?
- Und vor allem welcher Technik bedient sich Laravel 4 hier?
Intern versucht die Facade in einem Register „resolvedInstance“ den aktuell gesuchten FacadeAccessor (Config) aufzulösen. Kann die Instanz nicht aufgelöst werden, fragt diese bei unserer Hauptkomponente „Application“ nach, wie er das auflösen soll. Ist das auch nicht möglich, so liegt ein Fehler vor welchen wir fixen müssen. ;-)
FAZIT
Am Ende haben wird das ganze also durch eine „Dependency Injection„ aufgelöst (früher in etwas generischer Form auch „Inversion of Control„ genannt).
Nun haben wir die Rätsel ja gelöst, oder?
Warum das ganze überhaupt?
Laravel 4 bietet durch die Facaden einen unglaublich einfachen Zugriff auf die meist benutzen Komponenten vom Framework. Zusätzlich sind diese durch Dependency Injection austauschbar und somit testbar.
Nun kann ich ja viel behaupten, deshalb hier ein anschauliches Beispiel:
<?php class DummyConfig { public function get($value) { return 'notMysql'; } } class ReplaceStaticClassTest extends TestCase { public function test_withoutReplacement() { $this->assertEquals('mysql', Config::get('database.default')); } public function test_replaceIt() { App::instance('config', new DummyConfig); $this->assertEquals('notMysql', Config::get('database.default')); } }
Der erste Test stellt sicher das wir unsere tatsächliche Config-Klasse benutzen und schaut was wir aktuell für einen Datenbank-Adapter nutzen. Dieser scheint „mysql“ zu sein.
Soweit so gut.
Nun haben wir einen zweiten Test geschrieben welcher unsere eigene Config-Implementierung DummyConfig nutzen soll. Diese gibt erstmal nur „notMysql“ aus.
Was haben wir also geschafft? Wir konnten die aktuell instanzierte Config-Klasse mit unserer eigenen Implementierung überschreiben. Genial!
Um das noch kurz klar zu stellen. Laravel bietet die Möglichkeit ein Objekt zu instanzieren NUR wenn es noch keins gibt, eines zu überschreiben, oder zu einer Facade Listener/Composer anzufügen. Außdem ist die Umsetzung des Tests oben in der Realität ein Anti-Pattern, aber veranschaulicht hier gut was ich sage möchte.
Ich hoffe das Gerücht „Laravel 4 hat zu viele statische Klassen! Das ist schlecht!“ konnte hiermit aus der Welt geschafft werden und ich wünsche fröhliche Adventstage.
Noch 4 Tage bis zum zweiten Advent. :-)
Schreibe einen Kommentar