$str = '
<body>
<table><tr><td><b class="1">1</b></td></tr></table>
<table><tr><td><b class="2">1</b></td></tr></table>
<p>some text</p>
</body>';
$dom = new DOMDocument();
$dom->loadHTML($str);
$xpath = new DOMXpath($dom);
foreach($xpath->query('//table[//b[contains(@class, "2")]]') as $i)
print_r($i);
echo "------------------------------------------\n";
foreach($xpath->query('//table//b[contains(@class, "2")]/ancestor::table') as $i)
print_r($i);
第一个 XPath 选择两个表,而第二个 XPath 仅获取目标(第二个)表。为什么?
最佳答案
接受的答案纠正了错误,但并没有真正解释原始路径表达式出错的原因。
您的第一个表达式如下:
//table[//b[contains(@class, "2")]]
它有两个谓词,一个嵌套在另一个里面:
//table[//b[contains(@class, "2")]]
^---------------------^ inner predicate
^--------------------------^ outer predicate
将谓词视为应用于谓词左侧上下文的过滤器。在极端情况下,这种谓词要么不丢弃任何中间结果节点,要么丢弃所有中间结果节点。
只有当其右侧的谓词计算为 true
时,每个中间结果节点才会被保留.对于内部谓词:
//b[contains(@class, "2")]
//b
产生一组中间体 b
然后由谓词 b
过滤的元素节点(整个文档中的所有 [contains(@class, "2")]
元素节点) .给定您的输入 XML 文档,谓词中的表达式仅返回 true
对于 b
中的一个元素。
但是//b[contains(@class, "2")]
依次作为外部谓词的内容:
//table[outer predicate]
现在//table
选择所有 table
作为中间结果整个文档中的元素节点,并为它们中的每一个检查谓词中的表达式。
重要的是,外部谓词 //b[contains(@class, "2")]
将返回 true
对于两者 table
元素。这是因为对于他们两个来说,在整个文档的某处确实有一个 b
。 class
的元素属性包含 2
.
您实际上想做的是:从每个 table
的角度评估外部谓词表达式。元素 - 接受的答案显示了如何做到这一点。即,使用 .//
而不是 //
在谓词中。
关于php - 相同的 XPaths - 不同的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35314270/