Filter

1. Einführung

Ein wesentliches Konzept des Adventure PHP Framework ist die Trennung von URL bzw. URL-Layout und Funktionalität einer Anwendung. Die in vielen vergleichbaren Produkten zu findende, engmaschige Verquickung birgt den Nachteil, dass eine Änderung einer URL gleichbedeutend ist mit einer Anpassung der Software. Weiterhin lässt sich durch direkte Adressierung von Software-Komponenten über das URL-Layout keine ausreichende Granularität von Komponenten oder Elementen erreichen, die in sich für weitere Projekte wiederverwendbar sind.

Ein weiterer Aspekt ist die HMVC-Implementierung des APF (siehe Page-Controller), die es ermöglicht, eine Software oder Webseite aus beliebig vielen, autonom lauffähigen MVC-Elementen zu erstellen. Hier wäre eine Abbildung aller MVC-Document-Controller (siehe (Document-)Controller) nicht nur aufwändig, sondern auch hinsichtlich Lesbarkeits- und SEO-Gesichtspunkten kontraptoduktiv.

Aus diesem Grund setzt das APF auf eine Entkopplung von Software und URL, die auf dem Prinzip der Ein- und Ausgabe-Filter basiert. Diese sehen vor, dass eine (externe) Anfrage durch einen oder mehrere Input-Fiter in ein internes Format transformiert wird, das in seiner normalisierten Form von allen Software-Komponenten verarbeitet werden kann. Nach der Verarbeitung überführt dann ein Output-Filter die interne Sicht wieder in die gewünschte Aussensicht, damit die Applikation nach aussen die gewünschte Repräsentation erhält.

Neben dem klassischen Anwendungsfall URL-Layout existieren noch eine Reihe weiterer Möglichkeiten, Ein- und Ausgabe-Filter einzusetzen. Ein Beispiel hierfür ist die Komprimierung von HTML-Quellcode, das nachträgliche Injizieren von Inhalten, die während der Transformations-Phase gesammelt wurden und die Absicherung gegen potentiell gefährliche Eingaben des Benutzers.

2. Architektur

Das folgende Ablauf-Diagramm zeigt das Timing der Ein- und Ausgabe-Filter:

Filter Timing Modell

Ein- und Ausgabe-Filter sind jeweils als Kette von Filtern ausgeführt, die gemäß ihrer Reihenfolge ausgeführt werden. Innerhalb einer FilterChain kann jeder Filter entscheiden, ob der ein weiterer Filter der Kette zur Ausführung gebracht wird und welche Eingabe er dabei erhalten soll. Die mitgelieferten Standard-Filter unterbrechen die Kette nicht.

Die Chains für Eingabe- und Ausgabe-Filterung sind Singleton-Objekte (innerhalb eines Requests) und können so in allen APF-Komponenten zugegriffen werden. Die Konfiguration erfolgt jedoch üblicherweise in der Bootstrap-Datei (index.php).

Das Timing-Modell des Front-Controllers beschriebt, dass die FilterChain für die Eingabe-Filter vom Front-Controller zu Beginn der Request-Verarbeitung ausgeführt. Das stellt sicher, dass beim Start der eigentlichen Front-Controller-Aufgaben alle eingehenden Information in eine intern verständliche Form gebracht wurden.

Das Timing-Modell beschriebt ebenso, dass nach Ausführen aller Front-Controller-Aufgaben die FilterChain auf die erzeugten HTML-Inhalte des Page-Controller angewendet werden. Damit ist sichergestellt, dass die Inhalte wieder in die externe Repräsentation überführt werden können.

2.1. FilterChain

Aus Gründen der Konfiguration und um die unterschiedlichen Zwecke der FilterChains auch in der API auszudrücken, ist jeweils eine Chain für Ein- und Ausgabe vorhanden: InputFilterChain und OutputFilterChain.

Diese erben von einer generischen AbstractFilterChain, die das FilterChain-Interface implementiert:

PHP-Code
interface FilterChain { public function filter($input); public function appendFilter(ChainedContentFilter $filter); public function prependFilter(ChainedContentFilter $filter); public function removeFilter($class); } abstract class AbstractFilterChain implements FilterChain { ... public function filter($input) { ... } public function &appendFilter(ChainedContentFilter $filter) { ... } public function &prependFilter(ChainedContentFilter $filter) { ... } public function &removeFilter($class) { ... } public function &reset() { ... } public function &clear() { ... } }

2.2. Filter

Wie dem Code-Stub aus dem vorangegangenen Kapitel entnommen werden kann, verarbeiten die Filter-Chains jeweils Filter vom Typ ChainedContentFilter. Diese erhalten im Gegensatz zur Filter-Implementierung vor dem Release 1.14 nicht nur den zu verarbeitenden Input, sondern auch die FilterChain zur weiteren Verarbeitung:

PHP-Code
interface ChainedContentFilter { public function filter(FilterChain &$chain, $input = null); }

Jeder Eingabe- und Ausgabe-Filter ist damit in der Lage, die Abarbeitung der Kette fortzusetzen oder per direkter Rückgabe der gefilterten Inhalte zu unterbrechen. Es ist zudem jederzeit möglich, Filter in die Kette zu injizieren oder zu entfernen.

Ein Anwendungsfall für das Hinzufügen von Filtern innerhalb einer Chain kann eine flexible Konfiguration von Input-Filter sein, bei der je nach Eingabe ein eigener Filter genutzt werden soll. Dieser "RouterFilter" könnte beispielsweise an Hand des ersten URL-Segments bzw. Pfad-Abschnitts einen eigenen Filter zur Verarbeitung des gewünschten URL-Layouts hinzufügen (z.B. /shop -> ShopUrlLayoutInputFilter, /page -> PagesUrlLayoutInputFilter).

Bereits innerhalb der Kette ausgeführte Filter können während der Abarbeitung der FilterChain nicht mehr entfernt werden. Dies würde sonst zu einem inkonsistenten Zustand führen.

Die Implementierung eines Filters kann mit dem Release 1.14 beliebige Basis-Implementierungen (z.B. Funktionalität aus der Klasse APFObject o.a.) nutzen, muss aber das oben beschriebene Interface erfüllen.

Der folgende Code-Block zeigt einen Filter, der alle unnötigen Zeichen aus dem erzeugten HTML-Code entfernt:

PHP-Code
use APF\core\filter\ChainedContentFilter; class WhitespaceOutputFilter implements ChainedContentFilter { public function filter(FilterChain &$chain, $input = null) { // Zeilenumbrüche und Leerzeichen entfernen $input = preg_replace('/\r|\n|\t|\s{3,}/', '',$input); // Restliche Filter ausführen return $chain->filter($input); } }

Es ist sowohl vor als auch während der Ausführung einer FilterChain möglich Filter an den Anfang oder das Ende der Kette hinzuzufügen. Hierzu stehen die Methoden appendFilter() (Anhängen eines Filters an das Ende) und prependFilter() (Voranstellen eines Filters) zur Verfügung.

Bitte beachten Sie, dass während der Ausführung vorangestellte Filter nicht mehr ausgeführt werden könnten.

3. Konfiguration

3.1. Standard-Setup

Im Auslieferungs-Zustand des APF werden die Filter-Chains vom Front-Controller bereits mit den Standard-APF-Filtern vorbelegt. Mitgeliefert werden der ChainedUrlRewritingInputFilter als Eingabe-Filter zur Auflösung des in Kapitel 4 beschriebenen URL-Layouts und der ChainedUrlRewritingOutputFilter für das Umschreiben der Links in die externe Repräsentation im Rewrite-URL-Fall.

3.2. Angepasstes Setup

Die Konfiguration der Chains erfolgt - wie bereits angesprochen - in der Bootstrap-Datei (index.php), kann aber bei Bedarf auch in anderen Software-Komponenten on-demand erfolgen.

Die Konfiguration der FilterChain kann nach der Einbindung des Page- und Front-Controllers wie folgt erledigt werden:

PHP-Code
include('./APF/core/bootstrap.php'); use APF\core\filter\InputFilterChain; use APF\core\filter\OutputFilterChain; // Konfiguration eines eigenen, zusätzlichen Input-Filters use VENDOR\project\filters\AdditionalInputFilter; InputFilterChain::getInstance()->appendFilter(new AdditionalInputFilter()); // Konfiguration eines eigenen, zusätzlichen Output-Filters use VENDOR\project\filters\AdditionalOutputFilter; OutputFilterChain::getInstance()->appendFilter(new AdditionalOutputFilter()); // Konfiguration eines eigenen, alleinigen Input-Filters use VENDOR\project\filters\CustomInputFilter; InputFilterChain::getInstance()->clear()->appendFilter(new CustomInputFilter()); // Konfiguration eines eigenen, alleinigen Output-Filters use VENDOR\project\filters\CustomOutputFilter; OutputFilterChain::getInstance()->clear()->appendFilter(new CustomOutputFilter());

Da beliebig viele Filter in einer Chain registriert werden können, ist auch eine Kombination der beschriebenen Möglichkeiten machbar. Um mehrere eigene Input-Filter und einen zusätzlichen eigenen Output-Filter zu konfigurieren, reicht folgender Code:

PHP-Code
include('./APF/core/bootstrap.php'); use APF\core\filter\InputFilterChain; use APF\core\filter\OutputFilterChain; // Konfiguration von eigenen Input-Filtern use VENDOR\project\filters\AdditionalInputFilterOne; use VENDOR\project\filters\AdditionalInputFilterTwo; InputFilterChain::getInstance() ->clear() ->appendFilter(new AdditionalInputFilterOne()) ->appendFilter(new AdditionalInputFilterTwo()); // Konfiguration eines zusätzlichen Output-Filters use VENDOR\project\filters\AdditionalOutputFilter; OutputFilterChain::getInstance()->appendFilter(new AdditionalOutputFilter());

Soll die Reihenfolge innerhalb einer Chain verändert werden, so kann das durch Leeren und anschließender Neu-Befüllung der Chain erreicht werden.

3.3. Abbrechen der Chain

Wie in einem der vorangegangenen Kapitel angesprochen, kann die Abarbeitung der Chain durch die Implementierung des Filters direkt beeinflusst werden. Wird der Output direkt zurück gegeben, so wird die Abarbeitung von potentiell nach dem Filter in der Chain befindlichen Filter abgebrochen:

PHP-Code
use APF\core\filter\ChainedContentFilter; class IgnorantOutputFilter implements ChainedContentFilter { public function filter(FilterChain &$chain, $input = null) { return $input; } }

3.4. Abschalten der Filter

Um die standardmäßig konfigurierten Filter komplett abzuschalten ist folgender Code in der Bootstrap-Datei notwendig:

PHP-Code
include('./APF/core/bootstrap.php'); use APF\core\filter\InputFilterChain; use APF\core\filter\OutputFilterChain; InputFilterChain::getInstance()->clear(); OutputFilterChain::getInstance()->clear();

3.5. Entfernen von Filtern

Die Implementierung der Filter-Chain bietet die Möglichkeit, Filter an Hand ihres Klassen-Namen einzeln zu entfernen. Dies kann wie folgt erledigt werden:

PHP-Code
include('./APF/core/bootstrap.php'); use APF\core\filter\OutputFilterChain; // Entfernen eines Output-Filters OutputFilterChain::getInstance()->removeFilter('ChainedUrlRewritingOutputFilter');

4. URL-Layout

Der klassischer Anwendungsfall für Ein- und Ausgabe-Filter im APF ist - wie bereits angesprochen - das Auflösen von verschiedenen URL-Layouts und die Verarbeitung der Informationen (z.B. Front-Controller Actions). Eigene Filter können innerhalb einer Verarbeitungs-Kette eingestellt oder durch Konfiguration als alleinige Filter zur Kontrolle der Ein- und Ausgaben konfiguriert werden.

Das URL-Layout des APF behandelt zwei Fälle: normale URLs und so genannte Rewrite-URLs, also URL-Pfade, die in Wirklichkeit eine andere Darstellung für normale URLs sind. Im Folgenden werden die beiden Varianten näher beleuchtet:

4.1. Normale URLs

Normale URLs sind solche, die eine oder mehrere Bootstrap-Dateien adressieren und die Parameter dort dynamisch angehängt werden. Beispiel:

Code
http://www.example.com/index.php?page=news&news-page=2

Die beschriebenen Parameter werden in diesem Fall bereits vom Webserver als solche verarbeitet und an die PHP-Engine weiter gegeben. Eine konkrete Verarbeitung ist also nicht notwendig.

Normale URLs können jedoch ebenso Front-Controller-Anweisungen enthalten, die speziell codiert sind:

Code
http://www.example.com/index.php?APF_modules_captcha_biz-action:showCaptcha=name:0123456789|mode:transparent

Die beschriebene URL adressiert eine Action unter dem Namespace APF\modules\captcha\biz mit dem Namen showCaptcha. Dieser werden die Paremeter name und mode zur Ausführung mitgegeben. Jede dieser Action-Definitionen kann innerhalb einer URL beliebig oft vorkommen und kann eine beliebige Anzahl von Parametern aufweisen. Weiterhin können "normale" Paramter mit Front-Controller-Parametern gemischt werden.

Das Schema einer Action-Anweisung kann im Kapitel URL-Layout der Front-Controller-Dokumentation nachgelesen werden.

Die Aufgabe des Filters besteht darin, die codierte Anweisung in ihre Bestandteile (Namespace, Action-Bezeichner, Parameter) zu zerlegen und die so adressierte Action dem Front-Controller mitzuteilen.

4.2. Rewrite-URLs

Rewrite-URLs unterscheiden sich zu normalen URLs durch die Trennung der Parameter. Sätze von Parmetern werden nicht durch "&" getrennt, sondern durch "/"; Parameter-Namen und deren Werte ebenfalls durch "/". Zur Auflösung ist eine Konvention zur Semantik der Parameter notwendig, da einerseits normale Parameter-Pfade von Front-Controller-Anweisungen getrennt werden müssen und andererseits Front-Controller-Anweisungen untereinander.

Um dies zu bewerkstelligen wurde das Trennzeichen "/~/" vereinbart. An diesem unterscheiden sich Parameter-Wert-Ketten und Front-Controller-Anweisungen, die getrennt behandelt werden. Einfache Parameter-Wert-Zuweisungen werden aufgelöst und in das $_REQUEST-Array gefüllt, Front-Controller-Anweisungen werden gemäß der unter URL-Layout beschriebenen Semantik aufgelöst und wie im Fall der "normalen" URLs dem Front-Controller übergeben.

Damit ergibt sich für das im letzten Kapitel beschriebene Beispiel folgende URL für einfache Parameter:

Code
http://www.example.com/page/news/news-page/2

Die Front-Controller-Action versteht der Filter im URL-Rewrite-Fall wie folgt:

Code
http://www.example.com/~/APF_modules_captcha_biz-action/showCaptcha/name/0123456789/mode/transparent

Die notwendigen Anpassungen an der Konfiguration des APF können im Kapitel URL-Rewriting nachgelesen werden.

Im Tutorial Implementierung von eigenen URL-Layouts des APF Wiki finden sich noch weitere Implementierungs-Ansätze von URL-Layouts.

4.3. Ausgabe-Formatierung

Ein weiterer Bestandteil der mit dem APF mitgelieferten Filter zur Abstraktion des URL-Layouts bildet der Output-Filter. Dieser ist dafür zuständig, "normale" URLs in das genannte Layout zu transformieren. Der Filter wird dabei nach der Transformation der Seite und nach der Ausführung aller Front-Controller-Actions auf den Inhalt der transformierten Seite angewendet.

Zur Unterstützung des Ausgabe-Filters - bzw. diesen für die URL-Formatierung nicht einsetzen zu müssen - kann ein eigenes Link-Schema implementiert und konfiguriert werden. Auf diese Weise werden die Links bereits während der Transformations-Phase korrekt formatiert.

Kommentare

Möchten Sie den Artikel eine Anmerkung hinzufügen, oder haben Sie ergänzende Hinweise? Dann können Sie diese hier einfügen. Die bereits verfassten Anmerkungen und Kommentare finden Sie in der untenstehenden Liste.
« 1   »
Einträge/Seite: | 5 | 10 | 15 | 20 |
1
Christian Achatz 19.06.2012, 22:57:54
Eine Implementierung eines XSS-Filters findet sich im Wiki unter http://wiki.adventure-php-framework.org/de/XSS_Schutz_via_InputFilter.