[英]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.