5.10 Before and After: Transforming XML with XSLT

 <  Day Day Up  >  

PHP 4 has a separate XSLT extension that relies on the Sablotron XSLT parsing library. In PHP 5, that's replaced by integrated XSLT support with the DOM functions. Also, libxslt has replaced Sablotron as the processor of choice.

5.10.1 PHP 4

In PHP 4, the XSLT extension uses resources instead of objects:

 $xml = 'data.xml'; $xsl = 'stylesheet.xsl'; $xslt = xslt_create( ); $results = xslt_process($xslt, $xml, $xsl); if (!$results) {     error_log("XSLT Error: #".xslt_errno($xslt).": ".xslt_error($xslt)); } xslt_free($xslt); 

You pass xslt_process( ) the filenames of your XML and XSLT documents, and it loads the XML from disk. You can also read in from a variable (using the weird argument syntax), but not from a DOM object.

5.10.2 PHP 5

Using XSLT in PHP 5 involves two main steps: preparing the XSLT object and then triggering the actual transformation for each XML file.

To begin, load in the stylesheet using DOM. Then, instantiate a new XSLTProcessor object, and import the XSLT document by passing in your newly created DOM object to the importStylesheet( ) method.

 // Load XSL template $xsl = newDOMDocument; $xsl->load('stylesheet.xsl'); // Create new XSLTProcessor $xslt = new XSLTProcessor( );                                                                                                                        // Load stylesheet $xslt->importStylesheet($xsl); 

Now the transformer is up and running. You can transform any DOM object in one of three ways: into a string, into a file, or back into another DOM object.

 // Load XML input file $xml = newDOMDocument; $xml->load('data.xml'); // Transform to string $results = $xslt->transformToXML($xml); // Transform to a file $results = $xslt->transformToURI($xml, 'results.txt'); // Transform to DOM object $results = $xslt->transformToDoc($xml); 

When you call transformToXML( ) or transformToDoc( ) , the extension returns the result string or object. In contrast, transformToURI( ) returns the number of bytes written to the file, not the actual document.

These methods return false when they fail, so to accurately check for failure, write:

 if (false =  == ($results = $xslt->transformToXML($xml))) {     // an error occurred } 

Using = = = prevents a return value of from being confused with an actual error.

5.10.3 Setting Parameters

You can pass data from PHP into your XSLT stylesheet with the setParameter( ) method. This allows you to do things such as filter data in your stylesheet based on user input.

For example, the program in Example 5-22 allows you to find people based on their city.

Example 5-22. Setting XSLT parameters from PHP
 // This could also come from $_GET['city']; $city = 'Tel Aviv'; $dom  = new DOMDocument $$dom->load('address-book.xml'); $xsl  = new DOMDocument $xsl->load('stylesheet.xsl'); $xslt = new XSLTProcessor( );  $xslt->importStylesheet($xsl);  $xslt->setParameter(NULL, 'city', $city);  print $xslt->transformToXML($dom); 

The program uses the following stylesheet:

 <?xml version="1.0" ?>  <xsl:stylesheet version="1.0"    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="@*node( )">   <xsl:copy>     <xsl:apply-templates select="@*node( )"/>   </xsl:copy> </xsl:template> <xsl:template match="/address-book/person">    <xsl:if test="city=$city">     <xsl:copy>       <xsl:apply-templates select="@*node( )"/>     </xsl:copy>   </xsl:if> </xsl:template>  </xsl:stylesheet> 

The program and stylesheet combine to produce the following results:

  <?xml version="1.0"?>   <address-book>     <person id="2">   <!--Zeev Suraski-->   <firstname>Zeev</firstname>   <lastname>Suraski</lastname>   <city>Tel Aviv</city>   <state/>   <email>zeev@php.net</email>   </person>   </address-book>  

The PHP script does a standard XSLT transformation, except that it calls $xslt->setParameter(NULL, 'city', $city) . The first argument is the parameter's namespace, the second is the parameter's name , and the third is the parameter's value.

Here, the value stored in the PHP variable $city ”in this case, Tel Aviv ”is assigned to the XSLT parameter city , which does not live under a namespace. This is equal to placing the following in an XSLT file:

 <xsl:param name="city">Tel Aviv</xsl:param> 

You usually access a parameter inside a stylesheet like you do a PHP variable, by placing a dollar sign ( $ ) in front of its name. The stylesheet example creates a template that matches /address-book/person nodes.

Inside the template, you test whether city=$city ; in other words, is the city child of the current node equal to the value of the city parameter? If there's a match, the children are copied along; otherwise , the records are eliminated.

In this case, city is set to Tel Aviv , so Rasmus's record is removed and Zeev's remains.

5.10.4 Calling PHP Functions from Stylesheets

XSLT parameters are great when you need to communicate from PHP to XSLT. However, they're not very useful when you require the reverse. You can't use parameters to extract information from the stylesheet during the transformation. Ideally, you could call PHP functions from a stylesheet and pass information back to PHP.

PHP 4 solves this using a Sablotron feature known as scheme handlers. Scheme handlers aren't available in PHP 5, because PHP doesn't use the Sablotron XSLT processor. However, there's a new method that duplicates this functionality: registerPHPFunctions( ) . Here's how it's enabled:

 $xslt = new XSLTProcessor( );  $xslt->registerPHPFunctions( ); 

This allows you to call any PHP function from your stylesheets. It's not available by default, because it presents a security risk if you're processing stylesheets controlled by other people.

Both built-in and user-defined functions work. Inside your stylesheet, you must define a namespace and call the function( ) or functionString( ) methods:

 <?xml version="1.0" ?>  <xsl:stylesheet version="1.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"      xmlns:php="http://php.net/xsl"     xsl:extension-element-prefixes="php">  <xsl:template match="/">      <xsl:value-of select="php:function('strftime', '%c')" />  </xsl:template>  </xsl:stylesheet> 

At the top of the stylesheet, define the namespace for PHP: http://php.net/xsl . This example sets the namespace prefix to php . Also, set the extension-element-prefixes value to php so XSLT knows these are functions.

To call a PHP function, reference php:function( ) . The first parameter is the function name; additional parameters are the function arguments. In this case, the function name is strftime and the one argument is %c . This causes strftime to return the current date and time.

Example 5-23 uses this stylesheet, stored as stylesheet.xsl , to process a single-element XML document.

Example 5-23. Transforming XML with PHP 5, XSLT, and PHP functions
 $dom  = new DOMDocument; $dom->loadXML('<blank/>'); $xsl  = new DOMDocument $xsl->load('stylesheet.xsl'); $xslt = new XSLTProcessor( );  $xslt->importStylesheet($xsl); $xslt->registerPHPFunctions( );  print $xslt->transformToXML($dom);  Mon Jul 22 19:10:21 2004  

This works like standard XSLT processing, but there's an additional call to registerPHPFunctions( ) to activate PHP function support.

You can also return DOM objects. This example takes the XML address book and mangles all the email addresses to turn the hostname portion into three dots. Everything else in the document is left untouched:

 function mangle_email($nodes) {     return preg_replace('/([^@\s]+)@([-a-z0-9]+\.)+[a-z]{2,}/is',                         '@...',                         $nodes[0]->nodeValue); } $dom  = new DOMDocument; $dom->load('address-book.xml'); $xsl  = new DOMDocument $xsl->load('stylesheet.xsl'); $xslt = new XSLTProcessor( );  $xslt->importStylesheet($xsl);  $xslt->registerPhpFunctions( );  print $xslt->transformToXML($dom); 

Inside your stylesheet, create a special template for /address-book/person/email elements:

 <?xml version="1.0" ?>  <xsl:stylesheet version="1.0"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"    xmlns:php="http://php.net/xsl"             xsl:extension-element-prefixes="php">  <xsl:template match="@*node( )">   <xsl:copy>     <xsl:apply-templates select="@*node( )"/>   </xsl:copy> </xsl:template> <xsl:template match="/address-book/person/email">    <xsl:copy>     <xsl:value-of select="php:function('mangle_email', node( ))" />    </xsl:copy> </xsl:template>  </xsl:stylesheet> 

The first template ensures that the elements aren't modified, while the second passes the current node to PHP for mangling. In the second template, the mangle_email( ) function is passed the current node, represented in XPath as node( ) , instead of a string. Be sure not to place the node inside quotation marks, or you'll pass the literal text node( ) .

Nodes becomes DOM objects inside PHP and always arrive in an array. In this case, mangle_email( ) knows there's always only one object and it's a DOMText object, so the email address is located in $nodes[0]->nodeValue .

When you know that you're only interested in the text portion of a node, use the functionString( ) function. This function converts nodes to PHP strings, which allows you to omit the array access and nodeValue dereference:

 function mangle_email($email) {     return preg_replace('/([^@\s]+)@([-a-z0-9]+\.)+[a-z]{2,}/is',                         '@...',                         $email); } // all other code is the same as before 

The new stylesheet template for /address-book/person/email is:

 <xsl:template match="/address-book/person/email">    <xsl:copy>     <xsl:value-of         select="php:functionString('mangle_email', node( ))" />    </xsl:copy> </xsl:template> 

The mangle_email( ) function now processes $email instead of $nodes[0]->nodeValue because the template now calls the functionString( ) function.

The function( ) and functionString( ) methods are incredibly useful, but using them undermines the premise of XSL as a language-neutral transformation engine. When you call PHP from XSLT, you cannot easily reuse your stylesheets in projects that use Java, Perl, and other languages, because they cannot call PHP. Therefore, you should consider the trade-off between convenience and portability before using this feature.

 <  Day Day Up  >  


Upgrading to PHP 5
Upgrading to PHP 5
ISBN: 0596006365
EAN: 2147483647
Year: 2004
Pages: 144

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net