PHP Programming/XSL/registerPHPFunctions

The XSLTProcessor::registerPHPFunctions() method enables to use PHP (v5.0.4+) functions as XSLT-v1 functions.

It is a XSLTProcessor feature for exposing PHP functions or methods to the XSLT script (processed by importStyleSheet method). Very important for PHP users, because PHP (and any libxml2-dependent) not have a XSLT-v2 engine, and part of this functional lack can be overcome by using registerPHPFunctions. But, even in 2013's, most programmers share of the opinion that

... Is poorly documented and poorly supported, and has much ugliness about it. Rely on it as little as possible...

expressed by an experienced XSLT developer, F. Avila, for other developers

The objective of this chapter, a tutorial for use PHP functions with XSLT, is trying to change this "state of affairs".

NOTE: another functional complement is to use PHP support for EXSLT library (see http://www.exslt.org/). See also [1], [2], [3] ... and other tips.

PreparingEdit

The XSLTProcessor call need some inicializations, so, we can encapsulate this inicializations in a single function, that use XML data and a XSLT script as inputs, and print the XSLTProcessor result.

function XSL_transf($xml,$xsl) {
	$xmldoc = DOMDocument::loadXML($xml);
	$xsldoc = DOMDocument::loadXML($xsl);
	$proc = new XSLTProcessor();
	$proc->registerPHPFunctions();
	$proc->importStyleSheet($xsldoc);
	echo $proc->transformToXML($xmldoc);
}

For send a XSLT script to this XSL_transf() function, the XSLT script must be a string, so we can use a inline declaration (see PHP's Nowdoc and Heredoc),

$xsl = <<<'EOB'
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:php="http://php.net/xsl">
  <xsl:template match="/">
      ...
  </xsl:template>
</xsl:stylesheet>
EOB;
XSL_transf('<root/>',$xsl);

Another way is get it from a file,

  XSL_transf('<root/>',file_get_contents('xslt_script.xsl'));

or even changing changing the XSL_transf(),

function XSL_transf($xmlFile,$xslFile) {
	$xmldoc = DOMDocument::load($xml);
	$xsldoc = DOMDocument::load($xsl);
        ... remaining same code...
}

CautionsEdit

After <xsl:stylesheet version="1.0" ...> declaration you can change the default output by, p. example,

  <xsl:output method="text"/>

In the examples of this tutorial, use allways the XML method,

  <xsl:output method="xml" encoding="utf-8" indent="yes"/>

and, for see all tags withou need do open source-code in the browser, starts the PHP script with

  header("Content-Type: text/plain; charset=utf-8");

Using PHP functions with static XSLTEdit

In a first overview of the "exposing PHP to XSLT" feature, we can ignore the XML input data, using the XSLT script as a static template.

XSL receiving external string valuesEdit

Declare and use of a XSLT script with PHP function calls (see xsl:value-of), that bring string values from PHP functions (direct or parametrized).

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:php="http://php.net/xsl" exclude-result-prefixes="php">
 
  <xsl:template match="/">
	PHP time()=<xsl:value-of select="php:function('time')" />, 
	PHP rand()=<xsl:value-of select="php:function('rand')" />,
	PHP rand(11,99)=<xsl:value-of select="php:function('rand',11,99)" />,
	PHP xsl_myF1()=<xsl:value-of select="php:function('xsl_myF1_StrConstant')" />,
	PHP xsl_myF2(XX)=<xsl:value-of select="php:function('xsl_myF2_id','XX')" />.
  </xsl:template>
</xsl:stylesheet>

The first two functions can be called withou any parameter, the two last functions are user-declared:

  function xsl_myF1_StrConstant() { return "123"; }
  function xsl_myF2_id($str) { return $str; }

XSL_transf RESULT:

  PHP time()=1365869487, 
  PHP rand()=1410713536,
  PHP rand(11,99)=20,
  PHP xsl_myF1()=123,
  PHP xsl_myF2(XX)=XX.

XSL receiving external XML as stringEdit

The <xsl:value-of ... /> clause usually receives string values, but with the disable-output-escaping attribute, it can receive an entire XML fragment.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:php="http://php.net/xsl">
  <xsl:output method="xml" encoding="utf-8" indent="yes"/>
  <xsl:template match="/">
     PHP xsl_myF2('<someTag/>')=<xsl:value-of select="php:function('xsl_myF2_id','<someTag/>')" />
     PHP xsl_myF2('<someTag/>')=<xsl:value-of select="php:function('xsl_myF2_id','<someTag/>')" disable-output-escaping="yes"/>
     PHP xsl_myF3()=<xsl:value-of select="php:function('xsl_myF1_XmlConstant')" disable-output-escaping="yes"/>
  </xsl:template>
</xsl:stylesheet>

where

  function xsl_myF1_XmlConstant() { 
      return '<aBigFragment> text <someTag val="123"/> text </aBigFragment>'; 
  }

XSL_transf RESULT:

 
   PHP xsl_myF2=&lt;someTag/&gt;
   PHP xsl_myF2=<someTag/>
   PHP xsl_myF3=
        <aBigFragment> text <someTag val="123"/> text </aBigFragment>

XSL receiving external XML as DOMElementEdit

All XSLTProcessor activities relies in DOMDocument manipulations, so, to best performance, is better to send not a string, but directally a DOMElement object.

The clause <xsl:copy-of ... /> receives DOMElement or DOMDocument, and <xsl:for-each ...> receives DOMNodeList. So, if we have a PHP function that returns DOMDocument, we can use it.

  function xsl_myF4_DOMConstant() {
      static $xdom = DOMDocument::loadXML('<t> foo <tt val="123"/> bar </t>');
      return $xdom; 
  }

Calling xsl_myF4 into the XSLT script,

  <xsl:template match="/">
     PHP xsl_myF4()=<xsl:copy-of select="php:function('xsl_myF4_DOMConstant')" />
  </xsl:template>

RESULTS:

 <t> foo <tt val="123"/> bar </t>

Using PHP functions with dynamic XSLTEdit

"Real life" templates use XML input data for output. Suppose the following XML:

<allusers>
 <user> <uid>bob</uid> </user>
 <user> <uid>joe</uid> </user>
</allusers>

XSL sending and receiving string valuesEdit

To send an input node as string, you can use the XPath-v1.0 string() function of the Core Function Library, that converts a node to a string. If the argument is a XML fragment (a node with more than a single value), the "cast to string" replaces tags by blank spaces.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:php="http://php.net/xsl">
 <xsl:output method="xml" encoding="utf-8" indent="yes"/>
 <xsl:template match="allusers">
	Users: 
	<xsl:for-each select="user"> <xsl:value-of select="position()"/>:
	   myF2(uid)="<xsl:value-of select="php:function('xsl_myF2_id',string(uid))" />",
	   myF2(.)="<xsl:value-of select="php:function('xsl_myF2_id',string(.))" />",
	</xsl:for-each>  
 </xsl:template>
</xsl:stylesheet>

XSL_transf RESULT:

   Users: 
        1:
           myF2(uid)="BOB",
           myF2(.)=" BOB textTest ",
        2:
           myF2(uid)="JOE",
           myF2(.)=" JOE ",

XSL-registeredFunction communicating by DOMEdit

The most complete way to XSLT script send a node as PHP-function parameter, is sending it without string casting. PHP function will recive as parameter an array of DOMElements, and PHP can sends back a DOMElement to the XSLT script.

This is the identity function implemented with this "DOM communication":

function xsl_myF5_id($m) {  // $m is always an array
    $ele = $m[0];  // get_class($m[0])==DOMElement
    return $ele; // XSLT accepts only DOMElement or DOMDocument
}

Using this function in a loop over input nodes,

	<xsl:for-each select="user"> <xsl:value-of select="position()"/>:
	   copy-of  myF5(uid)="<copy-of select="php:function('xsl_myF5_id', uid)" />",
	   value-of myF5(uid)="<xsl:value-of select="php:function('xsl_myF5_id', uid)" />",
	   copy-of  myF5(.)=<xsl:copy-of select="php:function('xsl_myF5_id', . )" />.
	</xsl:for-each>

XSL_transf RESULT:

     1:
          copy-of  myF5(uid)="<uid>BOB</uid>",
          value-of myF5(uid)="BOB",
          copy-of  myF5(.)=<user> <uid>BOB</uid> textTest </user>.
     2:
          copy-of  myF5(uid)="<uid>JOE</uid>",
          value-of myF5(uid)="JOE",
          copy-of  myF5(.)=<user> <uid>JOE</uid> </user>.

Working with real-life applicationsEdit

... STANDARD LIB PROPOSAL ...

Versions and contexts where the examples runsEdit

Please colabore with your tests:

  • PHP 5.3.10-1ubuntu3.6 (Zend Engine v2.3.0). All examples runs.

External linksEdit

Last modified on 3 December 2013, at 21:15