To main heading

Smallsite Design

Technology

PHP functions in XSLT

The versions of XSLT and XPath provided with PHP are only version 1.0, but includes the facility to directly call PHP functions from XPath statements.

Later versions of XSLT have added many functions, but being able to call PHP functions may more than make up for not being able to use those new functions. The facility does not provide any new functions other than the php:function call itself, meaning that any desired functions have to be written in PHP, though existing PHP functions can be called as well. While the call is straightforward, care must be taken with the type of parameters to ensure that PHP can understand them.

Doing complex processing solely in XSLT can make for very complex code, with some functionality being impossible to implement, whereas using PHP functions can provide ad-hoc accessing of files or other XML documents, or just independent processing on the same document that the XSLT is processing, but which would require advanced XSLT understanding, and a brain the size of a planet to keep track of what is going on within it, or asking a lot of questions of Dimitre Novatchev on Stack Overflow.

A particularly useful use of such PHP functions is inline in the select attribute of a xsl:sort element to generate the sorting key from content not in the elements of the node set being sorted, but too complex to use xsl:key and the key function, or not in the XML tree at all.

For functions to be accessible to XSLT, their names must be added to the XSLTProcessor object created for the XSLT rendering, but after using its importStylesheet function to load the XSLT file. This is done using the processor's registerPHPFunctions with an array of PHP function names as its sole parameter. The names can be for inbuilt functions like grapheme_strlen, user functions, or class functions, like Support::clip. I have not tried using object instance functions, so I don't know if they will work.

To be able to use them in XSLT stylesheets, the php namespace has to be registered at the top of the file, by an xmlns:php attribute on the xsl:stylesheet element, with a value of http://php.net/xsl. To call a function from a Xpath statement, use php:function('name',par1,par2), where name is the name of the function, prefixed by class name and :: if required, and the rest is the parameter values. There can be many parameters, separating each with a comma.[1]

Where care must be taken, and which may have led some to rate PHP functions as unreliable, is in correctly specifying the parameters and return values, with XML attributes being the one that likely creates the most misunderstandings. Firstly, string and numeric literal parameters, like 'ltr' and 23 respectively, will create no problems as long as they are treated as expected by the PHP code. Likewise, XSLT variables created using them in a select attribute are also no problem. Boolean parameters work alright as well.

When an XSLT variable with XML elements or an attribute specified by an XPath statement in a select attribute is passed to a function, it is as a PHP array of those DOM elements or attribute, but it will be an empty array if nothing was returned by the XPath. There is no way for a PHP function to determine what type of entity was supposed to be provided if the array is empty. In XSLT, such an empty result can be used in a Boolean expression of a test attribute of an or xsl:when element, but PHP can only test for an empty array, not a boolean value.

It will be easier if the PHP functions only rely upon receiving elements in this way. Then an empty array will indicate that there are none.

When passing an attribute, make sure it is converted to a string by using the XSLT string function. If the attribute is never going to be tested for existence, use the string function in the variable definition, else use it for the parameter value. If the attribute can validly be blank, but the PHP function needs to know if the attribute existed and it doesn't, pass text that is never valid for the attribute, and test for that in the function.

When an element is passed to PHP, it is not deep, in that it is like an isolated element that has no parent, but only descendants. This is the same as when passing an element as a parameter in XSLT, whereas making the required element current as the context for the called template does allow traversing the ancestor tree. However, there is no way to pass a context to PHP in the same way. So if wanting PHP to process an element's ancestors, pass PHP the information it requires to independently find the element in the XML structure of the source file.

On the return side, a literal or variable that is a string, number or boolean value will be treated as such in the XSLT. The only way I found to return elements was within a single DOM element. Typically, I created the structure in SimpleXML and returned the element converted by dom_import_simplexml.

This could be used directly as the root element in the XPath of an select attribute of an xsl:for-each element, or from a variable created using the function in its select attribute, which can then be the root element for the XPath of a xsl:for-each element. If wanting to insert the returned element directly into the resulting document, use the function in the select attribute of an xsl:copy-of element.

XSLT variables cannot be modified, except that one defined within an xsl:template element will be used instead of a global of the same name defined as a child of the xsl:stylesheet element. However, PHP variables can, so a PHP function that can do arithmetic upon a PHP variable can be called to update or retrieve the value as required. These can be used within an xsl:for-each loop that iterates over elements as they are always processed in order, whereas attributes may not.

This can be used to increment counters that can be used in element id attributes for CSS customised for each element using content-dependent processing.

While I have observed that attributes in PHP's XSLT have always been processed in order, the XML specification does not require it, so the observed behaviour may change, even though there has been no attempt to bring XML, XPath and XSLT in PHP up to date for many years. To mitigate against future changes to that, iterate over a list of elements that match the attribute names in the order required, and use that to access the attribute values.

If the PHP function always returns a value, and wanting to use the value within XSLT without the returned value appearing in the XSLT rendered output, use it in the test attribute of an xsl:if element as it will not produce any output. This is typically used because the function does something that the XSLT cannot do, like logging to a file for debugging purposes. I have found that the XSLT debugging provided in PHP is too high level, like for function usage traceability rather than what the XSLT is actually doing, such as variable values.

Following these basic guidelines should provide reliable bidirectional dynamic inter-operability between PHP and the XSLT processor.

The PHP Programming/XSL/registerPHPFunctions article may provide additional information, but I found it confusing given my understanding at the time, so I did my own experimentation and verification using first principles from the PHP manual documentation for the XSLTProcessor class. This is what I often had to do when I wrote manuals for new technology as a technical writer.

  • Adding search
  • Using AI effectively
  • Using technical references
  • Contact   
  • Categories   Feed   Site map

  • This site doesn't store cookies or other files on your device when visiting public pages.
    External sites: Open in a new tab or window, and might store cookies or other files on your device. Visit them at your own risk.
    Powered by: Smallsite Design©Patanjali Sokaris