繁体   English   中英

在DomDocument中,重用DOMXpath,它是否稳定?

[英]In DomDocument, reuse of DOMXpath, it is stable?

我正在使用下面的功能,但不确定它总是稳定/安全...... 是吗?

什么时候和谁稳定/安全“重用DOMXpath准备程序的部分”?

为了简化XPath query()方法的使用,我们可以采用一个函数来记忆最后一次使用静态变量的调用,

   function DOMXpath_reuser($file) {
      static $doc=NULL;
      static $docName='';
      static $xp=NULL;
      if (!$doc)
                $doc = new DOMDocument();
      if ($file!=$docName) {
                $doc->loadHTMLFile($file);
                $xp = NULL;
      }
      if (!$xp) 
                $xp = new DOMXpath($doc);
      return $xp;  // ??RETURNED VALUES ARE ALWAYS STABLE??
   }

本问题类似于关于XSLTProcessor重用的另一个问题。 在这两个问题中,对于使用LibXML2作为DomDocument实现的任何语言或框架,可以推广该问题。

还有另一个相关的问题: 如何“刷新”LibXML2的DOMDocument实例?


说明

重用非常普遍(例子):

   $f = "my_XML_file.xml";
   $elements = DOMXpath_reuser($f)->query("//*[@id]");
   // use elements to get information
   $elements = DOMXpath_reuser($f)->("/html/body/div[1]");
   // use elements to get information

但是,如果您执行removeChildreplaceChild等操作(例如),

   $div = DOMXpath_reuser($f)->query("/html/body/div[1]")->item(0);  //STABLE
   $div->parentNode->removeChild($div);                // CHANGES DOM
   $elements = DOMXpath_reuser($f)->query("//div[@id]"); // INSTABLE! !!

可以发生外部事件 ,并且查询无法正常工作!!

  • 什么时候 (DOMDocument方法会影响XPath?)
  • 为什么我们不能使用像normalizeDocument这样的东西来“刷新DOM”(存在?)?
  • 只有“新的DOMXpath($ doc);” 总是安全吗? 还需要重新加载$ ​​doc吗?

DOMXpath受DOMDocument上的load *()方法的影响。 加载新的xml或html后,需要重新创建DOMXpath实例:

$xml = '<xml/>';    
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);

var_dump($xpath->document === $dom); // bool(true)

$dom->loadXml($xml);

var_dump($xpath->document === $dom); // bool(false)

在DOMXpath_reuser()中,存储静态变量并根据文件名重新创建xpath。 如果要重用Xpath对象,建议扩展DOMDocument。 这样你只需要传递$ dom变量。 它可以使用存储的xml文件以及xml字符串或您正在创建的文档。

以下类使用方法xpath()扩展DOMDocument,该方法始终为其返回有效的DOMXpath实例。 它也存储和注册命名空间:

class MyDOMDocument
  extends DOMDocument {

  private $_xpath = NULL;
  private $_namespaces = array();

  public function xpath() {
    // if the xpath instance is missing or not attached to the document
    if (is_null($this->_xpath) || $this->_xpath->document != $this) {
      // create a new one
      $this->_xpath = new DOMXpath($this);
      // and register the namespaces for it
      foreach ($this->_namespaces as $prefix => $namespace) {
        $this->_xpath->registerNamespace($prefix, $namespace);
      }
    }
    return $this->_xpath;
  }

  public function registerNamespaces(array $namespaces) {
    $this->_namespaces = array_merge($this->_namespaces, $namespaces);
    if (isset($this->_xpath)) {
      foreach ($namespaces as $prefix => $namespace) {
        $this->_xpath->registerNamespace($prefix, $namespace);
      }
    }
  }
}

$xml = <<<'ATOM'
  <feed xmlns="http://www.w3.org/2005/Atom">
    <title>Test</title>
  </feed>
ATOM;


$dom = new MyDOMDocument();
$dom->registerNamespaces(
  array(
    'atom' => 'http://www.w3.org/2005/Atom'
  )
);
$dom->loadXml($xml);
// created, first access
var_dump($dom->xpath()->evaluate('string(/atom:feed/atom:title)', NULL, FALSE));
$dom->loadXml($xml);
// recreated, connection was lost
var_dump($dom->xpath()->evaluate('string(/atom:feed/atom:title)', NULL, FALSE));

DOMXpath类(而不是另一个问题中的XSLTProcessor)在构造函数中使用对给定DOMDocument对象的引用。 DOMXpath基于给定的DOMDocument创建libxml上下文对象,并将其保存到内部类数据。 除了libxml上下文之外,它还s saves references to original contructor参数中给出的s saves references to original DOMDocument`的s saves references to original

那意味着什么:

部分样本来自ThomasWeinert回答:

var_dump($xpath->document === $dom); // bool(true)  
$dom->loadXml($xml);    
var_dump($xpath->document === $dom); // bool(false)

由于$dom已经保存了指向新libxml数据的指针,但DOMXpath在加载之前保存了$dom libxml上下文,并且在加载DOMXpath保存了指向真实文档的指针。

现在关于query工作

如果它应该返回XPATH_NODESET (如你的情况那样), XPATH_NODESET做一个节点拷贝 - 逐个节点迭代抛出检测到的节点集(从468行开始的\\ext\\dom\\xpath.c )。 复制但原始文档节点为父级 它意味着您可以修改结果但这消失了您的XPath和DOMDocument连接。

XPath结果提供了一个知道其来源的parentNode memeber:

  • 对于属性值,parentNode返回携带它们的元素。 一个例子是// foo / @ attribute,其中父元素是foo元素。
  • 对于text()函数(如在// text()中),它返回包含返回的文本或尾部的元素。
  • 请注意,parentNode可能并不总是返回一个元素。 例如,XPath函数string()和concat()将构造没有原点的字符串。 对于他们,parentNode将返回None。

所以,

  1. 没有任何理由缓存XPath 它除了xmlXPathNewContext之外没有任何东西(只是分配轻量级内部结构 )。
  2. 每次修改DOMDocument (removeChild,replaceChild等)时,都应该重新创建XPath
  3. 我们不能使用像normalizeDocument这样的东西来“刷新DOM”,因为它改变了内部文档结构并使在Xpath构造函数中创建的xmlXPathNewContext无效。
  4. 只有“新的DOMXpath($ doc);” 总是安全吗? 是的,如果你没有在Xpath使用之间更改$ doc。 还需要重新加载$ ​​doc - 否,因为它使以前创建的xmlXPathNewContext无效。

(这不是一个真正的答案,而是在此处发布的评论和答案的合并及相关问题)


问题的DOMXpath_reuser函数的这个新版本包含@ThomasWeinert建议(用于避免外部重新load DOM更改)和一个选项$enforceRefresh来解决不稳定性问题(因为相关问题显示程序员必须检测何时 )。

   function DOMXpath_reuser_v2($file, $enforceRefresh=0) {  //changed here
      static $doc=NULL;
      static $docName='';
      static $xp=NULL;
      if (!$doc)
                $doc = new DOMDocument();
      if ( $file!=$docName || ($xp && $doc !== $xp->document) ) { // changed here
                $doc->load($file);
                $xp = NULL;
      } elseif ($enforceRefresh==2) {  // add this new refresh mode
                $doc->loadXML($doc->saveXML());
                $xp = NULL;
      }
      if (!$xp || $enforceRefresh==1)  //changed here
                $xp = new DOMXpath($doc);
      return $xp;
   }

什么时候必须使用$ enforceRefresh = 1?

...也许是一个开放的问题,只有一些提示和线索......

  • 当DOM提交到setAttribute,removeChild,replaceChild等时
  • ...? 更多病例?

什么时候必须使用$ enforceRefresh = 2?

...也许是一个开放的问题,只有一些提示和线索......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM