PHP-Framework: Eine Serie

von Manuel am 23.02.2019 um 15:30 Uhr

Willkommen zurück! Ich habe einige Zeit nichts mehr von mir hören lassen. Das hat einen Grund. Ich habe mir die letzten Tage und Wochen den Kopf darüber zerbrochen, was ich hier als nächstes Schreiben soll. Obwohl meine bisherigen Beiträge vermuten lassen, dass ich eher auf Frontend spezialisiert bin, muss ich hier mal etwas klar stellen. Ich war die letzten Jahre eher in der Backend Entwicklung tätig und habe viel Spaß daran, mich stundenlang an bestimmten Algorithmen zu versuchen.

Die Liebe zur Frontend-Entwicklung ist erst über das letzte Jahr gewachsen, nachdem ich auf Udemy diverse Kurse zu verschiedenen Themen durchgearbeitet habe. In der Serie, die ich mit diesem Beitrag einläuten will, geht es aber wieder Richtung Backend und sogar in sehr tiefe Tiefen.

Ich möchte mich nämlich eingehend mit den technischen Details hinter PHP-Frameworks beschäftigen und euch entsprechende Einblicke in die Entwicklung eines solchen Frameworks geben. Dazu werde ich das Projekt nach Abschluss auf GitHub zur Verfügung stellen.

PHP - Meine Sprache für das Web

Auch wenn in den letzten Jahren Sprachen wie Ruby, Python oder auch JavaScript (Node.js) in die Web-Entwicklung auf Serverebene Einzug gehalten haben, möchte ich doch eine Lanze für PHP brechen. Es ist die erste Sprache, die ich gelernt habe, damals noch in Version 4. Zu der damaligen Zeit waren Funktionalitäten, wie ich sie auch in dieser Serie vorstellen möchte, natürlich weit entfernt vom Machbaren. Techniken wie OOP, Namespaces oder Reflection waren höchstens in den Köpfen der Sprachentwickler oder in den Träumen mancher Entwickler vorhanden.

Spätestens mit PHP 7 sind nun alle Features vorhanden, die eine gute Sprache ausmachen. Auch an der Ausführgeschwindigkeit wurde extrem gearbeitet. Maßgeblich dafür war die Einführung eines strengeren Typsystems. Verbindet man das Ganze noch mit Caching über OPCache, dann merkt man nicht mehr viel davon, dass es sich bei PHP eigentlich um eine interpretierte und nicht kompilierte Sprache handelt. Es muss also bei jedem Aufruf einer Seite der komplette Quellcode interpretiert werden.

Wieso ein eigenes Framework

Nachdem ich über die letzten Jahre Projekte eher mit PhalconPHP umgesetzt habe, bin ich durch meine Kurse im letzten Jahr zu Symfony 4 gekommen. Was ich da gesehen habe, hat mich schon stark beeindruckt. Das Framework nimmt einem sehr viel - meist stupide und nervige - Arbeit ab und man kann sich auf die eigene Businesslogik fokusieren. Während ich mir Kurse darüber angeschaut und die Dokumentation durchgelesen habe, habe ich mich häufiger gefragt, wie die ganzen Sachen im Hintergrund funktionieren. Vor ein paar Wochen habe ich dann den Entschluss gefasst, zu Übungszwecken mein eigenes Framework zu schreiben.

Da ich bereits seit einiger Zeit daran arbeite, sind manche Bestandteile schon weiter fortgeschritten als andere und bestimmte Ideen schwirren mir bisher nur im Kopf herum. Da ich während meines Informatik Studiums oft mit Java entwickelt habe und mich die häufig nötige Konfiguration sehr gestört hat, habe ich mich dazu entschieden, wo nur möglich, auf Konfiguration zu verzichten. Ganz lässt es sich leider nicht vermeiden.

Bestandteile

Genug von den einführenden Worten. Kommen wir als nächstes zu den Komponenten des Frameworks und damit den Beiträgen dieser Serie, auf die ihr euch freuen könnt.

Annotation Router

Jedes Framework, egal ob Full-Stack oder nur Backend zur Entwicklung von Schnittstellen, benötigt Routing. Um das Ganze so einfach wie möglich zu machen, werden Routen direkt über PHP Annotationen in den Controller-Klassen definiert. Neben dem Router selbst gibt es dabei auch den Matcher, der die passende Route zu einer aufgerufenen URL sucht. Als letztes kommt dann der Dispatcher zum Einsatz, der die gewünschte Aktion ausführt und das Ergebnis zurückliefert.

Natürlich dürfen auch dynamische Routen nicht fehlen. Seit also gespannt!

Dependency Injection

Innerhalb der Controller-Klassen wird es DI geben. Dazu gebt ihr einfach über den Typ eines Parameters die benötigte Abhängigkeit an und sie wird euch geliefert. Wenn ihr also den Request benötigt, müsst ihr nicht lange suchen.

Für die Dependency Injection wird es auch einen Object Manager geben, der die Instanzierung übernimmt. Die aufgelösten Abhängigkeiten sind entweder als Singleton umgesetzt, oder werden bei jeder Verwendung neu erstellt. Das beste Beispiel für eine Singleton Instanz ist der Request. Dieser ändert sich während des kompletten Programmablaufs nicht und kann somit einmal erstellt und mehrere Male verwendet werden.

Security Provider

Routen müssen natürlich geschützt werden. Dazu wird es sog. Security Provider geben, die verschiedene Authentifizierungsmethoden unterstützen. Den Anfang wird ein JWT Provider machen, da ich das Framework überwiegend als Basis für eine API verwenden möchte, auf die per JavaScript Frontend zugegriffen werden kann.

Zur Absicherung einzelner Routen können dann bestimmte Rollen definiert werden, die Zugriff auf eine bestimmte Methode haben. Das Ganze kann auch auf Controller Ebene definiert werden, wodurch man sich viel Schreibarbeit sparen kann.

Konsole

Jedes vernünftige Framework braucht eine Komponente, um Konsolenkommandos entwickeln zu können. Mein Framework wird auch bereits fertige Kommandos beinhalten. Aber auch ihr erhaltet die Möglichkeit, für eure Fälle nötige Kommandos umzusetzen. Die Kernkomponente kümmert sich dabei um die lästige Arbeit, wie die Verarbeitung von Argumenten oder das Formatieren von Ausgaben.

ODM für OrientDB

Ich muss gestehen, bei dieser Komponente habe ich am längsten gebraucht, um mich für die Eigenentwicklung zu entscheiden. Das liegt vor allem daran, dass es in diesem Bereich mit Doctrine eine hervorragende Bibliothek gibt und ich Teile davon auch beruflich verwende. Ein Object-Document-Mapper sorgt dafür, dass eure Models in die Datenbank gespeichert werden.

Letzten Endes habe ich mich dann aber gefragt, wo wäre da denn der Spaß? Und auch wie schon für Routen, werde ich für die Definition innerhalb der Models auf Annotationen setzen. Ich gehe nach aktueller Planung davon aus, dass dieser Teil am aufwendigsten werden wird, weil er neben dem Mappen von Models auch teilweise die Abstraktion von Datenbankabfragen übernehmen wird.

Weil ich keine Lust darauf hatte den x-ten Wrapper für MySQL zu schreiben, habe ich mich direkt für OrientDB entschieden. Dabei handelt es sich um eine Graph Datenbank, die für mich Neuland darstellt aber hochinteressant klingt. Ich freue mich schon sehr darauf, hier in die Feinheiten einzutauchen und das Ganze etwas zu Abstrahieren.

Fazit

Die hier beschriebenen Komponenten sind fest eingeplant und zum Teil schon relativ weit fortgeschritten. Alles was sich während der Entwicklung ergibt, werde ich natürlich zu gegebener Zeit in den Beiträgen beschreiben.

Nun will ich euch aber nicht länger aufhalten! Ich hoffe ihr freut euch auch so sehr auf die kommenden Beiträge wie ich.