Php 在DomDocument中,重用DOMXpath,是否稳定?
我正在使用下面的功能,但不确定它是否始终稳定/安全是吗? 何时以及谁能够稳定/安全地“重用部分DOMPath准备过程”? 为了简化使用,我们可以采用一个函数,用静态变量记忆最后的调用 函数DOMXpath\u重用器($file){ 静态$doc=NULL; 静态$docName=''; 静态$xp=NULL; 如果(!$doc) $doc=新的DOMDocument(); 如果($file!=$docName){ $doc->loadHTMLFile($file); $xp=NULL; } 如果(!$xp) $xp=新的DOMXpath($doc); 返回$xp;//?返回值始终稳定?? }Php 在DomDocument中,重用DOMXpath,是否稳定?,php,xpath,Php,Xpath,我正在使用下面的功能,但不确定它是否始终稳定/安全是吗? 何时以及谁能够稳定/安全地“重用部分DOMPath准备过程”? 为了简化使用,我们可以采用一个函数,用静态变量记忆最后的调用 函数DOMXpath\u重用器($file){ 静态$doc=NULL; 静态$docName=''; 静态$xp=NULL; 如果(!$doc) $doc=新的DOMDocument(); 如果($file!=$docName){ $doc->loadHTMLFile($file); $xp=NULL; } 如果
目前的问题类似于关于XSLTProcessor重用的问题。 在这两个问题中,这个问题可以推广到使用LibXML2作为DomDocument实现的任何语言或框架 还有一个相关问题:
说明 重用非常常见(示例): 但是,如果您执行类似于
removeChild
,replaceChild
等操作(示例)
可能会发生额外的事情,并且查询无法按预期工作
- 何时(哪些DOMDocument方法影响XPath?)
- 为什么我们不能使用类似“刷新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)
$xml='';
$dom=新的DOMDocument();
$dom->loadXml($xml);
$xpath=newdomxpath($dom);
var_dump($xpath->document==$dom);//布尔(真)
$dom->loadXml($xml);
var_dump($xpath->document==$dom);//布尔(假)
在DOMXpath_reuser()中,存储一个静态变量,并根据文件名重新创建xpath。如果要重用Xpath对象,建议扩展DOMDocument。这样,您只需要传递$dom变量。它可以处理存储的xml文件以及xml字符串或您正在创建的文档
下面的类使用xpath()方法扩展了DOMDocument,该方法始终为DOMDocument返回有效的DOMDxpath实例。它还存储和注册名称空间:
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));
类MyDOMDocument
扩展DOMDocument{
私有$_xpath=NULL;
私有$_名称空间=数组();
公共函数xpath(){
//如果xpath实例丢失或未附加到文档
如果(为空($this->uxpath)|$$this->\uxpath->document!=$this){
//创建一个新的
$this->xpath=newdomxpath($this);
//并为其注册名称空间
foreach($this->\名称空间为$prefix=>$namespace){
$this->_xpath->registerNamespace($prefix,$namespace);
}
}
返回$this->\u xpath;
}
公共函数注册表命名空间(数组$namespace){
$this->\u namespaces=array\u merge($this->\u namespaces,$namespaces);
if(isset($this->\uxpath)){
foreach($prefix=>$namespace的名称空间){
$this->_xpath->registerNamespace($prefix,$namespace);
}
}
}
}
$xml=loadXml($xml);
//创建,第一次访问
var_dump($dom->xpath()->evaluate('string(/atom:feed/atom:title'),NULL,FALSE));
$dom->loadXml($xml);
//已重新创建,连接已丢失
var_dump($dom->xpath()->evaluate('string(/atom:feed/atom:title'),NULL,FALSE));
(这不是真正的答案,而是此处发布的评论和答案以及相关问题的综合)
这个新版本的问题的
DOMXpath\u reuse
函数包含@ThomasWeinert建议(通过外部重新加载避免DOM更改)和一个选项$enforceRefresh
,以解决不稳定性问题(相关问题表明程序员必须在何时检测)
函数DOMXpath\u reuser\u v2($file,$enforceRefresh=0){//在此处更改
静态$doc=NULL;
静态$docName='';
静态$xp=NULL;
如果(!$doc)
$doc=新的DOMDocument();
如果($file!=$docName | |($xp&&$doc!==$xp->document)){//在此处更改
$doc->load($file);
$xp=NULL;
}elseif($enforceRefresh==2){//添加此新的刷新模式
$doc->loadXML($doc->saveXML());
$xp=NULL;
}
如果(!$xp | |$enforceRefresh==1)//在此处更改
$xp=新的DOMXpath($doc);
返回$xp;
}
何时必须使用$enforceRefresh=1?
。。。也许是一个公开的问题,只有一些小提示和线索
- 当DOM提交给setAttribute、removeChild、replaceChild等时
- 。。。?更多的案例
何时必须使用$enforceRefresh=2?
。。。也许是一个公开的问题,只有一些小提示和线索
- 当DOM受到索引不一致等影响时,请参阅
- 。。。?更多的案例
类(而不是中的XSLTProcessor)使用对构造函数中给定的
DOMDocument
对象的引用DOMXpath
基于给定的DOMDocument
创建libxml
上下文对象,并将其保存到内部类数据中。除了libxml
context之外,它还s保存对构造函数参数中给定的原始
DOMDocument`的引用
这意味着什么:
答案中的部分样本:
加载后给出false,因为加载前,$dom
已持有指向新libxml
数据的指针,但DOMXpath
持有libxml
上下文,加载后持有指向真实文档的指针
现在关于查询
工作
如果它应该返回XPATH\u NODESET
(与您的情况相同),那么它将创建一个节点副本-逐节点迭代抛出检测到的节点集(\ext)
$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)
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));
var_dump($xpath->document === $dom); // bool(true)
$dom->loadXml($xml);
var_dump($xpath->document === $dom); // bool(false)