Ein Evolutionssprung respektive Generationenwechsel bahnt sich innerhalb von PHP4 an. Der Sprung vom PHP3- zum PHP4-Paradigma mit einer verbesserten Modularität des Basiscodes (Zend Engine 1) war revolutionär. Mit der Veröffentlichung der Zend Engine 2 Ende 2002 soll PHP5 aber endlich Akzeptanz erlangen und den gewachsenen Ansprüchen im Business- bzw. Enterprise-Markt gerecht werden, um nicht nur im Low-End-, sondern auch im High-End-Sektor mitmischen zu können. Wenn von der Zend Engine 2 gesprochen wird, ist indirekt schon fast synonym die Rede von PHP5, da die meisten Änderungen dieses Versionssprungs durch die Änderungen am Sprachkern (Zend Engine) verursacht werden. Die Zend Engine 2 leitet eine neue Generation ein, wobei das Hauptaugenmerk auf der Implementierung essenzieller Sprachelemente und einer verbesserten objektorientierten Verhaltensweise liegt, die durch eine bessere sowie schnellere OO-Unterstützung glänzt und damit eine einfachere Codepflege und Wartung von Webapplikationen in PHP5 ermöglicht.
Historischer Rückblick
Um den revolutionären Versionssprung der Zend Engine 2 und dessen Bedeutung nachzuvollziehen, werde ich nachfolgend wesentliche Etappen auf dem Weg zur Zend Engine 2 skizzieren, um Ihnen eine Einführung zur Absicht und Notwendigkeit des verbesserten OO-Modells zu vermitteln.
Im Frühstadium von PHP, besser gesagt in den ersten veröffentlichten Versionen von PHP/FI und PHP/FI 2.0 bis einschließlich PHP3, war PHP intern nicht mehr als ein monolithischer Interpreter. In dieser Zeit war die starke Erweiterungsmöglichkeit gekoppelt mit einer soliden Infrastruktur ein Schlüssel zum gewaltigen Erfolg von PHP3. Unter dem monolithischen Interpreter kann man sich ein Programm vorstellen, welches kontinuierlich jede Zeile im Programmcode abarbeitet und diese ausführt. Da dies aber keinesfalls einer hochperformanten und zufriedenstellenden Lösung entsprach und der Quellcode von PHP3 zunehmend größer wurde, aber kein sinnvoller Ansatz zur Optimierung und Integration neuer Funktionen existierte, war keine konsequente Weiterentwicklung von PHP gewährleistet.
In dieser Zeit setzten sich die beiden Informatik-Studenten Andi Gutmans und Zeev Suraski, die bereits PHP3 mit der Bezeichung Zend Engine 0.5 schrieben, ein Ziel; sie wollten PHP eine grundlegend neue Architektur verschaffen. In einer der ersten Zend Engine [1] Varianten konnte man einen Paradigmen-Sprung vom alten Execute-while-Interpreting (PHP3) zum Compile-first/Execute-later (PHP4) Interpretationsvorgang ausmachen. Da PHP3 trotz flexibler Ausrichtung keinen der beiden befriedigen konnte, wurde der Interpreter modular aufgeteilt, was eine Abgrenzung der einzelnen Aufgabengebiete (Parser, Ressourcenverwaltung, Skriptausführung) mit sich brachte und den Startschuss für die Zend Engine 1.0 respektive PHP4 gab. Kurz und knapp könnte man die Zend Engine als Mittelpunkt all dieser Module charakterisieren, sozusagen als Herzstück von PHP, welche die grundlegenden Sprachelemente bereit stellt und sich um die konsistente und korrekte Zusammenarbeit der einzelnen Module und PHP-Extensions kümmert.
Ansprüche, Akzeptanz und Reaktionen
In den letzten Jahren hat PHP einen rasanten Wandel vollzogen. Vom einstigen PHP/FI Form Interpreter versucht PHP zunehmend in Bereiche vorzustoßen, in denen die Enterprise-Tauglichkeit eine immer größer werdende Rolle spielt. Bei der Entscheidung zum Enterpriseeinsatz spielen Faktoren wie Robustheit der Architektur, Skalierbarkeit und Modularität eine bedeutende Rolle, wobei PHP im Vergleich zu etablierten Sprachen (Java, ASP, JSP) erst aufschließen muss. Die Popularität von PHP ist rapide gestiegen und dies hat verschiedenste Erfolgsfaktoren. Bei derzeitig sieben Millionen Installationen und einer stetig steil steigenden Erfolgskurve ist PHP an einem Punkt angelangt, an dem ein entscheidender Schritt in Richtung Zukunft ansteht und sich zunehmend verschiedene Gruppierungen und zwiespältige Stimmungen innerhalb der Zielgruppen bilden. Teilweise kam man zu der Vermutung, dass PHP keine feste Vision vor Augen hat, also kein klares Ziel, in welche Richtung sich die Sprache weiterentwickeln soll.
Auf der einen Seite verspürt PHP zunehmend die Notwendigkeit zur Verbesserung des Objektverhaltens und der Implementierung weiterer objektorientierter Eigenschaften und zusätzlicher Funktionalitäten am Sprachkern, die man von etablierten OO-Hochsprachen wie Java [2] oder C++ kennt. Der Druck seitens professioneller bzw. erfahrener Programmierer wächst diesbezüglich zunehmend. Diese fordern eine Überarbeitung der Zend Engine, um weitere Akzeptanz zu gewinnen und um bei den Forderungen im Enterprise-Marktsektor bestehen zu können, die beiläufig die Umsetzung von unternehmenskritischen Applikationen mit sich bringen. Zusätzlich wird die Erschließung von Bereichen fokussiert, für die man bisher eher Programmiersprachen wie Java eingesetzt hat, jetzt aber auch PHP zum engeren Entscheidungskreis hinzuzählt.
Auf der anderen Seite kommen in diesem Zusammenhang allerdings zwei wesentliche Dinge zum Vorschein. PHP ist von Natur aus eine sehr einfache Sprache mit simpler Syntax und Semantik und einer sehr flachen Lernkurve, was für die wachsenden Marktanteile von PHP spricht und Quereinsteigern und Anfängern einen optimalen Einstieg gewährleistet. Man könnte die Einfachheit der Open Source-Sprache auch für ein wesentliches Überlebenskriterium von PHP überhaupt halten. Deswegen ist das teilweise eher konservative und zurückhaltende Verhalten der Kernentwickler sehr gut nachzuvollziehen, da die Zukunftsvisionen zu sehr auseinander driften. Trotz der bekannten historisch bedingten Designschwächen, die auf ein nicht wohlüberlegtes Language-Design zurückzuführen sind, können die Kernentwickler nicht von heute auf morgen einen Schnitt begehen und PHP somit zu einer vollständig an Java ausgerichteten OO-Sprache formen oder PHP5 so systemnah wie Perl gestalten.
Dieses Spannungsfeld zwischen den beiden Gruppen mit verschiedenen Zukunftsansichten, bestimmte in den letzten Monaten wesentlich die Diskussionen auf der eigens eingerichteten Zend Engine 2 Mailingliste [3] und beeinflusste auch den Entwicklungszyklus. Die Syntax soll auch weiterhin einfach zu verstehen bleiben, andererseits sollen die Wünsche ambitionierter Programmierer berücksichtigt werden, eine äußerst prekäre Konstellation.
PHP besitzt grundlegend alles, was eine Web-basierte Skriptsprache benötigt. Für viele reicht diese Ausrichtung auf Software-Lösungen im Web-basierten Umfeld allerdings nicht aus. Am Bereich der Enterprise-Lösungen sollte sich PHP aber nicht ausschließlich orientieren. Die dynamische Ausrichtung der Sprache ist einer der Pluspunkte und Gründe des Erfolgs, welcher zwangsläufig zur Effizienzsteigerung aufgrund der kurzen Entwicklungszeiten beiträgt. Es macht deshalb keinen Sinn, PHP zu einer vollständig strikt typisierten Programmiersprache analog zu C#, C++ oder Java zu formen, da es dafür einfach nicht konzipiert wurde.
Verlauf des Entwicklungszyklus
Im August 2001 wurde erstmals der Grundstein für die neue Generation in Form eines Entwurfmuster Design Drafts [4] der Zend Engine gelegt, in welchem man strukturiert alle Neuerungen respektive Leistungsmerkmale der zweiten Generation ausgearbeitet vorfand. Um das Konzept eingehend intensiv zu diskutieren, wurde eine Mailingliste ins Leben gerufen, um die geplanten Features zu präzisieren bzw. weiter auszuarbeiten und über die weitere Entwicklung der Zend Engine fortan zu diskutieren. Einige Monate später, nachdem die Evaluierung des Entwurfs vollständig abgeschlossen war, begann die Firma Zend Technologies, vertreten durch Andi Gutmans und Zeev Suraski, endlich mit der Entwicklung an der Version 2 der Zend Engine. Der Design Draft war salopp formuliert eine Art Wunschzettel der PHP-Community, welcher in einem umsetzbaren Rahmen zum Entwurfmuster geformt wurde. Dabei wurde allerdings streng darauf geachtet, dass während des Entwicklungszyklus die Rückwärtskompatibilität zur Zend Engine 1 in den meisten Fällen beibehalten wurde, um die Modifizierungen am Quellcode so gering wie möglich zu halten. Des Weiteren stellte sich die Entwicklung als sehr schwierig heraus, da man keine der beiden Zielgruppen verlieren wollte und oftmals noch subjektiven Wünschen nachkommen musste.
Genereller Überblick über die Zend Engine 2
Die Zend Engine 2 ist ein Versuch, trotz der zuvor erwähnten Designschwächen von PHP, PHP5 für den Einsatz im Enterprise-Sektor konkurrenzfähig zu gestalten und einzugliedern. Sicherlich wurde PHP auch schon vorher von Firmen gezielt eingesetzt, aber die Unterstützung in punkto Zuverlässigkeit und Sicherheit, die eine Programmiersprache geben sollte, war keineswegs zufriedenstellend ausgeprägt.
In der folgenden Übersicht befinden sich alle Kernbestandteile der Änderungen an der Zend Engine 2.0 [5], die sich momentan allerdings noch im Entwicklungsstadium befinden. Somit sind wohl noch einige Features vor der letztendlichen Veröffentlichung von PHP5 zu erwarten. Das gesetzte Ziel fokussierte sich primär auf eine verbesserte OO-Unterstützung und die Implementierung neuer Konstrukte, um das OO-Design zu erleichtern. In den folgenden Passagen werde ich detailliert auf die einzelnen unten aufgeführten Features eingehen und jeweils ein Quellcode-Listing zur Veranschaulichung hinzufügen.
Features der Zend Engine 2
- Exception Handling
- Built-in Backtracing
- Namespaces/Nested Classes
- Verbesserte OO-Verhaltensweise
- Implizite Referenzierung von Objektinstanzen
- Optimierter Objektrückgabemechanismus
- Object Cloning
- Konstruktoren & Destruktoren
- Datenkapselung
- Objekt-Dereferenzierung
Case Sensitivity
Bisher waren Funktionsnamen case-insensitiv, diese Eigenschaft bleibt auch zukünftig bestehen, trotz heftig geführter Diskussionen. Viele stimmten für die Umstrukturierung auf case-sensitiv, was bedeuten würde, dass eine Unterscheidung von Groß- und Kleinschreibung (
parseXML() würde nicht mit
parsexml() gleichgesetzt werden) stattfindet, was bei verschiedenen anderen Programmiersprachen praktiziert wird. Die beibehaltene case-insensitiv Variante ist zwar nicht die sauberste Lösung, aber durch die Implementierung von Namensräumen werden Namenskollisionen erheblich reduziert und die Kompatibilität zu früheren Versionen bleibt im Gegensatz zu case-sensitiv weiterhin bestehen. Des Weiteren werden die Objekt Overloading-Extensions nur case-sensitiv Parameter entgegen nehmen, um somit die durch PHP unterstützten case-sensitiven Technologien wie Java oder SOAP durch das Überladen von Objekten zu ermöglichen.
Exception Handling
Das Exception Handling ist ein bedeutsames Merkmal der Zend Engine 2 und stellt den Mechanismus zur Verfügung, um Ausnahmefälle (Fehler) von sensiblen Codeabschnitten innerhalb von PHP5 abzufangen und zu behandeln. Mit diesem Feature kann man endlich von einer sauberen und eleganten Methode reden, die es erlaubt, fehleranfälligen Programmcode zu kapseln und somit die Wiederherstellung bei Fehlern stark zu vereinfachen. In PHP4 existierte in diesem Zusammenhang keine native Methode, um Ausnahmebedingungen während der Programmausführung zu behandeln und man war somit zwangsläufig an viele Schachtelkonstruktionen mittels
if/
else gezwungen, die einen übersichtlichen und lesbaren Programmcode sichtlich behinderten.
Das in Java populär gewordene Exception Handling und die daran angelehnte Methode setzt genau an dieser zuvor erwähnten Problematik effizient an und offenbart sehr elegante Werkzeuge, um Ausnahmebedingungen zu verarbeiten und somit den Programmcode übersichtlicher, strukturierter und logischer zu gestalten. Man weiß im Voraus, wie man auf jede Situation reagieren kann und besitzt des Weiteren die Kontrolle darüber, Ausnahmefälle differenziert und gesondert auf bestimmte, selektierte Codepassagen anzuwenden, die an einem Ort platziert sind und durch das Exception Handling unter besonderer Bewachung stehen.
Unter Exceptions versteht man grundsätzlich ein Signal, welches innerhalb der Software, im Falle einer Ausnahmebedingung bzw. bei einem Fehler, auftritt. In diesem Kontext gibt es ein paar Exception-Anweisungen, die von zentraler Bedeutung sind. Im Falle eines Fehlers wird eine Exception erzeugt (
throw), um eine Ausnahmebedingung während des Programmablaufs zu signalisieren bzw. auszulösen. Um diese Ausnahmebedingungen nachfolgend gesondert zu behandeln, benötigt man die Anweisung (
catch), mit der man den Fehler abfängt. Das Ziel dieser Methode ist im Allgemeinen nichts anderes, als alle Aktionen durchzuführen, die den normalen konsistenten Zustand der Software wieder herstellen. Eine weitere auch aus Java bekannte Block-Anweisung
finally wurde nicht für die Implementierung vorgesehen, weil sich die Implementierung schwieriger als erwartet gestaltet hatte und außerdem diesbezüglich keine unbedingte Notwendigkeit bestand. Grundsätzlich erfüllt der
finally Block die Aufgabe, jegliche Anweisungen inmitten des Blocks garantiert auszuführen, unabhängig vom Status des beendeten
try Blocks, der normalerweise vorher gesetzt wird.
Ein typisches Szenario des Exception Handlings wäre ein durch
try gekapselter Codeabschnitt, der bei einem Ausnahmefall mittels
throw eine Exception erzeugen würde. Nachfolgend würde der Programmablauf gestoppt und nach dem passenden
catch Konstrukt gesucht werden, welches für die Behandlung der jeweiligen Ausnahmebedingung vorgesehen ist.
catch versucht mit allen Mitteln, das Programm wieder in einen ausführbaren Zustand zu bewegen. Einen idealen Einsatzzweck bilden neben jeglichen sensiblen oder fehleranfälligen Codepassagen jede Transaktions- oder Datenbank-basierte Anwendung.
Listing 1 <?php
// ... class Database MySQL {
// ... class Exception
function getMessage() {
return $this->message;
}
// ... class constructor and methods
function __construct($host = 'localhost', $user = 'root', $pass = '') {
$this->connect($host, $user, $pass);
}
function connect($host, $user, $pass) {
// try to connect to database
throw new Database::Exception('Could not connect to database.');
}
}
try {
$DB = new Database;
$DB->query(...);
}
catch (Database::Exception $e) {
print 'Database error: ('. $e->getMessage() .')';
}
?>
Innerhalb der Database-Klasse in Listing 1 befindet sich eine Exception-Klasse zur gesonderten Behandlung von Exceptions. Sollte im Fall einer Ausnahmebedingung innerhalb der Klassenmethoden eine Exception ausgelöst werden,
(throw) wird diese mit dem
catch Konstrukt abgefangen. Innmitten des Scopes (Gültigkeitsraum) der Klasse existiert ein Objekt
$e, das vom Typ Exception ist. Dieses wird nur existieren, wenn im Voranstehenden
try() Block eine Exception vom Typ
Exception ausgelöst wurde.
Built-In Backtracing
Das Backtracing ist eine äußert nützliche Eigenschaft der neuen Zend Engine 2, welche unmittelbar an das Exception Handling anknüpft und im besten Fall während des
throw Statements aufgerufen werden sollte. Es stellt die Fähigkeit bereit, die durch die Exception ausgelösten Ausnahmebedingungen bzw. Fehler zu präzisieren, eine Liste der Funktionsaufrufe an der aktuellen Position (Zeilennummer) zurückzugeben und ein Backtrace aufzuzeichnen. Somit werden Web-Applikationen leichter zu debuggen, einfacher zu warten sein und Fehler sind allgemein besser zu reproduzieren.
Die Funktionalität wurde bewusst einfach konzipiert und hilft Ihnen dabei, Skripte in einer Java ähnlichen Manier zu debuggen. Dabei liefert die parameterlose Funktion
debug_backtrace() ein Array mit vier Rückgabewerten zurück, die neben den aktuellen Zeilen- und Positionsangaben noch die aufgerufenen Funktions- und Skriptnamen ausgibt (vergl. Listing 2).
Listing 2 <?php
// ... XML Parser class {
// ... defined Exception Class
function __construct($backtrace) {
$this->backtrace = $backtrace;
}
function getException() {
foreach ($this->backtrace as $step) {
$msg = sprintf("%s [%s:%s]\n",
$step['function'],
$step['file'],
$step['line']
);
}
return $msg;
}
function setInputFile($file) {
// ...
// if an error with this file occured
throw new XML_Parser::Exception(debug_backtrace());
}
}
try {
// ...
} catch (XML_Parser::Exception $e) {
print ($e->getException());
}
?>
Objektorientierung in PHP5
Wie schon erwähnt, war die Objektorientierung in PHP4 stets ein großer Kritikpunkt, da sie in vielerlei Hinsicht Schwächen offenbarte. Elementar und oft gefordert war eine verbesserte objektorientierte Verhaltensweise und Implementierung gängiger Sprachelemente, wie man sie aus etablierten Hochsprachen wie Java oder C++ kennt. Angefangen vom Objektverhalten bis hin zum eher rudimentären Umfang unterstützter Features, wurden viele wichtige Elemente zur Vereinfachung der Programmierung oder Sprachwerkzeuge im besonderen Bezug zur OOP in der Zend Engine redlich vermisst. Oftmals wurden OO-Konzepte vom Design her sehr umständlich gelöst und boten kaum Flexibilität. Daher befinden sich die elementaren Änderungen auch mit im Objektmodell, also bei der OO-Programmierung selbst wieder. Allerdings ist PHP keine native objektorientierte Sprache, sondern bietet lediglich OO-Unterstützung an.
Um eine Sprache als objektorientiert gelten zu lassen, sollten Forderungen in punkto Abstraktion, Kapselung und Vererbung erfüllt sein. Hier spricht die Schwäche der Datenkapselung als einziges Argument gegen PHP, welche nachfolgend vorgestellt wird.
Die Projekte, die inzwischen mit PHP realisiert wurden und werden, sind erheblich gewachsen. Um im heutigen Businessumfeld mit komplexen Projektausrichtungen und unternehmenskritischen Applikationen bestehen zu können, ist die Objektorientierung und die Art der Ausprägung Maß aller Dinge. Sie entscheidet wesentlich über Akzeptanz oder Ablehnung einer Skriptsprache im Enterprise-Einsatz. Allerdings ist die OO-Ausprägung von PHP nicht das einzig entscheidende Kriterium, die Sprache sollte nach wie vor einfach zu erlernen sein. Insgesamt ist man sich nicht im Klaren, wo der Weg von PHP hinführen soll. Man kann sich nicht über die Art der Ausprägung einigen und hat keine klare Vision vor Augen. PHP ist die Sprache zur Generierung dynamischer Webseiten und realistisch betrachtet nicht für komplexe Software-Projekte geeignet, wo Schnelligkeit, Mächtigkeit und Integrationsfähigkeit gefragt sind. Deswegen sind Vergleiche mit Java oder anderen Hochsprachen im Zusammenhang mit OOP zum jetzigen Zeitpunkt einfach nicht relevant, da Java eine ganz andere Ausrichtung und Architektur im Gegensatz zu PHP besitzt. Diesen Entwicklungsrückstand muss PHP erst mühselig aufholen.
In letzter Zeit war die Thematik und Notwendigkeit eines Application Servers eines der vorherrschenden Themen innerhalb der PHP-Entwicklung. Damit möchte PHP unmittelbar dazu beitragen, die Softwarequalität im Mittelstandssektor mit der benötigten Performanz bei komplexen Objektstrukturen zu beeinflussen.
Weiterführend werden Begriffe wie Entwurfsmuster, OOA und OOD sowie UML-Case Tools, die zur konzeptionellen Planung von Software-Projekten im größeren Umfang elementar sind und in PHP5 mehr und mehr Fuß fassen, immer wichtiger. Ich möchte diese Begriffe aber nur am Rande rudimentär definieren, weil dies nicht der Schwerpunkt des Artikels ist, aber trotzdem wesentlich mit der OOP zu tun hat. Sollten Sie sich für diese Thematik ausgiebig interessieren, so kann ich nur das im 2. Quartal 2003 erscheinende Buch über die objektorientierte Programmierung für PHP Application Server von Sebastian Bergmann empfehlen, welches dieses breite Themenspektrum professionell aufgreift.
Design Patterns
Umso mächtiger die Objektorientierung einer Sprache wird, umso mehr wird die Planung objektorientierter Softwareentwicklung wichtiger und notwendiger. Entwurfsmuster (Design Patterns) [6] ebnen hier einen Weg, um wiederkehrende Entwurfsprobleme bei Softwareentwicklungsprozessen zu unterbinden und Lösungen in Form von bewährten Mustern (Patterns) bereitzustellen, um somit die Problemsituation zu erkennen und so effizient wie möglich zu lösen. Die Intention und der Grundgedanke zur Verwendung von objektorientierter Software besteht in der Widerverwendbarkeit (Code Reuse), um auch bei zukünftigen Anforderungen und Problemen zu bestehen. Der Entwurf von Software mit den zuvor genannten Ambitionen gestaltet den Prozess um einiges schwieriger.
Mit der Zeit wurden Lösungen für bestimmte Probleme gefunden, die erfolgreich eingesetzt wurden und somit einen Katalog mit insgesamt 23 Entwurfsmustern bildeten. Diese unterteilten sich jeweilig in drei Gruppen: Erzeugungsmuster, Strukturmuster und Verhaltensmuster, die typische und allseits bekannte Entwurfsmuster wie Singleton, Adapter, Iterator und Factory usw. bereitstellen.
PHP-Applikationsentwicklung mit Application Servern
In Verbindung mit komplexen Datenstrukturen in der OOP von PHP fällt immer wieder das Schlagwort Application Server. Ein Application Server stellt prinzipiell das Zusammenspiel zwischen einem Daemon (Server) und einem Programm (Application) her. In einem Application Server laufen die Applikationen persistent ab, d.h., jeder neue http-Request wird einer bereits laufenden Methode zugewiesen, die ihrerseits für ein Ergebnis sorgt. Die Vorteile sind relativ eindeutig, denn bei einer komplexen Applikation müssen nicht bei jedem neuen http-Request Objektstrukturen initialisiert werden, da nur eine Instanz für die Abarbeitung der Anfragen zuständig ist. Dies spart nicht nur Ressourcen, sondern auch wertvolle Ausführungszeit.
Die von Vulcan Logic stammende Script Running Magic (SRM) [7] ist einer der Pioniere dieser Technologie innerhalb von PHP. VL-SRM bietet neben der Einführung von Applikationsvariablen in Zukunft eine Java Beans [8] ähnelnde Komponententechnologie namens Bananas, die Komponenten persistent im Speicher hält, was das Halten von komplexen Daten- und Objektstrukturen analog zum oben erwähnten Prinzip im Speicher ermöglicht. VL-SRM könnte wesentlich dazu beitragen, die Softwarequalität im Mittelstandssektor mit der benötigten Performanz innerhalb von PHP5 zu beeinflussen.
Objektorientierte Analyse und Design
Beim Konzipieren von Software sollte grundlegend eine ausgiebige Planungsphase vorausgehen, die zur grundsätzlichen Zielsetzung des zu programmierenden Systems beiträgt. Diese Phase wird allgemein unter dem Begriff Objektorientierte Analyse, kurz OOA, zusammengefasst. Diese umfasst die Identifizierung aller Anforderungen in punkto: Benutzbarkeit, Effizienzsteigerung, Zuverlässigkeit, Erweiterbarkeit und Wartbarkeit.
Beim objektorientierten Design (OOD) [9] bietet sich der Einsatz spezieller Entwurfswerkzeuge an, die die Kluft zwischen Design und Realisierung dadurch verkleinern, in dem man mit den Objekten der Software näher an die Realität heranrückt. Real vorkommende Prozesse oder Gegenstände werden auf wesentliche Eigenschaften reduziert und in einer Klasse zusammengefasst. Dazu trägt auch die etablierte grafische Modellierungssprache Unified Modeling Language (UML) [10] bei, welche sich als de-facto Werkzeug herauskristallisiert hat, um Geschäftsprozesse, Datenbankschemata etc. mit verschiedenen Diagrammtypen wie Use Cases, Klassen- und Interaktionsdiagrammen zu modellieren, um somit Softwaresysteme bereits vor der eigentlichen Umsetzung besser zu konstruieren und zu visualisieren.
Mit der Zeit haben sich so genannte Case (Computer Acided Software Engineering)-Tools aufgetan, die den Umgang mit UML [11] erleichtern und im besten Fall den Quellcode anhand der angefertigten Diagramme selbstständig erzeugen. Ein erstes Case-Tool dieser Ausrichtung namens ArgoUML [12] glänzt bereits ansatzweise mit PHP-Unterstützung und Code Generation.
Zum Glück gehören viele der oben genannten Kritikpunkte der Vergangenheit an. Nach den ersten Benchmark-Ergebnissen ergab sich ein Performanceschub von 40 bis 50 Prozent gegenüber der Zend Engine 1.0, was wohl eine deutliche Sprache spricht.
Wenn man von Verbesserungen der OO-Verhaltensweise spricht, ist indirekt von der Deklarierung einer Klasse die Rede, in welcher Weise eine Instanz dieser Klasse zur Laufzeit angelegt wird und in welcher Form das Objekt nachfolgend verwendet werden kann. Gerade bei komplexen Applikationen fiel die unglaublich träge Initialisierungszeit von Objekten auf, welche Applikationen spürbar lähmte. Durch die Änderung des Java ähnelnden Referenzierungsverhaltens by reference wurde dieses Manko allerdings ausgemerzt.
Optimiertes Referenzverhalten - Object by handle
Das Sprachverhalten der Zend Engine sah es bisher vor, beim Anlegen oder bei der Übergabe von Objektinstanzen nicht das eigentliche Objekt zu übergeben, sondern lediglich eine Kopie davon, sowie man es von Variablen (Strings, Integer etc.) kennt. Dieses Verhalten wird im Fachjargon als copy by value bezeichnet und sorgte oft für Verwirrung. Die eigentliche Kernproblematik liegt darin, dass etwaige Änderungen der Eigenschaften des Objekts sich nicht auf das Objekt selbst auswirken würden, sondern auf die Kopie des Objekts. Um wirklich nur eine Referenz auf das Objekt anzuwenden, konnte man sich nur mit der expliziten Deklarierung des
&new-Operators aushelfen.
Die künftige Verhaltensweise und Umgangsweise von Objekten orientiert sich strikt an OO-Sprachen wie Java. Dort wird eine Objektinstanz mit einem object handle referenziert und dieses Handle bezieht sich in allen Aktionen auf das Ursprungsobjekt. Der große Vorteil dabei ist, dass die Zend Engine 2 dies implizit erledigt, was nicht nur eleganter und flexibler ist, sondern auch bei OO-basierten Applikationen spürbar die Performance beschleunigt, allerdings auch eine Menge an Speicherressourcen beansprucht. Der konsequente Wechsel zum object by handle-Paradigma bietet aber auch Vorteile beim Umgang in punkto Usability und Semantik, wodurch Features wie Destruktoren und Dereferenzierung profitieren.
<?php
$class = new Class();
// explizite Referenzzuweisung innerhalb der Zend Engine 1
$new_class = &$class;
?>
<?php
$class = new Class();
// implizite Referenzzuweisung der Zend Engine 2
$new_class = $class;
$new_class->method();
?>
Im obigen Beispiel wurde das zuvor beschriebene Verhalten noch einmal veranschaulicht, welches sich aber ausschließlich auf Objekte beschränkt. In jedem Fall wird das Ursprungsobjekt nicht mehr dupliziert, also beziehen sich auch Methodenaufrufe nur auf das Ursprungsobjekt, was die Datenintegrität konsistent hält. Sollten Sie trotzdem weiterhin die
&-Notation der Zend Engine 1 beibehalten, werden Sie darüber informiert, dass dies fortan nicht mehr nötig und empfehlenswert ist.
Objektrückgabemechanismus
In diesem Kontext war die Objektrückgabe innerhalb von Funktionen als Referenz auf Grund der copy by value-Problematik durch Zuweisung schwierig. Notationen wurden benötigt, um das Objekt auch wirklich als Referenz zurückzugeben. Bei gängigen Konzepten werden oftmals Entwurfsmuster angewendet, die Lösungen für bekannte OO-Probleme offenbaren. Um diese angemessen zu verwenden, ist die Funktionsrückgabe von Objektreferenzen elementar wichtig. Mit am besten eignet sich wohl das Factory Pattern als Erzeugungsmuster, um innerhalb von einer zentralen Funktion Objekte instanziert zurückzugeben.
<?php
function factory($interface_type) {
$class = $interface_type . "_API";
// load class interface.
include_once("api/". $class .".php");
// return requested object.
return new $class;
}
?>
Die Verwendung der
factory-Methode bietet sich meistens dann an, wenn Sie Ihrer Software bei mehreren Schnittstellen (Interfaces) mehr Flexibilität bei der Auswahl über die letztendlich instanzierbare Klasse geben möchten und generell die Erfüllung einer Aufgabe in unterschiedlichen Arten erfolgen soll.
Datenkapselung
Mit der Einführung der Version 2 der Zend Engine wird eine weitere wichtige, objektorientierte Technik in den Sprachkern eingegliedert. Es handelt sich um Attribute zum Verstecken und Kapseln von Daten innerhalb von Klassen, was die Möglichkeit offenbart, die Daten nur noch den in der Klasse definierten Methoden zugänglich zu machen und den unbefugten Zugriff von außen strikt zu unterbinden. Diese Datenkapselung beschränkt sich allerdings ausschließlich auf Variablen der Klasse und nicht auf die jeweiligen Methoden. Trotz des Fehlens von Kapselung in der Zend Engine 1, konnte dieses Problem durch Konventionen und Disziplin bei der Programmierung notdürftig umgangen werden, was logischerweise alles andere als perfekt war.
Die Verwendungs- und Einsatzzwecke reichen vom Verstecken private members von internen Details einer Klasse bis hin zu sichtbaren Klassenvariablen, die durch den Einsatz von privaten Attributen auf ein Minimum reduziert werden, um somit die Klassen ordentlich und elegant zu halten. Des Weiteren werden die Klassenvariablen vor einer direkten Datenmanipulierung geschützt. Somit bleibt die jeweilige Klasse stets in einem konsistenten Zustand und es entsteht eine Trennung zwischen Nutzungs- und Implementierungsebene.
Aus bekannten OO-Sprachen sind einem die Schlüsselwörter
private,
public und
protected bekannt, wobei
public dafür sorgt, dass der Zugriff von Methoden oder Attributen konsequent möglich ist. Im Gegensatz zu
private und
protected, welche den Zugriff von außen verbieten.
protected lässt nur diejenigen Attribute sichtbar erscheinen, die innerhalb der Klasse und allen weiteren Subklassen definiert wurden.
private wiederum bietet eine weitere Einschränkung und ist nur innerhalb der eigenen Klasse sichtbar. Letztendlich wurde nicht nur
private und
protected implementiert, sondern auch
public, was nichts anderes als ein Alias auf
var darstellt, welches normalerweise dafür verwendet wird um Klassenattribute festzulegen.
Listing 3 <?php
class FileHandle {
private $handle;
protected $mode = 'r';
public $filename;
function __construct($filename) {
$this->filename = $filename;
$this->open();
}
function open() {
$this->handle = fopen($this->filename, $this->mode);
}
function getFilename() {
return $this->filename;
}
// ...
}
class FileCompression extends FileHandle {
private $compression;
protected $mode;
function Compression() {
// ... $this->mode
}
// ...
}
$f = new FileHandle(__FILE__);
$f->mode; // geschütztes protected attribut ist nicht sichtbar
$f->getFilename(); // public attribut ist sichtbar
$c = new FileCompression(__FILE__);
$c->Compression(); // vererbtes protected attribut ist sichtbar
?>
Listing 3 veranschaulicht noch einmal den Einsatz des Datenkapselungs-Prinzips von PHP5. Dabei wird in der Klasse
FileHandle $handle als privat deklariert und ist somit nur für die eigene Klasse sichtbar, im Gegensatz zu
$filename worauf auch von außen zugegriffen werden kann.
$mode wiederum soll auch für die vererbte
FileCompression Klasse verfügbar sein.
protected Attribute müssen allerdings in jeder Klasse explizit deklariert werden, in denen Sie verwendet werden. Bei einem illegalen Zugriff außerhalb der Klasse auf das jeweilige
private oder
protected Attribut produziert die Zend Engine 2 allerdings keine Fehlermeldung, da sich dies wiederum zu stark auf die Performance auswirken würde.
Objekt-Dereferenzierung
Bei der Dereferenzierung von Objekten ist es grundsätzlich möglich, aus einer Funktion heraus ein Objekt zurückzugeben, welches ohne Instanz auskommt und trotzdem den Aufruf von Klassenmethoden des zurückgegebenen Objekts im globalen Namensraum ermöglicht. Diese Technik war schon unter Zend Engine 1 möglich, aber in sehr umständlicher Art und Weise, welche oftmals zu Fehlern führte, wie das nachfolgende Beispiel veranschaulicht:
<?php
$obj1 =& $obj->method();
$obj2 =& $obj1->method();
$obj2->method();
?>
Die neu eingeführte Schreibweise der Version 2 der Zend Engine ruft exakt dieselbe Methode mit einer eleganteren Syntax
$class->method()->method()->method()auf und greift darauf zu.
Listing 4 <?php
class DB::MySQL {
function query($query) {
// ...
}
}
class DB::Oracle {
function query($query) {
// ...
}
}
function DBFactoryMethod($database) {
switch ($database) {
case 'MySQL': return new DB::MySQL();
case 'Oracle': return new DB::Oracle();
}
}
DBFactoryMethod('MySQL')->query("...");
?>
In Listing 4 wird aus dem globalen Namensraum heraus, ohne Instanz auf die Klassenmethode
query() in dem jeweilig instanzierten Rückgabeobjekt, zugegriffen.
Static members
Durch die Eingliederung von statischen Klassenvariablen (
static members), die durch
static deklariert werden, ergibt sich zwangsläufig der Vorteil, eine Instanz einer Klasse nur einmal zu erzeugen und diese fortan kontinuierlich zu verwenden. Die meisten OO-Sprachen unterstützen dieses Konzept und bieten neben statischen Variablen auch noch statische Methoden und Klassen, was innerhalb der Zend Engine 2 nicht vorgesehen ist. Das bekannte Singleton-Design Pattern basiert unter anderem auf diesem Prinzip, welches grundlegend dafür geschaffen wurde, nur einmal zu existieren. Dies stellt auf der einen Seite eine globale Zugriffsmöglichkeit bereit und kommt dann zum Einsatz, wenn es sich um zeitintensive Objektinstanzierungen handelt.
Listing 5 <?php
class Counter {
var $counter = 0;
function increment_and_print() {
print ++$this->counter;
print "\n";
}
}
class SingletonCounter {
static $instance = NULL;
function Instance() {
if (self::$instance == NULL) {
self::$instance = new Counter();
}
return self::$instance;
}
}
SingletonCounter::Instance()->increment_and_print();
SingletonCounter::Instance()->increment_and_print();
?>
Durch die Verwendung von
static in Listing 5 wird sichergestellt, dass nur eine Instanz der Klasse existiert.
Instance() gibt entweder eine Objektinstanz zurück oder kreiert eine einmalige Instanz. In jedem Fall soll der Counter aber nur einmal existieren, um damit fortan zu arbeiten.
Objekte klonen
Das geänderte Zend Engine 2 Objekt-Referenzverhalten führt dazu, dass man stets eine Referenz auf eine Ursprungsklasse erzeugen kann und niemals eine Kopie eines Objekts samt Eigenschaften erhält. Wenn man komplexe Datenstrukturen besitzt und eine Klasse A erzeugt, die im weiteren Verlauf noch benötigt wird, es aber zusätzlich noch eine Klasse B gibt, die eine Kopie benötigt, um damit einzelne Teile der Objektstruktur zu bearbeiten, ohne dabei die Ursprungsinstanz der Klasse A zu verändern, spielt das neu eingeführte Klonen von Objekten (object cloning) eine bedeutsame Rolle. Dadurch erhält man sämtliche Eigenschaften des Ursprungsobjekts inklusiv aller Referenzabhängigkeiten. Ein Objekt wird nach dem Aufruf der Objektmethode
__clone() geklont. Möchten Sie alle Objekteigenschaften klonen, wird zu allererst überprüft, ob Sie die Methode
a __clone() selbstständig definiert haben, um das Klonen eigenmächtig zu übernehmen. In diesem Fall sind Sie dafür verantwortlich, die notwendigen Eigenschaften im erzeugten Objekt zu deklarieren. Sollte dies nicht der Fall sein, wird eine
default __clone Methode aufgerufen, die alle Objekteigenschaften dupliziert. Zusätzlich wird noch eine Funktion
object_clone_properties() bereitgestellt, die alle Eigenschaften des Ursprungsobjekts importiert. Somit besteht die Möglichkeit, die Eigenschaften des Ursprungsobjekts zu modifizieren, die aus irgendwelchen Gründen dringend geändert werden müssen.
Ein adäquater Anwendungsfall wäre beispielsweise das Klonen von PHP-GTK Widget-Elementen. Stellen Sie sich vor, es existiert ein Objekt, welche die Ressourcen eines
GTKWindow hält und darstellt. Dieses
GTKWindow wird in ähnlicher Form im weiteren Programmverlauf noch einmal benötigt. Somit wird ein Duplikat oder besser formuliert eine Arbeitskopie für das weitere
GTKWindow angelegt, damit einzelne Eigenschaften angepasst werden können, ohne das bestehende ursprüngliche
GTKWindow Objekt zu modifizieren oder ein neues GTK-Objekt zu instanzieren.
Listing 6 <?php
class GTKWindow {
public $window;
public $form_height;
public $form_width;
function Form() {
$this->window = new GTKWindow();
$this->window->set_title('Hello World');
// ...
}
function __clone() {
$this->form_height = 100;
$this->form_width = $clone->form_width;
// ...
}
}
$obj = new GTKWindow();
$obj->form_height = 300;
$obj->form_width = 600;
$clone_object = $obj->__clone();
print $clone_object->form_height ."\n"; // output: 100
print $clone_object->form_width ."\n"; // output: 600
// ...
gtk::main();
?>
Beim Beispiel in Listing 6 handelt es sich um ein
GTKWindow, wobei sich inmitten der Klasse die
__clone() Methode befindet. Dadurch besteht die Möglichkeit, Objekteigenschaften mit neuen Werten zu belegen, wobei
$clone inmitten der
__clone() Klassenmethode, analog zu
$this-> bei herkömmlichen Klassen, verwendet wird. Nach der Instanz des
GTKWindow Objekts werden die Klassenvariablen mit einem Wert initialisiert und die Objekteigenschaften werden weiterführend durch den Aufruf von
$obj->clone; geklont. Bei den nachfolgenden Ausgaben des geklonten Objekts ist festzustellen, dass die Höhe innerhalb der
__clone() Methode modifiziert wurde und deshalb einen veränderten Wert ausgibt. Der Breitenwert wurde nicht verändert und entspricht dem der vorherig initialisierten Klassenvariable.
Konstruktoren und Destruktoren
Der bereits aus PHP4 bekannte Konstruktor ist Teil eines Phasenmodells, wobei das Objekt nach dem Initialisierungszustand (Konstruktor) zur Benutzung freisteht und am Ende beseitigt wird (Destruktor). Ein typisches Szenario, welches wir auch täglich bei Aktionen im Alltag wieder finden, in dem wir beispielsweise Gegenstände auf- und abbauen. Ein Konstruktor ist nichts anderes als eine Funktion, die immer dann aufgerufen wird, wenn das Objekt der Klasse instanziert wird. Der Konstruktor wird ab sofort mit
__construct() deklariert, obwohl er auch dann weiterhin als Konstruktor erkannt wird, wenn er den Namen der Klasse trägt. Konstruktoren und Destruktoren besitzen im Gegensatz zu gewöhnlichen Funktionen keinen Returntyp. Alle anfälligen Aufräumarbeiten, die zum sorgfältigen Beseitigen eines Objekts beitragen, übernimmt der parameterlose Destruktor. Dazu gehören neben der zuletzt aufgerufenen Referenz die Freigabe jeglicher Speicherressourcen für das Objekt. Destruktoren aus Subklassen (parent destructors) müssen, wie schon bei der Version 1 der Zend Engine, explizit aufgerufen werden.
<?php
class DB {
// ... Connection to Database
// Constructor to initalize the connection.
function __construct($host = 'localhost', $user = 'root', $pass = '') {
$this->connect($host, $user, $pass);
}
// Closes the connection to the MySQL server.
function disconnect() {
if (is_resource($this->connection)) {
mysql_close($this->connection);
}
}
// ...
}
?>
Im obigen Beispiel sehen wir, dass der Konstruktor
__construct() nicht mehr den gleichen Namen wie die Klasse tragen muss und des Weiteren mit der späteren Instanzierung der Klasse
DB implizit eine Verbindung zur Datenbank herstellt. Der Vorteil von Destruktoren wird bei diesem Beispiel sehr deutlich, da die Datenbankverbindung explizit beendet wird und sämtliche Ressourcen freigegeben werden, die während der Datenbankprozedur benötigt wurden. Aber denken Sie bei einer Portierung zur Zend Engine 2 generell daran, keine Funktionen oder Methoden mit zwei Unterstrichen zu verwenden, da diese Notation für die zukünftige Entwicklung der Zend Engine 2 noch eine bedeutsame Rolle spielen könnte.
Nested Classes/(Namespaces)
Das vornehmlich durch die PEAR/PECL-Entwickler geforderte Namespace/Nested Class-Konzept ist ein Versuch, die Namensraumaufteilung flexibler und sauberer zu gestalten, Komponenten in verschiedene Namespaces zu separieren und sie aus dem globalen Namespace aufzurufen. Die Projekte, die mit PHP realisiert werden, sind immer komplexer. Die Spanne reicht von einfachen Klassen bis hin zu Frameworks wie dem PEAR Repository [13]. Deswegen wird es zunehmend schwieriger, Namenskollisionen zu vermeiden. PHP4 stellte dazu exakt drei Namensräume zur Verfügung: den Globalen, Klassen- und Funktions-Namespace.
Alle Namensräume inklusive der Klassennamensräume konnten Variablen enthalten. Allerdings konnte nur der globale und der Klassennamensraum Funktionen enthalten, während wiederum nur der globale Namensraum Konstanten und Klassen enthielt. Dies war eine sehr unelegante und verwirrende Lösung, um Namenskonflikten aus dem Weg zu gehen.
Namespaces sind als Nested Classes implementiert, wobei damit grundlegend der Zugriff auf Attribute im Gültigkeitsbereich, welcher im Fachjargon als Scope bezeichnet wird, gemeint ist. Der Vorteil ist generell die Vermeidung von Namenskollisionen, wobei Klassen deklariert werden können, die denselben Namen aufweisen, aber einem anderen Namensraum angehören. Des Weiteren wird die modulare Zerlegung von Bibliotheken durch Bildung von Namensräumen einfacher.
Namespaces wurden auf Basis des
class Schlüsselworts implementiert, wobei sich der Zugriff auf Klassenattribute grundlegend unterscheidet. Fortan ist es im Gegensatz zu PHP4 möglich, innerhalb einer Klasse Konstanten, Methoden, Klassen und statische Variablen unterzubringen. Der Syntax des Zugriffsmechanismus ist folgendermaßen aufgebaut:
- Um auf lokale Klassenattribute im Namespace zuzugreifen, verwenden Sie die Syntax self::$my_static_var bzw. MyClass::$my_static_var äquivalent.
- Aus dem globalen Namespace werden die Funktionen explizit mit dem Schlüsselwort main:: bzw. main::function1() aufgerufen.
- Für Konstanten, die ab sofort per const FOLDING = 'foo' deklariert werden, gilt die gleiche Syntax im Stil self::MY_CONSTANT bzw. main::MY_CONSTANT aus dem globalen Namespace heraus.
Bei Methodenaufrufen innerhalb von Klassen sucht die Version 2 der Zend Engine primär im Scope der eigenen Klasse und sekundär im globalen Namespace. Sollten im Namensraum zwei gleichnamige Methoden definiert sein, kommt es auch weiterhin zu einer logischen Namenskollision.
Des Weiteren existiert eine Möglichkeit, Funktionen, Konstanten und Klassen, nachfolgend Symbole genannt, aus Nested Classes heraus in den aktuellen globalen Namensraum zu importieren, welches mit dem
import Statement erfolgt. Dies reduziert zusätzliche Schreibarbeit, da der gewöhnliche Klassenzugriff mittels
(MyClass::) entfällt und nur einfach der Symbolname aufgeführt werden muss:
- import * from XML::Parser; // importiert alle Symbole aus der Klasse Parser mit dem Namespace XML.
- import function parseString, const Handler from XML::Parser // importiert einzelne Symboltypen, in diesem Fall die Funktion parseString() und die Konstante Handler.
Die Begriffe Nested Class und Namespace sind in PHP5 synonym, allerdings nicht auf syntaktischer Ebene.
class MyNamespace::Classname { } deklariert eine Klasse
Classname im Namespace
MyNamespace und ist äquivalent zur folgenden Nested Class-Schreibweise mit einer ähnlichen Semantik:
<?php
class MyNamespace {
class Classname { }
}
?>
Zusammengefasst bietet es dem PEAR Repository mit einem immer größeren Angebot von Klassen immense Vorteile, da fortan Klassennamen wie
PEAR::RPC oder
PEAR:MDB möglich sind. Diese hießen bisher
PEAR_SOAP und
PEAR_MDB und waren daran gebunden, trotz des gleichen Namespaces in unterschiedlichen Dateien (
PEAR_SOAP.php oder
PEAR_MDB.php) gespeichert zu werden. Dies gehört der Vergangenheit an und verschont uns in der Zukunft vor hässlichen Namenskonstruktionen, um eine weitere PEAR Uniqueness zu garantieren. Alter Code, der nicht die Vorteile des neuen Namespace-Konzepts beansprucht, läuft weiterhin problemlos ohne Modifizierungen am Quellcode.
<?php
class Package::Webservices {
const LIBARY = 'payment';
const INTERFACE = 'SOAP';
function InterfaceStatus() {
printf("Loading Libary: %s with Interface: %s", self::LIBARY, self::INTERFACE);
}
}
// Die Methode InterfaceStatus() wird in den globalen Namespace importiert.
import function InterfaceStatus from Package::Webservices;
InterfaceStatus();
// Äquivalent dazu, können Sie auch auf eine import Anweisung verzichten und explizit
// auf die Methode mittels, Package::Webservices::InterfaceStatus(); zugreifen.
?>
Aggregation, Overloading
Die nicht unmittelbar mit der Zend Engine 2 eingeführten und als sehr experimentell zu bezeichnenden Features bilden eine weitere Etappe auf dem Weg zu PHP5 mit erweiterten OO-Möglichkeiten.
Innerhalb von PHP4 stellte man oftmals die Notwendigkeit fest, Klassen zu erweitern oder anzupassen. Das im OO-Fachjargon auch als Vererbung bezeichnete Prinzip sah es vor, eine Reihe von existierenden Strukturen aus bereits alten Objekten zu übernehmen, um somit gleichartige oder ähnelnde Methoden respektive Attribute nicht neu zu implementieren. Beim ursprünglichen Objekt, von dem geerbt wurde, spricht man nun von einer Basis- oder Superklasse. Eine aus der Sprache C++ bekannte Methode erlaubt es, dass eine abgeleitete oder geerbte Klasse mehrere Basisklassen besitzen kann. Dabei handelt es sich um die Möglichkeit, parallel, also gleichzeitig, von zwei Klasseneigenschaften zu erben. In diesem Kontext wird dann oftmals von Mehrfachvererbung (multiple inheritance) gesprochen. Dieses Prinzip war ein lang diskutierter Themenpunkt, wobei die Performance Nachteile bei einer möglichen aufwändigen Implementierung letztendlich überwogen. Die Essenz dieser Diskussion stellte sich als durchaus positiv heraus, da man auf dem Grundprinzip zum gleichzeitigen Erben von Eigenschaften aus zwei verschiedenen Objekten weiterhin beharrte. Eine allgemeine akzeptierte Lösung bildete die Aggregations-Methode, die ebenfalls eine Mehrfachvererbung von Klassen durch Zusammenführen der Eigenschaften und Methoden zur Laufzeit ermöglicht. An ein Beispiel zur Veranschaulichung der erwähnten Theorie möchte ich an dieser Stelle verzichten und auf den Dev.Talk [14] von Sebastian Bergmann im PHP Magazin 2/2002 verweisen.
Bei einer weiteren Extension, die noch vor dem Release der Zend Engine 2 veröffentlicht wurde, handelt es sich um das Overloading. Hierbei ist das benutzerdefinierte Überladen der Zugriffe auf Klassenvariablen und Methodenaufrufe möglich, wodurch allgemein die Existenz von Methoden gleichen Namens möglich ist, sofern sie sich in der Art oder Anzahl ihrer Parameter unterscheiden. Die Overloading-Extension besitzt lediglich eine Funktion
overload(), welche den Namen der Klasse benötigt, um das Überladen von Eigenschaften und Methoden für eine Klasse zu aktivieren. Um das Überladen zu ermöglichen, müssen allerdings entsprechende Methoden
: __get(),
__set() und
__call() innerhalb des Objekts deklariert werden. Ein beiläufiges Beispiel finden Sie ebenfalls unter [14]. Des Weiteren wurde zum gegenwärtigen Zeitpunkt noch über eine Alternative zur Mehrfachvererbung nachgedacht, wobei es sich um die aus Java stammenden Delegationen handelte, die dem PHP Paradigma sehr entgegen kommen.
Klassen- und Objektfunktionen
__autoload(): Bislang verwendeten viele Packages einen absoluten Pfad zu ihren Klassen und luden diese einzeln mittels
include_once($class_path .MyClass.php);. Dies stellte nicht gerade die effizienteste Möglichkeit dar und beim Anblick von modular basierten Applikationen bekam man eine Anhäufung von
include Anweisungen innerhalb der Sourcecodes zu sehen. Mit der Java ähnelnden
__autoload() Funktion ist es jetzt nicht mehr notwendig, für jede Klasse eine eigene
include Anweisung anzulegen, da sie lediglich innerhalb der
__autoload() Funktion die
include Anweisung mit vollem Pfad definieren müssen. Dieser Mechanismus beschränkt sich allerdings nur auf den globalen Namensraum, da während der Entwicklung Probleme mit der Nested Classes-Hierarchie auftauchten:
<?php
function __autoload($classname) {
include_once('../'. $classname .".php");
}
main::__autoload("class_filename");
// Instanzierung der jeweilig eingebundenen Klasse
$f = new MyClass();
$f->func1();
?>
Die Funktion
main::__autoload($classname) wird dann aufgerufen, wenn versucht wird, ein Objekt der Klasse
$classname zu erzeugen, die Klasse aber nicht deklariert ist. In der Funktion kann dann über ein
include oder ähnliches die Klasse geladen werden.
Fazit und Zukunftsaussichten
PHP5 respektive die Version 2 der Zend Engine bildet einen großen Sprung zu einer mächtigeren dynamischen Skriptsprache unter Beibehaltung einer PHP-üblichen einfachen Syntax. Gängige OO-Konzepte können über PHP5 geplant, entworfen und umgesetzt werden. Der Einsatz von PHPDoc als Dokumentationstool wird zunehmend wichtiger werden, um den Überblick über seine APIs beizubehalten. Einige der ursprünglichen Doc Comments mit OO-Hintergrund finden jetzt endlich einen probaten Anwendungszweck.
@static deklariert statische Membervariablen,
@throws informiert den Aufrufer über einen möglichen Fehler zur Laufzeit des Programms, welcher mit Hilfe des Exception Handlings diagnostiziert wurde.
@access dient zum Kennzeichnen von
private,
public oder
protected Attributen zur Datenkapselung der Objekte. Weitere Informationen zur Verwendung von Doc Comments finden Sie unter [15]. Des Weiteren werden Klassen schon im Voraus in verschiedenen Testbedingungen auf Basis von Unit-Tests (PHP Unit) mehr und mehr getestet und ausprogrammiert werden, bevor mit der eigentlichen Programmierung begonnen wird, um die Softwarequalität zu steigern.
Beim Schreiben des Artikels stand die Veröffentlichung der dritten Alpha-Version [16] der Zend Engine 2 kurz bevor, die knapp ein Jahr nach Entwicklungsbeginn veröffentlicht wurde. Diese basiert auf dem aktuellen PHP4 CVS (4.3.0-dev). Da die Zend Engine 2 sich immer noch nicht für den Einsatz in Produktionsumgebungen eignet, können Sie in jedem Fall beim Testen mithelfen, um Fehler oder Ungereimtheiten aufzuspüren und somit eine schnellere Veröffentlichung von PHP5 zu gewährleisten. Scheuen Sie sich des Weiteren nicht davor, Meinungen oder vermisste Features ihrerseits der Zend Engine 2 Mailingliste mitzuteilen, die generell dafür geschaffen wurde, um Lösungen zu Problemen zu finden und als zentrale Diskussionsstätte zu fungieren. Möchten Sie die aktuelle, unstabile CVS-Version testen, so laden Sie sich aus dem CVS zuerst den PHP-Quellcode herunter, wonach Sie sich im Zend-CVS das Verzeichnis
Zend Engine2 herunterladen. Als letztes benennen Sie das Verzeichnis in
Zend um und kompilieren PHP neu.
Oftmals wird nach der Veröffentlichung von PHP5 respektive Zend Engine 2 gefragt. Dies ist sicherlich eine gute Frage, worauf selbst die Entwickler zum gegenwärtigen Zeitpunkt keine genaue Antwort wissen. Die Veröffentlichung von PHP5 hängt im besonderen Maße von der Fertigstellung der Zend Engine 2 ab, die sich noch vor einer langwierigen und intensiven Testphase befindet. Aber realistisch betrachtet könnte man mit der Veröffentlichung Ende 2002 rechnen.
Kritisch betrachtet weisen allerdings viele Leistungsmerkmale eklatante Designschwächen auf. Neben einer unzulänglich implementierten Datenkapselung, wobei auf private Klassenmethoden verzichtet wurde, existieren vielerlei weitere konzeptionelle Schwächen. PHP5 respektive die Version 2 der Zend Engine besitzt vielerlei Möglichkeiten und Potenziale, wobei schätzungsweise ein Entwicklungsrückstand von rund fünf bis sechs Jahren in Design- und Konzept-Aspekten im Vergleich zu anderen Sprachen aufzuholen ist, allerdings steht die Konkurrenz nicht still und entwickelt sich auch stetig weiter. PHP5 hat sich in punkto Objektorientierung zwar weiterentwickelt, aber die Lösungen (gerade Kapselung) entsprechen nicht einem wohlüberlegtem Konzept und spiegeln die Einstellung der Kernentwickler sehr gut wieder. Es muss endlich eine andere Philosophie und Denkweise bei den Kernentwicklern einkehren. Dass Software funktioniert, ist eine Grundvoraussetzung, aber um die angesprochenen Segmente bzw. Ziele zu erreichen, ist eine gute Softwarearchitektur notwendig. Deswegen muss man sich davon distanzieren, PHP einfach nur so weiter zu entwickeln, dass es ordnungsgemäß funktioniert, sondern endlich über wohlüberlegte Konzepte nachdenken, die zwar einen längeren Entwicklungszyklus beanspruchen, aber wesentlich die langfristige Qualität und die Akzeptanz von PHP verbessern.
Vielleicht hätte man sich für die Entwicklung der Zend Engine 2 noch mehr Zeit nehmen sollen, um wirklich alle Features perfekt ausgefeilt zu veröffentlichen und die Engine nicht in Rekordzeit zu entwickeln. Aber bei allem sollten wir nicht vergessen, dass PHP auf Open Source basiert. Open Source lebt bekanntlicherweise von der freiwilligen Bereitschaft der Entwickler, welche sehr hoch anzurechnen ist.
Aber nicht nur die Sprache ist zu kritisieren, auch die träge Entwicklung von Third Party-Produkten, die von Firmen endlich nachgereicht werden müssen. Damit sind gute IDEs, Profiler/Debugger, effiziente Dokumentationstools, Test- und Komponentenframeworks wie SRM gemeint. In dieser Richtung hat sich in letzter Zeit zwar etwas getan, aber man kann gespannt sein, was die Zukunft mit sich bringt.
Andre Gildemeister (ag@inocreation.com) ist Geschäftsführer von inocreation. Dieses Unternehmen (www.inocreation.com/) befasst sich verstärkt mit der Software-Entwicklung im web-basierten Umfeld und dessen Technologien. Des Weiteren ist er als freier Autor für verschiedene Online- und Print-Magazine rund um PHP und Webtechnologien tätig. Links
Literatur
- [6] "Design Patterns", Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Addison Wesley, Englische Ausgabe, ISBN 0201633612
- [9] "Objektorientierte Softwareentwicklung", Bernd Oestereich, Oldenbourg Wiss., Deutsche Ausgabe, ISBN 3486255738
- [11] "UML konzentriert. Strukturierte Einführung in die Standard-Objektmodellierungssprache", M.Fowler, K.Scott, Addison Wesley, Deutsche Ausgabe, ISBN 3827316170
- [14] Sebastian Bergmann: "Dev.Talk: Persistente Objekte - Aggregation und Overloading", PHP Magazin 2.02
- [15] Andre Gildemeister: "Effiziente Dokumentation: API Dokumentationen mit PHPDoc generieren", PHP Magazin 1.02