Objekte mit dem XML_Transformer ins Web bringen

Der XML_Transformer verfolgt die Lösung zweier Aufgaben, die nur auf den ersten Blick nichts miteinander zu tun haben. Zum einen suchen viele Entwickler nach einem eleganten Weg, um PHP-Objekte "ins Web zu bringen". Zum anderen ist die Transformation von XML-Daten aus einem Format in ein anderes bei der Entwicklung von Web-Applikationen allgegenwärtig.

Im Gegensatz zu den XSL Transformations, die eines der oben genannten Teilprobleme lösen, erlaubt der XML_Transformer die prozedurale Definition von XML-Transformationen. Hierbei können sämtliche Fähigkeiten von PHP, wie beispielsweise der Zugriff auf Datenbanken oder die Erzeugung von Grafiken, genutzt werden. Das Ergebnis eines Zwischenschritts kann im Gegensatz zu XSLT direkt als Eingabe für den nächsten Schritt dienen.

Abbildung 8.5. Installation von XML_Transformer

pear install --alldeps XML_Transformer


Den XML_Transformer verwenden

Im Wesentlichen besteht die Kodierung mit dem XML_Transformer aus der Nutzung vorhandener Namespace-Handler oder dem Schreiben eigener. In Beispiel 8.24 kommt der im Paket enthaltene Image Namespace-Handler zum Tragen. Dieser bietet unter anderem ein "intelligentes" <img />-Element, das die width- und height-Attribute automatisch auf Basis der per getimagesize() erkannten Bildinformationen setzt.

Hierzu werden zunächst die Klassen XML_Transformer und XML_Transformer_Namespace_Image geladen. Danach wird ein Objekt der Klasse XML_Transformer erzeugt, der img-Namespace wird im Konstruktor an die Klasse XML_Transformer_Namespace_Image gebunden. Die eigentliche Transformation wird von der Methode transform geleistet, der als einziger Parameter die XML-Eingabe als String übergeben wird.

Beispiel 8.24: Intelligentes <img />-Tag mit dem Image-Namespace-Handler

<?php
require_once 'XML/Transformer.php';
require_once 'XML/Transformer/Namespace/Image.php';
 
$t = new XML_Transformer(
  array(
    'debug'                => TRUE,
    'overloadedNamespaces' => array(
      'img' => new XML_Transformer_Namespace_Image
    )
  )
);
 
print $t->transform('<img:img src="image.png" />');
?>
<img height="42" src="image.png" width="23" />


Für das Setzen von Parametern und die Definition von Transformationen hält die Klasse XML_Transformer entsprechende Methoden bereit. So wird in Beispiel 8.25 zunächst ein Objekt der Klasse XML_Transformer erzeugt, für das im Anschluss der Debugmodus aktiviert und die Transformationen des Image-Namespace-Handlers geladen werden.

Parameter und Transformationen lassen sich auch zum Zeitpunkt der Objekterzeugung über den Konstruktor der Klasse XML_Transformer setzen (siehe Beispiel 8.24).

Beispiel 8.25: Setzen von Parametern mit den Methoden der Klasse XML_Transformer

<?php
require_once 'XML/Transformer.php';
require_once 'XML/Transformer/Namespace/Image.php';
 
$t = new XML_Transformer;
 
$t->setDebug(TRUE);
 
$t->overloadNamespace(
  'img',
  new XML_Transformer_Namespace_Image
);
?>


Eine XML_Transformer-Treiberklasse verwenden

Neben der direkten Verwendung der Klasse XML_Transformer und der Transformation der XML-Eingabe über deren transform-Methode bietet das Paket so genannte Treiberklassen an, die das Standardverhalten erweitern. Beispiel 8.26 zeigt die Verwendung der Treiberklasse XML_Transformer_Driver_OutputBuffer. Sie benutzt den Output-Buffering-Mechanismus von PHP, um die gesamte Ausgabe des Skriptes, in dem sie verwendet wird, zu puffern und dann als XML-Eingabedokument zu benutzen.

Beispiel 8.26: Verwendung des Output-Buffer-Treibers

<?php
require_once 'XML/Transformer/Driver/OutputBuffer.php';
require_once 'XML/Transformer/Namespace/Image.php';
 
$t = new XML_Transformer_Driver_OutputBuffer(
  array(
    'overloadedNamespaces' => array(
      'img' => new XML_Transformer_Namespace_Image
    )
  )
);
?>
<img:img src="image.png" />
 
 


Eine weitere Treiberklasse ist XML_Transformer_Driver_Cache, die das Ergebnis der Transformation in einem Cache speichert. Zukünftige Transformationen für identische XML-Eingabe und die verwendeten Transformationen lassen sich kostensparend aus dem Cache bedienen.

Die mitgelieferten Namespace-Handler

Mit der Klasse XML_Transformer_Namespace_Image haben wir bereits einen der Namespace-Handler kennen gelernt, die zum XML-Transformer-Paket gehören. Hier nun ein Überblick über sämtliche mitgelieferten Namespace-Handler:

  • Anchor

    Der Anchor-Namespace-Handler stellt eine Reihe von Tags zur Verfügung, die normalerweise an den a-Namespace gebundene indirekte, benannte Links, so genannte URNs ermöglichen.

    Beispiel 8.27: Beispiel für die Verwendung des Anchor-Namespace-Handlers

    <?php
    require_once 'XML/Transformer/Driver/OutputBuffer.php';
    require_once 'XML/Transformer/Namespace/Anchor.php';
     
    $t = new XML_Transformer_Driver_OutputBuffer(
      array(
        'overloadedNamespaces' => array(
          'anchor' => new XML_Transformer_Namespace_Anchor
        )
      )
    );
    ?>
    <!-- Erzeugt einen neuen Eintrag -->
    <anchor:link name="php" href="http://www.php.net/"
      title="PHP Homepage" />
     
    <!-- Erzeugt ein HTML <a /> Tag -->
    <anchor:iref iref="php">The PHP Homepage</anchor:iref>
     
     
    <a href="http://www.php.net/" title="PHP Homepage">The PHP Homepage</a>


  • DocBook

    Der DocBook-Namespace-Handler bietet Transformationen für eine Untermenge von DocBook XML nach HTML an. Er stellt eine Beispielimplementierung für Namespace-Handler dar, deren Transformationen mehrere Durchläufe benötigen. Querverweise werden hierbei, wie auch bei LaTeX, in einem ersten Durchlauf gesammelt, um in einem anschließenden zweiten Durchlauf verarbeitet zu werden.

  • Image

    Neben dem bereits erwähnten "intelligenten" <img />-Tag bietet der Image-Namespace-Handler Tags für die dynamische Generierung von Grafiken an. Funktionalität und Verwendung orientieren sich hierbei am <gtext />-Tag der Roxen-Plattform.

    Beispiel 8.28: Beispiel für die Verwendung des <img:gtext /> Tags

    <?php
    require_once 'XML/Transformer/Driver/OutputBuffer.php';
    require_once 'XML/Transformer/Namespace/Image.php';
     
    $t = new XML_Transformer_Driver_OutputBuffer(
      array(
        'overloadedNamespaces' => array(
          'img' => new XML_Transformer_Namespace_Image
        )
      )
    );
    ?>
    <html>
      <body>
        <img:gtext bgcolor="#ffffff" fgcolor="#000000">
          Graphical Text
        </img:gtext>
      </body>
    </html>
     
     
    <html>
      <body>
        <img alt="Graphical Text"
             src="/cache/gtext/51445ca8cca0490794648630776939e5.png"
             width="113"
             height="16" />
      </body>
    </html>


    Der Image-Namespace-Handler benutzt einen Cache, um Grafiken für <img:gtext />-Tags mit den selben Attributen nicht für jeden Request neu erzeugen zu müssen.

  • PHP

    Der PHP-Namespace-Handler erlaubt unter anderem die Einbettung von PHP-Code direkt in ein XML-Dokument sowie die dynamische Deklaration neuer Transformationen.

    Beispiel 8.29: Beispiel für die Verwendung des PHP-Namespace-Handlers

    <?php
    require_once 'XML/Transformer_OutputBuffer.php';
    require_once 'XML/Transformer/Namespace/PHP.php';
     
    $t = new XML_Transformer_OutputBuffer(
      array(
        'overloadedNamespaces' => array(
          'php' => new XML_Transformer_Namespace_PHP
        )
      )
    );
    ?>
    <dl>
      <dd><php:expr>time()</php:expr></dd>
      <php:setvariable name="foo">bar</php:setvariable>
      <dd>foo = <php:getvariable name="foo"/></dd>
    </dl>
     
     
    <dl>
      <dd>1045740810</dd>
      <dd>foo = bar</dd>
    </dl>


  • Widget

    Der Widget-Namespace-Handler stellt unter anderem Transformationen für Tags ähnlich dem <obox />-Tag der Roxen-Plattform zur Verfügung.

Einen eigenen Namespace-Handler programmieren

Im Folgenden soll es um die Programmierung eigener Namespace-Handler gehen. Wie die vorangegangenen Anwendungsbeispiele gezeigt haben, bindet der XML_Transformer PHP-Funktionen an XML-Elemente, indem eine Klasse an einen Namensraum gebunden wird. Der Pseudo-Namespace &MAIN dient dazu, dies ohne assoziierten Namespace zu tun. Für jedes Element des mit ihr assoziierten Namensraums implementiert die PHP-Klasse, die sich von XML_Transformer_Namespace ableitet, zwei Methoden, die die Transformation beschreiben: start_ELEMENT($attributes) wird für das öffnende Tag des Elements mit den Attributen als Parameter, end_ELEMENT($cdata) wird dagegen für das schließende Tag des Elements mit den CDATA-Daten als Parameter aufgerufen.

Beide Methoden können (müssen aber nicht) ein XML-Fragment als String zurückgeben, das anstelle des gerade bearbeiteten Elementes in die Ausgabe eingefügt werden soll. Hierbei ist zu beachten, dass die zurückgegebenen Fragmente die Wohlgeformtheit des XML-Dokuments nicht verletzen.

Beispiel 8.30 zeigt mit der Klasse HelloWorld einen Namespace-Handler, der mit den Methoden start_helloWorld() und end_helloWorld() eine Transformation für das <helloWorld />-Tag zur Verfügung stellt.

Beispiel 8.30: Der "Hello World!"-Namespace-Handler

<?php
require_once 'XML/Transformer/Driver/OutputBuffer.php';
require_once 'XML/Transformer/Namespace.php';
 
class HelloWorld extends XML_Transformer_Namespace {
  function start_helloWorld($attributes) {
    return '<html><body>';
  }
 
  function end_helloWorld($cdata) {
    return $cdata . 'Hello World!</body></html>';
  }
}
 
$t = new XML_Transformer_Driver_OutputBuffer(
  array(
    'overloadedNamespaces' => array(
      '&MAIN' => new HelloWorld
    )
  )
);
?>
<helloWorld />
 
 
<html>
  <body>
    Hello World!
  </body>
</html>


Die Klasse XML_Util stellt Methoden zur Verfügung, die bei der Entwicklung von XML-verarbeitenden PHP-Anwendungen nützlich sein können. Für die Programmierung eigener Namespace-Handler für den XML_Transformer sind vor allem die beiden folgenden Methoden von Interesse:

  • attributesToString($attributes) wandelt ein Array von XML-Attributen, wie es beispielsweise die start_ELEMENT($attributes)-Methode als Parameter erwartet, in einen String um.

    Beispiel 8.31: Beispiel für die Verwendung von XML_Util::attributesToString

    <?php
    require_once 'XML/Util.php';
     
    print XML_Util::attributesToString(
      array(
        'width'  => 23,
        'height' => 42
      )
    );
    ?>
    height="42" width="23"


  • splitQualifiedName($element) spaltet den Namen eines XML-Elementes in die Bestandteile Namespace-Name und Element-Name auf und liefert als Ergebnis ein Array.

    Beispiel 8.32: Beispiel für die Verwendung von XML_Util::splitQualifiedName

    <?php
    require_once 'XML/Util.php';
     
    print_r(
      XML_Util::splitQualifiedName('img')
    );
     
    print_r(
      XML_Util::splitQualifiedName('img:img')
    );
    ?>
    Array
    (
        [0] => &MAIN
        [1] => img
    )
    Array
    (
        [0] => img
        [1] => img
    )


Bei der Entwicklung eines Namespace-Handlers sollte man stets bedenken, dass der XML_Transformer intern mit dem SAX-Parser arbeitet. So sind weder umordnende Transformationen noch die Erzeugung von Inhaltsverzeichnissen in einem Durchgang durchführbar. Sollte man solche Operationen dennoch benötigen, transformiert man die XML-Eingabe in zwei Durchgängen. Das Setzen des Attributs $secondPassRequired auf TRUE bedeutet, dass ein zweiter Durchlaufs benötigt wird.

Zur Unterstützung bei der Entwicklung neuer Namespace-Handler bietet der XML_Transformer einen Debug-Modus an. Dieser kann, wie in Beispiel 8.24 und Beispiel 8.25 gezeigt, sowohl über einen Parameter im Konstruktor der Klasse XML_Transformer als auch über die Methode setDebug(TRUE) aktiviert werden.

  • Für das öffnende Tag eines XML-Elementes werden die aktuelle Ebene des Elementstapels, der Name des Elementes sowie dessen Attribute protokolliert.

  • Für das schließende Tag eines XML-Elementes werden die aktuelle Ebene des Elementstapels, der Name des Elementes sowie der Inhalt dessen CDATA-Bereiches protokolliert.

Nachstehend die Debug-Nachrichten für das Beispiel aus Beispiel 8.24:

Abbildung 8.6. Debug-Nachrichten für das <img>-Tag-Beispiel

startElement[1]: img:img  src="image.png"
endElement[1]: img:img (with cdata=)



Bei Verwendung der Standardeinstellungen werden Debug-Nachrichten an das Fehlerprotokoll des Webservers gesendet. Eine Bildschirmausgabe, die allerdings bei Verwendung der XML_Transformer_Driver_OutputBuffer-Treiberklasse nicht verwendet werden kann, ist ebenfalls möglich.

Fehler im verarbeiteten XML werden unabhängig von der Aktivierung des Debug-Modus in ähnlicher Weise protokolliert. Über den Stackdump kann man festzustellen, wo die Verletzung der Wohlgeformtheit erfolgt ist. Sie kann schon im XML-Eingabedokument vorhanden gewesen sein, oder eine Transformation kann sie verletzt haben.

Abbildung 8.7. XML-Transformer-Fehlermeldung mit Stackdump

Transformer: XML Error: unclosed token at line 1:0
line 1: <img:img src="image.png />

Stackdump (level: 0) follows:
level=0
element=:
cdata=