XSL / XSLT als Templatesystem

Bekanntlich setzt das papaya CMS bereits seit Version 1 (um 2000) auf XSL / XSLT als Templatesystem. Auch wenn der Standard schon viele Jahre besteht und z.B. im professionellen Umfeld (große Softwareprojekte, insbesondere im Java Bereich) eine große Wertschätzung und Verbreitung genießt, so ist die Wahrnehmung bei vielen PHP Entwicklern leider noch anders. Und das obwohl XSL / XSLT ein offener Standard ist, der die vielbeschworene Trennung von Logik, Inhalt und Layout sauber implementiert und der nicht zuletzt beliebige Ausgabeformate (HTML, XML, PDF u.v.m.) ermöglicht.

Umso erfreulicher ist es immer, wenn die mannigfaltigen Vorteile gegenüber proprietären Script- und Templatesprachen die Runde machen – wie aktuell in einem lesenswerten Beitrag von phpmonkeys.de.

Fazit der Autoren:

Der Vorteil von XSL ist, dass man dieses Verfahren mit jeder Programmiersprache einsetzen kann, die einen XSLTProzessor mitbringt. Und dabei können Template- und Layout-Dateien ohne Aufwand migriert werden. Man benötigt nur eine ähnliche Implementierung wie der HTMLGenerator und Logik, die die XML-Daten generiert.

…das völlig korrekt – neben weiteren Vorteilen:

  • offener Standard des W3C, keine proprietäre Lösung
  • in die Einarbeitung investierte Zeit ist nicht auf ein Produkt begrenzt, sondern kann in verschiedenen Projekten angewandt werden
  • hervorragend dokumentierter Standard, viel Literatur verfügbar
  • hohe Verbreitung in der Java-Welt
  • mächtiges Werkzeug/Programmiersprache
  • gut nutzbar für Internationalisierung der Ausgabe
  • und viele, viele mehr… 😉

Kurznotiz: XSL Cache leeren

Bei Projekten mit intensiver Nutzung von XSL bzw. XSLT macht die Verwendung des XSL Bytecode Caches sehr viel Sinn, siehe dazu auch diese sehr umfassende Erklärung meines Kollegen Max.

Wichtig ist aber auch, wie sich der Cache hier verhält – und das ist bei der NY Times, welche die Extension „XSLCache“ entwickelt hat, nachzulesen:

Neulich hatten wir die Aufgabe, in einem Projekt den XSL Cache zu „refreshen“ – ohne jedoch die Webserver neu starten zu wollen. Es gibt da eine sehr simple und schnelle Lösung (Doku hier bei der NY Times):

The XSL cache uses the modification time (mtime) attribute of stylesheets to figure out if a file is newer than the cached version and needs to be updated. There are several cases where a file might change but the modification time remains old that you should avoid if you want the XSL cache to reload changed stylesheets correctly:

tar and rsync/scp can sometimes preserve the modification time of the archived/copied file. You must be careful about the following:

  • tar, use the -m flag
  • rsync, avoid using the -t/–times flag
  • scp, avoid using the -p flag
  • Be careful about radically adjusting the system clock and modifying XSL files. Similarly, clock skew between web servers and NFS shares may create issues.
  • If all else fails, use touch to clear a stylesheet from the cache, by resetting its last modified timestamp.

In addition, this early release of XSL Cache does not use shared memory, so each PHP process/thread will have its own copy of the stylesheet. Although this is admittedly wasteful, the memory cost does not seem to be costly. However, if you use the cachesheet argument to clear a cached stylesheet from one web server process, it may still be cached in other processes.

(Quelle: http://code.nytimes.com/projects/xslcache/wiki/StaleCaches)

papaya Entwicklung: XSL Templates für Content Module

In einigen Fällen stellt sich dem Template-Programmierer die Frage, wie die Inhalte unter /page/content/topic ausgegeben werden sollen. Grundsätzlich stehen drei verschiedene Wege zur Verfügung:

  • <xsl:apply-templates> oder <xsl:call-template>
  • <xsl:value-of>
  • <xsl:copy-of>

Die erste Variante führt weitere Templates auf, die den Teilbaum in /page/content/topic weiterverarbeiten, wobei man mit call-template direkt ein bestimmtes Template aufrufen kann. Mit value-of gibt man lediglich alle Textknoten in die Ausgabe aus, die im referenzierten Teilbaum enthalten sind, während copy-of den gesamten Teilbaum in die Ausgabe kopiert.

Wann nimmt man was?

Welche Variante sollte man denn nun benutzen? Alle Methoden haben ihre Vor- und Nachteile.

1. value-of

Mit value-of erzeugt man sichere Ausgaben, da alle Formatierungen aus Texteingabefeldern entfernt werden. Besonders bei user-generiertem Content ist dies eine sehr einfache Methode, um Manipulationen zu verhindern. Auch Inhalte, die im Backend eingegeben werden, können auf diese Weise sauber gehalten werden.

Der Nachteil dieser Methode ist jedoch, dass man sie nicht überall einsetzen kann, wo man Formatierungen wie <h2>, <em>, <strong> usw. einsetzen will. Auch Hyperlinks gehen natürlich verloren. Für umfangreiche strukturierte Inhalte sollte man also diesen Weg nicht gehen.

2. copy-of

copy-of hat den Vorteil, dass alle Formatierungen und inhaltlichen Strukturierungen (<h1-6>) mit in die Ausgabe übernommen werden, genauso wie Hyperlinks, was durchaus erwünscht sein kann. Schließlich möchte man als Redakteur Artikel schreiben, die in Absätzen strukturiert sind, Zwischenüberschriften oder Links zu anderen Ressourcen enthalten.

Der Nachteil dieser Methode ist, dass man die Kontrolle über den Teilbaum aus den Händen gibt. Er wird ja einfach in die Ausgabe kopiert. Daher sollte man diese Anweisung auch nicht für user-generierten Content benutzen, der bösen Code enthalten könnte.

“Böser Code” kann zum Teil auch durch Sonderzeichenmaskierung (=Escaping) in der Methode getParsedData() in der Content-Modul-Klasse entschärft werden, indem man bspw. papaya_strings::escapeHTMLChars($myStringVar) benutzt (siehe auch $this->getXHTMLString()).

3. apply-templates oder call-template

Mit apply-templates kann man komplexe Strukturen innerhalb von /page/content/topic an zusätzliche XSLT-Templates zur Verarbeitung weiterdelegieren. Die Anweisung sucht dabei ein Template, dessen match-Muster am besten zum jeweiligen Element in der Knotenmenge des Kontextknotens passt. Für jeden Knoten im Kontextknoten wird also ein Template anzuwenden versucht. Falls es kein passendes Template gibt, wird einfach der entsprechende Textknoten ausgegeben. Wenn das select-Attribut benutzt wird, versucht XSLT, ein Template für jeden in select definierten Knotenmengenausdruck anzuwenden.

Diese Anweisung hat den Nachteil, dass sie nicht besonders performant ist, da der gesamte Teilbaum durchsucht wird. XSLT versucht dabei, für jeden Knoten das richtige Tempate zu finden.

Nach Möglichkeit sollte man also Templates direkt auswählen, indem man call-template benutzt und den gewünschten Teilbaum des Kontextknotens als Parameter übergibt. Auf diese Weise hat man auch die volle Kontrolle darüber, welches Template benutzt wird und welche Transformation angewendet werden soll.

Bei den aufzurufenden Templates steht man natürlich wieder vor der Wahl, ob man jetzt value-of oder copy-of benutzen soll. Wenn es sich um Inhalte handelt, die man im Backend in einzeilige Textfelder eingibt, wird man mit Sicherheit value-of benutzen, bei mehrzeiligen Textfeldern, die auch HTML-Eingabe erlauben, muss man auch copy-of benutzen, damit der HTML-Code auch in der Ausgabe landet. Um die Eingabe von HTML-Code oder sonstiges zu verhindern, sollte man die Content-Module so schreiben, dass entsprechende Validitätschecks bereits während der Eingabe im Backend den Inhalt überprüfen. Validitätschecks sollten also nicht die Aufgabe des Template-Programmierers sein.

(Credits: Text entstammt dem internen papaya-Blog und wurde von Max verfasst.)