Disclaimer: this article expects familiarity with using the DOM1 extension and XPath2 expressions.
The (currently undocumentednow documented3) DOMXPath::registerPHPFunctions method is available as of PHP 5.3.0 (it was added to the code base back in December 2006) and allows the use of PHP functions (and static methods) within XPath queries to complement the normal set of XPath functions2.
Description
void DOMXPath::registerPHPFunctions ([ string|array $restrict] )
Enables the use of PHP functions as XPath functions.
Parameters
- restrict
- Use this parameter to only allow certain functions to be called from XPath; it can be either a string (a function name) or an array of function names.
Return Values
No value is returned.
Examples
Note: The following examples load a sample XML document called book.xml with the following contents:
<books>
<book>
<title>PHP Basics</title>
<author>Jim Smith</author>
<author>Jane Smith</author>
</book>
<book>
<title>PHP Secrets</title>
<author>Jenny Smythe</author>
</book>
<book>
<title>XML basics</title>
<author>Joe Black</author>
</book>
</books>
Example #1: Call a PHP function in XPath with php:functionString
This example demonstrates the basic use of DOMXPath::registerPHPFunctions by replicating the substring XPath function. The first thing that needs to be done is to register the
php
namespace with the associated URI
http://php.net/xpath
. Don’t question it, it just needs to be done!
Next, we call DOMXPath::registerPHPFunctions on our object. If no arguments are used, as in this example, then the range of functions allowed to be called is not restricted—you can call any4 function from XPath. I would always advise restricting the functions which can be called, see example 2.
Within our XPath query, we use php:functionString which allows us to name a function and provide some parameters (or indeed, no parameters) to be passed to that function. There are two flavours which can be used here: php:functionString which passes an XML node/attribute as a string and php:function (see example 3) which passes an array of XML node/attribute objects (in DOMElement / DOMAttr / etc. form) to the function . In this example the PHP function substr is called, passing along the book’s title (in string form), an offset of
0
and length of
3
. This returns the first 3 characters of the book’s title which is then compared to the string PHP in order to filter our list of books down to those having titles starting with PHP.
$dom = new DOMDocument;
$dom->load('book.xml');
$xpath = new DOMXPath($dom);
// Register the php: namespace (required)
$xpath->registerNamespace("php", "http://php.net/xpath");
// Register PHP functions (no restrictions)
$xpath->registerPHPFunctions();
// Call substr function on the book title
$nodes = $xpath->query('//book[php:functionString("substr", title, 0, 3) = "PHP"]');
echo "Found {$nodes->length} books starting with 'PHP':\n";
foreach ($nodes as $node) {
$title = $node->getElementsByTagName("title")->item(0)->nodeValue;
$author = $node->getElementsByTagName("author")->item(0)->nodeValue;
echo "$title by $author\n";
}
?>
The example will output something like:
PHP Basics by Jim Smith
PHP Secrets by Jenny Smythe
Example #2: Restricting the functions available to XPath
To restrict the functions made available to XPath, provide either a string containing the name of the single function that you wish to allow or an array of strings containing function names as the restrict parameter (note: static methods can also be used, e.g. “
Classname::method
“). If functions were added with restrict and a function is called in XPath which is not one of them, an E_WARNING will be raised stating
Not allowed to call handler ‘function()’
(where function is the name of the function that cannot be called).
$dom = new DOMDocument;
$dom->load('book.xml');
$xpath = new DOMXPath($dom);
// Register the php: namespace (required)
$xpath->registerNamespace("php", "http://php.net/xpath");
// Register PHP functions (no restrictions)
$xpath->registerPHPFunctions("strtoupper");
// Get first book's title in uppercase
$title = $xpath->evaluate('php:functionString("strtoupper", //book[1]/title)');
echo $title;
// Try a function not in our restrictions list
$fail = $xpath->evaluate('php:functionString("strtolower", //book[1]/title)');
?>
The example will output something like:
<b>Warning</b>: DOMXPath::evaluate() [<a href='domxpath.evaluate'>domxpath.evaluate</a>]: Not allowed to call handler 'strtolower()'. in <b>example2.php</b> on line <b>18</b><br />
Example #3: Passing DOM objects using php:function
Up to now, the examples have both used php:functionString. As mentioned above, instead of passing a string value to the PHP function it is possible to pass along an array of DOM* objects to manipulate them as you please by using php:function.
$dom = new DOMDocument;
$dom->load('book.xml');
$xpath = new DOMXPath($dom);
// Register the php: namespace (required)
$xpath->registerNamespace("php", "http://php.net/xpath");
// Register PHP functions (no restrictions)
$xpath->registerPHPFunctions("example3");
function example3($nodes) {
// Return true if more than one author
return count($nodes) > 1;
}
// Filter books with multiple authors
$books = $xpath->query('//book[php:function("example3", author)]');
echo "Books with multiple authors:\n";
foreach ($books as $book) {
echo $book->getElementsByTagName("title")->item(0)->nodeValue . "\n";
}
?>
The example will output something like:
PHP Basics
Summary
Just to quickly summarise everything, here is a quick run-down. In PHP, make sure to register the php namespace (with the URI http://php.net/xpath) and then register your PHP functions (whether core, extensions or user-defined) or static methods with DOMXPath::registerPHPFunctions. In XPath, use php:functionString or php:function to call the PHP function.
If you have made use of this feature, or want to know more, then do feel free to comment. Thanks for reading.
Footnotes
- http://php.net/dom
- http://schlitt.info/opensource/blog/0704_xpath.html
- The documentation page,
when it gets written, will beis available at http://php.net/domxpath.registerphpfunctions - In truth, some functions are not suitable such as those that return non-scalar values (and cannot be cast to one) which XPath will not understand.
No Comments on Using PHP Functions in XPath Expressions.