If your expression returns a node set, you will get a DOMNodeList instead of a typed result. Instead, try modifying your expression from "//node[1]" to "string(//node[1])".
(PHP 5 >= 5.1.0, PHP 7, PHP 8)
DOMXPath::evaluate — Вычисляет переданное выражение XPath и возвращает типизированный результат, если возможно
$expression
, ?DOMNode $contextNode
= null
, bool $registerNodeNS
= true
): mixed
Выполняет переданное выражение XPath expression
и
возвращает типизированный результат, если это возможно.
expression
Выражение XPath для выполнения.
contextNode
Дополнительный параметр contextNode
может быть
указан для выполнения относительных запросов XPath. По
умолчанию запросы выполняются относительно корневого элемента.
registerNodeNS
Нужно ли автоматически регистрировать префиксы пространства имён в области видимости контекстного узла для объекта DOMXPath. Параметр помогает избегать ручного вызова метода DOMXPath::registerNamespace() для каждого пространства имён в области видимости. Когда префиксы пространства имён конфликтуют, регистрируется только префикс пространства имён близлежащего потомка.
Возвращает типизированный результат, если это возможно, либо объект
DOMNodeList, содержащий все узлы, соответствующие
заданному XPath-выражению expression
.
Если expression
построено
неправильно или contextNode
имеет неверное значение,
DOMXPath::evaluate() вернёт false
.
Пример #1 Получение количества всех английских книг
<?php
$doc = new DOMDocument;
$doc->load('book.xml');
$xpath = new DOMXPath($doc);
$tbody = $doc->getElementsByTagName('tbody')->item(0);
// запрос относительно узла tbody
$query = 'count(row/entry[. = "en"])';
$entries = $xpath->evaluate($query, $tbody);
echo "Есть $entries английские книги\n";
?>
Результат выполнения приведённого примера:
Есть 2 английские книги
If your expression returns a node set, you will get a DOMNodeList instead of a typed result. Instead, try modifying your expression from "//node[1]" to "string(//node[1])".
Note that this method does not provide any means to distinguish between a successful result that returns FALSE and an error.
For example, this will succeed and return FALSE:
<?php $xpath->evaluate("1 = 0"); ?>
One workaround when you know you are expecting a Boolean is to wrap the result with string(). e.g.
<?php $xpath->evaluate("string(1 = 0)"); ?>
This will return a string "false" on success, or the Boolean FALSE on error.
this class can substitute the method evaluate while it is not validated. Made for Yuri Bastos and Jo�o Gilberto Magalh�es.
<?php
class XPtahQuery
{
// function returns a DOMNodeList from a relative xPath
public static function selectNodes($pNode, $xPath)
{
$pos = strpos(self::getFullXpath($pNode),"/",1);
$xPathQuery = substr(self::getFullXpath($pNode),$pos);//to paste /#document[1]/
$xPathQueryFull = $xPathQuery. $xPath;
$domXPath = new DOMXPath($pNode->ownerDocument);
$rNodeList = $domXPath->query($xPathQueryFull);
return $rNodeList;
}
// function returns a DOMNode from a xPath from other DOMNode
public static function selectSingleNode($pNode, $xPath)
{
$pos = strpos(self::getFullXpath($pNode),"/",1);
$xPathQuery = substr(self::getFullXpath($pNode),$pos);//to paste /#document[1]/
$xPathQueryFull = $xPathQuery. $xPath;
$domXPath = new DOMXPath($pNode->ownerDocument);
$rNode = $domXPath->query($xPathQueryFull)->item(0);
return $rNode;
}
//utilitaries functions off selectSingleNode
private function getNodePos($pNode, $nodeName)
{
if($pNode == null)
{
return 0;
}
else
{
$var = 0;
if ($pNode->previousSibling != null)
{
if ($pNode->previousSibling->nodeName == $nodeName)
{
$var = 1;
}
}
return self::getNodePos($pNode->previousSibling, $nodeName) + $var;
}
}
//utilitaries functions off selectSingleNode
private function getFullXpath($pNode)
{
if($pNode == null)
{
return "";
}
else
{
return self::getFullXpath($pNode->parentNode) . "/" . $pNode->nodeName . "[" .strval(self::getNodePos($pNode, $pNode->nodeName)+1) . "]";//+1 to get the real xPath index
}
}
}
?>
To query DOMNodes by their HTML classes, use such snippet
<?php
// CssClassXPathSelector
function ccxs($class) {
return '[contains(concat(" ", normalize-space(@class), " "), " ' . $class . ' ")]';
}
// then just
$domitems = $this->xpath("//*[@id='searchResultsRows']//a" . ccxs('listing_row'));
?>
The only way how to distinguish FALSE returned value from syntax error FALSE is to re-run the XPath expression wrapped in string() function. If must return empty string. If it returns FALSE again then it is an error.
<?php
$ret=$this->xp->evaluate($eval, $context);
// Error detection: DOMXPath::evaluate() returns FALSE on error
// so does DOMXPath::evaluate("boolean(/nothing)")
// @workaround webdevelopers.eu
if ($ret === false && $this->xp->evaluate("string($eval)", $context) === false) {
throw new Exception("Invalid XPath expression ".json_encode($eval), 3491);
}
?>