Links

1. Einführung

URL-Strukturen sind ein wesentlicher Bestandteil einer Web-Applikation. Aus diesem Grund bietet das Adventure PHP Framework (APF) als Framework eine generische Lösung an, um URLs sehr einfach generieren bzw. manipulieren zu können.

In der Vergangeheit (bis einschließlich Release 1.13) wurde dabei auf die Komponenten LinkHandler und FrontcontrollerLinkHandler gesetzt, die statische Methoden zur Generierung einer neuen und Manipulation einer bestehenden URL angeboten haben. Die Manipulation einer bestehenden URL ist dabei die Grundlade der losen Kopplung von Software-Komponenten einer Webseiten-Lösung. Verändert jede Komponente nur "ihre" Parameter der URL und behält die übrigen bei, so können gleichzeitig mehrere Elemente einer Webseite gesteuert werden.

Nachteil dieser statischen Vorgehensweise ist, dass das URL-Layout und die Behandlung von URL-Bestandteilen für alle Komponenten identisch gehalten sein mussen. Damit ist keine effektive Trennung zwischen URL-Layout und Software möglich, wie es das Filter-Konzept des APF vorsieht.

Aus diesem Grund wurde der Link-Generierungs-Mechanismus im Release 1.14 einem Redesign unterzogen um die Implementierung von eigenen URL-Layouts (Beispiel siehe Wiki) auch für bestehenden Applikationen zu unterstützen.

2. Architektur

Die Architektur der Link-Generierung des APF ab Version 1.14 sieht eine Trennung zwischen Erzeugung einer URL und der URL selbst (Klasse Url) vor. Weiterhin steht mit dem LinkGenerator eine Komponente zur Verfügung, die sowohl "normale" URLs als auch solche mit Front-Controller-Action-Anweisungen generieren kann.

Die eigentliche "Arbeit" wird von einer LinkScheme-Implementierung verrichtet, die als Parameter eine vom Aufrufer parametrisierte URL entgegen nimmt und diese gemäß der Schema-Definition formatiert:

Link-Generierung mit dem APF

2.1. Url

Die Klasse Url repräsentiert eine beliebige URL gemäß RFC 1630 und ist unabhängig von der später vorgenommenen Formatierung. Dies ermöglicht es, über die Klasse jedes beliebige Link-Format abzubilden, bzw. aus einer "gewöhnlichen" URL zu generieren.

Die Klasse hat folgende Struktur:

PHP-Code
final class Url { const DEFAULT_HTTP_PORT = '80'; const DEFAULT_HTTPS_PORT = '443'; private $scheme; private $host; private $port; private $path; private $query = array(); public function __construct($scheme, $host, $port, $path, array $query = array()) { $this->scheme = $scheme; $this->host = $host; $this->port = $port; $this->path = $path; $this->query = $query; } ... }

Die in der Klasse enthaltenen Felder haben folgende Bedeutung:

Schema Host Port Pfad Query
http:// example.com :80 /news-archive/2011 ?id=123&print=true

Sowohl im Fall von "normalen" als auch rewrite URLs können die relevanten Steuer-Parameter ("Query") definiert werden, denn die Formatierung der URL ist von von der reinen Daten-Definition unabhängig.

2.2. LinkGenerator

Die Klasse LinkGenerator ist für die Koordination der Link-Formatierung zuständig. Sie delegiert die eigentliche Arbeit an das aktuell konfigurierte oder übergebene LinkScheme.

Im Wesentlichen dient der LinkGenerator also als Abstraktions-Komponente der Link-Generierung. Wird sie in jeder Software-Komponente eingesetzt (wie das in den Komponente des APF der Fall ist) lässt sich durch Austausch des Link-Schema ein on-the-fly Anpassung des URL-Layouts erwirken.

Der LinkGenerator hat folgende Gestalt:

PHP-Code
final class LinkGenerator { private static $LINK_SCHEME; ... public static function setLinkScheme(LinkScheme $linkScheme) { self::$LINK_SCHEME = $linkScheme; } ... public static function generateUrl(Url $url, LinkScheme $scheme = null) { return ... } public static function generateActionUrl(Url $url, $namespace, $name, array $params = array(), LinkScheme $scheme = null) { return ... } }

Wie bereits in der Einleitung angesprochen, besitzt der LinkGenerator eine Methode zur Generierung von expliziten Action-URLs (z.B. für die direkte Adressierung von Front-Controller-Actions).

Die Methode generateActionUrl() ist dazu gedacht, eine explizite Adressierung einer Action zu erzeugen. Actions, die aus Gründen der Funktion immer in der URL beibehalten werden sollen, werden von den mitgelieferten Link-Schemen bereits automatisch in die generierte URL eingebettet. Details zur Konfiguration können Kapitel 2.4.3 der Front-Controller-Dokumentation entnommen werden.

2.3. LinkScheme

Das Interface LinkScheme definiert das Aussehen einer konkreten Link-Formatierungs-Mechanik. Jede Implementierung muss in der Lage sein, aus einer Url oder einer Url und Angaben zu einer Front-Controller-Action eine gemäß dem gewünschten Layout formatierte URL zu generieren.

Mit anderen Worten: ein LinkScheme repräsentiert hinsichtlich der Generierung von Links das URL-Layout. Dabei ist zu beachten, dass ein URL-Layout nur in Verbindung mit einem geeigneten Input-Filter auch komplett funktionsfähig ist, da es nicht nur Aufgabe ist URLs zu generieren, sondern diese auch hinsichtlich ihrer Bedeutung zu interpretieren.

Das Interface hat folgende Struktur:

PHP-Code
interface LinkScheme { public function formatLink(Url $url); public function formatActionLink(Url $url, $namespace, $name, array $params = array()); public function setEncodeAmpersands($encode); public function getEncodeAmpersands(); }

Die Methode formatLink() ist für die Generierung von "normalen" URLs gedacht, formatActionLink() für Front-Controller-URLs. Die beiden Methoden setEncodeAmpersands() und getEncodeAmpersands() dienen im wesentlichen zur Konfiguration des Link-Schema.

In der aktuell vorliegenden Version liefert der APF zwei Implementierungen des Interfaces mit: DefaultLinkScheme und RewriteLinkScheme. Ersteres ist für die Formatierung von "normalen" URLs zuständig, letzteres kümmerst sich um umgeschriebene URLs gemäß der Layout Definition im Kapitel Rewrite-URLs in der Filter-Dokumentation bzw. URL-Layout in der Front-Controller-Dokumentation.

3. Anwendung

Die folgenden Kapitel beschreiben die Anwendung der mitgelieferten Komponenten und Link-Schemen.

3.1. Erzeugung von URL-Repräsentationen

Zur Generierung einer URL wird wie an der Signatur der Methode LinkGenerator::generateUrl() eine Instanz der Klasse Url benötigt. Um die Arbeit zu erweichtern gibt es mehrere Möglichkeiten:

PHP-Code
// Erzeugen einer relativen URL aus der Aktuellen $url = Url::fromCurrent(); // Erzeugen einer absoluten URL aus der Aktuellen $url = Url::fromCurrent(true); // Erzeugen einer relativen URL aus der Referer-URL $url = Url::fromReferer(); // Erzeugen einer absoluten URL aus der Referer-URL $url = Url::fromReferer(true); // Erzeugen einer URL aus einer beliebigen Zeichenfolge $url = Url::fromString('http://example.com/pages/news'); $url = Url::fromString('http://example.com/pages/news?page=3'); $url = Url::fromString('/pages/news?page=3'); $url = Url::fromString('?page=news'); // Erzeugen einer URL über den Konstruktor $url = new Url('http', 'example.com', null, '/pages/news', array()); $url = new Url('http', 'example.com', null, '/pages/news', array('page' => 3)); $url = new Url(null, null, null, '/pages/news', array('page' => 3)); $url = new Url(null, null, null, null, array('page' => 3));

Nach der Erzeugung der URL-Repräsentation kann diese in beliebiger Weise mit dem Methoden

  • setScheme()
  • setHost()
  • setPort()
  • setPath()
  • setQuery()
  • mergeQuery()
  • setQueryParameter()
  • resetQuery()

manipuliert werden.

Der übliche Anwendungsfall der Link-Generierung besteht in der Manipulation einer bestehenden URL. Hierzu kann eine aktuelle Url mit neuen Parametern versorgt und dann über den LinkGenerator formatiert werden. Dies ist wie folgt machbar:

PHP-Code
// Ergänzen bzw. Überschreiben der aktuellen Parameter über ein assoziatives Array $link = LinkGenerator::generateUrl(Url::fromCurrent()->mergeQuery(array( 'page' => 5, 'print' => 'true'))); // Ergänzen bzw. Überschreiben der aktuellen Parameter über einzelne Statements $link = LinkGenerator::generateUrl(Url::fromCurrent() ->setQueryParameter('page', 5) ->setQueryParameter('print', 'true'));
Die Klasse Url unterstützt für alle setter- und factory-Methoden ein fluent interface. Damit lässt sich eine Konfiguration sehr einfach aus verschiedenen Aufrufen kombinieren.

Darüber hinaus können in den beiden Beispielen ebenso die im vorangegangenen Kapitel gezeigten Methoden der Erzeugung genutzt werden, da das Ergebnis immer eine Instanz der Klasse Url ist. Anschließend lassen sich die Parameter sowie die übrigen Eigenschaften wie folgt manipulieren:

PHP-Code
$link = LinkGenerator::generateUrl(Url::fromString('/pages/news?page=3&print=true') ->setScheme('https') ->setHost('example.com') ->setQueryParameter('page', 4) ->setQueryParameter('print', null)); $link = LinkGenerator::generateUrl(Url::fromString('/pages/news?page=3&print=true') ->setScheme('https') ->setHost('example.com') ->mergeQuery(array('page' => 4, 'print' => null)));

Im Fall des DefaultLinkScheme ist das Ergebnis in beiden Fällen folgende URL:

Code
https://example.com/pages/news?page=4
Die mit dem APF mitgelieferten Link-Schemen interpretieren den Wert null als Marker, dass der Parameter, dem dieser Wert zugewiesen wurde, entfernt wird. Da eine solche Auslegung einer LinkScheme-Implementierung ist, gilt diese Aussage nicht im Allgemeinen!

Ein weiterer Anwendungsfall ist die Generierung eines Links über ein explizites Link-Schema. Dies ist insbesondere dann sinnvoll, wenn es sich um eine spezielle URL handelt, die nicht mit dem Standard-Schema generiert werden kann (z.B. AJAX-URLs) oder darf (z.B. externe URLs). In diesem Fall kann der Methoden generateUrl() das gewünschte Schema mitgegeben werden:

PHP-Code
$ajaxUrl = LinkGenerator::generateUrl(Url::fromString('news.php') ->setQueryParameter('page', 4), new AjaxLinkScheme());

Die hier gewählte Vorgehensweise initialisiert ein neues Link-Schema. Darüber hinaus ist es ebenfalls möglich, ein bestehendes Schema zu konfigurieren und dieses für einen definierten Anwendungsfall einzusetzen:

PHP-Code
$scheme = LinkGenerator::cloneLinkScheme(); $scheme->setSpecialParameter(true); $specialUrl = LinkGenerator::generateUrl(Url::fromCurrent() ->setQueryParameter('page', 4), $scheme);

Front-Controller-Actions besitzten ein Flag, das anzeigt, ob die Repräsentation einer Action in der zu generierenden URL eingebunden werden soll oder nicht. Dies ist - wie im Kapitel 2.4.3 beschrieben - eine technische Möglichkeit, die Entkopplung von Software-Komponenten über die URL auch für Action-Anweisungen zu realisieren. Für den Entwickler ist diese Möglichkeit insbesondere dann wichtig, wenn eine Action zur Steuerung einer Applikation dauerhaft in der URL bestehen bleiben soll, die innerhalb der Applikation ausgeführten Teile jedoch davon keine Kenntnis besitzen dürfen.

Die im letzten Absatz beschriebene Funktionalität wird bereits von den mit dem APF ausgelieferten Link-Schemen abgedeckt. Sofern eigene Link-Schemen implementiert werden und die aufgezeigte Funktionalität erhalten bleiben soll, muss das jeweilige Link-Schema für die Inkludierung selbst sorgen!

In diesem Kapitel geht es jedoch primär um Links, die dedizierte, nicht als permanent in der URL vorhanden markierte Actions adressieren. Hierzu steht die Methode LinkGenerator::generateActionUrl() zur Verfügung. Diese kann Links auf eine Action an Hand einer Basis-URL, einem Namespace, dem Action-Namen und einem Satz an Parametern generieren:

PHP-Code
$url = LinkGenerator::generateActionUrl(Url::fromString('/pages/news'), 'tools::media', 'streamMedia', array( 'namespace' => 'modules_usermanagement_pres_images', 'extension' => 'png', 'filebody' => 'icon_delete' ) );

Die über den gezeigten Aufruf generierte URL setzt sich aus einer Basis-URL - in diesem Beispiel aus einem String erzeugt - und einer zusätzlichen Action-Adressierung zusammen. Die anschließend aufgeführte Code-Box zeigt das Ergebnis der Formatierung über das RewriteLinkScheme:

Code
/pages/news/~/tools_media-action/streamMedia/namespace/modules_usermanagement_pres_images/extension/png/filebody/icon_delete

Das Ergebnis für die Nutzung des DefaultLinkScheme gestaltet sich wie folgt:

Code
/pages/news?tools_media-action:streamMedia=namespace:modules_usermanagement_pres_images|extension:png|filebody:icon_delete

Sollte die Basis-URL bereits eine hinsichtlich der URL permanente Action-Anweisung enthalten, wird die oben übergebene am Ende hinzugefügt. Der Unterschied zur Generierung von "normalen" URLs besteht im Wesentlichen darin, dass die Eigenschaften der Action als eigenständige Parameter mitgegeben werden.

Darüber hinaus ist es natürlich möglich, dass die Generierung der Action-URL mit einer angepassten Basis-URL ausgeführt wird. Hierzu können die in Kapitel 3.2 beschriebenen Methoden genutzt werden.

Analog zur Methode LinkGenerator::generateUrl() kann auch der Methode LinkGenerator::generateActionUrl() ein explizites Link-Schema mitgegeben werden:

PHP-Code
$url = LinkGenerator::generateActionUrl(Url::fromString('/pages/news'), 'tools::media', 'streamMedia', array( 'namespace' => 'modules_usermanagement_pres_images', 'extension' => 'png', 'filebody' => 'icon_delete' ), new SpecialLinkScheme() );

Wie in den vorangegangenen Kapiteln bereits beschrieben, liefert das APF die beiden Schemen DefaultLinkScheme und RewriteLinkScheme mit. Diese implementieren das LinkScheme-Interface und können für einige Anwendungsfälle konfiguriert werden.

Aktuell beschränken sich die Konfigurations-Einstellungen auf die Aktivierung bzw. Deaktivierung der Codierung des kaufmännischen Und-Zeichens (&). Ist es beispielsweise nicht erwünscht, das Und-Zeichen zu escapen, können folgende Maßnahmen ergriffen werden:

PHP-Code
// Angepasstes Link-Schema für dedizierte Generierung nutzen $scheme = LinkGenerator::cloneLinkScheme(); $scheme->setEncodeAmpersands(true); $url = LinkGenerator::generateUrl(..., $scheme); // Globale Änderung des Verhaltens $scheme = LinkGenerator::getLinkScheme(); $scheme->setEncodeAmpersands(true); LinkGenerator::getLinkScheme($scheme); $url = LinkGenerator::generateUrl(...);

Sofern eigene Link-Schemen im Einsatz sind, kann diese Vorgehensweise gleichermaßen Anwendung finden.

4. Erweiterung

Das oben beschrieben Konzept der Link-Generierung ist nicht nur auf Flexibilität, sondern vor Allem auf Erweiterbarkeit ausgelegt. Durch die Trennung von LinkGenerator und LinkScheme kann einer Applikation ein völlig anderes Link-Scheme injiziert werden ohne die Funktionalität derselben zu verändern.

Um dieses Verhalten zu gewährleisten, ist es wichtig, dass die Applikation stets auf einem allgemeingültigen URL-Layout arbeitet, das vom Link-Scheme in die jeweilige externe Repräsentation transformiert werden kann.

Die folgenden Kapitel beschrieben die Erweiterung des Link-Generierungs-Mechanismus des APF.

Ein Link-Schema ist durch das Interface LinkScheme definiert. Zur Realisierung eines eigenen Schema muss der Entwickler die Methoden formatLink() und formatActionLink() sowie setEncodeAmpersands() und getEncodeAmpersands() implementieren.

Aufgabe der beiden zuerst genannten Methoden ist es, das vereinbarte Layout aus der übergebenen URL-Abstraktion zu generieren. Das folgende Code-Beispiel zeigt eine Implementierung, die den Seiten-Identifier als Pfad abbildet und die restlichen Parameter als "echte" Request-Parameter anhängt. Beispiel:

Code
/news?page=2

Der zugehörige Code sieht folgendes vor:

PHP-Code
class SpecialLinkScheme implements LinkScheme { private $encodeAmpersands = true; public function __construct($encodeAmpersands = true) { $this->encodeAmpersands = $encodeAmpersands; } public function formatLink(Url $url) { $ampersand = $this->encodeAmpersands ? '&' : '&'; $link = '/' . $url->getQueryParameter('page'); $params = array(); foreach ($url->getQuery() as $name => $value) { if (!empty($value)) { $params[] = $name . '=' . $value; } } if (count($params) > 0) { $link .= '?' . implode($ampersand, $params); } return $link; } public function formatActionLink(Url $url, $namespace, $name, array $params = array()) { $link = $this->formatLink($url); $link .= strpos('?') !== false ? '&' : '?'; $link .= str_replace('::', '_', $namespace) . '-action:' . $name; $actionParams = array(); foreach ($params as $name => $value) { if (!empty($value)) { $actionParams[] = $name . ':' . $value; } } if (count($actionParams) > 0) { $link .= '=' . implode('|', $actionParams); } return $link; } public function getEncodeAmpersands() { return $this->encodeAmpersands; } public function setEncodeAmpersands($encodeAmpersands) { $this->encodeAmpersands = $encodeAmpersands; } }

Die beschriebene Implementierung ist ausdrücklich als Beispiel zu verstehen und besitzt folgende Einschränkungen:

  • Mit $keepInUrl = true gekennzeichnete Actions werden nicht automatisch in die URL aufgenommen.
  • Es wird aus Gründen der Einfachheit auf eine explizite Prüfung der Existenz des Parameters page verzichtet.
  • In der URL verwendete Angaben zu Schema, Host, Port und Pfad werden ignoriert.
  • Das Link-Schema ist nur für "normale" URLs einsetzbar und bedarf eines korrespondierenden Input-Filters zur Auflösung des Layouts.

Um vorhandene oder eigene Link-Schemen global konfigurieren zu können, ist folgendes in der Bootstrap-Datei notwendig:

PHP-Code
// Globale Konfiguration eines bestehenden Link-Schema include('../apps/core/pagecontroller/pagecontroller.php'); import('tools::link', 'LinkGenerator'); LinkGenerator::setLinkScheme(new RewriteLinkScheme(true)); // Globale Konfiguration eines eigenen Link-Schema include('../apps/core/pagecontroller/pagecontroller.php'); import('tools::link', 'LinkGenerator'); import('...', 'SpecialLinkScheme'); LinkGenerator::setLinkScheme(new SpecialLinkScheme());

Wird keine Konfiguration in der Bootstrap-Datei vorgenommen, so wird das DefaultLinkScheme verwendet, solange der Registry-Key URLRewriting aus dem Namespace apf::core den Wert false besitzt. Ist das URL-Rewriting aktiviert, so kommt das RewriteLinkScheme zum Einsatz.

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.
Für diesen Artikel liegen aktuell keine Kommentare vor.