Kann man XSL als richtiges Templatesystem verwenden? Diese Frage stellte sich mir, nachdem ich vor einigen Tagen erst in der Vorlesung davor hörte und mich danach näher damit beschäftigte. Und: Wenn man es kann, ist es dann effizienter oder gar ganz schlecht perfomant gegenüber ein normales PHP-Script oder ein Smarty-Pendant?

Um zu testen, wie es sich mit der Performance verhält, stellte ich kleines Szenario auf: Der hauptsächliche Teil besteht daraus 1000 Einträge mit den Ausprägungen Name, E-Mail, ICQ, Website, etc. aufzustellen. Dabei ist Name einer von 3 unterschiedlichen, damit man eine bedingte Anzeige im Template realisieren kann.

PHP der Ameisenklasse:

class DataStressTest {
  private static $if = null;
  public static function init( $if ) {
    self::$if = $if;
    self::stress();
  }

  private static function stress() {
    $sections = array(
      array(
        'header'=>'Gummibärchen',
        'content'=>'..'
      ),
      array(
        'header'=>'Autobahnwahn',
        'content'=>'..'
      ),
      array(
        'header'=>'Copy for Dummys',
        'content'=>'..'
      )
    );

    $websites = array(
      'www.microsoft.com',
      'www.google.com',
      'pentagon.mil',
      'web.de',
      'gmx.de',
      'ask.com',
      'knallisworld.de'
    );

    $results = array();
    $names = array('John','Mary','James');

    for ($i = 0; $i < 1000; $i++) {
      $results[] = array(
        'id' => mt_rand(1,1000),
        'name' => $names[array_rand($names)],
        'mail' => 'john@wayne.de'.mt_rand(1,1000),
        'icq'=> mt_rand(1000,10000),
        'hobby' => 'schnucken',
        'homepage' => $websites[array_rand($websites)]
      );
  }

  //assignment
  self::$if->assign('sections', $sections);
  self::$if->assign('members', $results);

  self::$if->assign('title', 'Performance Stress Test by knalli');
  }
}

Varianten

Szenario 1: Normales PHP-Template

Es wird ganz regulär eine PHP-Datei per Include eingebunden, die ausnahmslos nur Platzhalter und Ausgabelogik enthält. Das bedeutet, es befinden sich in der Regel nur for/foreach, if und echo Konstrukte. Zusätzlich können besondere Ausgabefunktionen eingebunden werden, wie zB utf8_encode().

Szenario 2: Smarty-Template

Es wird ein entsprechendes Smarty-Template genutzt; das generierte Template seitens Smarty entspricht so grob dem Szenario 1.

Szenario 3: XML-Generierung und XSL-Processing

Zunächst wird mit DOMDocument von PHP5 das DatenXML aufgebaut; wir sehen später noch, das DOMDocument::createElement die Hauptarbeit machen wird. Anschließend geschieht das Processing.

Die Ergebnisse

Mittels Advanced PHP Debugger loggte ich alle Varianten mit. Die übliche Methode, mit PHP::microtime() die Sekunden zu zählen, bringt weniger Informationen über die echte Laufzeit und zeigt keine Informationen über die internen Laufzeiten.

Szenario 1: Normales PHP-Template

Total Elapsed Time = 1.54
Total System Time  = 0.13
Total User Time    = 0.50

Szenario 2: Smarty-Template

Total Elapsed Time = 1.85
Total System Time  = 0.17
Total User Time    = 0.59

Szenario 3: XML-Generierung und XSL-Processing

Total Elapsed Time = 4.71
Total System Time  = 0.40
Total User Time    = 1.36

Nun, dieses Ergebnis war niederschmetternd. Während die Differenz zwischen den ersten beiden Szenarien auch so erwartet wurden, war das dritte einfach nur schlecht.

Nun, wie ich bereits sagte, verwendete ich den APD; dadurch kann man u.a. auch sehen, welche Funktionen und Methoden wie oft aufgerufen und – vor allem – wie viel Zeit beansprucht haben. Und siehe da, das problem lag gar nicht am XSLTProcessor, sondern an den DOMDocument::createElement() und DOMDocument::appendChild() Methoden, die nämlich zusammen bereits 50% ausmachten – neben den anderen DOMDocument-Methoden und PHP-Funktionen des Scripts.

Also was tun? Da es derzeit scheinbar keine Möglichkeit gibt, mit einem OOP-Ansatz ein XML-Dokument zu erstellen, ergriff ich eine andere Alternative, die wesentlich einfacher und effizienter ist. Man erstellt einfach Strings mit den XML-Tags, lädt diesen String anschließend als DOMDocument-Objekt durch den Processor.. und siehe da:

Total Elapsed Time = 1.19
Total System Time  = 0.12
Total User Time    = 0.41

Anmerkung: Die Zeiten sind kleiner, weil ein Randomize fehlt; ich Depp hab das wohl verschlampt. Die Zeiten sind natürlich (geringfügig) größer als die von “Nur-PHP”. Das korrekte Ergebnis liefere ich nach.

Und? Was sagt uns das?

XSL als Template-System ist eine Alternative. Das Processing selber ist auf keinen Fall langsamer als ein entsprechendes Smarty-Pendant. Man kann natürlich mit einem Accelerator die PHP-Files beschleunigen – gleichzeitig bestehen bei XSL-Processing die Möglichkeiten, einzelne Teilbäume aka Bereiche zu cachen. Und es sei nicht zu vergessen: Wenn irgendwann alle Browser XSL können, kann man das Transformieren auf den Client schieben.. und das ist auf jeden Fall am schnellsten!

Gleichzeitig muss man natürlich auch abwiegen, wie viel die einzelnen Möglichkeiten “können”.

XSL greift mittels XPath auf die XML-Daten zu, d.h. alles basiert auf der Graphentheorie und mit der dazugehörigen Funktionssemantik (Eltern, Kinder, Anzahl Kinder, letztes Kind, usw..). Durch zahlreiche Eigenschaften und entsprechende Funktionen besteht ein durchaus umfangreicher Funktionsschatz. Zusätzlich lassen sich, sofern man Serverside Processing verwendet, die PHP-Funktionen in das XSL einbinden.

Eins ist sicher: Mittels XSL hat man folgende, fast unschlagbaren Argumente:

  • mindestens wohlgeformten XHTML-Code, sofern man im Template einmal validen XHTML-Code hat, ist es immer valid
  • wirkliche Trennung von Daten (XML), Layout (XSL) und Design (CSS)
  • theoretische Transformierung auf dem Client

Im Vergleich: Smarty funktioniert erstens nur auf dem Server und ist des Weiteren ein Interpreter des Smarty-Templates, welches mit dem PHP-Interpreter aufgerufen wird.. kurz gesagt: Eigentlich sinnlos.