28 Jan, 2008
Zend Framework How-To Teil 1: Projektaufbau, Konventionen und modulare Verzeichnisstruktur
Posted by: Fritz Thomas In: Zend Framework
Das ist der erste Teil meiner Tutorial oder How-To Serie für das Zend Framework.
Dieser erste Artikel wird etwas früher als mit der Implementierung des bootstrapping Prozesses oder dem Erstellen neuer Controller, Models oder Views beginnen. Ich werde hier als erstes grundlegende Gedanken und Überlegungen in den Raum stellen, die man sich vor der Implementierung stellen sollte – wie zum Beispiel die Wahl der richtigen Verzeichnisstruktur und die Ausarbeitung eigener Konventionen.
Wozu die ganze Theorie?
Die Wahl der Verzeichnisstruktur oder das überlegen und / oder aufschreiben eigener Konventionen mag im ersten Moment vielleicht etwas nebensächlich erscheinen und auch so behandelt werden, doch will man eine Applikation von vorn herein so modular wie möglich aufbauen um später nicht mehr Arbeit zu haben um neue Features (Module, Plugins, Controller, I18N, Caching, Suche, etc..) zu implementieren sollte man sich etwas mehr Zeit nehmen um über die jetzigen und auch die zukünftigen Anforderungen an die Applikation, nachzudenken. Die Wichtigkeit dessen steigt, um so mehr Entwickler an einem Projekt beteiligt sind, da darauf um so mehr zu achten ist, dass jeder Entwickler weiß welche Code Conventions einzuhalten sind, und vor allem WIE (und nicht ob!!!) dokumentiert werden muss.
Analyse der Anforderungen
Jeder Entwickler, der das eine oder andere größere Projekt fertig gestellt hat, weiß das genaue ausgedehnte Analysen zu Beginn der Entwicklung nicht verschwendete Zeit ist. Vorausgesetzt man nutzte die Zeit auch wirklich produktiv!
Als erstes sollte man darüber nachdenken welche Anforderungen an das Projekt gestellt werden:
- Soll das Projekt über eine Suche verfügen?
- Soll sie Mehrsprachige Texte zur Verfügung stellen (Stichwort – Internationalisation oder I18N)?
- Wenn es eine im Internet öffentlich zugängliche Seite ist, wieviele Hits werden erwartet (Stichwort: Caching)?
- Welche Features sind für die Zukunft geplant?
- Wer und wieviele Entwickler werden an dem Projekt arbeiten?
- Wie soll daher der Sourcecode verwaltet werden?
Keine Sorge, ich werde nicht auf jede einzelne Frage eingehen. Sie sollten eher als Gedankenanstoß gedacht sein. Es macht aber durchaus Sinn sich zu Beginn solche Fragen zu stellen und im Team zu besprechen und dazu alle Ideen, Bedenken und Vorschläge aufzuschreiben. Einige im Team sind optimistisch, andere wieder pessimistisch – genau die Mischung finde ich kann sich sehr positiv auf ein Projekt auswirken. Was das Brainstorming in einem Team betrifft habe ich die Ratschläge von Zack Urlocker sehr nützlich gefunden.
Programmierpraktiken und Konventionen
Aufgrund der gewonnen Erkenntnisse die man aus den Überlegungen zu Beginn und aus vorigen Projekten erlangt hat sollte man, auch wenn man alleine entwickelt, sich einerseits über vorhandene Konventionen informieren und andererseits sich eigene Programmierpraktiken (Best Practices) und Konventionen aneignen.
Dabei sollte man sich die in der Praxis als gut bewährten stets behalten und an denen, die sich als schlecht herausgestellt haben, arbeiten und verbessern. Das klingt schon wieder so theoretisch, aber ich denke Ihr wisst was ich meine.
Natürlich sollte man das ganze nicht übertreiben und jedes einzelne Detail bestimmen und festlegen. Vor allem in einem Team von Entwicklern muss es auch gestattet sein Ausnahmen von den Konventionen zu machen sofern sie die Lesbarkeit und Wartbarkeit des Codes nicht verschlechtern.
Eine konkrete ‘Konvention’ wäre zum Beispiel das man die Konfiguration für eine Komponente (Action_Helper, View_Helper, etc…) immer mit dem Klassennamen als Key in der Zend_Config oder in der Zend_Registry speichert oder aber auch das man jedes Modul als geschlossene Einheit betrachtet und es auch so entwickelt das man sie in anderen Applikationen wieder verwenden kann.
Ein andere Konvention an die man sich in seinen Projekten durchgehend halten sollte wäre die Verzeichnisstruktur.
Verzeichnisstruktur
Die Verzeichnisstruktur macht es unter anderem (“Programmierpraktiken und Konventionen”) leichter vorhandene Module in weiteren Projekten mit wenig oder gar keinem Aufwand wieder zu verwenden. Und das sollte doch das Ziel eines jeden Entwicklers sein, oder wer erfindet schon gerne das Rad immer wieder neu?
Eine solche modulare Verzeichnisstruktur könnte so aussehen:
application/ htdocs/ library/
Der Document_Root des WebServers ist hier ‘htdocs/’. Das Zend Framework und evtl. andere Librarys sind im Verzeichnis ‘library/’ zu finden. Der include_path sollte auch entsprechend für dieses Verzeichnis gesetzt sein und muss nicht zwingend im selben Verzeichnis wie htdocs und application sein. Die eigentlichen Dateien und Verzeichnisse der Applikation liegen in ‘application/’. Das erleichtert es auch den Sourcecode zu verwalten und später das Aktualisieren der fertigen Applikation.
Der Inhalt des ‘application/’ Verzeichnisses:
bootstrap.php config/ layouts/ library/ log/ modules/ temp/
bootstrap.php
In der bootstrap.php findet das eigentliche Initialisieren der Applikation statt. In der index.php im ‘htdocs/’ Verzeichnis wird ‘bootstrap.php’ lediglich ‘required’:
htdocs/index.php:
< ?php require '../application/bootstrap.php';
wie schon erwähnt, in der bootstrap.php findet das eigentlich bootstrapping statt:
application/bootstrap.php:
< ?php $applicationPath = dirname(__FILE__); define('APPLICATION_PATH', $applicationPath); unset($applicationPath); require_once 'Zend/Loader.php'; Zend_Loader::registerAutoload(); $front = Zend_Controller_Front::getInstance(); $front->addModuleDirectory(APPLICATION_PATH . DIRECTORY_SEPARATOR . 'modules'); $front->dispatch();
In Zeile 3 wird eine Konstante mit dem absolute Pfad des application Verzeihnisses angelegt. Das ermöglicht es anderen Komponenten oder Modulen im Kontext des application Verzeichnisses zu arbeiten. Zum Beispiel Log Dateien schreiben, Ausgaben cachen, oder aber auch in einem Modul Unterverzeichnis Dateien auszulesen. Um zum Beispiel im Modul ‘Filesystem’ ein Unterverzeichnis ‘files/’ auszulesen:
Da in der index.php nur eine einzige Zeile PHP Code, im einzigen nach aussen offenen Verzeichnis ‘htdocs/’, zu finden ist, wird ausserdem bei einem nicht richtig funktionierenden Webserver, der den Inhalt von php Dateien ausgibt anstatt den Code auszuführen, verhindert das viele Informationen preisgegeben werden.
modules/filesystem/controllers/IndexController.php:
< ?php require_once 'Zend/Controller/Action.php'; class Filesystem_IndexController extends Zend_Controller_Action { /** * The default action - show the home page */ public function indexAction () { $directory = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'filesystem' . DIRECTORY_SEPARATOR . 'files'; $res = opendir($directory); $contents = array( ); if (false !== $res) { while (false !== ($content = readdir($res))) { if ($content !== '.' && $content !== '..') { $contents[] = $content; } } } $this->view->contents = $contents; } }
So ist es möglich alle zu einem Modul gehörenden Files im Modul Verzeichnis zu verwalten. Installiert man dieses Modul in einer Applikation müssen so keine Anpassungen vorgenommen werden oder irgendwelche Pfade und Variablen zu den Pfaden angepasst werden.
config/
Wie der Name schon vermuten lässt werden hier globale Konfigurationen die in der gesamten Applikation gültig sind gespeichert. Egal ob es sich dabei um eine ini Datei handelt oder um eine xml Datei.
‘application/library/’
Das ‘application/library/’ Verzeichnis kann dazu verwendet werden vorhandene Klassen im globalen ‘library/’ Verzeichnis zu überladen. Das ermöglicht es auf Servern wo mehrere unterschiedliche ZF Applikationen gehostet sind und gemeinsam eine Library nutzen, einzelne Klassen des Zend Frameworks oder der eigenen Library zu überladen. Dazu sollte mittels set_include_path() in der bootstrap.php der Pfad an die erste Stelle gesetzt werden:
bootstrap.php:
< ?php $applicationPath = dirname(__FILE__); define('APPLICATION_PATH', $applicationPath); unset($applicationPath); require_once 'Zend/Loader.php'; Zend_Loader::registerAutoload(); $oldIncludePath = get_include_path(); $localLibraryDir = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'library'; set_include_path($localLibraryDir . PATH_SEPARATOR . $oldIncludePath); $front = Zend_Controller_Front::getInstance(); $front->addModuleDirectory(APPLICATION_PATH . DIRECTORY_SEPARATOR . 'modules'); $front->dispatch();
log/
Log Dateien der Applikation. Modul Log Files sollten ebenfalls in diesem Verzeichnis gespeichert werden. Hier sollten nicht die Apache Log Files geloggt werden.
modules/
Alle Zend Framework Module werden hier drin gespeichert.
Ein einzelnes Modul sollte nach diesem Muster aufgebaut sein:
Modulverzeichnisstruktur:
default/ config/ controllers/ models/ views/ helpers/ filters/ scripts/ layouts/
Die Bedeutung der einzelnen Verzeichnisse sollte soweit klar sein.
temp/
In diesem Verzeichnis sollten alle temporären Dateien, wie zum Beispiel caching files fuer Zend_Cache, oder auch das ‘templates_c’ Verzeichnis von Smarty, gespeichert werden. PHP Sessions sollten sollten nicht hier gespeichert werden, weil es das Auslesen vorhandener Sessions erleichtern würde, in dem Fall das die Applikation durch einen Fehler es ermöglicht fremden Code auszuf¨hren.
Im nächsten Teil meines Zend Framework Tutorial werde ich anhand einer Beispiel Applikation die Verwendung von Zend_Layout und den neuen Zend_View_Helpern Placeholder, Partial und Action erklären.
Welche Verzeichnisstruktur verwendet Ihr? Was haben Eure Erfahrungen gezeigt? Ich denke es würde allen Lesern interessieren.