Seit ein paar Tagen ist PHP 5.4 verfügbar. Eines der neuen Features der jetzt aktuellen PHP-Version ist der integrierte Development-Webserver, der auf der lokalen Entwicklungsmaschine einen schwergewichtigen Webserver wie Apache oder Nginx ersetzten kann. In diesem Artikel möchte ich einige Tipps und Tricks zum neuen internen Webserver ansprechen, die mir beim Ausprobieren in den letzten Tagen aufgefallen sind.
Die wichtigsten Randbedingungen für die Verwendung des internen Webservers sind im PHP-Manual erklärt. Mit den Schaltern -S <addr>:<port> startet man den Server, mit -t <docroot> kann man zusätzlich ein Verzeichnis angeben, das als Docroot verwendet wird. Zusätzlich kann man noch ein sogenanntes Router-Skript angeben, dazu später mehr.
Beispiel: Start des internen Webservers
$ php -S localhost:8080 -t /projects/test/public
Konfiguration
Da der Webserver auf der Kommandozeile gestartet wird, verwendet er die für das PHP-CLI-Binary festgelegte php.ini. Allerdings sind die Einstellungen für CLI-Skripte nicht immer auch für eine Webserver-Umgebung sinnvoll. Auf vielen Systemen ist es daher üblich, für CLI und Web (Apache) eine eigene php.ini zu haben. Um dieser Trennung gerecht zu werden, kann man dem internen Webserver beim Starten eine eigene php.ini mitgeben. Ein klarer Nachteil hierbei ist, dass man diese bei jedem Aufruf erneut eingeben muss. Hier lohnt es sich schon fast, ein entsprechendes Shellskript zu schreiben.
Beispiel: Start des Webservers mit einer eigenen Konfigurationsdatei:
$ php -S localhost:8080 -c /projects/test/php.ini
Auf diese Art und Weise kann man sich entweder für jedes einzelne Projekt eine eigene Konfiguration anlegen oder sich eine allgemeingültige php.ini zur Verwendung in mehreren Projekten auf dem Rechner ablegen.
Wer sich nicht sicher ist, welche INI-Datei verwendet wird, kann dies über den Aufruf php –ini erfahren:
$ php --ini Configuration File (php.ini) Path: /usr/local/etc Loaded Configuration File: /usr/local/etc/php.ini Scan for additional .ini files in: /usr/local/etc/php5/conf.d Additional .ini files parsed: (none)
Zusätzlich zu einer Konfigurationsdatei lassen sich Einstellungen natürlich auch direkt im Kommandozeilenaufruf angeben:
$ php -S localhost:8080 -d display_errors=off
Ersatz für .htaccess: Das Router-Skript
Startet man den internen Webserver mit einem Router-Skript, so wird die Abarbeitung der Anfrage zuerst an das angegebene Skript übergeben. Anhand der Daten des aktuellen Requests lassen sich nun unterschiedliche Aufgaben ausführen. Verlässt man das Router-Skript mit einem return false, so führt der Webserver den ursprünglichen Request fort und liefert die angefragt Datei aus seinem Docroot (oder einen 404-Fehler falls die Resource nicht existiert).
Beispiel: Start des Webservers mit einem Router-Skript:
$ php -S localhost:8080 -t /projects/test/public /projects/test/router.php
Die Hauptaufgabe eines Router-Skripts ist der Ersatz für URL-Rewriting von zum Beispiel Apaches mod_rewrite. Vor allem MVC-Frameworks wie Zend Framework benötigen normalerweise eine entsprechende Technik, um alle Requests an eine zentrale PHP-Datei zu leiten. Ein Beispiel für diesen Einsatz könnte das folgende Router-Skript sein.
Einfacher Beispiel-Router (router.php)
<?php if (file_exists($_SERVER['DOCUMENT_ROOT'] . $_SERVER['REQUEST_URI'])) { return false; } include $_SERVER['DOCUMENT_ROOT'] . "/index.php"
Bei der Verwendung eines Router-Skripts ist es wichtig, dem internen Webserver beim Start auch den gewünschten Document-Root mitzuteilen, da er sonst nicht weiß, aus welchem Verzeichnis Ressourcen wie Javascript-, CSS- und Bilddateien geladen werden sollen.
Router im Frontcontroller-Skript
Eine andere Alternative ist es, Routing-Anweisungen gleich in das Frontcontroller-Skript (index.php) der Anwendung zu schreiben. Um dabei den Einsatz auf anderen Webservern nicht zu gefährden, sollte der entsprechende Code nur für den internen Webserver erreichbar sein. Der folgende Code lässt sich prima am Anfang einer index.php für ein Zend Framework Projekt verwenden:
Routing-Anweisung in der index.php:
if (php_sapi_name() == 'cli-server' && $_SERVER['SCRIPT_NAME'] != '/index.php' && file_exists($_SERVER['DOCUMENT_ROOT'] . $_SERVER['REQUEST_URI'])) { return false; }
Den Webserver startet man nun aus dem ZF Projektverzeichnis mit:
$ php -S localhost:8080 -t public/ public/index.php
Funktionalität im Router-Skript
Mit ein wenig Kreativität kann man das Router-Skript erweitern. Als Beispiel zeigt der folgende Router die phpinfo-Seite, sobald man /phpinfo auf dem Webserver aufruft:
<?php if ($_SERVER['REQUEST_URI'] == '/phpinfo') { phpinfo(); return; } if (file_exists($_SERVER['DOCUMENT_ROOT'] . $_SERVER['REQUEST_URI'])) { return false; } include $_SERVER['DOCUMENT_ROOT'] . "/index.php";
Systemweites Router-Skript
Um nicht in jedem einzelnen Projekt wieder ein eigenes Router-Skript anlegen zu müssen, kann man ein globales Skript erstellen. Zusammen mit einem kleinen Shellskript lässt sich nun aus jedem Zend Framework Projektordner bequem ein Webserver für das Projekt starten.
phpserv.sh:
#!/bin/bash BASE_DIR=/Users/florian/work/cli-server php -S localhost:8080 -c ${BASE_DIR}/php.ini -t public/ ${BASE_DIR}/router_simple.php
Für das Shellskript habe ich unter /usr/bin/zfserv einen Link gesetzt. So reicht jetzt ein einfaches „zfserv“ auf der Konsole, um den Webserver mit dem globalen Router-Skript zu starten.
Mit ein wenig Aufwand könnte man das Shellskript natürlich noch erweitern und Parameter zulassen, um zum Beispiel den Servernamen oder den Port übergeben zu können. Für meine Ansprüche genügt das einfache Skript allerdings.
Debuggen mit dem internen Webserver
Auf dem internen Webserver kann man auch selbstverständlich einen Debugger installieren und laufen lassen. Xdebug seit ein paar Tagen in der neuen Version 2.2.0rc1 verfügbar, die jetzt PHP 5.4 unterstützt.
In der php.ini für den internen Webserver lässt sich Xdebug einfach einrichten:
zend_extension="/path/to/xdebug.so" xdebug.remote_connect_back = 1 xdebug.remote_enable = 1
Nun lässt sich der PHP-Code zum Beispiel mit PhpStorm ohne weitere Konfiguration debuggen. Vorsicht hierbei: Beendet man den aktuellen Request in PhpStorm mit einem Klick auf das rote Quadrat, so wird der komplette Webserver auf der Konsole beendet!
Und wenn wir gerade bei PhpStorm sind: in der aktuellen EAP-Version des kommenden PhpStorm 4.0 ist bereits Unterstützung für den internen Webserver eingebaut. Über das Run-Menü lässt sich nach entsprechender Konfiguration direkt ein interner Webserver aus der Entwicklungsumgebung starten.
Erreichbarkeit des Webservers
Startet man den Webserver mit dem Servernamen localhost, so ist er natürlich nur von der lokalen Maschine aus erreichbar. Um von anderen Rechnern aus auf den Server zugreifen zu können, muss man diesen mit dem Hostnamen oder der IP-Adresse des Rechners starten.
Start mit IP-Adresse
$ php -S 192.168.1.100:8080
Fazit
Der interne Webserver kann natürlich seinen großen Brüdern nicht das Wasser reichen. Für die lokale Entwicklungsumgebung ist er aber durchaus eine Alternative. Seit ein paar Tagen entwickle ich an einer Zend Framework Anwendung mit dem internen Webserver und mir sind bisher keine Probleme untergekommen. Mit Hilfe einer QA-Umgebung und entsprechenden Selenium-Tests auf einer Continuous Integration Platform kann man immer noch sicherstellen, dass die eigenen Anwendung auch mit einem großen Webserver wie Apache oder NGINX läuft.