Using PHP Functions in XPath Expressions
23 Oct 2009Disclaimer: this article expects familiarity with using the DOM1 extension and XPath2 expressions.
The (currently undocumented
now 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 <var>$restrict</var>] )
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:
<?xml version="1.0" encoding="UTF-8"?>
<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.
<?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:
Found 2 books starting with 'PHP':
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).
<?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("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:
PHP BASICS
Warning: DOMXPath::evaluate() [domxpath.evaluate]: Not allowed to call handler 'strtolower()'. in example2.php on line 18
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()
.
<?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("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:
Books with multiple authors:
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, is 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.