• Index
  •  » Articles
  •  » XML + XSLT transformation with the help of Sablotron

XML + XSLT transformation with the help of Sablotron

XML + XSLT transformation with the help of Sablotron

Given material is an alternative introduction to the XSLT with Sablotron in PHP. If anybody has ever interested in XSLT he should have written the standard example from the manual. Here is similar example:

<?php

$xmlData 
"<?xml version="1.0" encoding="Windows-1251"?>
<document>
<game>
    <title>Railroad Tycoon II Platinum</title>
    <genre> economic strategy </genre>
    <designer>PopTop software</designer>
    <publisher>G.O.D. games</publisher>
    <year>2001</year>
</game>
<game>
    <title>Grand Prix 4</title>
    <genre>autosimulator</genre>
    <designer>Geoff Crammond & Simergy</designer>
    <publisher>Infogrames Entertainment</publisher>
    <year>2002</year>
</game>
</document>"
;

$xslData "<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="
1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" encoding="Windows-1251"/>

<
xsl:template match="/">
    <
xsl:apply-templates/>
</
xsl:template

<
xsl:template match="document">
    <
html><head>
    <
title>Games</title>
    </
head>
    <
body>
    <
h1>Games</h1>
    <
table cellpadding="2" cellspacing="2" border="1">
    <
tr>
        <
td>Name</td>
        <
td>Genre</td>
        <
td>Year</td>
        <
td>Developer</td>
        <
td>Publisher</td>
    </
tr>
        <
xsl:apply-templates select="game"/>
    </
table>
    </
body></html>
</
xsl:template

<
xsl:template match="game">
    <
tr>
        <
td><b><xsl:value-of select="title"/></b></td>
        <
td><xsl:value-of select="genre"/></td>
        <
td><xsl:value-of select="year"/></td>
        <
td><xsl:value-of select="designer"/></td>
        <
td><xsl:value-of select="publisher"/></td>
    </
tr>
</
xsl:template

</
xsl:stylesheet>";

$xh = xslt_create();

    $arguments = array(
        "
/_xml" => $xmlData,
        "
/_xsl" => $xslData
    ); 

    $result = @xslt_process($xh, "
arg:/_xml", "arg:/_xsl", NULL, $arguments);
    

if ($result)
    print ($result);
else {
    print ("
There was an error that occurred in the XSL transformation...n");
    print ("
tError number" . xslt_errno($xh) . "n");
    print ("
tError string" . xslt_error($xh) . "n");
    exit;
}

?>

Listed above example is very simple and it illustrates the algorithm of the XSL transformation in PHP. If you want that code to work you have to install the XSLT processor Sablotron. The steps of installing are as follows:

  • Place iconv(-1.3).dll, expat.dll and sablot.dll to C:\windows\System
  • Open C:\windows\php.ini and find there extension_dir parameter. If its value is "." or something like "./" change it to "f:\usr\local\php\extension" for example (or your directory’s address).
  • Place there php_xslt.dll file (version PHP 4.2.x) or php_sablot.dll file (version PHP 4.0.x)<.li>
  • In php.ini uncomment extension=php_xslt.dll string (4.2.x) or extension=php_sablot.dll (4.0.x)

XSLT using allows to separate formatting and data representation from the php-scripts.

Lists output

Example 2. Articles’ list on the site with article highlight that is currently read, color alternation and list numeration.

XML:

<current-date>2002-05-30</current-date>

<list-article date="2002-10-03">Bug searching in PHP</list-article>
<list-article date="2002-10-02">Introduction to PHP</list-article>
<list-article date="2002-06-03">Working with MySQL. Part 7. Trees</list-article>
<list-article date="2002-05-30">Manual sorting in the web-applications</list-article>
<list-article date="2002-05-29">Introduction to SQLite</list-article>
<list-article date="2002-05-27">Relax this is PHP</list-article>

XSLT:

...
<table>
<xsl:apply-templates select="list-article"/>
</table>
...

<xsl:template match="list-article">
    <tr>
    <xsl:if test="position() mod 2 = 1">
        <xsl:attribute name="bgcolor">#cccccc</xsl:attribute>
    </xsl:if>
    <td>
    <xsl:value-of select="position()">
    <a href="/{@date}.htm"><xsl:value-of select="."/></a>
    <xsl:if test="@date = ../current-date">&nbsp;&lt;</xsl:if>
    </td>
    </tr>
</xsl:template>

Arbitrary markup

Transforming site with text to XML you would like to create your own markup. For example you would like to mark some important places in the text, or write quotes such as <quote> Text <quote> and have the opportunity to change their style.

A lot of people facing with that problem can’t find the way out. If you select the paragraph to the <para> tag and create the template for it there are three methods to output the content:

  • xsl:value-of tag outputs the text but removes all tags in the paragraph
  • xsl:copy-of tag outputs the copy of the content and container <para>...</para>
  • xsl:apply-templates tag applies templates to all the children, but miss the text

It seems that there is no way out. But it isn’t right. I use “magic” templates that output tags and text with attributes without any changes. Example 3.


XML:

<text>
    <para>That example uses <strong>magic templates</strong> 
for parsing the arbitrary markup. It allows to escape such complaints as:
    </para>
    <quote>Please help!I can’t output tags in the text using 
value-of!
    </quote>
    <hr/>
    <strong>Remember these templates!</strong>
    <para>You will be able to process <u>any</u> <a href="http://www.txt.com">?????</a>
 Almost any.
    </para>
</text>

XSLT:

<xsl:template match="text"><xsl:apply-templates/></xsl:template>

<xsl:template match="strong">
    <font color="#cc0000"><b><xsl:apply-templates/></b></font>
</xsl:template>

<!—Three magic templates -->
<!-- 1. General -->
<xsl:template match="*">
    <xsl:copy>
        <xsl:apply-templates select="@*" />
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

<!-- 2. For text -->
<xsl:template match="text()">
    <xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>

<!-- 3. For tags and attributes -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>
//

When apply-templates instruction is called XSLT-processor searches templates for every element. Element strong has the template, that’s why in accordance with it such elements will be processed. Hyperlink doesn’t have the template, that’s why it will be output as is. Actually you can add the template to the link as well.


<xsl:template match="a[@href]">
<xsl:copy-of select="."/> <a href="{@href}" 
target="_blank"><img src="/window.gif" width="15" height="15" 
alt="Open in new page"/></a>
</xsl:template> 

* match="a[@href]" parameter is used in the template. That template will be applied only for those links that have href field.

Invalid code and &nbsp

The necessity of writing the valid XML code scares a lot of XSLT neophytes. Ok, let’s write valid articles, fortunately we can check the presence of some errors in the text, such as mismatched tag or invalid token. But it requires to transform all the archive to the valid code.

The solution is very simple: write as you want. As for  , XML doesn’t have nbsp element. There are such elements as lt, gt, quot. That’s why it should be declared in the document or used inside the <![CDATA[...]]>.

Example 4

XML:

<text>
    <bad-markup><![CDATA[In that <a href=http://detail.php.net>text</a>
    there is used invalid markup. 
    <br> But it’s ok.]]></bad-markup>
    <quote>Please help!</quote>
    <hr/>
    <strong>Remember these templates too!</strong>
</text>

XSLT:

<xsl:template match="text"><![CDATA[ >>> You can do the same in the XSL too! <<< ]]> 
<xsl:apply-templates/></xsl:template>

<xsl:template match="bad-markup">
    <xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>

<xsl:template match="*">
    <xsl:copy>
        <xsl:apply-templates select="@*" />
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

<xsl:template match="text()">
    <xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

Cycles

Let’s suppose that we have to create the form for editing the article and its date. For more convenience let’s create three opening lists – date from 1 up to 31, month and year. First solution is to write the HTML-code of these lists in PHP and insert to XML in the CDATA container, and then transform to XSL with disable-output-escaping="yes" parameter.

XSLT can do the same. The only thing you have to do is to insert date, month and year to XML.

Let’s create the template meant for none of the document’s elements. It will be called by xsl:call-template command and get two parameters: counter’s value and maximum.

Example 5

XML:

<month-name>January</month-name>
<month-name>February</month-name>
<month-name>March</month-name>
<month-name>April</month-name>
<month-name>May</month-name>
<month-name>June</month-name>
<month-name>July</month-name>
<month-name>August</month-name>
<month-name>September</month-name>
<month-name>October</month-name>
<month-name>November</month-name>
<month-name>December</month-name>

<article>
    ...
    <day>7</day>
    <month>10</month>
    <year>2002</year>
</article>

XSLT:

<xsl:template match="article">
    ...
    <select name="d">
    <xsl:call-template name="day">
        <xsl:with-param name="count">1</xsl:with-param>
    </xsl:call-template>
    </select>
    <select name="m">
    <xsl:call-template name="month">
        <xsl:with-param name="count">1</xsl:with-param>
    </xsl:call-template>
    </select>
    ...
</xsl:template>

<xsl:template name="day">
    <xsl:param name="count"/>
    <option value="{$count}">
        <xsl:if test="$count = //artcile/day">
            <xsl:attribute name="selected">yes</xsl:attribute>
        <xsl:if>
        <xsl:value-of select="$count"/>
    </option>
    <xsl:if test="$count &lt; 31">
        <xsl:call-template name="day">
            <xsl:with-param name="count">
                <xsl:value-of select="$count + 1"/>
            </xsl:with-param>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<xsl:template name="month">
    <xsl:param name="count"/>
    <option value="{$count}">
        <xsl:if test="$count = //artcile/month">
            <xsl:attribute name="selected">yes</xsl:attribute>
        <xsl:if>
        <xsl:value-of select="//month-name[position() = $count]"/>
    </option>
    <xsl:if test="$count &lt; 12">
        <xsl:call-template name="month">
            <xsl:with-param name="count">
                <xsl:value-of select="$count + 1"/>
            </xsl:with-param>
        </xsl:call-template>
    </xsl:if>
</xsl:template>


 

  • Top