Um meiner Übersicht über das Zend Framework etwas Leben einzuhauchen, habe ich beschlossen ein kleines Tutorial über die Validierung von Eingaben zu schreiben. Es ist zum Überfliegen oder auch Nachbauen gedacht und soll einen kleinen Einblick in die Arbeitsweise des Zend Frameworks geben. Ein komplettes Skript mit den hier erklärten Inhalten ist hier zu finden.

Das Zend Framework stellt für die Validierung und die Verarbeitung verschiedene Objekte zur Verfügung. Für die Validierung ist das Modul Zend_Validate zuständig. Für die Nachverarbeitung Zend_Filter. Um Filter und Validatoren gesammelt auf eine Eingabe auszuführen, wird ein spezielles Objekt vom Typ Zend_Filter_Input verwendet.

Zend_Validate

Dieses Paket enthält mehrere Klassen, mit deren Hilfe Werte geprüft werden können. Jede Klasse bietet dabei die Methode isValid($value) an. Je nachdem was geprüft werden soll, muss die entsprechende Klasse instanziiert und schließlich die Methode isValid aufgerufen werden. Folgendes Beispiel zeigt wie geprüft werden kann ob ein gegebener Wert eine Ganzzahl ist:

$value = 1234;
$value2 = "otto";
$validator = new Zend_Validate_Int();
$validator->isValid($value); //true
$validator->isValid($value2); //false

Ein Array mit den exakten Fehlermeldungen erhält man mit $validator->getMessages(). Jede Klasse hat dabei eigene Fehlermeldungen, die in der Dokumentation zu finden sind. Dabei gibt es eine ganze Reihe von Validierungsobjekten für jeden erdenklichen Fall:

  • Zend_Validate_Alnum = Prüft ob Wert ausschließlich alphanumerische Zeichen enthält (Achtung: Umlaute sind keine alphanumerischen Zeichen)
  • Zend_Validate_Email = Prüft ob der Wert eine Emailadresse darstellt (optional sogar mit Prüfung ob für die angegebene Domain ein MX Eintrag existiert)
  • Zend_Validate_Between = Prüft ob Wert zwischen zwei Werten liegt.
  • weitere siehe Dokumentation

Neben den oben genannten Validate Klassen kann auch selbst eine Validierungsklasse erstellt werden. Das Beispielskript enthält so eine Beispielklasse.

Zend_Filter

Um den Inhalt von Variablen zu verarbeiten, gibt es die Zend_Filter Klassen. Hier wird ebenfalls eine gewünschte Klasse instanziiert und dann der zu verändernde Wert an die Methode filter($value) übergeben. Folgender Filter entfernt alle nicht alphanumerischen Zeichen aus einem String:

$value = "Das ist <blbl> test öäü. (bla)";
$filter = new Zend_Filter_Alnum(true); // true = auch Leerzeichen sind erlaubt
$filteredValue = $filter->filter($value);
echo $filteredValue; // liefert: Das ist blbl test  bla

Auch hier ist die Liste lang, so gibt es Filter die alle HTML Tags aus einem String entfernen, Sonderzeichen in HTML Entitäten umwandeln, Leerzeichen entfernt etc. (siehe Dokumentation):

Zend_Filter_Input

Für sich sind Validatoren und Filter zwar komfortabel, der wirkliche Vorteil kommt aber erst durch den Einsatz der Klasse Zend_Filter_Input zum Vorschein. Dies ist kein gewöhnlicher Filter, sondern eine Klasse, die es ermöglicht Filter und Validatoren in einem Durchlauf auf einen Satz an Eingaben (z.B. Formulardaten) anzuwenden. Ein Beispiel zeigt am besten wie elegant und übersichtlich so die Eingabeverarbeitung wird.

Als Beispiel wird ein einfaches Eingabeformular, wie es bei einem herkömmlichen Blog verwendet wird validiert.

input formular

Der HTML Quellcode hinter diesem Formular, wie man ihn kennt, ohne Besonderheiten:

<form action="index.php" method="post">
<label>Name:</label> <input type="text" name="name" />
<label>Email:</label> <input type="text" name="email" />
<label>Homepage:</label> <input type="text" name="homepage" />
<label>Nachricht:</label> <textarea name="message"></textarea>
<input type="submit" value="abschicken" name="submit" />
</form>

Nun sollen für alle Eingabefelder überflüssige führende und folgende Leerzeichen entfernt werden (z.B. “  Heinz Müller ” zu “Heinz Müller”). Der Name darf nur aus alphanumerischen Zeichen bestehen, sonst soll eine Fehlermeldung erscheinen. Die Email Adresse soll gültig sein, also @ und eine gültige Domain enthalten. Das Feld Homepage ist optional und im Feld Nachricht sind keine HTML Tags erlaubt und sollen, falls vorhanden, entfernt werden.

In dem verarbeitenden PHP Skript index.php werden zuerst die nötigen Zend Klassen inkludiert:

require_once('Zend/Filter/Input.php');
require_once('Zend/Filter/StringTrim.php');
require_once('Zend/Filter/StripTags.php');
require_once('Zend/Validate/EmailAddress.php');
require_once('Zend/Validate/Alnum.php');

Dann werden die nötigen Zend Validatoren vorbereitet. Dabei werden die per default eingestellten Nachrichten, die im Fehlerfalle ausgegeben werden, mit deutschen Texten überschrieben. Dies geschieht mit der Funktion setMessage($message, $error) die als ersten Parameter die neue Fehlermeldung und als zweiten Parameter die Fehlerart erwartet. Letzterer findet sich in der API Beschreibung der jeweiligen Klasse und unterscheidet sich auch je nach verwendeter Validierungs-Klasse

$validatorEmail = new Zend_Validate_EmailAddress();
$validatorEmail->setMessage("ung&uuml;ltige Email Adresse", Zend_Validate_EmailAddress::INVALID);

$validatorAlnum = new Zend_Validate_Alnum(true);
$validatorAlnum->setMessage("es sind nur alphanumerische Werte erlaubt", Zend_Validate_Alnum::NOT_ALNUM);
$validatorAlnum->setMessage("Bitte f&uuml;llen sie dieses Feld aus", Zend_Validate_Alnum::STRING_EMPTY);

Nun kommt die Klasse Zend_Filter_Input zum Einsatz. Der Konstruktor ist hier etwas komplexer:

$input = new Zend_Filter_Input( $array_filter, $array_validators, $data, $options );

Für unser Beispiel ist so folgender Konstruktoraufruf notwendig:

$input = new Zend_Filter_Input(
// Filter
array(
'name' => new Zend_Filter_StringTrim(),
'email' => new Zend_Filter_StringTrim(),
'homepage' => new Zend_Filter_StringTrim(),
'message' => array( new Zend_Filter_StripTags(), new Zend_Filter_StringTrim() )
),

// validators
array(
'name' => $validatorAlnum,
'email' => $validatorEmail,
'homepage' => array( Zend_Filter_Input::ALLOW_EMPTY => true ),
'message' => array()
),

// data
$_POST,

// options
array(
Zend_Filter_Input::NOT_EMPTY_MESSAGE => "Bitte f&uuml;llen sie '%field%' aus",
Zend_Filter_Input::BREAK_CHAIN => false
)
);

Der erste Parameter enthält alle Filter. Es wird hierfür ein assoziatives Array mit dem jeweiligen Feldnamen und einem Filter erwartet. Werden mehrere Filter benötigt (siehe im Beispiel das Feld “message”) dann können auch mehrere Filter in einem Array übergeben werden.
Der zweite Parameter enthält alle Validatoren, auch hier wieder als assoziatives Array. Hier kann auch festgelegt werden ob ein Feld vorhanden sein muss oder optional ist (optionale Felder erhalten den Wert “Zend_Filter_Input::ALLOW_EMPTY => true”, per default sind alle angegebenen Felder Pflichteingaben)
Der dritte Parameter enthält die Daten, hier im einfachen Fall werden alle Formulardaten ($_POST) übergeben.
Der letzte Parameter enthält Optionen. Zend_Filter_Input::NOT_EMPTY_MESSAGE legt die Fehlermeldung fest, die erscheint wenn ein Feld nicht befüllt wurde. Zend_Filter_Input::BREAK_CHAIN => false besagt, dass im Falle eines Fehlers die Verarbeitung nicht abgebrochen wird, sondern alle anderen Felder ebenfalls validiert werden.

Nun kann mit der Methode isValid() die Eingabe geprüft werden:

if($input->isValid())
echo "Validierung erfolgreich ".$input->getEscaped("name");
else
$errors = $input->getMessages();

Mit der Methode getMessages() können ggf. die Fehlermeldungen abgeholt werden. Es handelt sich dabei um ein assoziatives Array aus Feldname => array( Fehlermeldung1, Fehlermeldung2, … ).

Für ein besseres Verständnis, gibt es hier ein komplettes Beispielskript zum Download. Es enthält über diese Anleitung hinaus noch den Code für eine Ausgabe der Fehlermeldungen und eine eigene Validierungsklasse, welche überprüft ob ein Name auch aus zwei Teilen besteht (z.B. Heinz Müller). Das macht zwar nicht viel Sinn, aber ergibt ein gutes Beispiel wie man selbst Validierungsklassen erstellt und nahtlos in diesen Vorgang einfügen kann.

Nachdem ich das Zend Framework nun etwas genauer unter die Lupe genommen habe und es als Basis für meine Homepage und diesen Blog einsetze, will ich hier einen kleinen Überblick geben. In vielen Bereichen konnte es mich sehr überzeugen. Probleme und Funktionalitäten, die immer wieder zutage treten, werden mittels komfortabel nutzbaren Komponenten abgedeckt. Vorteile sind der umfangreiche Katalog an Funktionen und Features und die komplett objektorientierte Struktur. Dabei werden viele Standard Patterns verwendet (Adapter Pattern, Singleton etc.). Kritik wird allerdings an der Performance geübt: primär hat man bis zur aktuellen Version 1.6 versucht möglichst viel Features abzudecken, was natürlich einen gewissen Overhead produziert. Ein Nachteil, der sich in den meisten Fällen mittels Caching sehr einfach kompensieren lässt.

Wer einen Blick auf die Zend Framework Dokumentation wirft wird schnell feststellen, dass hier einiges geboten wird. Sehr positiv ist hier die ausführliche Dokumentation, die in mehreren Sprachen (auch auf Deutsch) vorliegt und viele Beispiele enthält. Meist genügen wenige Klicks und man hat die Information die man benötigt. Häufig reicht auch ein Blick in die API und dank eindeutiger und vielsagender Bezeichnungen ist dann meistens schon alles gesagt. Die Dokumentation kann auch als PDF Dokument heruntergeladen werden.

Das Framework besteht auf vielen Einzelkomponenten, die weitgehend unabhängig voneinander funktionieren. Das macht den Einstieg leicht und der Benutzer kann sich bei Bedarf das herauspicken, was er für sein Projekt benötigt. So kann man z.B. die Cache Komponente nutzen und in einer eigenen Programmstruktur einsetzen, ohne die gesamte Applikation auf das Zend Framework portieren zu müssen. Die wichtigsten Komponenten, die alle gängigen Anforderungen abdecken, will ich hier ganz kurz vorstellen. Mit den API Schnittstellen zu Diensten wie Amazon, Yahoo, Technorati, Delicious uvm. habe ich mich bisher nicht auseinandergesetzt, da ich sie bisher schlicht nicht benötigt habe. Für andere wiederum habe ich lediglich die Dokumentation gelesen (z.B. RPC) und werde diese in der nächsten Zeit nach und nach austesten.

Model View Controller (siehe Zend_Controller)
Die Kernkomponente schlechthin ist das MVC Pattern, welches eine übersichtlichere und leichter erweiterbare Programmstruktur ergibt (näheres zu diesem Prinzip gibt es auf Wikipedia). Ein sehr flexibler Routingmechanismus wertet dabei die URL aus und führt die jeweilige Aktion im angesprochenen Controller aus. Diese Implementierung sollte man unbedingt unter die Lupe nehmen, wenn man eine neue Applikation plant.

Datenbankzugriff (siehe Zend_Db)
Die Datenbankkomponente bietet eine Klasse für einen direkten Zugriff auf die Datenbank (wie man es bereits von PDO her kennt). Mittels einem Adapter können unterschiedliche Datenbanksysteme angesteuert werden (darunter auch MySQL mittels PDO). Neben dem Ausführen von SQL Statements besteht auch die Möglichkeit SQL Statements durch Klassen generieren zu lassen. Das ist sehr komfortabel, wenn man eine SQL Anfrage durch Parameter erst schrittweise aufbaut. Der eigentliche Komfort und hohe Nutzen liegt aber in der Klasse Zend_Db_Table. Mit dieser muss dann nur noch einmal eine Model Klasse definiert werden, die dann eine logische Entität darstellt. Hinter dieser Entität steht dann eine Tabelle in der Datenbank. Mittels einfacher Methoden lassen sich dann Einträge suchen, ändern, einfügen und auch abhängige Datensätze (z.B. Bestellpositionen zu einer Bestellung) laden. Dabei müssen Informationen wie die zugrundeliegende Tabelle, das Feld welches den Primärschlüssel darstellt oder die Beziehungen (1:n und m:n) nur einmal definiert werden. Diese Form der Datenhaltung führt zu sehr übersichtlichen Code, da dieser keine SQL Statements mehr enthält.

Validierung und Filterung (siehe Zend_Validate, Zend_Filter und Zend_Filter_Input)
Eine lästige Standardaufgabe ist die Validierung von Eingaben. Hier bietet Zend ein sehr flexibles System, das beliebig erweitert werden kann. Validate Objekte überprüfen die Eingaben (z.B. ob ein übergebener Parameter eine Ganzzahl ist), Filter verarbeiten Eingaben (z.B. entfernen alle nicht alphanumerischen Zeichen) und die Klasse Zend_Filter_Input führt diese zusammen und wendet Filter und Validate Objekte auf die jeweiligen Parameter einer Eingabe an. Dabei kann man selbst Filter und Validate Klassen definieren (z.B. die Prüfung ob ein Artikel, der durch eine ID angegeben wurde auch existiert).

Benutzer- und Rechtemanagement (siehe Zend_Acl)
Mit Hilfe einiger Klassen (Zend_Acl_Role, Zend_Acl_Resource) lassen sich Berechtigungsstrukturen (Zugriffskontrolllisten) aufbauen und durch Objekte darstellen. Mit dieser Komponente hab ich noch nicht allzu viel gemacht. Hier fehlt mir eine vernünftige Möglichkeit die Informationen persistent zu speichern und schnell zu laden. Ohne Caching macht aus meiner Sicht die Verwendung von ACL wenig Sinn.

Authentifikation (siehe Zend_Auth)
Diese Komponente dürfte wohl in nahezu jeder Anwendung nötig sein. Mittels Zend_Auth kann ein Objekt instanziiert werden, das Benutzername und Passwort prüft. Dabei wird neben der klassischen Authentifizierung, mit Hilfe einer Datenbanktabelle, auch Verfahren wie LDAP oder OpenID unterstützt.

Sessions (siehe Zend_Session)
Hiermit lassen sich neue Sessions erzeugen und beliebige Werte und Objekte speichern. Primär ist dies ein Wrapper für die eigentliche Session Implementierung mit einigen Detailverbesserungen und dem Komfort von Objektorientierung.

Caching (siehe Zend_Cache)
Zend_Cache ist eine hervorragende Implementierung für ein vernünftiges Caching. Dabei wird zwischen Frontend und Backend unterschieden. So kann für ein Frontend (z.B. Cachen der gesamten Seite, Cachen von einzelnen Funktionsergebnissen etc.) beliebig das Backend getauscht werden (z.B. von dateibasiertes Caching auf Memcached). Für meine Homepage und den Blog hab ich eine sehr primitive Strategie des Cachens gewählt, aber man kann sehr detaillierte Einstellungen vornehmen und genau festlegen wann der Cachingmechanismus greifen soll. Aus meiner Sicht ein absolutes Highlight des Frameworks.

Lokalisierung (siehe Zend_Locale)
Dieses Modul ist eines der Komponenten, die sich für große Systeme sehr schnell bezahlt machen. Es umfasst nicht nur die Verwendung von Sprachdateien, sondern auch die Konvertierung von Formaten (z.B. die Darstellung von Zahlen, Datumangaben und Uhrzeiten) und das Umrechnen von Einheiten. Dabei ist das Ganze so ausgelegt das, dass aktuell verwendete Lokalisierungsobjekt festlegt, wie die Ausgabedarstellung zu erfolgen hat (bzw. wie die Eingaben eines Benutzers ausgewertet werden müssen). Wer jemals eine Applikation für verschiedene Länder realisiert hat, der wird diese Komponente sofort lieben!

Feeds (siehe Zend_Feed)
Eine überschaubare Kollektion von Klassen, die es erlaubt mit einfachsten Mitteln RSS und Atom Feeds zu erzeugen und zu verarbeiten. Hier gefällt mir die einfache Art der Handhabung.

Mails (siehe Zend_Mail)
Klassen zum Versenden und Empfangen von E-Mails. Hier habe ich bisher nur das Versenden ausgetestet, was geradezu simpel ist: Klasse instanziieren, setSubject, setBodytext, setFrom, addTo und am Ende send aufrufen.

Konfigurationsdateien (siehe Zend_Config)
Ein ebenfalls sehr kompaktes Modul. Es bietet die Möglichkeit Konfigurationsdateien in den Formaten XML, INI und als PHP Array einzulesen. Simpel aber nützlich. Insbesondere die Verwendung von INI Dateien find ich sehr sauber und übersichtlich.

Logging (siehe Zend_Log)
Ein Logger wie man ihn kennt und das Schreiben in Datenbanken, Streams und Dateien unterstützt. Besonders toll: wer unter Firefox die Firebug Erweiterung installiert hat, kann die Loggingmeldungen direkt an diesen übertragen (was über den HTTP Header passiert, damit die Darstellung der Seite nicht verändert wird). Bisher hab ich den Logger noch nicht verwendet (dafür ist die Homepage bzw. der Blog dann doch zu überschaubar).

Was hier oben aufgelistet ist, ist nur ein kleiner (aus meiner Sicht aber der entscheidende) Auszug aus dem Framework. Zahlreiche Komponenten sind unerwähnt geblieben wie z.B. Zend_Captcha, Zend_XmlRpc, Zend_Soap, Zend_Json. Es lohnt sich also immer zuerst die Doku zu prüfen, denn möglicherweise ist das, was man aktuell braucht schon fertig und man muss es nur noch benutzen.

Ein gutes Tutorial für den Einstieg gibt es auf Ralf Eggerts Blog. Leider ist es nicht mehr ganz aktuell, gibt aber trotzdem einen guten Einblick in die Prinzipien und Möglichkeiten. Auf t-mow findet ihr ein weiteres, etwas aktuelleres Tutorial.

So nun gibt es wieder ein Update für diesen Blog. Eigentlich ist das ursprüngliche Skript nicht schlecht gewesen, dennoch habe ich mich dazu entschlossen das Backend auf das Zend Framework umzurüsten. Primär ging es mir darum das MVC Framework gründlich auszutesten und ein wenig Übung im Umgang mit diesem zu bekommen. Insgesamt bin ich sehr begeistert von dem Komfort und dem weitreichenden Funktionsumfang den das Framework bietet. Probleme wie Internationalization und Caching werden sehr elegant gelöst und überzeugen auch im Detail. Wenn das Interesse da ist, dann bin ich gerne bereit mehr zu den Thema zu schreiben und Teile des Blogs vorzustellen. Wer also mehr dazu lesen will: einfach kommentieren.

Im Zuge der Umstellung habe ich das Frontend etwas verbessert (meist nur Details) und auch die Administrationsoberfläche mit zusätzlichen Funktionen ausgestattet (die der Besucher leider nicht zu sehen bekommt).

Die nächste Zeit werde ich wieder etwas aktiver sein, es haben sich ein paar Sachen angesammelt über die ich noch schreiben wollte. Über Feedback freue ich mich immer, auch über Grundsätzliches z.B. welche Beiträge ihr euch wünscht!