PHP-Frameworks im Test (5)

3.3. Zend Framework


3.3.1. Allgemeines

Roadmap des Projekts
Das Zend Framework stellt unter http://framework.zend.com/whyzf/future/ seine Roadmap für die zukünftigen Releases vor. Ein konkreter Release-Plan fehlt jedoch genauso wie ein Zeitplan für das erscheinen der aufgezeigten Features.


Aktualität der Version
Die Seite http://framework.zend.com/download gibt eine Übersicht über die letzten 6 Releases, die jeweils im Abstand zwischen 10 und 30 Tagen erfolgten. Das aktuelle Release, mit dem auch beim Verfassen dieses Artikels zurückgegriffen wurde ist 1.0.1 vom 30.07.2007.


Release-Packages in verschiedenen Formaten
Das Release-Package ist in den Varianten ZIP und TGZ verfügbar. Die Dokumentation wurde seit dem Release 1.0.0 RC2 aus dem Release-Package ausgegliedert. Die aktuelle Version der Dokumentation kann unter http://framework.zend.com/manual eingesehen werden.


CVS/SVN-Repositories
Unter http://framework.zend.com/download/subversion/ ist beschrieben, wie der Entwickler eine Kopie des SVN-Repositories, bzw. "nighly builds" beziehen kann. Damit ist gewährleistet, dass Bugfixes schnell verfügbar sind.


Bug-Tracking / Ticketing
Der Bug-Tracker http://framework.zend.com/issu...board.jspa war während der Zeit der Evaluierung nicht durchgängig verfügbar und der Aufruf wurde oft mit einem Fehler 502 quittiert. Der Autor geht davon aus, dass das Zend Framework über einen funktionierenden Bug-Tracker verfügt, da Jira über ähnliche Features wie das Software- Paket Trac verfügt.


Version für PHP 4
Eine Version für PHP 4 ist nicht erhältlich.


Version für PHP 5
Die installierte Version ist zu PHP 5 (im Test: Version 5.2.1) kompatibel. Um das Zend Framework betreiben zu können wird eine PHP Version > 5.1.2 vorausgesetzt.


3.3.2. Installation

Extract & Go
Die Installation des Frameworks gestaltet sich erwartungsgemäß einfach. Nach dem Extrahieren des Paketes kann der Anwender mit der Implementierung seiner ersten Anwendung beginnen. Auch bei diesem Framework wurde ein lokaler VHOST angelegt um z.B. URL-Rewriting sauber testen zu können. Es fällt jedoch sofort auf, dass das Zend Framework keine Beispiel-Applikation oder eine Startseite mit wichtigen Hinweisen im Release mitbringt.

Konfiguration
Der Einsteiger stößt nach einer Weile des Suchens auf ein Einsteiger-Tutorial, das zeigt, wie eine Anwendungsstruktur und eine "Hallo-Welt"-Applikation erstellt werden kann. Hinsichtlich Konfiguration beschreibt http://framework.zend.com/manu...troduction dass die Zend Framework Bibliotheken via PHP include_path erreichbar sein müssen. Dieser muss entweder global oder in der Bootstrap-Datei der Applikation gesetzt sein. Für den Test entschied sich der Autor für die zweite Alternative.
Weiterhin muss die Bootstrap-Datei (/myapp/html/index.php) die benötigten Bibliotheken laden, im Fall der beschriebenen Beispiel-Applikation ist das der Zend Framework FrontController. Dieser kann nach dem Setzen des include_path's per
PHP-Code
require_once 'Zend/Controller/Front.php';
eingebunden werden.


3.3.3. Erste Schritte

Demo-Software
Weder das Release-Package noch das Wiki beinhalten eine einfache Demo-Software, mit dem der Anwender einen ersten Einblick in die Funktionsweise und die Anwendung des Frameworks bekommen kann.


Einführung / Quickstart
Da kein ausgewiesenes Einsteiger-Tutorial auf der Seite framework.zend.com verfügbar ist, versuchte der Autor über das Wiki ein für Einsteiger relevantes Tutorial zu finden. Nach einer Recherche konnte http://akrabat.com/zend-framework-tutorial/ ausgemacht werden. Das dort verfügbare Einsteiger-Tutorial, das auch in Deutsch erhältlich ist, beschreibt das Erstellen einer einfachen CD-Verwaltung in für Einsteiger nachvollziehbaren Schritten.


3.3.4. Erstellung einer Webseite

Template-Bau / Layoutgestaltung
Das unter 3.3.3 genannte Tutorial gibt erste Einblicke in die Art und Weise des Template-Baus, die nun zur Erstellung der Demo-Seite verwendet werden können. Dazu wird unter einem neuen Ordner (/myapp) eine Strukur für die Applikation gemäß http://framework.zend.com/manu...troduction angelegt. Auch das Zend Framework verfolgt die Philosophie, dass die URL gemäß dem Pattern
APF-Template
http://www.example.com[/{Module}]/{Controller}/{Action}[/{Param1}/../{ParamN}]
aufgebaut ist. Der Einfachheit wegen wird die Demo-Seite ohne die Verwendung der Modul-Logik aufgebaut. Zunächst muss unter /myapp/html eine index.php mit folgendem Inhalt angelegt werden:
PHP-Code
// Set include_path set_include_path(get_include_path().';'.'***/zendtest.de/library'); // Run FrontController require_once 'Zend/Controller/Front.php'; $fC = Zend_Controller_Front::getInstance(); $fC->throwExceptions(true); $fC->setControllerDirectory('../application/controllers'); $fC->dispatch();
Die Aktivierung des Parameters throwExceptions des Front-Controllers ist dabei von großer Bedeutung für Anfänger, da der FrontController sonst nur Meldungen der Form
APF-Template
Fatal error: Uncaught exception 'Zend_Controller_Dispatcher_Exception' with message 'Invalid controller specified ...
wirft und keinen Hinweis auf den eigentlichen Fehler ausgibt.

Auf Grund des gewünschten URL-Designs wird nun unter /myapp/application/controllers die Datei SeiteController.php mit der Klasse SeiteController angelegt. Der darin enthaltene Action-Controller erbt von der Klasse Zend_Controller_Action. Das Grundgerüst sieht wie folgt aus:
PHP-Code
class SeiteController extends Zend_Controller_Action { // Abfangen von falschen / nicht vorhandenen Actions public function __call($method, $args){ // indexAction if($method == 'indexAction'){ return $this->render('startseite'); } // return error page return $this->render('error'); } }
Die Methode __call() wird verwendet um Standard- und Fehler-Fälle abzufangen. Im hier aufgezeigten Fall wird die Fehlerseite angezeigt, falls es keine anzeigbare Seite gibt. Die View- Templates wurden im Ordner /myapp/application/views/scripts/seite angelegt und mit der Endung .phtml versehen. Um nicht bei jeder Seite den Quellcode des Headers, des Menüs, des News-Bereichs, des Top-Menüs und des Footers kopieren zu müssen wurden diese Teile in den Ordner /myapp/application/views/scripts/seite/design ausgelagert. Dort existieren nun fünf Dateien:
  • header.phtml: Beinhaltet den Teil vor dem Content-Bereich. Inkludiert News, Top-Menü und Menü.
  • news.phtml: Newsbereich der Seite
  • topmenu.phtml: Horizontales Menü im oberen rechten Bereich
  • menu.html: Haupt-Menü in linken mittleren Bereich
  • footer.phtml: HTML-Fragmente der Fußzeile der Seite
Wird nun die URL
APF-Template
http://zendtest.de/Seite/Benchmark
aufgerufen, so lädt der FrontController die ActionKlasse SeiteController und führt die Methode benchmarkAction() aus. Diese ist im Moment noch nicht implementiert und die Fehler- Seite wird angezeigt. Legt der Benutzer nun die Datei benchmark.phtml im Ordner /myapp/application/views/scripts/seite an und erweitert den Controller um die Methode
PHP-Code
public function benchmarkAction(){ }
so wird die Benchmark-Seite angezeigt. Die Template-Datei für die Benchmark-Seite ist dabei wie folgt aufgebaut:
PHP-Code
<?php $view = new Zend_View(); $view->setScriptPath('../application/views/scripts/seite/design'); // Header ausgeben echo $view->render('header.phtml'); <font style="font-size: 26px; font-weight: bold;">Benchmark</font> <br /> <br /> [..] <?php // Footer ausgeben echo $view->render('footer.phtml'); ?>


Handling von Controllern
Unter Template-Bau / Layoutgestaltung wurde bereits angerissen, wie Controller im Zend Framework aufgebaut sind und wie sich diese verhalten. Auch wurde bereits vorweg genommen, wie die Namensgebung der Controller festgelegt ist. Um das im Test gewünschte URL-Format abbilden zu kännen und um zu erreichen, dass beim Aufruf der URL
Html-Code
http://zendtest.de/
keine Fehlermeldung, sondern die gewünschte Seite angezeigt wird, muss das Standard-Routing angepasst werden. Hilfestellung dazu bietet das Manual auf der Seite http://framework.zend.com/manu...ult-routes. Etwas unter geht hier jedoch die Tatsache, dass die Variable $router eine Referenz auf die Instanz des Rewrite-Routers sein muss. Die index.php muss dann um die Zeilen
PHP-Code
$router = $fC->getRouter(); $default_route = new Zend_Controller_Router_Route( '', array( 'controller' => 'Seite', 'action' => 'index' ) ); $seite_route_default = new Zend_Controller_Router_Route( 'Seite', array( 'controller' => 'Seite', 'action' => 'index' ) ); $seite_route_dynamic = new Zend_Controller_Router_Route( 'Seite/:Action/*', array( 'controller' => 'Seite' ) ); $router->removeDefaultRoutes(); $router->addRoute('default',$default_route); $router->addRoute('SeiteDefault',$seite_route_default); $router->addRoute('SeiteDynamic',$seite_route_dynamic);
zwischen
PHP-Code
require_once 'Zend/Controller/Front.php'; $fC = Zend_Controller_Front::getInstance(); $fC->throwExceptions(true);
und
PHP-Code
$fC->setControllerDirectory('../application/controllers'); $fC->dispatch();
erweitert werden. Weitere Routing-Einträge können analog dazu eingetragen werden. Hier sind die Hinweise auf der zuvor referenzierten Manual-Seite zu beachten. Das gewählte Routing impliziert jedoch weitere Änderungen im Controller. Es war dem Autor nicht möglich sowohl das Default-Routing als auch das dynamische Routing so zu konfigurieren, dass der Controller in der oben aufgezeigten Form sowohl für
Html-Code
http://zendtest.de/
als auch für
Html-Code
http://zendtest.de/Seite
oder
Html-Code
http://zendtest.de/Seite/Literatur
die richtigen Views rendert und ausliefert. Der Router unterstützt zwar ein Remapping von URLs, jedoch kein Remapping von Wildcard-Parametern in die URL-Parameter selbst, so dass der Dispatcher weiterhin erkennt, dass der zweite Parameter die Action ist. Das Manual (englisch) spricht zwar von
APF-Template
Route definition can contain one more special character a wildcard represented by '*' symbol. It is used to gather parameters similarly to the default Module route (var => value pairs defined in the URI). The following route moreorless mimics the Module route behavior: $route = new Zend_Controller_Router_Route( ':module/:controller/:action/*', array('module' => 'default') ); $router->addRoute('default', $route);
, das bewirkt jedoch nur ein Mappen der URL-Pfad-Abschnitte in Request-Variablen, die im Request-Objekt ausgelesen werden können. Der zweite - bzw. im Beispiel des Manuals dritte - URL-Parameter wird jedoch nicht automatisch dem Dispatcher als Action-Kenner übergeben.

Daher wurde die __call()-Methode wie folgt umgestellt, so dass diese zunächst versucht eine Methode für das Rendering aufzurufen, sollte diese nicht vorhanden sein, versucht den View zu rendern und ansonsten eine Fehler-Seite anzeigt. In PHP-Code gestaltet sich das wie folgt:
PHP-Code
public function __call($method,$args){ // Request-Objekt holen $Request = $this->getRequest(); // Action-Parameter holen (muss in Route definiert sein $ActionParam = strtolower($Request->getParam('Action')); // Request-Methode zusammenbauen $RequestedMethod = $ActionParam.'Action'; // Prüfen, ob Methode existiert und diese dann ausführen if(method_exists($this,$RequestedMethod)){ return $this->$RequestedMethod(); } else{ // Falls lediglich der View existiert, diesen ohne Hilfe einer Action-Methode rendern if(file_exists('../application/views/scripts/seite/'.$ActionParam.'.phtml')){ return $this->render($ActionParam); } } // Für alle übrigen Fälle Fehler-Seite zeigen return $this->render('error'); }
Etwas ungenerisch ist hierbei die Prüfung gewählt, ob das View-Script existiert. Es wurde jedoch nicht weiter nach einer Lösung gesucht, da sich die Änderungen auf Grund des URL-Routings bereits als zeitintensiv erwiesen haben.


Erweiterbarkeit der GUI-Komponenten
Wie auch in den bisherigen Tests befinden sich im Quellcode der Datei benchmark.phtml neben "normalen" HTML-Tags auch XML-Tags, zwischen den PHP-Codes eingeschlossen sind, die formatiert auf der Seite dargestellt werden sollen. Dies sind
APF-Template
<php:highlight> </php:highlight>
für das Highlighting und
APF-Template
<doku:navigation />
für die Darstellung des dynamischen Menüs. Dies beiden Aufgaben können durch Action-Helper realisiert werden, die dem FrontController bzw. Dispatcher in der index.php bekannt gemacht werden. Auf der Seite http://framework.zend.com/manu...per.broker wird diese Möglichkeit entsprechend beschrieben wird. Der Action-Helper muss dabei immer von Zend_Controller_Action_Helper_Abstract erben und kann die unter http://framework.zend.com/manu...ingyourown beschriebenen Methoden implementieren. Im Fall des Formatierens von XML-Tags kommt hier die Funktion postDispatch() in Frage, da zu diesem Zeitpunkt alle Views gerendert sind und der HTML-Quelltext im Response-Objekt vollständig vorliegt. Nachteil der Lösung ist jedoch, dass nicht nur der betroffene ein Teil des Quelltextes (Content-View) durchsucht werden muss, sondern der komplette Quelltext, was u.U. zu unerwünschten Ergebnissen oder zu schlechter Performance führen kann. Eine andere Lösung konnte jedoch auch unter http://www.zfforum.de/showthread.php?t=685 nicht erarbeitet werden.

Die beiden Helper wurden unter /myapp/applications/views/helpers angelegt und mit Highlight.php und DokuNavi.php benannt. Die Quelltexte der zwei Helper gestaltet sich wie folgt:

Highlight.php:
PHP-Code
require_once 'Zend/Controller/Action/Helper/Abstract.php'; class Helper_Highligh extends Zend_Controller_Action_Helper_Abstract { public function postDispatch(){ // Response-Objekt holen $Response = $this->getResponse(); // Inhalt vom Response-Objekt holen $Body = $Response->getBody(); // PHP-Highlight erzeugen $Body = $this->highlight($Body); // Body in das Response-Objekt zurückschreiben $Response->setBody($Body); } public function highlight($content){ // Quelltext parsen return preg_replace_callback( '=\<php\:highlight\>(.*?)\<\/php\:highlight\>=si', array('Helper_Highligh','highlight_it'), $content ); } public function highlight_it($content){ $HighlightedContent = highlight_string(trim('<?php'.ltrim(rtrim($content[1]),"\x0A..\x0D").' ?>'),true); // PHP-Anfangstag ersetzen $HighlightedContent = str_replace('< font color="#007700"><?< /font>', '', $HighlightedContent); $HighlightedContent = str_replace('< font color="#0000BB"><?php ', '< font color="#0000BB">', $HighlightedContent); $HighlightedContent = str_replace('< font color="#0000BB">php', '< font color="#0000BB">', $HighlightedContent); $HighlightedContent = str_replace('< font color="#0000BB"> < /font>', '', $HighlightedContent); // PHP-Endtag ersetzen $HighlightedContent = str_replace('< font color="#0000BB">?>< /font>','',$HighlightedContent); // Code im DIV zurückgeben return '< div class="phpcode">'.$HighlightedContent.'< /div>'; } }

DokuNavi.php: (gekürzt)
PHP-Code
require_once 'Zend/Controller/Action/Helper/Abstract.php'; class Helper_DokuNavi extends Zend_Controller_Action_Helper_Abstract { public function postDispatch(){ // // Navigation erzeugen // // Array füllen $this->__fillSitesArray(); // Aktuelle Seite deklarieren $Request = $this->getRequest(); // Action-Parameter holen (muss in Route definiert sein $ActionParam = $Request->getParam('Action'); if($ActionParam == null){ $page = 'Startseite'; } else{ $page = $ActionParam; } // Vorherige Seite deklarieren $PreviousPage = $this->__getNeighborPageName($page); // Nächste Seite deklarieren $NextPage = $this->__getNeighborPageName($page,'next'); // Rückgabe-Puffer deklarieren $Buffer = (string)''; [..] // // Ergebnis in Response implantieren // // Response-Objekt holen $Response = $this->getResponse(); // Inhalt vom Response-Objekt holen $Body = $Response->getBody(); // Ausgabe in Response-HTML-Quelltext einsetzen $Body = str_replace('<doku:navigation />',$Buffer,$Body); // Body in das Response-Objekt zurückschreiben $Response->setBody($Body); } private function __fillSitesArray(){ [..] } private function __getNeighborPageName($CurrentPage,$Type = 'previous'){ [..] } }
Um den Navi-Helper dem Dispatcher bzw. FrontController bekannt zu machen, muss die index.php wie folgt geändert werden:
PHP-Code
require_once '../application/views/helpers/Highlight.php'; require_once '../application/views/helpers/DokuNavi.php'; Zend_Controller_Action_HelperBroker::addPath('../application/views/helpers','Helper'); $Helper_Highlight = new Helper_Highligh(); $Helper_DokuNavi = new Helper_DokuNavi(); Zend_Controller_Action_HelperBroker::addHelper($Helper_Highlight); Zend_Controller_Action_HelperBroker::addHelper($Helper_DokuNavi);


Komplexe Layouts
Die Erstellung einfacher Layouts für eine Webseite konnte sehr einfach beschritten werden. Das Gestalten von komplexen Layouts scheitert auch hier an der "Ein Controller - eine Action"-Logik im Bereich der Umsetzung des MVC-Pattern und am URL-Design. So ist es weder möglich einem eingebundenen View einen Controller zuzuweisen, der dem View dynamisch Inhalte liefert, noch ist vorgesehen, dass mehrere Front- bzw. ActionController-Actions in der URL deklariert werden können. Hierzu wurde eine ausführliche Diskussion unter http://www.zfforum.de/showthread.php?t=359 mit dem Ergebnis geführt, dass das Framework bisher keine Möglichkeit vorsieht, ein Layout mit mehreren und vor Allem unabhängigen Actions für unterschiedliche GUI-Teile zu erstellen.


FormularDesign
Wie auf der Seite http://framework.zend.com/manu...rs.initial beschrieben ist, kann der Entwickler zur Erstellung von Formularen auf eine Vielzahl von Helper- Methoden zurückgreifen. Um das hier zu erstellende Formular mit dem Zend Framework nachempfinden zu können, wird zunächst eine View-Script-Datei mit dem Namen kontakt.phtml angelegt und eine weitere (danke.phtml) für die Anzeige der Erfolgsmeldung. Die Datei kontakt.phtml beinhaltet neben den üblichen Bereichen für das Anzeigen von Header- und Footer-Komponenten den folgenden Quelltext:
Html-Code
<font style="font-size: 26px; font-weight: bold;">Kontakt-Formular</font> <br /> <br /> Wenn Sie mit mir in Kontakt treten möchten, dann benutzen Sie einfach dieses Formular. Geben Sie Ihre Nachricht ein und schon kann es los gehen. Ich werden mich dann umgehend mit Ihnen in Verbindung setzten. <strong>Bitte füllen Sie das Formular vollständig aus!</strong>. <br /> <br /> <form action="<?php echo $this->url(); ?>" method="post"> <span style="width: 47px; border: 0px solid black; margin-right: 69px;">Person:</span> <?php echo $this->formSelect('Person', $this->Person, array( 'class' => 'eingabe_feld', 'style' => $this->PersonStyle ), array( '' => '', '1' => 'Max Mustermann', '2' => 'Bianka Mustermann' ) ); ?> <?php echo $this->PersonError; ?> <br /> <br /> <span style="width: 56px; border: 0 solid black; margin-right: 64px;">Ihr Name:</span> <?php echo $this->formText('Name', $this->Name, array( 'class' => 'eingabe_feld', 'style' => 'width: 280px;'.$this->NameStyle ) ); ?> <?php echo $this->NameError; ?> <br /> <br /> <span style="width: 108px; border: 0px solid black; margin-right: 10px;">Ihre eMail-Adresse:</span> <?php echo $this->formText('EMail', $this->EMail, array( 'class' => 'eingabe_feld', 'style' => 'width: 280px;'.$this->EMailStyle ) ); ?> <?php echo $this->EMailError; ?> <br /> <br /> <span style="width: 57px; border: 0px solid black; margin-right: 61px;">Ihr Betreff:</span> <?php echo $this->formText('Subject', $this->Subject, array( 'class' => 'eingabe_feld', 'style' => 'width: 280px;'.$this->SubjectStyle ) ); ?> <?php echo $this->SubjectError; ?> <br /> <br /> Ihre Nachricht: <br /> <?php echo $this->formTextarea('Comment', $this->Comment, array( 'class' => 'eingabe_feld', 'style' => 'height: 200px; width: 400px; overflow: auto; '.$this->CommentStyle ) ); ?> <?php echo $this->CommentError; ?> <br /> <br /> <?php echo $this->formSubmit('Absenden','Absenden',array('class' => 'eingabe_feld')); ?> </form>
Im Controller wird für das Formular-Handling eine neue Methode mit dem Namen kontaktAction angelegt, die das Überprüfen der Formular-Eingaben und das reagieren auf Fehleingaben erledigt. Dabei wird von der Möglichkeit Gebrauch gemacht, dass dem View beliebige Variablen zur Verwendung durch Setzen der Attribute des aktuellen View-Objekts zugewiesen werden können. Der Quelltext der Methode ist folgender:
PHP-Code
public function kontaktAction(){ // View-Variablen setzen $this->view->Person = (string)''; $this->view->Name = (string)''; $this->view->EMail = (string)''; $this->view->Subject = (string)''; $this->view->Comment = (string)''; // Variablen zur Ausgabe-Steuerung setzen $this->view->PersonStyle = (string)''; $this->view->NameStyle = (string)''; $this->view->EMailStyle = (string)''; $this->view->SubjectStyle = (string)''; $this->view->CommentStyle = (string)''; // Variablen für Fehlermeldungen setzen $this->view->PersonError = (string)''; $this->view->NameError = (string)''; $this->view->EMailError = (string)''; $this->view->SubjectError = (string)''; $this->view->CommentError = (string)''; // Prüfen, ob Formular abgeschickt wurde if(isset($_POST['Absenden']) && $_POST['Absenden'] == 'Absenden'){ // Validatoren erstellen $Val_StrLen = new Zend_Validate_StringLength(3); $Val_EMail = new Zend_Validate_EmailAddress(); // Werte des Formulars presetten $this->view->Person = $_POST['Person']; $this->view->Name = $_POST['Name']; $this->view->EMail = $_POST['EMail']; $this->view->Subject = $_POST['Subject']; $this->view->Comment = $_POST['Comment']; // Post-Daten prüfen $FormValid = true; if(empty($_POST['Person'])){ $this->view->PersonStyle = ' background-color: red; border: 2px solid red;'; $this->view->PersonError = 'Bitte wählen Sie eine Kontakt-Person aus!'; $FormValid = false; } if(!$Val_StrLen->isValid($_POST['Name'])){ $this->view->NameStyle = ' border: 2px solid red;'; $this->view->NameError = 'Bitte geben Sie einen Namen ein!'; $FormValid = false; } if(!$Val_EMail->isValid($_POST['EMail'])){ $this->view->EMailStyle = ' border: 2px solid red;'; $this->view->EMailError = 'Bitte geben Sie eine korrekte E-Mail-Adresse ein!'; $FormValid = false; } if(!$Val_StrLen->isValid($_POST['Subject'])){ $this->view->SubjectStyle = ' border: 2px solid red;'; $this->view->SubjectError = 'Bitte geben Sie ein Betreff ein!'; $FormValid = false; } if(!$Val_StrLen->isValid($_POST['Comment'])){ $this->view->CommentStyle = ' border: 2px solid red;'; $this->view->CommentError = 'Bitte geben Sie einen Kommentar ein!'; $FormValid = false; } // Formular absenden, fals Valide ausgefüllt if($FormValid == true){ header('Location: /Seite/Danke'); } } // Formular anzeigen $this->render('kontakt'); }
Das Zend Framework bringt - wie unschwer zu erkennen ist - einen Satz von Validatoren mit, die einfach zur Validierung der Benutzereingaben verwendet werden können. Das Presetting von Benutzer-Eingaben in Formular-Komponenten muss der Entwickler, genauso wie Markierung von nicht korrekt ausgefüllten Feldern oder Fehlermeldungs-Ausgaben - trotz Form-Helper - zu Fuß implementieren. Verglichen mit z.B. CakePHP wird das Formular-Handling nur rudimentär unterstützt und der Template-Bauer spart sich durch den Einsatz der View-Helper nur wenig Arbeit. Das Presetting von Select-Feldern wird jedoch im Gegensatz zu den View-Helpern von CodeIgniter durch Übergabe des POST-Wertes unterstützt.



3.3.5. URL-Handling

Unterstützung von URL-Rewriting
Das Ausliefern der Applikation über Rewriting-URLs wird vom Zend-Framework standardmäßig unterstützt. Damit alle Anfragen auf die Bootstrapping-Datei weitergeleitet werden muss die RewriteRule
APF-Template
RewriteEngine on RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php
für den VHOST aktiviert werden. Über die Unterstützung von URLs ohne Rewriting wurde im Manual kein Eintrag gefunden.


Generik des URL-Layouts
Wie bei den bisherigen Test-Kandidaten wird das URL-Layout der Form
APF-Template
http://www.example.com[/{Module}]/{Controller}/{Action}[/{Param1}/.../{ParamN}]
fest vorgeschrieben und kann lediglich durch Routing-Einträge manipuliert, jedoch nicht in seiner Bedeutung geändert werden.


URL-Manipulations-Tools / Linkgenerierung
Zur Unterstützung der URL-Generierung liefert das Zend Framework die Komponente Zend_Uri mit. Diese unterstützt aktuell HTTP- und HTTPS-Schemata und kann beispielsweise mit
PHP-Code
require_once 'Zend/Uri.php'; $uri = Zend_Uri::factory( 'http://www.zend.com:8180/Module/Controller/Action/Value1/Value2/Value3?param4=val4¶m5=val5' ); $uri->setPath('/Seite/Danke'); $uri->setPort(''); $uri->setHost('www.test.de'); $uri->setQuery('param4=show¶m5=false'); echo $uri->getUri();
die URL
APF-Template
http://www.zend.com:8180/Module/Controller/Action/Value1/Value2/Value3?param4=val4&param5=val5
zu
APF-Template
http://www.test.de/Seite/Danke?param4=show&param5=false
verändern. Auffällig ist jedoch, dass Ampersands in URLs nicht codiert werden, was auf Ausgabe-Seite zum Effekt führen kann dass statt der oben aufgeführten URL
APF-Template
http://www.test.de/Seite/Danke?param4=show¶m5=false
oder jede mögliche andere Kombination von HTML-Entitys ausgegeben werden.



3.3.6. Design des Frameworks

Umfang der mitgelieferten Komponenten
Das Zend Framework besticht in der aktuellen Version durch eine Fülle von Komponenten für die verschiedensten Anwendungsfälle. Es werden im Paket Klassen für Benutzer-Authentifizierung, Rechte-Verwaltung (Zend_ACL), Webservices, Google-Data Client, Mail-Versand, PDF-Generierung, Mehrsprachigkeit, XMLRPC und viele mehr mitgeliefert. Einen Überblick und Detail-Informationen können unter http://framework.zend.com/manual/de/ eingesehen werden. Die Evaluation jeder dieser Komponenten wurde nicht in der Evaluierung einbezogen. Der Umfang wird jedoch als sehr positiv in die Bewertung eingebracht. Besonders aufgefallen ist, dass sich die Entwickler des Zend Frameworks der Einheiten-Thematik eine eigene Komponente gewidmet haben und die Lucene-Suche bereits out-of-the-box unterstützt wird. Ebenso positiv ist das konsequente Exception-Handling für die jeweiligen Module.

Negativ zu bewerten ist, dass das Zend Framework keine eigene Benchmark-Möglichkeit mitbringt um den Quellcode profilen zu können.


Einsatz von Design-Pattern
Design-Pattern spielen beim Zend Framework eine herausragende Rolle. Im Mittelpunkt stehen wie bei den übrigen bisherigen Probanden auch das MVC- und FrontController-Paradigma als zentrale Präsentations- und Business-Schicht-Pattern. Um die Struktur und den Aufbau genauer analysieren zu können wurde auch hier eine Komplett-API-Dokumentation in Doxygen und Dot erzeugt. Trotz des hinsichtlich PHP 5 möglichen sauberen Interface-Designs und der Verwendung von abstrakten Basis-Klassen ist auch beim Zend Framework im Gegensatz zu CakePHP und dem Adventure PHP Framework kein einheitliches Klassen-Design auszumachen, sondern jedes Package besitzt eine oder mehrere eigene Definitionen einer abstrakten Basis-Klasse oder eines Interfaces.
Auffällig ist, dass sehr viele Klassen entweder statisch verwendet werden (Zend_Controller_Action_HelperBroker) oder das Singleton-Pattern in jeder Klasse neu implementieren (Zend_Controller_Front). Ein abstrakter Ansatz (evtl. "abstract singleton" oder "generic fabric pattern") wird nicht verfolgt, was den Anwender darauf beschränkt, nur diejenigen Klassen singleton instanziieren oder verwenden zu können, die das Feature auch unterstützen.


Struktur des Quellcodes / Design der Klassen
Der Aufbau des Packages wurde nach dem Zend Namensgebungsprinzip stringent aufgebaut und der Anwender findet sich in den Komponenten sehr schnell zurecht. Die Klassen und deren Methoden sind auf einem sehr hohen Abstraktionslevel abgefasst, was designtechnisch als sehr positiv zu bewerten ist, Einsteigern das Analysieren des Quellcodes jedoch etwas erschwert. Nichtsdestoweniger wird durchgängig Gebrauch von der Parameter-Typ-Deklaration in den Funktionsdefinitionen gemacht, was das Debugging erleichtert, da nur erlaubte Daten- und Objekt-Typen akzeptiert werden.


Einsetzbarkeit für mehrere Applikationen
Um Anwendungen und Module in unterschiedlichen Umgebungen verwenden zu können bietet das Zend Framework die Komponenten
  • Zend_Registry
  • Zend_Config
  • Zend_Locale.
Mit diesen kann der Entwickler sehr einfach Sprach- und Umgebungs-abhängige Konfigurationen erzeugen und in den Anwendungen verwenden. Im Klassen-Design fehlt die Sprach- und Umgebungs-Abhängigkeit jedoch noch, so dass es nicht möglich ist in einem Controller an Hand der Sprache des Controllers selbst eine Konfiguration zu laden, sondern man muss auf Mittel wie Registry zurückgreifen, was zunächst nicht negativ auffällt, aber etwas aufwändiger ist.


Erweiterbarkeit
Engagierte Entwickler können das Zend Framework einfach erweitern. Möglichkeiten werden bereits im Manual aufgezeigt und es wird auf Situationen verwiesen, zu denen dieses auch notwendig ist. Dies sollte dem Anwender auch nicht schwer fallen, denn zu den jeweiligen Klassen existiert üblicherweise eine Interface-Definition der zu erweiternden Klasse. Durch die Masse der im Zend Framework integrierten Klassen und Module besteht jedoch auch hier die Gefahr, dass dieses zu einer monolithischen Klassensammlung wie PEAR mutieren könnte und das Kern-Design verloren geht. Wie bei den bisherigen Testkandidaten auch, muss der Entwickler bei der Erstellung komplexer GUI-Strukturen auf Drittprodukte ausweichen, was vom Autor als problematisch hinsichtlich des ursprünglichen Designs des Frameworks betrachtet wird.


Scaffolding
Ein Rapid Development Feature im Sinne von CakePHP und CodeIgniter ist im Zend Framework nicht vorgesehen. Es sind zwar ähnliche Strukturen vorhanden (Zend_Db_Adapter, Row- / Table-Data-Gateway), jedoch gibt es keinen Scaffolding-Mode, bei dem mit wenigen Handgriffen eine komplette GUI-Anwendung erzeugt werden kann. Aus Sicht des Autors ist dieses jedoch auch nicht Teil der Strategie bzw. des Designs des Frameworks, muss aber in der Bewertung als fehlend kenntlich gemacht werden.


3.3.7. Dokumentation

Dokumentation des Quellcodes
Der Quellcode des Frameworks ist sehr gut dokumentiert wodurch der Blick in den Quellcode eine einfache Analyse - natürlich im Rahmen der Fähigkeiten des Programmierers betrachtet - zulässt.


API-Dokumentation
Unter http://framework.zend.com/apidoc/core/ findet der interessierte Entwickler eine API-Dokumentation, die mit PHPDocumentor aus dem Quelltext generiert wurde.


Einführungen, Tutorials und Anwendungs-Beispiele
Die Seite http://framework.zend.com/ dient dem Entwickler als zentrale Ressource für Dokumentationen. Das Manual selbst ist im deutschen an vielen Stellen schlecht übersetzt, oder es fehlen Übersetzungen. Die Beispiele sind oft unvollständig und "verschweigen" die Bedeutung einiger Variablen (wie oben aufgezeigt $router). Viele der Code-Snippets zeigen zudem nur die Verwendung einer Komponente als Prototyp, unterstützen jedoch nicht den konkreten Anwendungsfall.
Ein weiteres Manko ist die Verfügbarkeit des Wikis (http://framework.zend.com/wiki/display/ZFDEV/Home). Dieses ist aus München nur in ca. 50% der Fälle verfügbar und wird üblicherweise mit einem "Bad Proxy Request" quittiert. Im Wiki sind nach Meinung der Redaktion keine für Anfänger geeigneten Tutorials enthalten - die Beiträge helfen lediglich bereits mit dem Thema vertrauten Personen.


ChangeLogs / Migrations-Hinweise für API-Änderungen
Die detaillierten ChangeLogs und Änderungshinweise zu den Versionen sind im Manual eingearbeitet und werden auch auf http://framework.zend.com/changelog nochmal gesondert für die bereits veröffentlichten dargestellt.



3.3.8. Support
Für den Support des Frameworks werden dem Besucher unter http://framework.zend.com/support mehrere kostenlose und kostenpflichtige Services offeriert. Unter die kostenlosen Services fallen das Wiki, verschiedene Mailing-Listen, Chats, Blogs und der Bugtracker. Im deutschsprachigen Raum wird das "ZF Forum" (http://www.zfforums.de/), das oben bereits zitiert wurde, angeboten. Darüber hinaus bietet Zend das englischsprachige Pendant unter http://www.zfforums.com/, bzw. die Developer Zone (http://devzone.zend.com/public/view). Alles in Allem ist der Umfang der von Zend angebotenen Support-Möglichkeiten sehr groß und sehr vielseitig.



3.3.9. Benchmark
Das Benchmark-Ergebnis wurde mit der im Adventure PHP Framework integrierten Benchmark-Komponente BenchmarkTimer gemessen. Hierzu wurde ein
PHP-Code
$T = &Singleton::getInstance('BenchmarkTimer'); $T->start('ZFPage');
am Anfang der index.php und ein
PHP-Code
$T->stop('ZFPage'); echo $T->createReport();
am Ende eingefügt. Es wurde eine Durchschnitts-Wert von 0.2498 s gemessen. In der Rendering-Zeit ist die komplette Ausführungszeit für das Generieren der Views und der Formatierung der Quelltexte eingeschlossen.


» Weiter auf Seite 6 (Bewertung Adventure PHP Framework).


Kommentare

Möchten Sie den Artikel eine Anmerkung hinzufügen, oder haben Sie ergänzende Hinweise? Dann können Sie diese hier einfügen. Die bereits verfassten Anmerkungen und Kommentare finden Sie in der untenstehenden Liste.
« 1   »
Einträge/Seite: | 5 | 10 | 15 | 20 |
1
Christian 28.10.2007, 12:18:20
Da im Test bewusst auf den Einsatz von ByteCodeCaching-Mechanismen verzichtet wurde hier eine manuelle Test-Reihe zum Zend Framework und dem Adventure PHP Framework:

http://www.adventure-php-framework.org/frontend/media/PerformanceTest_mit_eAccelerator.txt