Einstiegspunkte (in Webapplikationen)
Dieses Mal geht es um Einstiegspunkte und eine fast schon triviale Idee, um diese Einstiegspunkte zu strukturieren.
Einstiegspunkte sind die Punkte, an denen die Applikation anfängt, eine
Abfrage abzuarbeiten. Das ist vor allem, wenn auch nicht
ausschließlich, bei Webapplikationen notwendig. Jeder, der bereits mit
PHP gearbeitet hat, dürfte dieses Konzept kennen, denn eine (von außen
sichtbare) PHP-Datei auf einem Webserver ist nichts Anderes als ein
solcher Einstiegspunkt (Bsp.: www.example.com/news.php).
Allerdings weisen diese grundlegenden Einstiegspunkte einige Mängel auf:
- Es ist schwieriger, eine sogenannte Bootstrap-Datei zu verwenden, da keine zentrale Datei existiert, die vor dem Aufruf des Einstiegspunkts ausgeführt wird.
- Suchmaschinen-relevante Optimierungen werden komplexer, da man GET-Parameter per mod_rewrite umschreiben muss (z.B. news.php?id=42).
Der erste wichtige Punkt ist, eine Struktur zur Integration mit
einer Boostrap-Datei zu bieten. Normalerweise wird dies über einen
einfachen mod_rewrite-Befehl erledigt, der besagt, dass jeder URL, der
zu keiner statischen Ressource (z.B. Bilder oder Stylesheets) zeigt,
auf die Bootstrap-Datei umgeleitet wird. Diese Bootstrap-Datei startet
die Applikation anschließend und bestimmt über den angeforderten URL
(vgl. $_SERVER['REQUEST_URI']) den Einstiegspunkt.
Ab diesem Punkt scheiden sich die Wege: Das Zend Framework basiert auf einem Schema, bei dem einer Controller-Klasse eine Methode zugewiesen wird, die aufgerufen werden soll.
Beispiele:
- /foo/bar/id/42 -> FooController::barAction(id => 42)
- /foo -> FooController::indexAction()
- / -> IndexController::indexAction()
Das Problem ist, dass hier verhältnismäßig viel Struktur vorgegeben
wird, denn man muss sich an Konventionen halten (*Action-Methoden und
*Controller-Klassen). Zwar kann man der Parameterübergabe mit einem
sog. Router Herr werden, allerdings stellt das einen Mehraufwand dar.
Es wird also nach einem Algorithmus zum Finden solcher Einstiegspunkte
anhand des Anfrage-URLs gesucht, der intuitiver wirkt und einfacher
funktioniert. Damit die Art der Strukturierung intuitiv verwendbar ist,
muss es in wenigen Worten erklärbar sein und sollte möglichst viel vom
normalen Weg mitnehmen. Deshalb verwenden wir weiterhin die Übergabe
per GET, allerdings besteht die Möglichkeit, unbenannte Parameter an
einen Einstiegspunkt zu übergeben, womit es uns möglich wird,
suchmaschinen-relevante Optimierungen vorzunehmen. Der Algorithmus baut
stark auf dem Dateisystem auf: Es wird eine Ordnerstruktur von
Einstiegspunkten vorgegeben, die sich im Ordner "entrances" befindet.
Die Bootstrap-Datei heißt "index.php" und befindet sich im Ordner
"www". Wir gehen von dieser Ordnerstruktur aus:
- entrances
- index.php
- blog
- show.php
- www
- index.php
Haben wir nun einen URL "blog/show/42", so sollte er die Datei "entrances/blog/show.php" mit dem unbenannten Parameter 42 aufrufen. Der Algorithmus lässt sich folgendermaßen definieren:
- Zerlege den URL anhand des Schrägstrichs in Teile, wir nennen diese Teile nachfolgende Komponenten.
- Gehe so tief in die Ordnerstruktur wie möglich:
- Gibt es einen Ordner mit dem Namen der aktuellen Komponente (sofern existent), betritt ihn.
- Andernfalls, brich ab.
- Suche nach dem ersten passenden Einstiegspunkt:
- Befindet sich im aktuellen Ordner eine PHP-Datei, die den Namen der aktuellen Komponente trägt, wähle sie als Einstiegspunkt und übergib die nicht verwerteten Komponenten als Parameter.
- Andernfalls, versuche es im übergeordneten Ordner.
- Wurde ein Einstiegspunkt gefunden, gib ihn zurück, andernfalls wähle "index.php" als Einstiegspunkt.
Jetzt führen wir den Algorithmus anhand des o.g. Beispiels aus.
- Komponenten des URLs "blog/show/42" sind ["blog", "show", "42"].
- Wir befinden uns im Ordner "entrances".
- Der Ordner "blog" existiert -> wir befinden uns nun in "entrances/blog".
- Der Ordner "show" existiert nicht -> wir brechen hier ab.
- Die Datei "show.php" existiert -> wir wählen "entrances/blog/show.php" als Einstiegspunkt und die Liste ["42"] als Parameter.
Zum Definieren von Einstiegspunkten braucht man also nur eine
beliebige PHP-Datei im Ordner "entrances" erstellen und schon hat man
einen Einstiegspunkt definiert.
Die Implementierung dieses Algorithmus kann man in weniger als 30 Zeilen realisieren.
list($uri, $query) = explode('?', $_SERVER['REQUEST_URI'], 2);
$path = array_filter(explode('/', substr($uri, 1)));
Dieser Codeschnipsel bewirkt lediglich das Zerlegen des URLs, das
Kernstück des Algorithmus folgt mit der Funktion "dispatch", der
möglichst tief in die Ordnerstruktur geht und anschließend nach dem
erstbesten Einstiegspunkt sucht.
function dispatch ($base, $path)
{
if($path == array()) return false;
if($path[0] == '..') throw Exception('.. not allowed.');
$file = $dir = $base . strtolower($path[0]);
$file .= '.php';
$tail = array_slice($path, 1);
if(file_exists($dir) && is_dir($dir))
{
if(($result = dispatch($dir . '/', $tail)) !== false)
{
return $result;
}
}
return (file_exists($file) ? array($file, $tail) : false);
}
Nun benötigen wir nur noch den Code, der die dispatch-Funktion aufruft und ggf. den Einstiegspunkt "index.php" wählt.
$base = '../entrances/';
$res = dispatch($base, $path);
list($entrance, $args) = ($res === false ? array($base . 'index.php', $path) : $res);
include $entrance;
Das ist die komplette Implementierung des o.g. Algorithmus. Mehr ist und sollte nicht notwendig sein, um Einstiegspunkte zu definieren. Was die Einstiegspunkte für Code enthalten, ist komplett dem Entwickler überlassen.