簡體   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