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.
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
);
?>
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.
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.
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=