PHP 5: Im Bann von OOP
Artikel erschienen in Swiss IT Magazine 2004/16
Seit Anfang August steht die erste Version von PHP 5 zum Download bereit und hält eine Menge neuer Funktionen und Konzepte bereit, die den Programmiereralltag noch weiter vereinfachen sollen. In den vier Jahren seit der Veröffentlichung des Vorgängers hat man sich vor allem um ein runderneuertes Objektmodell sowie zahlreiche neue Extensions gekümmert, welche nun die Basis für die nächsten Jahre Programmierarbeit darstellen. Dabei wurde aber auch darauf geachtet, nicht den Kontakt zur Vorversion zu verlieren und für einen reibungslosen Übergang zu sorgen, was dazu führt, dass die meisten PHP-4-Applikationen auch noch mit der neuen Version arbeiten.
Die wohl auffälligste Änderung, mindestens für die Fans von OOP, ist das komplett überarbeitete Objektmodell im Rahmen der Zend Engine 2, das nun quasi vom Kopf auf die Füsse gestellt wurde.
In den Zeiten von PHP 3.0 und PHP 4.0 waren PHP-Objekte im Vergleich zu Objekten in «richtigen» objektorientierten Sprachen ein eher wunderliches Gebilde. Man kann ihre Stellung am besten mit Beigemüse für assoziative Arrays vergleichen, die einen Container für Properties darstellten. Die Grundlage für Objekte stellen aber Klassen dar, die, wie man vielleicht schon aus eigener Erfahrung weiss, aus Properties (Eigenschaften) und Methoden (Funktionen) bestehen. Eine derartige Klasse wird mit dem Operator new initiiert und, mit extends erweitert (Vererbung/Inheritance), ohne die Klasse von Grund auf neu schreiben oder kopieren zu müssen. Und es ist möglich, auf Methoden sowie Properties von innerhalb als auch von ausserhalb des Objekt-Kontexts zuzugreifen. Dies war zwar alles möglich, doch brachte dieses Modell, dem ein assoziativer Array nicht gerecht werden kann, eine Menge von Limitierungen und Problemen mit sich. Dies zeigte sich je länger je mehr, vor allem, da der objektorientierte Ansatz bei den PHP-Programmierern unerwartet beliebt wurde.
Das grösste Problem war dabei die Tatsache, dass in PHP nicht nur die normalen Variablen 'by value' von Funktion zu Funktion durchgereicht wurden, sondern auch die Objekte. Das sorgte dafür, dass diese bei jeder Weitergabe dupliziert wurden. Dies führte nicht nur zu einigen merkwürdigen Bugs, die sich nur schwer finden liessen, wenn man um das Problem nicht wusste, sondern vergeudete auch noch kostbare Ressourcen (siehe Listing 1).
Dies wurde nun behoben, indem PHP intern alle Objekte mit einer Nummer bezeichnet und diese nun in einer Liste führt. Wird das Beispiel von oben mit PHP 5 ausgeführt, wird bei der Zuweisung $math2 = $math1; nicht mehr das komplette Objekt weitergegeben, sondern nur die Zahl, welche das Objekt bezeichnet. Und alles funktioniert wie erwartet (siehe Listing 2).
Bei der Überarbeitung des Objektmodells wurde auch der Funktionsumfang der PHP-Objekte um Möglichkeiten erweitert, die man schon aus anderen objektorientierten Sprachen kennt. So findet man unter anderem eine Zugriffskontrolle über die Schlüsselwörter public/protected/private, statische Methoden, Destruktoren, magische Methoden (__call(), __get(), __set()…), Type Hinting, abstrakte Klassen, Interfaces sowie finale Klassen.
Listing 1: Objekt-Übergabe in PHP 4, Listing 2: Objekt-Übergabe in PHP 5
Listing 3: Ausnahmen für mysqli
Eine weitere stark vermisste Funktion war bis anhin das Exception-Handling. Dessen Fehlen konnte besonders beim Entwerfen von Low-Level-Klassen für erhebliche Kopfschmerzen sorgen.
Tritt ein Fehler in einer Low-Level-Klasse auf, hat man prinzipiell zwei Möglichkeiten, darauf zu reagieren: Entweder, man wirft einen Error und bricht die Verarbeitung sofort ab (trigger_error() oder die()/exit;), oder man gibt an die übergeordnete Ebene ein Signal zurück, dass etwas falsch gelaufen ist, damit sich diese dann mit dem Fehler befassen kann. Bei diesem Signal kann es sich entweder um einen boolschen Wert (Beispiel: return false) oder um einen Errorcode (Beispiel: return -2) handeln.
Während das Abbrechen der Verarbeitung in der Regel eine schlechte Idee ist, da nicht angemessen darauf reagiert werden kann, ist die Fehlersignalisation mit Hilfe eines Signals auch nicht das Gelbe vom Ei: Während mit einem boolschen Wert (true/false) nur signalisiert wird, dass irgendetwas falsch gelaufen ist, lässt sich ein Errorcode in einigen Fällen nicht von einem regulären Rückgabewert unterscheiden, wie folgendes Beispiel zeigt
Ob die Division nun -2 als Resultat oder -2 als Fehler ausgibt, ist nicht herauszufinden. Dieses Problem lässt sich nun elegant mit einer Exception lösen, mit deren Hilfe man immer sicher sein kann, ob man nun einen Fehler oder ein gültiges Resultat vor sich hat:
Durch das try…catch-Konstrukt ist ein Fehler ohne Nebenwirkungen zu erkennen und zu behandeln. Mit einer unterschiedlichen Benennung der abgeleiteten Exception-Klassen (class mathInputException extends Exception {}) lassen sich verschiedene Fehler jeweils angemessen behandeln. Denkbares Beispiel: Bei einer unerwarteten Eingabe kann man eine entsprechende Fehlermeldung ausgeben, schlägt eine Datenbankoperation fehl, kann man dagegen die aktuelle Transaktion beenden.
Das neue Objektmodell bringt eine weitere sehr praktische Funktionalität mit: Das Erweitern von internen Objekten. Zu den essentiellen Überlegungen des Objekt-orientierten Programmierens gehört die Vererbung. Bei der Vererbung erhält die von einer Eltern-Klasse abgeleitete Klasse sämtliche Eigenschaften und Methoden ihrer Eltern-Klasse, kann aber auch um Methoden und Eigenschaften erweitert werden, ohne die Funktionalität der Eltern-Klasse neu implementieren zu müssen. So kann man zum Beispiel der Extension mysqli Exception-Support hinzufügen, ohne gleich einen Wrapper schreiben zu müssen (Beispiel siehe Listing 3). Im Beispiel wird die Klasse mysqli erweitert und die Funktion query() mit Exception-Support ausgestattet. parent::query($query) sorgt dabei dafür, dass die originale Funktionalität von der Eltern-Klasse ausgeführt wird, während die abgeleitete Klasse einzig dafür zuständig ist, im Falle eines Fehlers (mysqli_er ror()) eine Exception zu werfen und ansonsten das Resultat weiterzureichen.