Aus Linux-Magazin 01/2008

PHP-Anwendungsentwicklung mit Prado

Wer Web- oder SaaS-Anwendungen anbieten will, bekommt es mit Kodierungsrelikten der HTML- und Skriptsprachen-Frühzeit zu tun. Abhilfe schaffen moderne Frameworks wie Prado, die viele Elemente und Templates bereits als fertige Bausteine bereitstellen. Doch deren Struktur will zuerst verstanden sein.

Webserver und Browser bilden das am weitesten verbreitete Client-Server-Zweigespann. Die Schöpfer der Auszeichnungssprache HTML hatten aber lediglich Texte mit wenigen eingebundenen Grafik im Auge. CSS und Ajax bohren HTML-Seiten jedoch so weit auf, dass sich im Browser selbst Anwendungen mit dem Look&Feel eines Desktop-Programms realisieren lassen.

Die Sprachen und Standards, auf denen auch moderne Webanwendungen basieren, wurden jedoch für weit niedriger gesteckte Ziele entwickelt. Die historisch gewachsenen Standards für dynamische Webseiten zwingen die Entwickler oft, um die Ecke zu denken, und nötigen ihnen schon bei relativ einfachen Programmfeatures kreative Workarounds ab. Integriert die Anwendung Ajax-Funktionalität, so sollte der Client-seitige Javascript-Code auf allen verbreiteten Browsern laufen, muss also den Besonderheiten der unterschiedlichen Javascript-Implementierungen Rechnung tragen. Umfangreiche Kompatibilitätstests sind unverzichtbar.

Plan und Soll

Für eine effiziente und vor allem plan- und kalkulierbare Entwicklung ist es also gerade bei Web- und SaaS-Anwendungen sinnvoll, das Rad nicht für jede Detaillösung neu zu erfinden, sondern die Bausteine eines Entwicklungs-Framework zu nutzen. Eine umfangreiche, in vieler Hinsicht ausgereifte Entwicklungsumgebung für Webanwendungen stellt das Prado-Framework [1] bereit. Die Buchstaben des Namens stehen für PHP Rapid Application Development Object-oriented.

Prado deckt viele Bereiche der Anwendungsentwicklung ab: Es stellt GUI-Komponenten bereit, die von einfachen Eingabefeldern bis hin zu einem Wizard-Interface reichen. Die Syntax der XML-Template-Sprache lässt sich in normalen HTML-Editoren problemlos bearbeiten. Alle Formularkomponenten merken sich ihren Status, ohne dass der Entwickler sich darum kümmern muss.

Prado stellt auch Client-seitige Validation-Controller zur Verfügung, die vom Anwender eingegebene Daten schon vor dem Verschicken des Formulars prüfen. Ein integrierter Object-Relational-Mapper [2], der auf die PHP Data Objects aufsetzt, abstrahiert von der eingesetzten Datenbank und erleichtert den Wechsel des Datenbanktyps: Änderungen am Code sind dabei nur an einer einzigen Stelle nötig. Eine Active-Records-Klasse [3] vereinfacht den Umgang mit Datenbanken noch weiter, indem sie Methoden für häufige Transaktionen wie Einfügen, Updaten, Löschen oder Finden von Daten bereitstellt.

Die Architektur des Framework orientiert sich am MVC-Modell (Model View Controller). Jede Prado-Anwendung besteht aus einem oder mehreren Templates, die das Aussehen der angezeigten Seite festlegen. Eine PHP-Datei stellt die zugehörige Anwendungslogik bereit. Jedes Template kann Code oder andere Template-Dateien inkludieren, außerdem lässt sich ihm ein so genanntes Master-Template zuordnen, das anders als einfache Includes auch zugehörige Controller-Komponenten einbindet.

Eine Beispielanwendung veranschaulicht am besten, wie viel Arbeit Prado dem Entwickler abnimmt: Ein Mausklick soll das Türchen eines Web-Adventskalenders öffnen (Abbildung 1). Einmal geöffnete Türchen bleiben offen, die Anwendung muss sich also deren Status merken. Der umrandete Kasten unter den Türchen zeigt nach dem Öffnen den Inhalt des Kalenderfachs an.

Abbildung 1: Beispiel für eine einfache interaktive Anwendung: Die Türchen des Adventskalenders öffnen sich nach einem Mausklick, im weißen Feld darunter erscheint der Inhalt.

Abbildung 1: Beispiel für eine einfache interaktive Anwendung: Die Türchen des Adventskalenders öffnen sich nach einem Mausklick, im weißen Feld darunter erscheint der Inhalt.

Die Anwendung soll so viel Code wie möglich wiederverwenden. Damit ist es selbstverständlich, dass der Code für die Türchen sowohl in der View- als auch in der Controller-Komponente nur einmal auftauchen möge. Aus Platzgründen enthält der Beispiel-Adventskalender nur Türchen für eine Woche.

Der erste Schritt

Für die Installation des Prado-Framework auf einem Webserver, der PHP-5-Seiten interpretiert, reicht es, Prado in den Root-Ordner des Webservers zu entpacken. Prado-Anwendungen setzen eine bestimmte Verzeichnisstruktur voraus, in der nur eine Datei im Stammordner für den Webserver erreichbar ist (Abbildung 2). Der eigentliche Code liegt unterhalb des »protected«-Verzeichnisses, das durch eine ».htaccess«-Datei für den Webserver gesperrt ist.

Abbildung 2: Prado-Seiten bestehen aus einem XML-Template und einer PHP-Datei für die Anwendungslogik.

Abbildung 2: Prado-Seiten bestehen aus einem XML-Template und einer PHP-Datei für die Anwendungslogik.

Ein Kommanozeilen-PHP-Skript erstellt die Ordnerstruktur im Verzeichnis des Aufrufs. Als einzigen Parameter benötigt er den Namen der Anwendung. Im Ordner »protected/pages« liegt nach dem Aufruf das Dateipaar »Anwendungsname. page« und »Anwendungsname.php«, das die View- und Controller-Komponente der neuen Anwendung repräsentiert. Die Listings 1 und 2 zeigen deren Inhalt für das Adventskalender-Beispiel.

Listing 1

01 <!--- Formular --->
02 <com:TForm>
03   <!--- Repeater ("Datengrid") --->
04   <com:TRepeater ID="Repeater"
05       OnItemCommand="open_door" > <!--- alle Events aus Repeater --->
06       <!--- iterierter Code: Grafik für Adventskalendertürchen, Beschriftung und verstecktes Feld --->
07       <prop:ItemTemplate>
08         <div style="float:left">
09           <com:TImageButton ID="door" ImageURL="door.png" />
10           <div align="center">
11             <com:TLabel ID="tbox" text="<%# $this->Data['text'] %>" />
12           </div>
13         </div>
14         <com:THiddenField ID="inside" data="<%# $this->Data['inside'] %> " />
15       </prop:ItemTemplate>
16       <!--- Ende iterierter Code --->
17   </com:TRepeater>
18 
19   <!--- Anzeigebereich für den Inhalt des Kalenders --->
20   <div style="clear:left;border-width:2px; border-style:solid; background-color:#ffffff;
21   width:650px; height:100px; text-align:center; padding:20px; border-color:black;">
22     <com:TImage id="content"  /> <!--- Bild --->
23   </div>
24 </com:TForm>

Listing 2

01 <?php
02 
03 // Klasse für die Homepage
04 class Home extends TPage {
05 
06 // Diese Funktion liefert die Daten
07 protected function getData()
08   {
09     return array( // Text und Bild-URL für die Türchen
10     array('text' => 'So 2.', 'inside' => 'engel1.png'),
11     array('text' => 'Mo 3.', 'inside' => 'glocken.png'),
12     array('text' => 'Di 4.', 'inside' => 'kaefer.png'),
13     array('text' => 'Mi 5.', 'inside' => 'kugel.png'),
14     array('text' => 'Do 6.', 'inside' => 'engel2.png'),
15     array('text' => 'Fr 7.', 'inside' => 'stern.png'),
16     array('text' => 'Sa 8.', 'inside' => 'stern.png'),
17     );
18   }
19 // Repeater (Datengrid) initialisieren
20 public function onLoad($param) {
21   if (!$this->isPostBack) { // Nur beim ersten Aufruf
22     // Funktion als Datenquelle zuordnen
23     $this->Repeater->DataSource=$this->getData();
24     $this->Repeater->dataBind(); // Repeater initialisieren
25     }
26   }
27 // zum Öffnen der Türchen Bild wechseln
28 public function open_door($sender, $param)
29   { //beim Klick auf das Türchen erhält d. Eventhandler ein Objekt ...
30     $item=$param->getItem(); // ... das das aufrufende Objekt enthält
31     $item->door->setImageURL('door_open.png'); // Neues Bild für Türchen
32     $image=$item->inside->getData(); // Bild-URL aus dem Repeater ...
33     $this->content->ImageURL=$image; // ... unter dem Kalender anzeigen
34   }
35 }
36 ?>

Die View-Komponente (Listing 1) definiert in Zeile 2 mit »<com:TForm>« zunächst ein HTML-Formular. Die Zeilen 8 bis 14 gestalten ein Türchen des Kalenders. Das Template-Tag »<com:TImageButton … >« (Zeile 9) bindet das Bild des geschlossenen Türchens ein. Ein Div-Block mit der CSS-Style-Eigenschaft »float:left« sorgt dafür, dass sich die Fächer des Adventskalenders in einer Zeile aneinanderreihen. Die Template-Dateien können also sowohl am Präfix »<com:« erkennbare Prado-Tags als auch normale HTML-Tags enthalten.

Ein weiteres »div«-Tag unterhalb des Bildes zentriert den Datumstext. Für den tageweise wechselnden Text ist in das Template eingebundener PHP-Code zuständig. Die umrahmenden »<%# … %>«-Tags teilen Prado mit, dass es sich bei dem umschlossenen Code um PHP-Quelltext, nicht um Template-Code handelt. Um ihn zu verstehen, ist es nötig, einen Blick auf das Tag »<com:TRepeater>« in Zeile 4 zu werfen.

Ein Repeater, entlehnt aus ASP.Net von Microsoft, ähnelt einer »foreach()«-Schleife. Er wiederholt den eingeschlossenen Quelltext entsprechend der Zahl der Datensätze der im Controller-Code mit ihm verknüpften Datenquelle (Abbildung 3). »$this« verweist in diesem Kontext auf den Repeater. Jeder Repeater stellt das Attribut »Data[]« zur Verfügung, ein Array, das die Datensätze des Schleifendurchlaufs enthält.

Abbildung 3: Aus einem mach sieben: Die Prado-Template-Sprache enthält Kontrollstrukturen, die Elemente vervielfältigen und die einzelnen Instanzen mit Daten aus Arrays oder Listen versorgen.

Abbildung 3: Aus einem mach sieben: Die Prado-Template-Sprache enthält Kontrollstrukturen, die Elemente vervielfältigen und die einzelnen Instanzen mit Daten aus Arrays oder Listen versorgen.

Fertigbaustein

Prado-Repeater bieten mehr Funktionalität als einfache »foreach()«-Schleife in PHP: Header- und Footer-Bereich zeigt Prado nur einmal an. Nur den Bereich zwischen »<prop:ItemTemplate>« und »</prop:ItemTemplate>« wiederholt das Framework der Datenmenge entsprechend. Gibt es einen mit »<prop:ItemAlternatingTemplate>« ausgezeichneten Bereich, dann wechseln sich der Inhalt des »ItemTemplate« und der des »ItemAlternatingTemplate« ab. Auf diese Weise ergeben sich zum Beispiel zeilenweise abwechselnd gefärbte Tabellen (Beispiel 1 unter [4]).

Datenquelle

Erst die beim Start der Anwendung aufgerufene Methode »onLoad()« der Seiten-Basisklasse erweckt den Repeater zum Leben: Die Zeilen 23 und 24 in Listing 2 verknüpfen den Repeater und die Datenquelle. »$this« verweist in beiden Fällen auf die aktuelle Klasse, also das Seitenobjekt. In einem Seitenobjekt stehen die implementierten Methoden gleichwertig neben dem durch ein ID-Attribut kenntlich gemachten Prado-Tag im Template. »$this->Repeater« zeigt daher auf den Repeater, der in »Home.page« mit dem Attribut »ID=”Repeater”« ausgezeichnet ist. Das Attribut »DataSource« der Prado-Klasse »Repeater« nimmt die Daten auf. Der Code weist ihr den Rückgabewert der Methode »getData()« zu.

Anspruchsvollere Webanwendungen erhalten ihre Daten aus einer Datenbank, die Prado über seinen Object-relational-Mapper oder die bereits erwähnte Active-Record-Klasse einbindet. Im Beispiel stammen die Daten lediglich aus einem Array. Die Bedingung »!$this->isPostBack« ist nur dann wahr, wenn der Besucher die Seite zum ersten Mal aufruft, aber nicht, wenn er die Seite nach einer Benutzeraktion neu lädt.

Der Repeater startet also über die Methode »dataBind()« nur beim ersten Seitenaufruf mit den Standardwerten. Da Prado-Komponenten grundsätzlich statusbehaftet sind, bleibt der Repeater nach der Initialisierung auch bei mehrfachem Seitenaufruf im gleichen Zustand. Für die Session-Funktionalität sorgt das Framework automatisch, weder im Template noch im Controller ist dafür Code erforderlich.

Das macht sich auch die zweite Methode der Seite, »open_door()«, zunutze, die die Grafik des geschlossenen Türchens durch die eines geöffneten ersetzt (Abbildung 1): Sie tauscht die Grafik einer Instanz des durch den Repeater vervielfachten Türchens des Adventskalenders (Zeile 9 in Listing 1) aus. Hat »open_door()« die URL verändert, so bleibt auch die veränderte Variante bei erneuten Seitenaufrufen konstant. Einmal geöffnet schließt sich das Türchen des Kalenders nicht mehr.

Wer zeigt auf wen?

Klickt der Benutzer auf ein Türchen, soll dieses sich öffnen, nicht das erste Fach oder alle. Um die richtige Referenzierung sicherzustellen, ließen sich die verschiedenen Instanzen der Kalenderfächer durch dynamisch generierte IDs differenzieren, die die »OnClick«-Eventhandler dann als Parameter erhalten müssten. In Prado geht es einfacher: Der Eventhandler »OnItemCommand« (Listing 1, Zeile 5) fängt die Events aller Steuerelemente innerhalb des Repeaters ab.

Prado übergibt der aufgerufenen Funktion als zweiten Parameter ein Objekt, das im Beispiel »$param« heißt. Dessen Methode »getItem()« gibt quasi eine Referenz auf den Schleifendurchlauf zurück, in dem der Eventhandler ausgelöst wurde. Daher lassen sich die durch die Wiederholung entstanden Instanzen des Bild-Objekts mit der statischen ID »door« (Listing 1, Zeile 9) über den Rückgabewert von »getItem()« und der statischen ID ansprechen. »$item->door->setImageURL« zeigt also stets auf die Grafik, auf die der Benutzer mit der Maus geklickt hat. Die Methode »setImageURL« tauscht schließlich das Bild aus, die Tür des Kalenderfachs ist offen.

Repeater geben die Daten während der Schleifendurchläufe an die eingebundenen Objekte weiter, die sie mit dem Aufruf »$this->Data[\’Feldname\’]« abfragen. Nach Anzeige der Seite sind die Daten im Repeater selbst nicht mehr verfügbar. Das versteckte Feld in Zeile 14 von Listing 1 speichert die ihm übergebenen Werte jedoch. Es lässt sich wie die iterierten Bilder über das Schleifendurchlaufobjekt und seine statische ID ansprechen: »$item->inside->getData()« liest seinen Inhalt aus. »$this->content->ImageURL« setzt in Zeile 22 die URL des bisher leeren Bild-Tags »<com:TImage id=”content” />«. Unter dem Adventskalender erscheint der Inhalt des gerade geöffneten Fachs.

Breite Basis

Das Adventskalender-Beispiel gibt einen ersten Eindruck des umfangreichen PHP-Framework. Außer dem Datenbankzugriff bleiben weitere Bereiche außen vor: Prado unterstützt Lokalisierung, Themes und Skins und kann außer Webseiten auch SOAP-Webservices bereitstellen. Es enthält Klassen, die das Errorhandling erleichtern, und integriert den »SafeHTML«-Parser [5] zur Vermeidung von XSS-Attacken. Seiteninhalte, die der Anwender mit »TSafeHTML«-Tags umschließt, überprüft das Framework auf potenziell gefährlichen Code.

Prado speichert den Zustand der Seiten in versteckten Formularfeldern, die Benutzer jedoch einsehen und manipulieren können. Als Gegenmaßname prüft das Framework bei entsprechender Einstellung interne Datenfelder und Cookies anhand eines Server-seitig hinterlegten Keys auf Manipulation.

Ein integrierter Cache erspart das Parsen der Templates bei jedem Seitenaufruf. Bei einem umfangreichen Framework wie Prado muss der PHP-Interpreter beim Erzeugen einer Seite viele Include-Dateien laden. Abhilfe schafft die Datei »pradolight.php«. Sie enthält das gesamte Framework in einer einzigen Datei, die, um alle Codekommentare erleichtert, nur etwa 250 KByte groß ist.

Ärger mit Ajax

Die Adventskalender-Anwendung verzichtet auf Ajax-Funktionalität, bei jeder Statusänderung lädt die Seite neu. Abhilfe schaffen die mit Prado 3.1.0 Mitte 2007 eingeführten Active Controls. Der Umgang mit ihnen ist aber nicht immer einfach: Zwar verhalten sie sich, wenn der Entwickler sie als statische Elemente einzeln in ein Template einfügt, bis auf den eingesparten Seiten-Reload genauso wie andere Tags.

Schwierig wird es allerdings, wenn der Entwickler Active Controls mit Repeatern und eigenen Eventhandlern kombinieren möchte, denn die funktionieren nicht wie bei statischen Kontollelementen out of the Box. Vielmehr muss der Entwickler oft mit zusätzlichen Tags die asynchronen Serverkontakte per Hand auslösen. Wie dies funktioniert, dokumentiert die Prado-Webseite an keiner Stelle systematisch, viele Links auf der Tutorialseite zu den Active Controls [6] führen außerdem ins Leere. Erst eine Suche im Forum liefert mit etwas Glück die Informationen.

Zwar bietet das Forum mit etwa 4000 registrierten Benutzern eine wertvolle Hilfestellung bei der Entwicklung mit Prado. Eine systematische Dokumentation kann es jedoch grade für Einsteiger nicht ersetzen. Die besteht aber zum größten Teil aus einer automatisch erzeugten Dokumentation des Klassen-Framework, in dem manchmal nur ein oder wenige Sätze die Funktion einer Klasse erläutern. Zwar steht auch ein Quickstart-Tutorial zur Verfügung, das das Wichtigste anschaulich erläutert und auch Beispielanwendungen enthält. Für die Lösung spezieller Probleme gerade mit den Ajax-Elemente geht es aber nicht tief genug.

Neben Schwächen bei der erst etwa seit einem halben Jahr verfügbaren Ajax-Funktionalität trübt also auch die Dokumentation den insgesamt positiven Eindruck von Prado. Ohloh.net [7] bescheinigt dem Framework dagegen ein mit elf Kernentwicklern relativ großes Team, zudem eine gute Kommentierung des Quellcode. Es besteht also Hoffnung, dass auch die Ajax-Komponenten in kommenden Versionen noch die Qualität des übrigen Framework erreichen.

Alternativen

Prado ist ein umfangreiches Framework, das die wichtigsten Bereiche der Anwendungsentwicklung in PHP erleichtert. Mit seiner verständlichen und leistungsfähigen Template-Sprache und seiner Datenbank-Abstraktionsschicht stellt es eine stabile Modell-View-Controller-Architektur zur Verfügung. Die Ajax-Elemente alias Active Controls sind allerdings noch nicht so gut in das Framework integriert wie statischen Elemente, deren Aktualisierung ein Neuladen der Seite voraussetzen.

Ebenfalls in PHP geschriebene Alternativen sind Cakephp [8] oder Silverstripe [9], das außer einem Anwendungs-Framework bereits ein fertiges CMS enthält. Der nächste Verwandte was Umfang und Funktionsvielfalt angeht, findet sich aber in der Java-Welt: Das Openlaszlo-Framework [10] umfasst wie Prado die Model-View-Komponente und enthält auch Methoden für den Umgang mit Daten.

Infos

[1] Prado: [http://www.pradosoft.com]

[2] Datenbank-Anbindung: [http://www.pradosoft.com/demos/quickstart/?page=Database.DAO]

[3] Active Records: [http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord]

[4] Repeater: [http://www.pradosoft.com/demos/quickstart/?page=Controls.Repeater]

[5] SafeHTML: [http://pixel-apes.com/safehtml]

[6] Active Controls: [http://www.pradosoft.com/demos/quickstart/?page=ActiveControls.Home]

[7] Prado auf Ohloh.net: [http://www.ohloh.net/projects/3182]

[8] Cakephp: [http://cakephp.org]

[9] Silverstripe: [http://www.silverstripe.com]

[10] Openlaszlo: [http://www.openlaszlo.org/]

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben