[英]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
但是,如果您執行removeChild
, replaceChild
等操作(例如),
$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! !!
可以發生外部事件 ,並且查詢無法正常工作!!
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:
所以,
XPath
。 它除了xmlXPathNewContext
之外沒有任何東西(只是分配輕量級內部結構 )。 DOMDocument
(removeChild,replaceChild等)時,都應該重新創建XPath
。 Xpath
構造函數中創建的xmlXPathNewContext
無效。 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;
}
...也許是一個開放的問題,只有一些提示和線索......
...也許是一個開放的問題,只有一些提示和線索......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.