Schlagwort-Archive: PHP
PHP 8 und GDLib im Docker Container
WordPress REST API ist nicht erreichbar
PHP meets Maven – Teil 4

[Teil 1] [Teil 2] [Teil 3] [Teil 4]
Für die Integration in IDEs ist es unerheblich, bei welcher IDE Ihre persönlichen Präferenzen angesiedelt sind, der Funktionsumfang der Integration ist in beiden IDEs weitgehend identisch und unterscheidet sich nur in Details. Während NetBeans von Haus aus Maven-Projekte unterstützt, ist für die meisten Eclipse-Distributionen die zusätzliche Installation des Eclipse-Maven-Plug-ins m2e notwendig.
Der Vorteil, die Funktionalitäten von Maven innerhalb einer IDE nutzen zu können, ist enorm. Ein Aspekt ist beispielsweise der Import bestehender Maven-Projekte in die Entwicklungsumgebung. Anhand der POM werden die notwendigen Konfigurationen des gesamten Projekts wie zum Beispiel Verzeichnisse für Sourcen, Test und Dependencies aus der POM gelesen. Ein mühseliges Adaptieren der Projekteigenschaften nach einem Import entfällt ebenso wie das Verteilen der IDE-Konfiguration über das Konfigurationsmanagement. Dadurch hat der Entwickler mehr Freiheit bei der Wahl seiner Entwicklungsumgebung. Eine Grundvoraussetzung für den erfolgreichen Import eines Projekts in eine IDE ist, dass die verwendeten Dependencies lokal oder remote verfügbar sind. Hin und wieder kommt es vor, dass einzelne Artefakte manuell in das lokale Repository installiert werden müssen. Diese Aufgabe lässt sich in beiden IDEs sehr komfortabel mit wenigen Mausklicks bewerkstelligen und ein optisches Feedback des Erfolgs kann über die Views der Repository-Browser eingeholt werden.

Ältere Projekte, die nicht im Maven-Format vorliegen und damit nicht die notwendige Verzeichnisstruktur und POM aufweisen, lassen sich in den meisten Fällen über die Konsole automatisiert migrieren. Der schnellere Weg ist allerdings eine manuelle Migration, da die automatisch generierte POM in aller Regel im Nachhinein weiter von Hand angepasst werden muss. Über Archetypes werden die Verzeichnisstruktur und die POM erzeugt. Im zweiten Schritt sind die Sourcen et cetera in die entsprechenden Verzeichnisse zu kopieren, um abschließend die Dependencies zu konfigurieren. In späteren Arbeitsschritten kann die POM den Projektanforderungen weiter angepasst werden.

Eine wichtige Eigenschaft ist unter anderem auch die Möglichkeit, das vorhandene Maven Build-in der IDEs durch eine eigene Maven-Installation auszutauschen. Der Vorteil einer externen Installation ergibt sich aus dem größtmöglichen Einfluss auf den Entwicklungsprozess, da beispielsweise festgelegt wird, welche Version von Maven verwendet wird. Vor allem, wenn stets auf die neueste Version zurückgegriffen werden soll, ist diese Option von unschätzbarem Wert, das es meist einige Zeit dauert, bis das entsprechende Plug-in aktualisiert wird.
Die größten Unterschiede zwischen NetBeans und Eclipse finden sich bei der Bearbeitung der POM. Während NetBeans auf eine Code-Vervollständigung setzt, bietet Eclipse einen grafischen POM-Editor. Für den korrekten Betrieb von Maven ist die Auszeichnung der Schemadefinition der POM nicht notwendig. Das Weglassen der XML-Schemadefinition quittiert Eclipse mit einer Fehlerausgabe, die folgende Auszeichnung des <project> Tags beendet die Belästigung umgehend:
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
XML

Das Anstoßen der einzelnen Build-Lifecycles aus Eclipse oder NetBeans heraus ist mittlerweile recht intuitiv. Der Übersichtlichkeit wegen werden nur die wichtigsten Phasen der Lifecycles zum direkten Ausführen über die Toolbar beziehungsweise das Kontextmenü angeboten. Dazu zählen vor allem Build, Clean, Clean Build und Test. Wenn dennoch einmal ein spezielles Goal gestartet werden muss, bieten beide IDEs die Möglichkeit, über einen Wizard die entsprechende Phase mit dem gewünschten Goal zu konfigurieren und auszuführen. Im Screenshot ist beispielhaft die Run-Konfiguration von Eclipse abgebildet. Für php-maven Projekte existiert ein Eclipse-Plug-in, das von Martin Eisengart entwickelt wurde. Aktuell ist dazu eine neue Version für Eclipse Indigo erschienen. Eine wichtige Eigenschaft dieses Plug-ins ist die Konvertierungsfunktion für Maven-Projekte nach php-maven. Nach erfolgreicher Konvertierung zeigt Eclipse in der View Problems den Fehler, dass das maven-plugin-Plug-in nicht ausgeführt werden kann. Diese Meldung ist kein wirklicher Fehler, sondern ergibt sich aus den Restriktionen des m2e-Plug-ins, das für alle unbekannten Plug-ins Fehler ausgibt.

Sehr komfortabel ist das Generieren der Site über das Plug-in. Dazu hält der Menüeintrag die Punkte generate, view und deploy bereit. Besonders angenehm ist die Option, die generierte Seite im Browser auszugeben, ohne umständlich über das Target-Verzeichnis navigieren zu müssen.
Wenn die Testfälle über das Kontextmenü in das Projekt eingebunden wurden, kann die View PHPUnit die wichtigsten Informationen der durchlaufenen Testfälle visualisieren. Neben den Testergebnissen wird auch eine Coverage ausgegeben (Bild 6).

Berichtswesen
Neben dem Build- und Clean-Lifecycle existiert als Dritter im Bunde der Site-Lifecycle, mit dem Reports und sogar komplette Webseiten automatisch generiert werden können. Ein gutes Beispiel der Site-Generierung ist die Homepage des php-maven Projekts, die mit Maven erzeugt wurde.
Innerhalb der POM können verschiedene Angaben zu wichtigen Projektinformationen gemacht werden, die über eine Projektseite publiziert werden können. Typische Informationen sind unter anderem der Projektname mit einer Kurzbeschreibung, dem Gründungsjahr und der Lizenz des Artefakts. Neben diesen allgemeinen Informationen können auch die URLs zu CI-Servern, Sourcecode-Repositories, Mailing-Listen und beteiligten Personen angegeben werden. Die notwendigen Einträge der POM zeigt Listing 1.
<licenses>
<license>
<name>BSD 3-Clause</name>
<url>http://www.opensource.org/licenses/BSD-3-Clause/</url>
</license>
</licenses>
<name>CMS</name>
<description>
A Collection of diffrent Modules for a CMS.
</description>
<url>https://elmar-dott.com</url>
<inceptionYear>2012</inceptionYear>
<scm>
<url>https://git.elmar-dott.com</url>
<connection>https://git.elmar-dott.com</connection>
<developerConnection>https://git.elmar-dott.com</developerConnection>
</scm>
<issueManagement>
<system>Redmine</system>
<url>https://issues.elmar-dott.com/</url>
</issueManagement>
<ciManagement>
<system>Jenkins</system>
<url>http://localhost/jenkins</url>
</ciManagement>
<developers>
<developer>
<name>Elmar Dott</name>
<id>ed</id>
<email>ed@elmar-dott.com</email>
<roles>
<role>Release-Management</role>
</roles>
<organization>Elmar Dott Consulting</organization>
<organizationUrl>https://elmar-dott.com</organizationUrl>
<timezone>+1</timezone>
</developer>
</developers>
Um der Seite statische Inhalte zuzufügen, stehen unterschiedliche Mechanismen zur Auswahl. Grundlegend ist das Verzeichnis site unterhalb von src im Projektverzeichnis anzulegen, in dem unter anderem auch der Site-Deskriptor hinterlegt wird. Über den Site-Deskriptor site.xml werden unter anderem die Navigation zusammengebaut und zusätzliche Inhalte hinzugefügt. Es können drei unterschiedliche Content-Typen erzeugt werden: APT (Almost Plain Text) ist ein an Wiki Style angelehntes Format, während fml eine FAQ-Struktur erzeugt und überwiegend in Maven-1-Projekten zum Einsatz kam. Am verbreitetesten ist xDoc, ein XML-basiertes Format, um Inhalte zu erstellen.
Um der Seite verschiedenste Reports hinzuzufügen, ist das Site-Plug-in entsprechend zu konfigurieren. Der übliche Weg über den Abschnitt <reports>
ist mittlerweile als deprecated gekennzeichnet und sollte nicht weiter verwendet werden. Um nicht benötigte Reports auszusparen, werden diese in der Konfiguration des <reportSets>
weggelassen. Auf der Maven-Plugin-Seite finden sich noch weitere Plug-ins zu Reports, beispielsweise das Checkstyle-Plug-in, um den Code auf die Einhaltung festgelegter Style-Guides zu prüfen.
- Teil 1 – Einführung, CoC, DRY
- Teil 2 – POM, Testen & API Dokumentation
- Teil 3 – Web-Anwendungen mit Maven
- Teil 4 – IDE-Integration
PHP meets Maven – Teil 3

[Teil 1] [Teil 2] [Teil 3] [Teil 4]
Wer bereits einmal in die Verlegenheit gekommen ist eine im Produktivzustand arbeitende PHP-Webapplikation zu aktualisieren, wird mir sicherlich beipflichten, das diese Arbeit äußerst ungern gemacht wird. Eine andere Unschönheit ergibt sich daraus, wenn ein solches System für die Entwicklung eines neuen Webauftritts beispielsweise lokal installiert wird. Nach getaner Arbeit sind dann verschiedene Hürden zu meistern, um die Anwendung über ein QS-System auf dem Live-Server lauffähig zu bekommen. Viele Probleme lassen sich bereits während der Entwicklungsphase durch etwas Planung und eine saubere Architektur vermeiden. Gerade bei Webanwendungen kann durch eine effiziente Modularisierung in Kombination mit Maven ein erheblicher Mehrwert erzielt werden.
Ziel dieses Teils der Artikelserie ist es nicht, Migrationswege für bereits bewährte Webapplikationen wie beispielsweise Magento, Media-Wiki und Jomoola nach Maven aufzuzeigen. Ein solches Vorhaben sollte aus verschiedenen Gründen reiflich überlegt werden und ist eher etwas für erfahrene Entwicklungsteams. Für eine erfolgreiche Migration ist tiefgreifendes Systemwissen unbedingt notwendig.
Gezeigt wird, wie mit PHP und Maven moderne und zukunftssichere Webanwendungen erstellt werden können. Die Basis dazu bilden die bereits vorgestellten Library-Artefakte, die nun zu einer gesamten Anwendung orchestriert werden. Etablierte Applikationen wie Magento, um nur einen willkürlich gewählten Vertreter zu nennen, sind weitaus älter als die vorgestellten OOP-Eigenschaften, die durch PHP 5.3 eingeführt wurden. Deswegen ist auch kein direkter Architekturvergleich möglich.
Die Segel in Richtung Zukunft
Die Vision in der Software-Entwicklung besteht vor allem darin, einmal entwickelte Module wiederverwenden zu können. Im PHP-Maven-Projekt ist das erklärte Ziel, ein umfangreiches Repository an freien und kommerziellen Artefakten im Lauf der Zeit anzusammeln und zur Verfügung zu stellen. Um Namenskonflikten aus dem Weg zu gehen, ist die Verwendung von Namespaces in Library-Projekten unumgänglich. Wichtige Designregeln sollten zwingend eingehalten werden, wofür die folgende Checkliste herangezogen werden kann:
- echo und print sind innerhalb des Produktivcodes absolut tabu.
- Die Entwicklung erfolgt rein objektorientiert (OOP).
- Namespaces sind zu verwenden.
- Eine Klasse pro Datei, wobei Klasse und Dateinamen identisch sind (korrespondieren).
- Kein Modul darf direkt auf eine Datenbanktabelle eines anderen Artefakts zugreifen; es sind nur API-Aufrufe gestattet.
- Content wird über Datenbanktabellen persistiert.
- Die Konfiguration erfolgt über XML- oder INI-Dateien.
Diese Liste der aufgezählten Punkte stellt eine Mindestanforderung für Artefakte dar, die darauf abzielen, ihre Funktionalität möglichst vielen Projekten über einen langen Release-Zeitraum zur Verfügung zu stellen. Die Reihenfolge ist keine Priorisierung. Ein klarer Stil der Codierung sollet stringent eingehalten werden. Beachtet man diese Punkte nicht, kann sich das negativ auf den Entwicklungsprozess auswirken.
Die Problematik der Namespaces wurde bereits erläutert. Die Forderung, dem OOP-Paradigma zu folgen, begründet ihren Ursprung vor allem in der Kapselung der Funktionalitäten und der guten Strukturierung des Codes. Dass der Dateiname mit der Klasse zu korrespondieren hat, dient ebenfalls der besseren Übersicht und ermöglicht das Verwenden von Auto-Class-Loadern. In aller Regel werden fertige Artefakte durch eine übergeordnete Anwendung aufgerufen. Erzeugt ein Artefakt eigenständig sichtbare Systemausgaben in der Anwendung, ist dies ein ernstes Problem. Fehler oder Debug-Ausgaben sind aus diesem Grund ausschließlich über ein Logging-Verfahren zu behandeln. Ein sehr wichtiger Punkt im Hinblick auf die Wartbarkeit einer Applikation ist die Forderung nach Zugriffen auf Datenbanktabellen. Sicherlich mag im ersten Moment ein SQL-Statement attraktiver wirken als ein API-Aufruf. Immerhin attestiert es dem Entwickler einen tiefen Einblick in das vorhandene System. Dummerweise offenbart ein solches Vorgehen nicht die Brillanz des Akteurs, sondern dessen mangelnde Teamfähigkeit.
Ein weiterer Aspekt ist das Persistieren von Daten. Die Faustregel zur Entscheidung, wie Daten langfristig zu speichern sind, ist, dass alle Einstellungen, die das Verhalten eines Systems beeinflussen, in Textdateien abgelegt werden sollten. Durch Nutzer erzeugte Inhalte wie Texte gehören in eine Datenbank. Typische Konfigurationseinstellungen sind Datenbankparameter, da sie sich je nach System unterscheiden. Solche Dinge in einer Datenbank abzulegen erschwert den Aufwand des Deployments erheblich. Content hingegen hat keinen direkten Einfluss auf die Applikation und muss daher nicht in die Entwicklungssysteme synchronisiert werden. Im Gegenteil, dieser Zustand wäre ein erhebliches Sicherheitsrisiko. Ein Beispiel wären Accountdaten mit Adresse und Bankverbindung der Nutzer eines Webshops. Diese Information ist nur dem Betreiber zugedacht und nicht der Entwicklungsabteilung der Applikation.
Strukturarbeiten

Nachdem nun die Voraussetzungen für optimales Artefakt-Design bekannt sind, ist es an der Zeit, diese durch eine Webanwendung zu einem Gesamtwerk zu vereinen. Auch wenn es auf den ersten Blick trivial erscheint: Ein geschickt gewähltes Verzeichnislayout ist bereits die halbe Miete. Bild 1 enthält eine empfohlene Verzeichnisstruktur für Webprojekte mit den wohlbekannten Elementen. Einzige Ausnahme bildet hier der Ordner PHP-INF mit sämtlichen geschützten Inhalten, die für Außenstehende nicht einsehbar sein dürfen. Das Vorbild dieses Verzeichnisses ist Java-Webprojekten entnommen. Um das PHP-INF Verzeichnis vor unerwünschten Blicken verborgen zu halten, bietet sich eine .htaccess Datei in Kombination mit einer robots.txt an, die sämtliche Suchmaschinen aussperrt, um nur einige Schutzmechanismen aufzuzeigen.
Von besonderem Interesse sind die Dateien des Unterverzeichnisses libs. Wie diese PHP-Archive erzeugt werden, wurde im vorangegangenen Teil dieser Serie beschrieben. Im Kontext der Webanwendung sind diese Artefakte einfache Dependencies, die durch Maven verwaltet werden und über die Bootstrap-Datei index.php eingebunden sind. Auf diese Weise entsteht im Lauf der Zeit ein Baukastenprinzip, ähnlich einem Komponenten-Framework.
Im Gegensatz zum vorgestellten Library-Projekt befinden sich die Sourcen nun im Ordner resources. Der Grund dafür ist sehr schnell aufgezeigt. Maven kopiert aus diesem Verzeichnis die Dateien in der gleichen Hierarchie in das target Verzeichnis. Ein besonders hilfreiches Feature ist, dass Maven im resources Verzeichnis Filter anwenden kann, die es ermöglichen, Texte zu ersetzen. Dazu ist lediglich über den Build-Lifecycle das Filtering in der POM zu aktivieren:
<build>
<resources>
<resource>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
XMLDiese Eigenschaft ist besonders wertvoll für das Deployment. In den Konfigurationsdateien der Anwendung können so Systemeigenschaften in Platzhalter ausgelagert werden. So erklärt sich auch die strikte Forderung, Systeminformationen in Textdateien vorzuhalten. Es besteht natürlich auch die Option, in SQL-Dateien eine Textersetzung vorzunehmen, um Konfigurationen vorzuhalten. Man sollte sich aber bewusst sein, dass Datenbanktabellen, die im schlimmsten Fall in einer Spalte Konfigurationen vorhalten, kaum zum Verständnis des Systems beitragen. Besonders aus Sicht der Wartbarkeit bietet eine Konfigurationsdatei mehr Flexibilität als SQL-Statements. Die Eigenheit, alles möglichst über Datenbanktabellen abzuspeichern, hat eine eher einfache Ursache. Eine Konfigurationsdatei im Filesystem muss durch verschiedene Mechanismen vor unbefugtem Zugriff geschützt werden. Datenbanktabellen bieten von Hause aus mehr Sicherheit. Ähnlich verhält es sich bei den bekannten config.php Files.
Um Texte ersetzen zu können, werden sogenannte Profile benötigt, die in dem vorgestellten Beispiel über die POM vorgehalten werden. Die verschiedenen Profile werden über eine ID unterschieden.
<profiles>
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<dbms>mysql</dbms>
<db.server>localhost</db.server>
<db.name>test</db.name>
<db.prefix>test_</db.prefix>
<db.user>User</db.user>
<db.pwd>login</db.pwd>
</properties>
</profile>
<profile>
<id>qs-stage</id>
<properties>
<dbms>mysql</dbms>
<db.server>localhost</db.server>
<db.name>test</db.name>
<db.prefix>test_</db.prefix>
<db.user>User</db.user>
<db.pwd>login</db.pwd>
</properties>
</profile>
</profiles>
XMLEin möglicher Weg, um ein Profil zu aktivieren, ist das <activeByDefault>
-Tag. Es gibt natürlich auch noch viele andere Wege, die auf der Manual-Page beschrieben werden.
PHP-CLI
Damit Maven seine volle Kraft ausschöpfen kann, ist es notwendig, das Command Line Interface (CLI) für PHP in der Konsole zu aktivieren.
Mit dem CLI ist es möglich, PHP-Skripts ohne Webbrowser direkt auf der Kommandozeile auszuführen. Diese Funktion wird beispielsweise benötigt, um aus Maven heraus die Sourcecode-Dokumentation über den php-Documentor anzustoßen. Sobald der Pfad zum Verzeichnis der php.exe in die PATH-Variable aufgenommen wurde, können PHP-Skripts über die Konsole ausgeführt werden. Der Erfolg einer Installation lässt sich durch die Anweisung php –v rasch überprüfen. Im Erfolgsfall wird sie mit der Ausgabe der installierten PHP-Version quittiert.
Packungsinhalte
Nachdem die Projektstruktur von Webapplikationen mit ihren Besonderheiten vorgestellt worden ist, ist es nun an der Zeit, einige Details über die POM zu erwähnen. Um die Vielseitigkeit von Maven zu demonstrieren, wird der Packagetyp rar gewählt. Durch etwas Zauberei wird allerdings keine RAR-Datei, sondern eine ZIP-Datei ausgeliefert. Der Grund für diese Entscheidung: Diese Webanwendung ist ein individuelles Projekt und soll nicht innerhalb anderer Projekte verwendet werden. Daher ist es nicht notwendig, das Artefakt in einem Repository vorzuhalten. Aus dieser Tatsache ergibt sich auch das verify
.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<delete>
<fileset dir="${project.build.directory}/${package.dir}" includes="*.phar"/>
<fileset dir="${project.build.directory}/${package.dir}" includes="**/*placeholder"/>
<fileset dir="${project.build.directory}" includes="*.rar"/>
</delete>
<zip destfile="${project.build.directory}/${package.dir}.zip"
basedir="${project.build.directory}/${package.dir}"
update="true" />
</target>
</configuration>
</execution>
</executions>
</plugin>
Der Auszug der POM zeigt unter anderem auch, wie das Library-Projekt als Dependency eingebunden wird. Der Scope weist das Artefakt für die Verwendung zur Laufzeit aus. Damit die entsprechenden Dateien im target-Verzeichnis vollständig zu einer ZIP gepackt werden können, sind innerhalb des <build>
Tags noch einige Plug-ins zu konfigurieren.
Eine zentrale Rolle spielt das antrun Plug-in. Um in Maven in Archiven zusätzlichen Inhalt einzufügen, sind Assemblies vorgesehen. Wesentlich einfacher ist der Weg über ANT. Das antrun Plug-in ermöglicht das Ausführen von ANT-Tasks.
Die Konfiguration des Plug-ins ist weitgehend selbsterklärend. Innerhalb von <configuration>
können verschiedene Task definiert werden. Eine ausführliche Übersicht bietet das User-Manual von ANT.
Elternteile
In den POMs der Library-Artefakte ist der Eintrag zu finden, der auf eine parent-pom für PHP-Maven-Projekte verweist. Dieses Konstrukt bedeutet, dass dem aktuellen Projekt noch ein Projekt übergeordnet ist. Grundsätzlich können Projekte verschiedenster Art beliebig tief verschachtelt werden. Damit das gesamte Konstrukt aber auch überschaubar bleibt, sollte vorher reiflich überlegt werden, wie feingranular ein Projekt aufgebaut werden muss. Um ein Multiprojekt zu erzeugen, muss lediglich die -POM angegeben werden, und über den Eintrag kann auf die untergeord-
neten Module verwiesen werden:
<parent>
<groupId>org.phpmaven</groupId>
<artifactId>php-parent-pom</artifactId>
<version>2.0-SNAPSHOT</version>
</parent>
<groupId>de.banaalo</groupId>
<artifactId>modules</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<modules>
<module>validator</module>
</modules>
Der Vorteil des Multiprojekts modules ist, dass die gesamte Konfiguration für die Unterprojekte in der übergeordneten POM erfolgt. Es werden nur noch die individuellen Konfigurationen in den Unterprojekten ergänzt:
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.banaalo</groupId>
<artifactId>modules</artifactId>
<version>1.0</version>
</parent>
<groupId>de.banaalo.modules</groupId>
<artifactId>validator</artifactId>
<version>1.0</version>
<packaging>php</packaging>
</project>
Wie an der POM des Validators zu sehen ist, ist die Konfiguration erfreulich kurz. Die Effizienz ergibt sich, sobald mehr als ein Modul im gleichen Kontext erzeugt wird. Die Parent-POM stellt sicher, dass für alle Teilprojekte dieselben Dependencies verfügbar sind. So kann verhindert werden, dass beispielsweise Modul A für die XML-Verarbeitung ein anderes Artefakt verwendet als Modul B. Dies ist ein wichtiger Aspekt für die Qualität von Software.
Ausblick
Nachdem Sie nun viele Details zu den Möglichkeiten von Maven kennengelernt haben, stellt der nächste und abschließende Teil dieser Serie das Eclipse-Plug-in für Maven for PHP vor und zeigt unter anderem, wie Webseiten und Reports über Maven generiert werden.
- Teil 1 – Einführung, CoC, DRY
- Teil 2 – POM, Testen & API Dokumentation
- Teil 3 – Web-Anwendungen mit Maven
- Teil 4 – IDE-Integration
PHP meets Maven – Teil 2

[Teil 1] [Teil 2] [Teil 3] [Teil 4]
Der erste Teil der Serie hat gezeigt, dass Maven den Paradigmen DRY und COC folgt. Aus diesem Grund sollte beim Anlegen eines Projekts die vorgegebene Verzeichnisstruktur eingehalten werden. Es ist durchaus möglich, von dieser Empfehlung abzuweichen, was aber zur Folge hat, dass der Konfigurationsaufwand in der pom.xml erheblich anwächst und sich schnell Fehler einschleichen können. Bild 1 stellt eine einfache Ordnerstruktur für ein typisches Maven-Projekt dar.

Die beschriebene Struktur ist bis auf wenige Abweichungen für sämtliche Maven-Projekte identisch. Der wichtigste Teil ist die pom.xml im Wurzelverzeichnis des Projekts. Das optionale Verzeichnis site enthält alle notwendigen Dateien, um eine Projekt-Homepage durch Maven generieren zu lassen. In src vermuten Sie zu Recht die verschiedensten Quelltextdateien. Im Ordner test befinden sich sämtliche Testdateien. Die resources-Verzeichnisse sind optional und besitzen eine besondere Funktion: Dort werden vor allem Konfigurationsdateien abgelegt, in denen Platzhalter zur Textersetzung eingebunden werden können. Um die Zusammenhänge schneller zu erkennen, sollten Sie einen Blick in die Konfigurationsdatei pom.xml werfen.
Project Object Model
Das Project Object Model (POM) bildet die zentrale Steuereinheit für Maven und enthält alle nötigen Informationen. Eine vollständige Übersicht der Konfigurationsmöglichkeiten kann auf der Maven-Projektwebseite nachgeschlagen werden. Beginnen wir zuerst mit den wichtigsten Einträgen:
<groupId>org.phpmaven</groupID>
<artifactId>php-libary</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>php</packaging>
Diese vier Zeilen sind der essenzielle Bestandteil einer jeden POM. Nun wird auch ersichtlich, was es mit den Platzhaltern $groupID und $artifactID in der Projektstruktur auf sich hat. Die artifactId ist der Projektname, dem eine Version zugeordnet wird. Bei der groupId handelt es sich um die Domain des Projekts.
Diese Informationen benötigt Maven, um die Artefakte im Dependency-Management organisieren zu können. Daraus ergibt sich beim Anlegen eigener Projekte die Forderung, dass die Kombination aus Domäne, Projektnamen und Version nicht mehrfach vergeben werden darf, da sonst vorhandene Artefakte im lokalen Repository ohne Rückfrage sofort überschrieben werden. Da Maven für Java-Projekte konzipiert wurde, nutzte es auch den in Java vorhandenen Mechanismus der Packages. Das bedeutet für den Wert org.phpmaven in groupId, dass Maven den Punkt als Trennzeichen interpretiert und erst org und darunter phpmaven als Verzeichnis erzeugt. Auf diese Weise können Sie Ihre Artefakte im lokalen Repository jederzeit aufspüren.
Für Java-Projekte ist es notwendig, anhand des Package-Namens, der sich aus groupId und artifactId zusammensetzt, eine Ordnerhierarchie zu erzeugen. In PHP-Projekten ist das Anlegen einer solchen Struktur nicht zwingend notwendig. Mit Blick auf künftige Mehrfachverwendung von Artefakten in den verschiedenen eigenen Projekten ist die Verwendung von Namespaces unumgänglich. Das Risiko von Namenskonflikten zwischen den Artefakten steigt mit der Anzahl der verfügbareren Artefakte.
Der Eintrag php weist Maven an, aus den Source-Files ein Phar-Archiv zu erzeugen. Es gibt neben php noch weitere Package-Typen, etwa zip und pom. Der Package-Typ pom erlaubt die Verwendung von Multi-Projekten, auf die später noch eingegangen wird. Listing 1 zeigt eine vollständige POM für ein einfaches PHP-Library-Projekt, das Sie als Abhängigkeit in anderen Projekten nutzen können. Dependencies werden in der POM im Bereich eingetragen. Im Beispiel ist das Test-Framework PHPUnit eingebunden. Die Angabe teilt Maven mit, dass es sich bei diesem Artefakt um ein PHP-Archiv handelt. Ohne diese Konfiguration würde Maven ein Java-Archiv (JAR) erwarten. Der Scope einer Dependency konfiguriert die Sichtbarkeit des Artefakts im Build Lifecycle. Es gibt vier unterschiedliche Scopes:
- compile: Das Artefakt ist in allen Phasen verfügbar (Default).
- provide: Das Artefakt ist nur bis zur compile-Phase sichtbar und wird nicht mit deployed. Dieser Scope ist vor allem für Java-Projekte wichtig, um die Ressourcen korrekt zu verlinken.
- runtime: Diesen Scope verwenden Dependencies, die nur zur Laufzeit benötigt werden, was für PHP-Projekte die Regel darstellt. Artefakte, die mit runtime gekennzeichnet sind, werden mit der Anwendung zusammen deployed.
- test: Abhängigkeiten, die mit diesem Scope konfiguriert sind, werden nicht mit deployed und sind nur für die test-Phasen des Build-Lifecycles sichtbar.
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.phpmaven</groupId></plugin>
<artifactId>php-parent-pom</artifactId><plugin>
<version>2.0-SNAPSHOT</version>
</parent>
<groupId>com.elmar.dott</groupId>
<artifactId>validator</artifactId>
<version>1.0-SNAPSHOT</version>
<version>2.10</version>
<packaging>php</packaging>
<build>
<plugins>
<plugin>
<groupId>org.phpmaven</groupId>
<artifactId>maven-php-plugin</artifactId>
<version>${phpmaven.plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.0</version>
<inherited>true</inherited>
<configuration>
<reportPlugins>
<plugin>
<groupId>org.phpmaven</groupId>
<artifactId>maven-php-plugin</artifactId>
<version>${phpmaven.plugin.version}</version>
<reportSets>
<reportSet>
<reports>phpdocumentor</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.phpmaven</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>${phpmaven.plugin.version}</version>
<reportSets>
<reportSet>
<reports>report-only</reports>
</reportSet>
</reportSets>
</plugin>
</reportPlugins>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>de.phpunit</groupId>
<artifactId>PHPUnit</artifactId>
<version>3.6.10</version>
<type>phar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Nachdem Sie nun eine Projektstruktur und die dazugehörige POM haben, wird es Zeit, Maven in Aktion zu erleben. Die Idee ist, eine Bibliothek zu erzeugen, die Benutzereingaben validiert. Dieses Artefakt kann dann später von Ihnen dahingehend erweitert werden, in Ihren Projekten an verschiedensten Stellen Benutzereingaben auf Gültigkeit zu überprüfen.
Mögliche Erweiterungen könnten etwa Validierungen von ISBN- und IBAN-Nummern sein. Um ohne Verzögerung zu beginnen, können Sie das fertige Projekt von der Website herunterladen und in einer aktuellen IDE Ihrer Wahl öffnen.
Das Artefakt besteht aus der Klasse Validator mit den beiden Methoden makeSecure()
und validate()
. Die Methode makeSecure()
soll HTML-Tags escapen und Slashes sowie Backslashes aus dem übergebenen String entfernen. validate()
prüft eine Eingabe gegen einen regulären Ausdruck auf Gültigkeit. In der Klasse sind bereits einige RegEx als Konstanten definiert. Damit haben wir schon alles, um erste Resultate zu sehen.
Validierungen
Öffnen Sie eine beliebige Konsole und navigieren Sie in das Projektverzeichnis zur POM. Nun müssen sie lediglich mvn package
eingeben. Diese Anweisung bewirkt, dass Maven den Build Lifecycle bis zur Phase package abarbeitet. Die nachfolgenden Phasen install
und deploy
werden nicht aufgerufen. Die Phase install
kopiert das erzeugte Artefakt direkt in das lokale Repository und deploy würde zusätzlich das Artefakt in einem Remote-Repository ablegen. Für den Moment genügt es uns, das Phar-Archiv erzeugt zu haben.
Wegen des POM-Eintrags install unterhalb von <build>
genügt es, lediglich mvn install
in der Konsole zu schreiben, um das erzeugte Artefakt ins lokale Repository zu kopieren. Nachdem mvn package
ausgeführt wurde, hat Maven im Projektverzeichnis einen temporären Ordner namens target erzeugt und quittiert die Ausgabe mit BUILD SUCCESS. In dem neu angelegten Verzeichnis sind sämtliche generierten Dateien abgelegt, unter anderem auch das erwünschte Phar-Archiv.
Um negative Synergien zu vermeiden, sollte das target-Verzeichnis vor einem jedem Build gelöscht werden. Das erledigt die Anweisung mvn clean
.
Das erzeugte Phar-Archiv lässt sich mit den folgenden Zeilen in eine Applikation einbinden:
require_once 'phar://validator-1.0-SNAPSHOT.phar/index.php';
use de\banaalo\validator\Validator as Validator;
$validator = new Validator();
$input = 12345;
echo $validator->validate
($validator->NUMBER, $input);
Der Trick besteht darin, sämtliche Klassen des Archivs in der Datei src/main/php/index.php mittels include bekannt zu machen. Bei dieser Herangehensweise muss lediglich die index.php wie in der ersten Zeile des Listings geladen werden. Anschließend ist noch der Namespace aufzulösen und dann können die Klassen des Artefakts wie gewohnt verwendet werden.
Testgetriebene Entwicklung
Dank des Extreme Programming (XP) hat das Testen von Sourcecode mittlerweile einen sehr zentralen Stellenwert im Software-Entwicklungsprozess erhalten. Implementierte Funktionalität wird nicht erst nach Beendigung der Entwicklung auf Korrektheit hin überprüft, sondern schon während der Entwicklungsphase. Eine etablierte Form des Testens sind Unit-Tests, wofür das Framework PHPUnit von Sebastian Bergmann als Dependency in den Maven-Build-Lifecycle eingebunden ist. Die aktuell verwendete Version von PHPUnit ist 3.6.10. Ein ausführliche Übersicht zu PHPUnit bietet das Manual des Frameworks.
Sämtliche Testfälle sind im Verzeichnis test/php abzulegen. Wenn eine Verzeichnisstruktur für die Sourcen erzeugt wurde, sollte diese ebenfalls für die Testfälle übernommen werden. Eine weitere Konvention ist, die Testklassen analog der zu testenden Klassen zu benennen und das Postfix test anzufügen.
Zur Klasse Validator existiert die korrespon-ierende Klasse ValidatorTest, die von PHPUnit_Framework_TestCase abgeleitet ist. Um später in der Test-Auswertung bei Fehlern das betroffene Fragment schneller identifizieren zu können, sollten die Methoden sprechende Namen haben. Im vorgestellten Beispielprojekt wird die Validierung von Zahlen durch den Validator getestet. Dazu existiert die Methode testNumberValidation()
. Um Klassen testen zu können, sollte schon während der Implementierung darauf geachtet werden, dass nur ein Einstiegspunkt und ein definierter Ausstiegspunkt vorhanden ist. Mehr als das Erstellen der Testfälle und das Einbinden von PHPUnit als Abhängigkeit ist nicht notwendig. Beim Ausführen von Maven werden nun jedes Mal alle Testfälle abgearbeitet. Falls ein Test fehlschlägt, wird der Build mit einer Fehlermeldung abgebrochen.
API-Dokumentation auf Knopfdruck
Ein weiterer wichtiger Aspekt in der SoftwareEntwicklung ist das Erzeugen einer aussagekräftigen API-Dokumentation. Für diese Aufgabe ist in Maven der Site-Lifecycle vorgesehen. Um den phpDocumentor dem Site-Lifecycle bekannt zu machen, muss die Konfiguration des site-Plug-ins überschrieben werden. In der Beispiel-POM ist dies bereits geschehen und deswegen muss die POM nicht weiter angepasst werden. Wie auch bei PHPUnit ist es nicht notwendig, das Tool phpDocumentor auf dem System zu installieren. Auf der Homepage des php Documentors ist das Werkzeug gut dokumentiert. Auf der Webseite ist unter anderem eine Übersicht der möglichen Annotationen zu finden. Um die Dokumentation zu erzeugen, muss der Site-Lifecycle mit der Anweisung mvn site ausgeführt werden.
Wie alle anderen durch Maven erzeugten Dateien wird auch die fertige API-Dokumentation im target-Verzeichnis abgelegt.
Jedes Mal aufs Neue von Hand eine Projektstruktur anzulegen ist mühsam. Damit das Erzeugen neuer Projekte automatisiert geschehen kann, bietet Maven den Mechanismus der Archetypen. Ein Archetyp erzeugt eine vorgegebene Verzeichnisstruktur und die dazugehörige POM. Für PHP existieren derzeit drei verschiedene Archetypen: library, web und zend, die im Lauf der Zeit noch erweitert werden. Der nachfolgende Code-Ausschnitt zeigt die benötigte Anweisung, um einen Archetyp auszuführen:
mvn archetype:generate \
-DarchetypeGroupId=org.phpmaven \
-DarchetypeArtifactId=php5-lib-archetype \
-DarchetypeVersion=2.0.0-beta-3\
-DgroupId=de.banaalo \
-DartifactId=validator \
-Dversion=1.0-SNAPSHOT
Falls Sie aus einem bereits bestehendem Projekt einen Archetyp erzeugen wollen, können Sie mvn archetype:create-from-project
ausführen. Es kann auch vorkommen, dass anschließend der generierte Archtyp von Maven nicht gefunden wird. Der Grund ist in der Datei archetypecatalog.xml zu suchen. Um den neuen Archetyp in den Katalog aufzunehmen, ist die Anweisung mvn archetype:crawl
auszuführen. Ein manuelles Editieren dieser Datei ist nicht notwendig.
Fazit
Library-Projekte sind Artefakte, die Funktionalitäten kapseln und diese Funktionalitäten in anderen Projektformen als Abhängigkeit zur Verfügung stellen. Im nächsten Teil der Serie wird gezeigt, wie Homepage-Projekte mit Maven verwaltet werden können, die wiederum Artefakte einbinden. In diesem Zusammenhang wird auch auf Multi-Projekte und deren Verwendung eingegangen. Sie erfahren auch, wie Sie erzeugte Artefakte für andere Entwickler in einem Remote-Repository verfügbar machen.
- Teil 1 – Einführung, CoC, DRY
- Teil 2 – POM, Testen & API Dokumentation
- Teil 3 – Web-Anwendungen mit Maven
- Teil 4 – IDE-Integration
PHP meets Maven

[Teil 1] [Teil 2] [Teil 3] [Teil 4]
Wikipedia liefert für den Begriff Maven folgende Erklärung: »Ein Maven ist ein Experte, der andere berät und deren Entscheidungen beeinflusst«. Wegen seiner Bedeutung wurde der Begriff Maven für die Namensgebung der Software auserkoren. Jason van Zyl, Gründer und CTO von Sonatype, beschreibt das Programm mit folgenden Worten: »Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and documentation from a central piece of information.«
Maven ist ein in Java geschriebenes plattformunabhängiges Projektmanagement-Tool. Es ist ein Open-Source-Projekt der Apache Software Foundation und frei erhältlich. In der Version 2 wurde das Programm völlig neu entwickelt. Das ist auch ein Grund dafür, dass Erweiterungen der Version 2 nicht abwärtskompatibel sind. Derzeit ist die Version 3 aktuell, welche auch mit Maven-2-Projekten kompatibel ist.
Wie bereits angedeutet, handelt es sich bei Maven um ein schlankes Werkzeug, das durch seinen modularen Aufbau problemlos durch eigene Plug-ins erweitert werden kann. Diese Möglichkeit nutzt das Projekt Maven for PHP und stellt eigene Erweiterungen für PHP bereit.

Bevor wir nun mit der Installation der Software beginnen, ist es notwendig, etwas über das Konzept von Maven zu erfahren, da dieses Werkzeug mächtiger ist als sein Pendant Ant.
Maven erzeugt aus Java-Quellcode Binärdateien. Die hohe Akzeptanz erreichte das Tool aber durch das transitive Auflösen von Abhängigkeiten zu anderen Softwarebibliotheken. Im Maven-Jargon werden Bibliotheken oder Module als Artefakt bezeichnet. Das Verwalten von Artefakten ist allerdings nur ein kleiner, wenn auch sehr maßgeblicher Aspekt für den Einsatz in PHP-Projekten.
Nicht ohne Grund stellt Maven an sich selbst den Anspruch, ein Projekt-Management-Werkzeug zu sein. Im Projektalltag wird zwischen drei verschiedenen Einsatzbereichen unterschieden. Der umfangreichste Teil ist der Build-Life-cycle mit seinen 21 Unterschritten, die als Phase bezeichnet werden. Die Teilbereiche einer Phase werden wiederum als Goal bezeichnet. So erreicht man eine feingranulare Struktur, in die sich leicht eigene Funktionen einbringen lassen. Ein anderer Aspekt ist die Reporting-Engine, die ihre Arbeit unter dem Site-Lifecycle verrichtet. Für das Aufräumen nach dem vollendeten Werk ist der Clean-Lifecycle zuständig. Was es mit den einzelnen Lifecycles auf sich hat wird an späterer Stelle noch detaillierter geklärt.
Zentrale Steuereinheit
Im Wurzelverzeichnis eines Projekts liegt die zentrale Steuereinheit. Es handelt sich dabei um eine einfache XML-Datei mit dem Namen pom.xml, in der die gesamte Konfiguration des Projekts vorgehalten wird. Die Bezeichnung POM steht im Übrigen für Project Object Model. Bei umfangreichen Projekten kann diese Datei schnell eine beachtliche Größe erlangen. Aus diesem Grund verfolgt Maven das Prinzip »Don’t repeat yourself« (DRY) und »Convention over Configura tion« (COC). Das besagt, es müssen lediglich die Dinge in der POM-Datei eingetragen werden, die vom vorgegeben Standard abweichen. Um den Überblick auch zu einem späteren Zeitpunkt nicht zu verlieren, ist es sehr ratsam, nur in absoluten Ausnahmefällen vom Standard abzuweichen.
Nachdem Sie nun die wichtigsten Begriffe kennen, können wir zur ersten Tat schreiten und die Software installieren.
Schnell installierte Basis

Für Maven existiert keine Installationsroutine, wie sie beispielsweise in Windows-Systemen üblich ist. Da das Programm in Java geschrieben ist, muss ein aktuelles Java-SDK auf dem System bereits vorhanden sein. Nach dem erfolgreichem Download und dem anschließenden Entpacken des Archivs sind folgende Schritte auszuführen:
- Setzen der Umgebungsvariablen
JAVA_HOME = {install.java}
, - Setzen der Umgebungsvariablen
M2_HOME = {install.maven}
, - Erweitern der PATH-Variable
%M2_HOME%\ bin
und%JAVA_HOME%\bin
.
Mit {install.maven} ist der Ort des ausgepackten Maven-Archivs gemeint. Unter Windows 7 ist die Einstellung der Umgebungsvariablen über die Systemsteuerung hinter der Option System versteckt, die sich dort über den Punkt Erweiterte Systemeinstellungen öffnen lässt. Bei geglückter Installation quittiert Maven die Konsolenausgabe für mvn -vers

Dependency Management

Wie Sie bei der Installation bemerkt haben, ist der Kern von Maven recht kompakt. Die umfangreichen Funktionen werden durch Erweiterungen über die Plug-in-Schnittstelle realisiert. Soll beispielsweise eine Dokumentation mit der Anweisung mvn site erzeugt werden, holt sich Maven die notwendigen Artefakte über das Internet und speichert diese auf dem Rechner in einem lokalen Repository. Die Dateien sind im Home-Verzeichnis des aktuellen Benutzers unter .m2/repository/ zu finden. Wenn man sich die Ordnerstruktur aus Bild 4 ein wenig genauer ansieht, kann man erkennen, dass Maven die Artefakte nach Namen und Versionsnummer organisiert. In dem Screenshot sind im lokalen Repository für das Test-Framework JUnit vier unterschiedliche Versionen mit der dazugehörigen POM-Datei hinterlegt. Anhand des POM ist Maven nun in der Lage, die notwendigen Abhängigkeiten von JUnit ebenfalls aufzulösen. Die Archivierung der Versionen eines Artefakts ermöglicht es später, in einem Projekt innerhalb kürzester Zeit für eine Bibliothek die neueste Version auszuprobieren und im Fall einer Inkompatibilität die Änderung in wenigen Augenblicken wieder rückgängig zu machen. Es muss lediglich in der POM-Datei die Versionsnummer des Artefakts geändert werden.
Ein weiterer Vorteil ist, dass im Konfigurationsmanagement die verwendeten Artefakte nicht mehr archiviert werden müssen. Diese Arbeit übernimmt nun künftig in zuverlässiger Weise Maven. Dank dieses Umstands werden im Konfigurationsmanagement einige MByte an Speicher eingespart, was bei größeren Projekten mit vielen Artefakten einiges an Zeit beim Checkout einsparen kann. Eine Entlastung des Netzwerks ist eine weitere angenehme Randerscheinung.
Nun stellt sich berechtigterweise die Frage, von welchem Server Maven die benötigten Artefakte beziehen kann? Neben dem standardmäßig eingestelltem Remote-Repository repo1.maven.org können auch andere Repositories hinzugefügt werden. Dazu ist die Maven-Installation über die Datei settings.xml den eigenen Bedürfnissen anpassen. Hierbei können zwei Stufen genutzt werden:
- User Level: Im Home-Directory des aktuellen Nutzers legt Maven den Ordner .m2 an, in dem die settings.xml zu finden ist. Dies ist vor allem im Mehrbenutzerbetrieb nützlich, da hier Passwörter für Logins et cetera hinterlegt werden können.
- Global Level: Die Datei settings.xml ist in diesem Fall im Ordner conf der Maven-Installation abgelegt. Diese Konfiguration gilt für alle Nutzer auf einem System.
Die Konfiguration über die settings.xml ist projektübergreifend und enthält beispielsweise Einträge für das Login zum Versionskontrollsystem (SCM), den Pfad zum lokalen Repository beziehungsweise zu weiteren Remote-Repositories. Um PHP Maven nutzen zu können, muss das öffentliche Repository in einer der beiden Settings-Dateien eingetragen werden. Listing 1 enthält die hierfür notwendige Konfiguration.
Der Codeblock aus dem Listing ist in einer der settings.xml-Dateien unterhalb der Ebene des Root-Elements einzutragen. Die eigentliche Konfiguration befindet sich im Profil mit der ID profile-php-maven, welches auch als aktiv registriert ist. Es ist ohne Weiteres möglich, mehrere Profile in der Konfiguration aktiv zu setzen, wenn dies notwendig sein sollte. Im Profil werden die verschiedenen Remote-Repositories für PHP Maven registriert. Mit diesen Einträgen ist Maven nun in der Lage, die notwendigen Artefakte aus dem öffentlichen Repository zu laden.
<activeProfiles>
<activeProfile>profile-php-maven</activeProfile>
</activeProfiles>
<profiles>
<profile>
<id>profile-php-maven</id>
<pluginRepositories>
<pluginRepository>
<id>release-repo1.php-maven.org</id>
<name>PHP-Maven 2 Release Repository</name>
<url>http://repos.php-maven.org/releases</url>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
<pluginRepository>
<id>snapshot-repo1.php-maven.org</id>
<name>PHP-Maven 2 Snapshot Repository</name>
<url>http://repos.php-maven.org/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>release-repo1.php-maven.org</id>
<name>PHP-Maven 2 Release Repository</name>
<url>http://repos.php-maven.org/releases</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository>
<id>snapshot-repo1.php-maven.org</id>
<name>PHP-Maven 2 Snapshot Repository</name>
<url>http://repos.php-maven.org/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>
Mittlerweile betreiben immer mehr Unternehmen eigene Repository-Server für ihre Produkte. Dazu gehören unter anderem Google, Java .NET und Activity, um einige öffentliche Server zu nennen. Da das Projekt Maven for PHP vergleichsweise sehr jung ist, sind derzeit nur die bekanntesten PHP-Projekte, wie beispielsweise Zend und Symfony, in das Repository migriert. In absehbarer Zukunft darf jedoch durchaus erwartet werden, dass weitere PHP-Projekte das Repository bereichern werden. Bei besonders dringlichen Fällen kann es hier hilfreich sein, eine Anfrage über die Google Mailing List zu stellen. Natürlich ist auch jeder, der sich an PHP for Maven beteiligen möchte, eingeladen, sich im Rahmen seiner Möglichkeiten einzubringen.
PHP-CLI
Damit Maven sich voll entfalten kann, ist es notwendig, das Command Line Interface (CLI) für PHP in der Konsole zu aktivieren. Mit dem CLI ist es möglich, PHP-Skripts ohne Webbrowser direkt auf der Kommandozeile auszuführen.

Diese Funktion wird beispielsweise benötigt, um die Source-Code-Dokumentation über den phpDocumentor aus Maven heraus anzustoßen. Sobald der Pfad zum Verzeichnis der php.exe in die PATH-Variable aufgenommen wurde, können PHP-Skripts über die Konsole ausgeführt werden. Wie diese Aufgabe für Windows-Systeme gelöst wird, wurde bereits in dem vorangegangenen Abschnitt über die Installation ausführlich beschrieben. Der Erfolg dieser Bemühung lässt sich durch die Anweisung php –v rasch überprüfen und wird im Erfolgsfall mit der Ausgabe der installierten PHP-Version quittiert. Auch wenn im ersten Blick die Installation umfangreich erscheint, sind nur wenige Schritte durchzuführen, um ein einsatzfähiges System zu erhalten. Der geringe Aufwand wird durch umfangreiche Funktionen zur Projektautomatisierung schnell entlohnt.
Ein Füllhorn an Möglichkeiten
Es steht dem Benutzer frei, für welches Szenario er Maven in einem Projekt einsetzen möchte. Hier ein Liste möglicher Einsatzszenarien:
Die erste Position dieser Liste führt erwartungsgemäß das Dependency Management an. Über den Mechanismus können Artefakte fremder Hersteller ebenso dem eigenen Projekt hinzugefügt werden wie selbst entwickelte Bibliotheken.
Neben diesem Aspekt ist ein weiteres wichtiges Thema der Softwaretest. Maven erlaubt es, verschiedenste Test-Frameworks im Build-Lifecycle zu integrieren. Der bekannteste Vertreter unter den Unit-Tests, phpUnit, ist bereits im Public-Repository von Maven for PHP enthalten.
Wie bereits angedeutet, kommt die Generierung der API-Dokumentation mit dem phpDocumentator ebenfalls nicht zu kurz.
Aber auch ein Blick zu den klassischen Maven-Plug-ins ist in einigen Fällen lohnenswert. So besteht die Möglichkeit, aus Maven heraus Ant-Aufrufe zu starten und abzuarbeiten, was die Funktionsvielfalt um einiges erweitert.
Ein beliebtes Thema für Webseiten sind script- und CSS-Minimierer. Code-Beautifier oder Obsfukatoren stellen weitere Punkte auf der Liste dar. Auch Code-Analyzer-Werkzeuge wie Checkstyle können eingebunden werden und bieten damit der Projektleitung einen guten Einblick in die Softwarequalität.
Eine weitere Möglichkeit besteht darin, ganze Internetseiten zu generieren. So ist beispielsweise die Homepage des Projekts aus Maven heraus erzeugt worden.
Das Charmante an all diesen Möglichkeiten ist die Tatsache, dass für die Funktionalität keine weitere Software installiert werden muss. Alle Funktionen sind durch Maven-Plug-ins umgesetzt und werden bei Bedarf über das Dependency Management nachgeladen. Sollte dennoch Wünsche offen bleiben, ist es jederzeit möglich, über das Plug-in-API eigene Erweiterungen zu schreiben.
Für Unternehmen bietet Maven einen weiteren Vorteil. Der Einsatz dieses Werkzeugs unterstützt bei gutem Moduldesign die Wiederverwendung von Softwarekomponenten. Diese Tatsache kann den entscheidenden Vorsprung gegenüber der Konkurrenz bedeuten, da Projekte schneller abgewickelt werden können.
- Teil 1 – Einführung, CoC, DRY
- Teil 2 – POM, Testen & API Dokumentation
- Teil 3 – Web-Anwendungen mit Maven
- Teil 4 – IDE-Integration
PHP Web Application Deployment mit Apache ANT

Schon in einem recht frühen Stadium müssen die fertigen Sourcen auf verschiedenste Zielsysteme eingespielt werden. Üblicherweise existieren mehrere zentrale Entwicklungsserver, zu denen noch für jeden Entwickler eine eigene lokale Installation hinzukommt. Bei dieser Vielzahl an Systemen, auf der das Projekt lauffähig sein muss, rechnet sich schnell der Aufwand, den eine zentrale und wiederverwendbare Build-Logik erfordert.
Wer schon das eine oder andere Deployment hinter sich gebracht hat, weiß, dass die Softwareverteilung einiges an Tücken zu bieten hat und in aller Regel nichts mal schnell eingespielt wird. Um die üblichen und immer wiederkehrenden Fallstricke vermeiden zu können, werden in der Software-Entwicklung seit Jahren verschiedenste Automatisierungswerkzeuge eingesetzt. In diesem Artikel lernen Sie das Werkzeug Ant kennen und erfahren, wie Sie es in Ihren PHP-Projekten verwenden können.
Das Akronym Ant steht für Another Neat Tool und bedeutet frei übersetzt: Ein weiteres hübsches Werkzeug. Zusätzlich bedeutet nt im Englischen Ameise. Genau diese Bezeichnung beschreibt Ant mit nur einem Wort sehr treffend, denn es ist wie eine Ameise: klein, fleißig, äußerst robust, zuverlässig und leistet im Vergleich zu seiner Größe Unglaubliches.
Ant ist ein Top-Level-Projekt der Apache Group und wird seit Jahren in unzähligen Projekten erfolgreich eingesetzt. Auf der Projekt-Homepage können die bereits kompilierten Binarys sowie die Sourcen kostenlos heruntergeladen werden. Die Grundintention von Ant ist die Verwendung als Build-Werkzeug im Java-Umfeld. Die enorme Vielfalt der Funktionen und die einfache Erweiterbarkeit machen das Tool auch außerhalb der Java-Welt interessant. Mit Ant können Sie unter anderem komplette Verzeichnisse kopieren, komprimieren und dabei einzelne Dateien ausschließen. In Textdateien können Variablen definiert werden, die dann durch konkrete Werte ersetzt werden, und es gibt FTP- und SVN-Anbindungen.
Besonders durch den Leistungsumfang, die Stabilität, die einfache Installation und die reichhaltige Dokumentation unterscheidet sich Ant von anderen Deployment-Tools für PHP, wie beispielsweise dem in Ruby implementierten PHP-Build-Tool Capistrano.
Deployment-Strategien
Unter Deployment versteht man in der IT sämtliche Maßnahmen, die zur Verteilung von Software auf die Zielumgebungen notwendig sind. Für PHP-Applikationen zählt dazu beispielsweise das Anpassen der Konfiguration des Webservers und der Datenbank sowie das Einspielen der Datenbankschemata und der PHP-Skripts.
Das folgende Szenario stellt den Rahmen für die Komposition unserer Build-Logik dar. Die Vorgehensweise lässt sich auch ohne Weiteres auf andere Projekte übertragen und den eigenen Anforderungen anpassen.
Auf dem Entwicklungsrechner befinden sich zwei unabhängige Webserver-Installationen sowie ein Datenbank-Server, den sich die beiden Webserver teilen. Ein Webserver wird Develop benannt und hat eine lockere Konfiguration der Sicherheitseinstellungen. Die Ausgabe von PHP- und SQL-Fehlern wird hier nicht unterdrückt. Beim zweiten Webserver verhält sich die Konfiguration schon bedeutend anders. Auf diesem als Quality benannten System herrschen starke Sicherheitsrestriktiven, zum Beispiel, dass keine Dateien per Remote im Skript verarbeitet werden dürfen. Der Quality-Server (QS) hat im Idealfall die gleiche Konfiguration wie später das Live-System des Kunden. Beide Systeme teilen sich einen Datenbank-Server, auf dem für jedes System ein eigener Benutzer mit eigenem Schema angelegt ist. Die Grafik in Bild 1 verdeutlicht den Zusammenhang.

Für unser Szenario ist der populäre Webshop Magento als Build-Projekt auserkoren, es kann aber auch jedes beliebige andere CMS- beziehungsweise PHP-Projekt zur Entwicklung Ihrer Deployment-Strategie genutzt werden.
Nach dem Download und dem Entpacken des aktuellen Magentos in ein frisches Projektverzeichnis auf dem Development-Server wird das Programm über den mitgelieferten Webinstaller auf dem Server Develop installiert. Dazu kommen noch alle notwendigen Translation-Files und zusätzliche Plug-ins. Nachdem der Shop so weit funktionsfähig ist, werden alle Dateien unter das Konfigurationsmanagement gestellt und dieser Stand mit inital installation markiert.
In dem hier beschriebenem Szenario soll das Konfigurationsmanagement der Wahl die Versionsverwaltung Subversion sein. Würden wir nun diesen Stand unverändert in das Live-System einspielen wollen, würden wir feststellen das der Shop nicht läuft, weil unter anderem die Datenbankparameter nicht korrekt sind. Außerdem ist es auch nicht wünschenswert, die .svn-Verzeichnisse samt Inhalt in das Live-System zu übertragen, da diese schnell zu einer nicht unerheblichen Datenmenge anwachsen.
Mit dem QS-System haben wir nun eine Zwischenstufe eingebaut, mit der das Deployment für das Zielsystem risikofrei getestet werden kann. In größeren Projekten werden zu diesem Zweck sogar mehrere QS-Instanzen parallel betrieben.
Die Installation
Bevor es losgeht, muss Ant auf dem Entwicklungsrechner verfügbar sein. Wer bereits Eclipse, Netbeans oder Intellij Idea als Entwicklungsumgebung verwendet, hat bereits alles an Bord, was notwendig ist. Sollte Ihre IDE keine native Ant-Integration besitzen, können Sie das Tool schnell nachinstallieren.
Ant selbst ist ein Programm, das klassisch über die Textkonsole im Root des aktuellen Projektverzeichnisses mit dem Kommando ant aufgerufen wird. Die einzelnen abzuarbeitenden Anweisungen innerhalb eines Targets werden Task genannt. Die build.xml beinhaltet die gesamte Build-Logik. Alle Anweisungen werden in XML notiert, und es ist keine zusätzliche Skriptsprache notwendig.
Damit die build.xml fehlerfrei von Ant verar-eitet werden kann, muss sie wohlgeformt und valide sein. Die Verwendung der bereits genannten Entwicklungsumgebungen unterstützt Sie tatkräftig beim Schreiben dieser Konfigurationsdatei und ermöglicht das komfortable Starten der einzelnen Tasks über eine grafische Oberfläche.
Die Konfigurationsdatei
Der Name der Konfigurationsdatei ist stets build.xml und sie befindet sich im Root-Verzeichnis des aktuellen Projekts. Listing 1 zeigt den grundlegenden Aufbau.
<project name="Magento Project" default="start" basedir=".">
<description>Deploymentfile for PHP Projects</description>
<property file="build.properties" />
<property name=" deploy.directory” value=”_build”/>
<target name="start" description="Full Deployment">
<echo>Ant PHP Deploy Script for PHP Projects</echo>
<antcall target="clean_deploy_dir" />
</target>
<target name="clean_deploy_dir">
<echo message="Delete the current Deploy Directory for clean up"/>
<delete dir="${deploy.directory}" />
</target>
</project>
XMLListing 1: Die build.xml
In wird angegeben, um welches Projekt es sich handelt und wo die Projektdateien relativ zur build.xml liegen. Es ist also durchaus möglich, die build.xml auch an eine andere Stelle als ins Root-Verzeichnis zu platzieren – was allerdings nicht zu empfehlen ist, da dies schnell zu ungewünschtem Verhalten führen kann. Mit erfolgt eine kurze Projektbeschreibung, die optional ist und zum besseren Verständnis beiträgt.
Über steigen wir schon voll in die Ant-Syntax ein. definiert Platzhalter, die dann über ${Platzhalter.Name} in der gesamten build.xml verwendet werden können. Diese Eigenschaft ist eine wichtige Grundlage, um saubere Build-Files zu schreiben, weshalb an dieser Stelle der Mechanismus etwas ausführlicher erläutert werden soll.
Platzhalter sind deswegen so wichtig, weil damit Werte befüllt werden, die sich oft ändern können. Beispielsweise wird der Pfad des Verzeichnisses, in das die fertigen Dateien kopiert werden sollen (deploy.directory) an vielen verschiedenen Stellen benötigt. Ändert sich nun dieser Wert, ist es lästig, die gesamte build.xml danach zu durchsuchen und das neue Verzeichnis per Hand an den jeweiligen Stellen zu ändern. Viel angenehmer ist es, diesen Wert zentral an einer festen Position pflegen zu können.
Mit dem Attribut file können die Propertys auch in eine Datei ausgelagert werden. Vor allem Werte, die sich in jedem Fall auf den einzelnen Systemen unterscheiden, sind hervorragende Kandidaten für ausgelagerte Propertys.
Auf diese Weise vermeidet man, dass der Entwickler aus Versehen in der build.xml Änderungen vornimmt und die Build-Logik damit zerstört. Die Syntax in solchen Property-Dateien folgt dem Schema key=value. Die anschließende Benutzung der Platzhalter in der build.xml erfolgt genauso wie für Propertys, die direkt in der Build-Datei definiert werden.
Im Listing sind außerdem zwei definiert. Das Target start wird automatisch beim Aufruf der build.xml ausgeführt. Das Attribut default in bestimmt für den Konsolenaufruf den Task, welcher initial ausgeführt werden soll. Im ersten Target wird ein auf ein weiteres Target mit der Bezeichnung clean_deploy_dir aufgerufen.
Das Auslagern kleiner Operationen in eigene Tasks erhöht die Lesbarkeit der gesamten Datei. Über den antcall können dann die Subtasks in einem zusammenführenden Task wieder orchestriert werden, wobei die Reihenfolge der einzelnen Calls von Bedeutung ist, da die Aufgaben nacheinander ausgeführt werden. Im zweiten Target wird ein delete aufgerufen, das die Verwendung des Platzhalters ${deploy.directory} für das zu löschende Verzeichnis demonstriert.
Eine der wichtigsten Operationen ist das Kopieren von Dateien. Im Kopiervorgang sollen die Inhalte der .svn-Ordner nicht mit berücksichtigt werden. Um wieder das Beispiel Magento aufzugreifen, sollen während des Copys auch noch die Datenbankparameter in der local.xml ersetzt werden. Um diese Aufgabe zu lösen, erweitern wir das vorige Listing um ein neues , das copy_all heißen soll (Listing 2).
<property name="project.deploy.excludes” value=” build.xml, ${deploy.directory}/**, .svn/**, **/.svn”/>
<target name="copy_all">
<echo message="Copy all Files." />
<mkdir dir="${deploy.directory}" />
<copy todir="${deploy.directory}" overwrite="true" >
<fileset dir="." includes="**/*" excludes="${project.deploy.excludes}" />
</copy>
<copy file="local.xml" toFile="${deploy.directory}/local.xml" overwrite="true">
<filterchain>
<replacetokens>
<token key="@DB_HOST@" value="localhost"/>
<token key="@DB_USER@" value="datenbank_nutzer"/>
<token key="@DB_NAME@" value="magento"/>
<token key="@DB_PWD@" value="password "/>
</replacetokens>
</filterchain>
</copy>
</target>
XMLListing 2: Copy.
Mit wird das Verzeichnis erzeugt, in dem anschließend die Dateien zu finden sind. Der -Befehl erwartet als Attribut todir für das Verzeichnis, in das die Dateien kopiert werden sollen. Hier macht sich die Verwendung der Propertys bezahlt. Mit overwrite=“true“ werden vorhandene Dateien überschrieben. Das eingebettet Element gilt für das Verzeichnis, in dem die build.xml liegt, was durch dir=“.“ bestimmt wird. Es können natürlich auch andere Verzeichnisse angegeben werden. Include und Exclude sind zwar selbsterklärend und es lässt sich schnell ahnen, welche Bewandtnis es damit hat. Die Verwendung ist aber nicht ganz so simpel. Als Werte sind für diese beiden Attribute unter anderem Wildcards möglich, /* bedeutet: »Nimm alles inklusive der Unterverzeichnisse.« Die Excludes sind der Übersicht halber wieder in eine eigene Property ausgelagert. Wie gut zu erkennen ist, dürfen die Verzeichnisse und Dateien kommasepariert als Wert übergeben werden. Auch hier sind verschiedene Kombinationen von Wildcards gestattet.
Als Nächstes lernen wir die Textersetzung kennen. Für die Datei local.xml sollen die Datenbank-Verbindungsparameter vom Zielsystem verwendet werden.
Die Ersetzung selbst ist recht einfach zu handhaben. Es wird der bereits bekannte -Task verwendet, der wiederum aufruft. Der Matcher sollte in der Form @ERSET-ZUNGSTEXT@ in den Template-Dateien verwendet werden.
Datenbanken
Eine weitere Grundfunktion von Ant ist der Zugriff auf Datenbanken. Um diese Funktion nutzen zu können, muss man wissen, dass Java-Programme die Java-Database-Connectivity-Treiber für den Zugriff auf Datenbanken benötigen. Diese Treiber sind einfache JAR-Dateien die von der Webseite des Datenbankherstellers heruntergeladen werden können. Das Listing 3 zeigt, wie mittels Ant auf eine MySQL-Datenbank zugegriffen wird.
<target name="db">
<sql driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://${db.local.host}:3306"
userid="${db.local.user}"
password="${db.local.pwd}"
print="yes">
<classpath>
<pathelement location="${build.extensions}/mysql-connector-java-5.1.7.jar"/>
</classpath>
<transaction src="${deploy.dir}/sql/database.sql"/>
</sql>
</target>
XMLListing 3: SQL
In sind die spezifischen Datenbankparameter hinterlegt. In wird für Ant die Position zur JDBC-Treiberdatei hinterlegt. Neben der Angabe einer SQL-Datei in können innerhalb dieses Tags auch reine SQL-Statements eingetragen werden.
Es ist nun nicht weiter nötig, aufwendig ein Datenbank-Dump-File manuell zu erzeugen, um dieses dann auf die gleiche Art in das Zielsystem einzuspielen. Mit dem Task sql können Sie diese Aufgabe zukünftig an Ant delegieren.
Kompression und FTP
Nachdem alle notwendigen Dateien in das Deploy-Verzeichnis kopiert wurden, könnten die nächsten Aufgaben darin bestehen, die benötigten Skripts in einer ZIP-Datei zu komprimieren und per FTP auf den Webserver zu übertragen. Das Listing 4 bewirkt genau dieses.
<target name="zip">
<zip destfile="${deploy.dir}/${ftp.deploy.file}"
basedir="${deploy.dir}"
excludes="**/install.php" />
</target>
<target name="ftp">
<ftp server="ftp.host" userid="ftp.user" password="ftp.pwd">
<fileset dir="${deploy.dir}">
<include name="${ftp.deploy.file}"/>
<include name="install.php"/>
</fileset>
</ftp>
</target>
XMLListing 4: ZIP & FTP
Dank des bereits erworbenen Wissens ist das Listing selbsterklärend. Es stellt sich jedoch die Frage, was passiert, wenn die Datei auf dem Webserver übertragen wurde. Hier bietet sich die Verwendung eines einfachen PHP-Skripts zum Entpacken des Archivs an (Listing 5).
$file = deploy.zip;
$zip = new ZipArchive;
if ($zip->open($file) === TRUE) {
$zip->extractTo(‘serverpath’);
$zip->close();
echo 'Installation done.';
} else {
echo 'Installation failed.';
}
XMLListing 5: PHP UnZip Script
So einfach, wie das Skript gestaltet ist, hat auch diese Lösung ihre Eigenheiten. Wer eine komplette Magento-Installation komprimiert und auf diese Art auf dem Live-Server entpacken möchte, wird schnell die PHP-Grundeinstellung max_execution_time seines Servers kennenlernen. Komplexe Applikationen wie Magento sind sehr umfangreich und benötigen zum Entpacken eine längere Ausführungszeit des Skripts, als von der PHP-Konfiguration vorgesehen.
Eine Lösung wäre, die verschiedenen Verzeichnisse einzeln zu komprimieren und diese Dateien dann zu übertragen und nacheinander zu entpacken. Das bietet sich eher bei einer Gesamtinstallation an. Bei einem einfachem Update ist die Verwendung der Diff-Funktion der Versionsverwaltung wesentlich eleganter.
Ant Erweitern
Der modulare Aufbau von Ant erlaubt es auch, das Werkzeug um eigene Funktionen, die nicht im Core enthalten sind, zu erweitern. Neben der bereits erwähnten Erweiterung für Subversion existieren im Web noch zahlreiche weitere Plugins, zum Beispiel ein Javascript-Minimizer oder der PHP-Documentator.
Der Javascript-Minimizer optimiert die JavaScript-Dateien, in dem er unter anderem Kommentare und unnötige Leerzeichen entfernt (Listing 6). Nach der Komprimierung hat die Javascript-Datei einiges abgespeckt. Dadurch wird sie schneller vom Browser geladen. definiert einen neuen Task mit dem Namen jsmin und verweist mit classpath auf die Datei des Plug-ins. Der classname ist die ausführende Klasse und muss aus der Dokumentation des entsprechenden Plug-ins entnommen werden. Anschließend wird in das bereits bekannte Element verwendet, um die zu komprimierenden Javascript-Dateien anzugeben. Mit der Einstellung force=“true“ werden bereits vorhandene Dateien überschrieben.
<taskdef name="jsmin" classname="net.matthaynes.jsmin.JSMin_Task" classpath="jsmin-0.2.4.jar"/>
<target name="jsCompressor">
<echo message="minimize all JS."/>
<jsmin force="true">
<fileset dir="${deploy.dir}/js" includes="**/*.js" />
</jsmin>
</target>
XMLListing 6: JavaScript minimieren
Dokumentation erzeugen
Das an Java Doc angelehnte PHP Doc lässt sich auch mit Ant erzeugen. Mit ist Ant in der Lage, sogenannte Executables auszuführen. Da PHP Doc in PHP implementiert ist, muss die php.exe die Dokumentation generieren. Das Listing 7 demonstriert das Vorgehen.
<target name="phpdoc">
<echo message="PHP DOC" />
<exec executable="${php.exe}" dir=”.”>
<arg value="${phpdoc.extension.inc}" />
<arg value="-f" />
<arg value="*.php" />
<arg value="-t" />
<arg value="${deploy.dir.phpdoc}" />
<arg value="-ti" />
<arg value="API Documentation" />
</exec>
</target>
XMLListing 7: PHPDOC mit ANT
Die Werte, welche durch übergeben werden, sind Kommandozeilen-Parameter, mit denen PHP gestartet wird. In der Property ${php.exe} wird der absolute Pfad zur php.exe hinterlegt. Der Dokumentator startet im aktuellen Projektverzeichnis, wozu er mit dir=“.“ bewegt wird. Die fertige Doku wird in das Verzeichnis geschrieben, das über die Property ${deploy.dir.phpdoc} angegeben wurde. Schließlich ist es noch notwendig, den Pfad zu PHP Doc anzugeben, was mit der Property ${phpdoc.extension .inc} getan wurde.
Applikationskonfiguation mit XML Dateien

Windows Nutzer kennen die typischen INI-Files, in denen Werte zur Laufzeit in eine Applikation geladen werden können. Dieses Konzept hat mit der starken Verbreitung von XML eine neue Renaissance erfahren. In diesem Zusammenhang sind die Paradigmen Don’t Repeat Yourself (DRY) und Convention Over Configuration (COC) zum Maß der Dinge avanciert. Die freien Frameworks Ruby On Rails und Maven 2, gehören zu den bekanntesten Tools, die mit dieser Technologie arbeiten.
In Hochsprachen wie C oder Java müssen die Quelldateien in ein binäres Format gebracht werden, dieser Vorgang kann je nach Umfang eines Programms, einiges an Rechenzeit verbrauchen. Um verschiedene Parameter einer Anwendung zur Laufzeit verändern zu können, wurde eine Möglichkeit geschaffen, die Werte aus einfachen Textdateien lesen kann.
Damit erübrigt sich das Kompilieren, da die Initialisierung der Variablen nun nicht mehr im Quelltext hinterlegt ist. Hinter dem Begriff DRY verbirgt daher nichts anderes, als die Vermeidung unnötiger und sich wiederholender Arbeitsschritte. COC erweitert diese Idee um die Forderung, dass jede Variable die konfiguriert werden kann, mit einem Defaultwert vorbelegt ist. In der Konfigurationsdatei werden nur noch Parameter angegeben, die vom vorgegebenen Standart abweichen. Die Übersichtlichkeit und Lesbarkeit der Konfiguration steigt somit rapide an, da es im praktischen Einsatz sehr unwahrscheinlich ist, dass alle Konfigurationsmöglichkeiten überschreiben werden müssen. Um effektiv mit den Paradigmen DRY und COC arbeiten zu können ist es, wichtig die möglichen Konfigurationsparameter und deren Bedeutung zu kennen. Daher sollte an einer ausführlichen Dokumentation nicht gespart werden.
Für PHP ist die Verwendung von XML als Konfigurationsdatei aus zwei Gründen sinnvoll. Die wichtigste Eigenschaft ist vor allem eine Trennung von internen Applikationszuständen, die dem Anwender verborgen bleiben sollen. Ausschließlich die notwendigen Anpassungen an das Zielsystem, wie Datenbankanbindungen und Layouteinstellungen werden zentral in der Konfigurationsdatei vorgehalten. Der zweite Aspekt, der für XML spricht, ist die Möglichkeit Daten einfach und lesbar zu strukturieren. So praktisch die Nutzung von XML auch ist, so soll die davon ausgehende Gefahr nicht unterschlagen werden. Ohne geeignete Maßnahmen können die Daten der Konfigurationsdatei von Dritten ausgespäht werden. Der Aspekt über die Sicherheit wird im Anschluss an eine kurze Einführung zu XML besprochen.
XML
Die Extensible Markup Language (XML) wird für die unterschiedlichsten Aufgaben verwendet. Der Grund liegt in der einfachen Möglichkeit, Strukturen und Daten zu modellieren. Im Gegensatz zu HTML sind die Tags nicht mehr fest vorgeschrieben, sondern können frei gewählt werden. Genauso verhält es sich auch mit den Attributen für die Elemente. Damit dies auch gelingt, muss eine XML-Datei verschiedenen Regeln entsprechen. Werden die Regeln eingehalten, spricht man von einem wohlgeformten Dokument. Einige XML Editoren verweigern das Abspeichern nicht wohlgeformter Dokumente.
Um festzustellen, ob das Dokument auch der vorgegebenen Datenstruktur entspricht, kann entweder gegen eine Dokument Type Definition (DTD) oder ein XML Schema (XSD) validiert werden. Entwicklungsumgebungen greifen oft auf die angegebene DTD bzw. XSD Datei zu, um eine intelligente Codevervollständigung anbieten zu können. Wer mehr über das breite Spektrum der Extensible Markup Language erfahren möchte, dem sei andieser Stelle die einschlägige iteratur zur Thematik Herz gelegt, da dieser Artikel lediglich die Grundlagen von XML streift und seinen Fokus auf die Verwendung mit PHP legt.
Regeln für wohlgeformte XML Dokumente:
- Das Wurzel-Element (Root) darf nur einmalig im Dokument vorkommen
- Tags werden stets kleingeschrieben und sind durch die Zeichen < > begrenzt
- Namen für Tags dürfen nur mit _ und Buchstaben beginnen, anschließend können Zahlen,
- Buchstaben, Punkte, Bindestriche und Unterstriche verwendet werden.
- Jedes geöffnete Tag muss geschlossen werden, Beispiel:
- Verschachtelte Tags müssen immer in der Reihenfolge geschlossen werden wie sie geöffnet werden
- Die Sondernotation für Elemente, die kein schließendes Tag benötigen, nennt sich leeres Tag und
- hat folgende Syntax:
- Werte für Attribute müssen durch Anführungszeichen eingeschlossen werden
Es existiert mittlerweile eine erhebliche Menge an XML Editoren, die um die Gunst der Nutzer buhlen. Im kommerziellen Umfeld ist der XMLSpy von Altova der absolute Platzhirsch. XMLSpy beherrscht sämtliche Disziplinen, die im Umgang mit XML notwendig sind, dafür müssen für die Anschaffung der Enterprise Version allerdings ca. 800€ eingeplant werden. Wer lieber auf Freeware zurückgreifen möchte, findet in Notepad++ einen sehr vielseitigen Editor. Verfechter von Microsoft können auf eine sehr gelungene Anwendung mit dem Namen XML Notepad zurückgreifen. Unter den IDE’s ist vor allem Eclipse zu nennen, das neben XML auch ein sehr leistungsstarkes PHP-Plugin bietet. Gerade die Tatsache, das PHP und XML mit ein und demselben Werkzeug bearbeitet werden ist maßgebend für einen flüssigen Arbeitsablauf.
Datenbank vs. XML
Sicherlich wird sich der ein oder andere Leser die Frage stellen, wieso die in der XML gespeicherten Daten nicht einfach in eine Datenbanktabelle ausgelagert werden. Die meisten Applikationen benötigen generell von Haus aus eine Datenbank zur Datenhaltung, also kann diese, die Aufgabe der Konfiguration mit übernehmen. Die Verwaltung komplexer Datenbanksysteme ist dank geeigneter Werkzeuge wie beispielsweise phpMyAdmin auch relativ gut beherrschbar. Da die typischen Verbindungsparameter wie Host und Datenbankname nicht in der Datenbank gespeichert werden können, müssen diese in der Anwendung vorgehalten werden. Um alle notwendigen Einstellungen nicht in der gesamten Applikation zu verstreuen, hat sich das DRY und COC Prinzip etabliert. Die mitgelieferten Installationsroutinen zum Deployen der Programme besitzen einen entscheidenden Nachteil.
Meist wird eine solches System auf einem lokalen Testsystem installiert und soll dann später per FTP auf den Webserver übertragen werden. Zu 99% aller Fälle beginnt nun das große Suchen nach den Einstellungen, die angepasst werden müssen, um die Applikation auf dem Webserver lauffähig zu bekommen. Selbst ein kompletter Database -Dump vom Testsystem auf Produktionsumgebung beherbergt stets die Gefahr falsche Konfigurationseinstellungen mit zu übertragen. Das Szenario lässt sich noch weiter steigern, mit der Annahme eines Datenbank Clusters und verschiedener Replikationsstrategien. Der Abschnitt XML und PHP zeigt in der Beispielanwendung wie zwischen Testumgebung und Produktionssystem unterschieden werden kann. Für kleinere Projekte, kann es durchaus sinnvoll sein komplett auf Datenbanken zu verzichten und die gesamte Datenhaltung über XML zu organisieren. Das erspart zum einen den Aufwand der Datenbankkonfiguration und vereinfacht das Backup der Datenhaltung. Eine simpler Link oder Terminverwaltung kann ohne Content Management System (CMS) auch problemlos vom Kunden gepflegt werden. Vorraussetzung ist, dass die Datenmenge gering ist und der Anwender ein geeignetes Maß an Erfahrung mitbringt. Unter Verwendung dieser Technologie lassen sich äußerst kostengünstig Projekte realisieren.
Sicherheitsaspekte
Da es sich bei XML um eine einfache Textdatei handelt, kann sie problemlos von Menschen gelesen und interpretiert werden. Wenn der Name und der Pfad zur Datei auf dem Server bekannt ist, wird beim Aufruf in aller Regel der Inhalt der Datei im Webbrowser angezeigt. Dieses Verhalten ist natürlich für unseren Fall äußerst problematisch, da in der Datei wichtige Daten hinterlegt sind, die nicht für Dritte bestimmt sind. Das Finden solcher Informationen kann durch unglückliche Dateinamen begünstigt werden, da mit sehr hoher Wahrscheinlichkeit bei einem Angriff zuerst config.xml im Root Verzeichnis des Webservers ausprobiert wird. Das Ergebnis einer solchen Abfrage ist im Screenshot_01 zu sehen.

Ein wirksamer Schutz besteht aus mehreren Schritten. Die Grundidee ist zuerst die config.xml gut zu verstecken. Damit das Versteck auch sicher ist, wird es mit einem simplen aber effektiven Zugriffsschutz verschlossen. Als Erstes wird ein Verzeichnis unterhalb des Root im Projekt mit dem Namen var angelegt. Dieses Verzeichnis darf nicht in der robots.txt mit auftauchen, damit würde man einem potenziellen Angreifer eine Wegbeschreibung in die Hand geben. Es besteht auch keine Gefahr für eine unbeabsichtigte Indizierung des Verzeichnisses durch eine Suchmaschine, da die nur den Links auf URL-Ebene folgen kann. Wer dennoch auf Nummer sicher gehen möchte, kann in var eine eigenständige robots.txt erstellen, die sämtliche Suchmaschinen aussperrt. Dazu genügen die folgenden Einträge:
User-agent: *
Disallow: *
Die beiden Zeilen gelten für alle Dateien und Verzeichnisse, die sich im selben Directory wie die robots.txt befindet. Die Indexierung wird allen Suchmaschinen untersagt. Wichtig ist das es außerhalb des geschützten Verzeichnisses, keinen Hinweis auf dessen Existenz vorhanden ist. Im zweiten Schritt wird eine index.php in var erzeugt. Der gewünschte Effekt ist, dass nun nicht mehr sämtliche Files des Verzeichnisses aufgelistet werden, sondern automatisch die index.php aufgerufen wird. Die Index -File kann leer bleiben oder einen redirect auf die Domain des eigenen Webauftritts haben.
$url = "Location: http://".$_SERVER['SERVER_NAME'];
header("HTTP/1.1 301 Moved Permanently");
header('$url');
header("Connection: close");
Zu Beginn des Listings wird die Zieladresse festgelegt, die in unserem Fall automatisch die Server URL zugewiesen bekommt. Einige Provider haben ihre Webserver mit einem ähnlichen Verhalten konfiguriert. Der Provider 1 und 1 zeigt beispielsweise bei fehlendem Index eine leere Seite an. Die übrigen Funktionen steuern das HTTP Protokoll. Die Header – Funktion ist ein sehr mächtiges Werkzeug, das bei seiner Verwendung einige Sorgfalt erfordert. Nur wenige Operationen sind vor den Funktionen gestattet. Die Verwendung von echo gehört zu den beliebten Fehlern im Umgang mit header. Wer diese Maßnahmen beachtet, kann getrost seine Konfiguration in eine XML-Datei auslagern, ohne böse Überraschungen zu erleben.
XML und PHP
Ab der Version 5 ist die SimpleXML Extension Bestandteil von PHP und ermöglicht, damit eine sehr einfache Art und Weise XML Dateien zu verarbeiten. Zur Demonstration greife ich auf ein kleines Beispiel zurück, das die Datenbankkonfiguration aus einer XML-Datei ausliest und anschließend Werte von einer Datenbank ausgibt. Der Aufbau der Datenbank ist über das Listing database.sql ersichtlich, das auch gleich zum Anlegen der Datenbank verwendet werden kann.
CREATE TABLE IF NOT EXISTS links (
url char(255) NOT NULL,
category char(255) NOT NULL DEFAULT 'sonstige',
description TEXT
);
Die Ausgabe des Listings config.xml ist im Screenshot_01 bereits zu sehen. Der Wurzelknoten ist als dessen Kind angefügt wurde. In dem Tag wird das verwendete Datenbankmanagmentsystem angegeben. Wegen der vielen möglichen Umgebungen in die eine PHP Applikation installiert werden kann besteht der Wunsch nach Flexibilität. Auf das Element wird üblicherweise ein Factory Pattern [8] angewendet, um die verwendete Datenbank problemlos ohne Quelltextänderungen austauschen zu können. Diese Option ist hier nur der Vollständigkeit erwähnt, da eine genauere Erläuterung den Rahmen des Artikels übersteigt.

Spannend ist das folgende Tag . Hier werden zwei Verbindungen gespeichert. Die Überlegung ist, eine lokale (Testumgebung) und eine Server-Konfiguration (Produktionsumgebung) zu definieren. Das erspart bei der Übertragung der der Projektdateien zum Server eine vorherige Anpassung an die dort vorhandene Datenbank. Das Tag besitzt als Attribute name und prefix. Mit name ist der Datenbankname gemeint, während prefix eine „Vorsilbe“ der Tabellen definiert. Ein Präfix ist stets optional und gestattet eine mehrfache Installation der Anwendung in der gleichen Datenbank. Das ergibt sich aus der Forderung, das Tabellennamen stets eindeutig sein müssen und nicht mehrfach vergeben werden dürfen. Die Elemente Host, User und Login sind selbstsprechend. Type ist der Indikator, welcher uns verrät ob es sich um die Test- oder die Produktionsumgebung handelt. Eine spätere Abfrage der Variable $_SERVER['SERVER_NAME'] == “localhost”;
stellt die Prüfbedingung für das Zielsystem. Soweit nur zur Vorbereitung.
Der erste Schritt besteht darin, die zu verarbeitende Datei zu öffnen. Dies geschieht mit der Anweisung: $config = simplexml_load_file(“config.xml”);
in der die Variable $config
steht nun der gesamte Inhalt aus config.xml zur Verfügung. Um dbms auslesen zu können, wird folgende Zeile benötigt: $dbms = $config->dbms;
für den Fall das es mehrere Tag’s mit derselben Bezeichnung gibt kann der gestammte Inhalt sequentiell ausgelesen werden.
foreach ($config->connection->database as $xmlvars) {
$database = $xmlvars['prefix']." ".$xmlvars['name'];
$host = $xmlvars->host;
$user = $xmlvars->user;
$login = $xmlvars->login;
}
Das Beispiel iteriert mit einer foreach Schleife über dem Kindelement , das sich unterhalb von befindet. In dem assoziativen Array $xmlvars, kann nun auf die einzelnen Element zugegriffen werden. Wie in Zeile 01 bereits zu erkennen ist, muss nicht zwangsläufig die gesamte XML Datei ab dem Root- Element verarbeitet werden, um ein Ergebnis zu erzielen. Die Bezeichnungen aus dem Beispiel leiten sich aus den Namen der XML-Elemente ab. In Zeile 02 wird demonstriert, wie Attribute eines Tags ausgelesen werden. In den Zeilen 03 bis 05 werden die Werte der Kindelemente , und den Entsprechenden Variablen zugewiesen. Würde man beabsichtigen in dieser Konstellation auf ein Attribut von zu zugreifen ist die korrekte Syntax: $hostAttribut = $xmlvars->host['attribut']
. Wie man sieht, enthalten die wenigen Zeilen des Listings den Großteil des notwenigen Know-hows für den praktischen Einsatz. Die Logik für die Unterscheidung zwischen Webserver und lokaler Installation erweitert das vorangegangene Listing und ist zwischen die Zeile 01 und 02 einzufügen.
if($_SERVER['SERVER_NAME'] == "localhost" && $xmlvars->type == "offline") {
$database = $xmlvars ['name'];
$host = $xmlvars->host;
$user = $xmlvars->user;
$login = $xmlvars->login;
break;
}
Die IF-Anweisung wird erst dann ausgeführt, wenn die Server-Variable den Wert localhost annimmt. Alternativ kann auch anstatt des Domainnamenes die IP-Adresse zur Identifizierung des Rechners herangezogen werden. Um den korrekten Datensatz an den ermittelten Server zu binden, wird eine zweite Prüfbedingung benötigt. In iesem Beispiel werden die korrekten Einstellungen über das Element erkannt. Sobald beide Kriterien erfüllt sind, können die Variablen ihre Werte aufnehmen. Die break Anweisung sorgt dafür, dass die foreach Schleife nach der Initialisierung verlassen wird. Das verhindert ein versehentliches überschreiben der Werte durch den nächsten Datensatz in einem weiteren Schleifendurchlauf. Das würde zwangsläufig passieren, da der nachfolgende Datensatz keiner Prüfbedingung standhalten muss. Wenn mehrere Server zu berücksichtigen sind, kann die IF–Anweisung um den ifelse Ausdruck erweitert werden. Selbstverständlich lässt sich das Konstrukt auch durch eine switch Kontrollstruktur abbilden. Eine Datenbankabfrage sowie die Ausgabe werden durch die üblichen Bordmittel von PHP realisiert. Die auf der Heft CD beigefügten Listings sind vollständig und zeigen, wie die gesamte Applikation arbeitet.
Der aufmerksame Leser erwartet, sicherlich an dieser Stelle geeignete Möglichkeiten in eine XML-Datei zu schreiben. Diese Erwartung muss leider enttäuscht werden, da die SimpleXML Extension keine Funktionen zum Schreiben von Dateien bereitstellt. Dies ist auch nicht notwendig, da die bereits in PHP vorhandenen Möglichkeiten völlig ausreichend sind. Dafür können in der Verarbeitungslogik Reguläre Ausdrücke, Stringoperationen und Sortieralgorithmen benutzt werden. Das Ergebnis wird dann über die fwrite Funktion in eine Datei geschrieben.
Resümee
Wie man sehen konnte, ist die Verarbeitung von XML Dateien mit PHP Mitteln recht unkompliziert und Erfolge werden schnell sichtbar. Der Artikel hat zudem einige Anreize für die Verwendungsmöglichkeiten aufgezeigt und Hinweise für Sicherheitsaspekte beschrieben. Dem regen praktischen Einsatz in Ihren eigenen Projekten steht nun nichts mehr im Wege.