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.
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), where name is the name of the function, and par1 is an XSLT value. 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 created 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.
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 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 returned if there was nothing. In XSLT, such an empty result can be used in a boolean expression of a test attribute of an xsl:if or xsl:when element, but PHP can only test for an empty array, not a boolean value.
A similar situation can occur when passing an XPath expression for an XSL element or an attribute directly as a parameter. If the XPath string function is used for an attribute, either when defining a XSLT variable or directly as the parameter, it will be blank if it didn't exist. This may be the best way to pass attribute values. That means if the attribute is never going to be tested for existence, use the string function in the variable definition, else use it as the parameter. This simplifies the testing in the PHP function when handling attributes, leaving an empty array for indicating no elements.
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 the 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 element as they are always processed in order, whereas attributes may not.
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 up to date. If the PHP function always returns a value, to modify the value from XSLT without using the returned value, use it in the test attribute of an xsl:if element.
Following these basic guidelines should provide reliable bidirectional dynamic inter-operability between PHP and the XSLT processor.