simplexml_import_dom()之后的SimpleXml :: xpath()在整个DomDocument上运行,而不仅仅是在Node上运行

时间:2018-07-30 19:56:58

标签: php xpath simplexml

我不确定这是否是预期的行为,或者我做错了什么:

<?php

$xml = '<?xml version="1.0"?>
<foobar>
<foo>
<nested>
<img src="example1.png"/>
</nested>
</foo>
<foo>
<nested>
<img src="example2.png"/>
</nested>
</foo>
</foobar>';

$dom = new DOMDocument();
$dom->loadXML($xml);

$node = $dom->getElementsByTagName('foo')[0];

$simplexml = simplexml_import_dom($node);
echo $simplexml->asXML() . "\n";

echo " === With // ====\n";
var_dump($simplexml->xpath('//img'));


echo " === With .// ====\n";
var_dump($simplexml->xpath('.//img'));

即使我仅导入了特定的DomNode,并且asXml()仅返回该部分,但xpath()似乎仍然可以在整个文档上运行。

我可以通过使用.//img来防止这种情况,但这对我来说似乎很奇怪。

结果:

<foo>
<nested>
<img src="example1.png"/>
</nested>
</foo>
 === With // ====
array(2) {
  [0] =>
  class SimpleXMLElement#4 (1) {
    public $@attributes =>
    array(1) {
      'src' =>
      string(12) "example1.png"
    }
  }
  [1] =>
  class SimpleXMLElement#5 (1) {
    public $@attributes =>
    array(1) {
      'src' =>
      string(12) "example2.png"
    }
  }
}
 === With .// ====
array(1) {
  [0] =>
  class SimpleXMLElement#5 (1) {
    public $@attributes =>
    array(1) {
      'src' =>
      string(12) "example1.png"
    }
  }
}

1 个答案:

答案 0 :(得分:2)

这是预期的行为。您正在将DOM元素节点导入到SimpleXMLElement中。这不会在后台修改XML文档-节点保留其上下文。

这里是Xpath表达式,它们向上(parent::ancestor::)或兄弟姐妹(preceding-sibling::following-sibling::)上升。

/开头的位置路径始终相对于文档,而不是上下文节点。使用.显式引用当前节点可避免该触发。 .//imgcurrent()/descendant-or-self::img的缩写-另一个选择是descendant::img

但是,您不需要将DOM节点转换为SimpleXMLElement即可使用Xpath。

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);

foreach ($xpath->evaluate('//foo[1]') as $foo) {
  var_dump(
    $xpath->evaluate('string(.//img/@src)', $foo)
  );
}

输出:

string(12) "example1.png"

//foo[1]获取文档中的第一个foo元素节点。如果文档中没有匹配的元素,它将返回一个空列表。使用foreach可以避免这种情况下的错误。它将重复一次或永不重复。

string(.//img/@src)获取后代src元素的img属性,并将第一个元素转换为字符串。如果此处没有匹配的节点,则返回值将为并且为空字符串。 DOMXpath::evaluate()的第二个参数是上下文节点。