在原始 DOMDocument 被销毁后访问导入的元素

Posted

技术标签:

【中文标题】在原始 DOMDocument 被销毁后访问导入的元素【英文标题】:Accessing an imported element after the original DOMDocument is destroyed 【发布时间】:2013-01-19 15:47:33 【问题描述】:

我最近一直在搞乱 DOMDocument,我注意到为了将元素从一个文档传输到另一个文档,我必须在目标 DOMDocument 上调用 $DOMDocument->importNode()

但是,我遇到了奇怪的问题,一旦原始文档被破坏,克隆的元素就会出现异常。


例如,这里有一些可爱的工作代码:

$dom1 = new DOMDocument;
$dom2 = new DOMDocument;

$dom2->loadhtml('<div id="div"><span class="inner"></span></div>');
$div = $dom2->getElementById('div');

$children = $dom1->importNode( $div, true )->childNodes;

echo $children->item(0)->tagName; // Output: "span"

这是一个演示:http://codepad.viper-7.com/pjd9Ty


当我在原始文档超出范围后尝试使用元素时出现问题:

global $dom;
$dom = new DOMDocument;

function get_div_children () 

    global $dom;

    $local_dom = new DOMDocument;
    $local_dom->loadHTML('<div id="div"><span class="inner"></span></div>');

    $div = $local_dom->getElementById('div');

    return $dom->importNode( $div, true )->childNodes;


echo get_div_children()->item(0)->tagName;

以上导致以下错误:

PHP 警告:无法获取 DOMElement。节点不再存在于 ...PHP 通知:未定义的属性:DOMElement::$tagName 在 ...

这是一个演示:http://codepad.viper-7.com/c0kqOA


我的问题有两个:

    返回的元素是否应该在原始文档被销毁后仍然存在,因为它们被克隆到当前文档中?

    一种解决方法。由于各种原因,我必须在原始文档被销毁之后,但在我真正将它们插入到另一个DOMDocument 的 DOM 之前对元素进行操作。有没有办法做到这一点?


澄清:我知道如果将元素插入到 DOM 中,它会按预期运行。但是,如上所述,我的设置要求在插入 DOM 之前对元素进行操作(长篇大论)。鉴于这里的第一个示例有效 - 并且在 DOM 之外操作元素是 javascript 中的标准过程 - 这不应该在这里也是可能的吗?

【问题讨论】:

这没有发生,因为$local_dom 正在被销毁。还有一些其他问题正在发生。请参阅this demo,我将两个 DOM 对象都设置为全局对象,但问题仍然存在。 @nickb +1 哇!这真的很奇怪。它基本上与无功能代码完全相同。有一秒钟,我认为这可能与原来的 $div 元素被破坏有关,但 making that global doesn't either help。知道如何攻击它吗? 这里是another example;它仍然以某种方式与范围相关,但即使是unset($local_dom) can't simulate it。 @Jack - 是的。这只是变得越来越奇怪。恐怕我别无选择,只能重写我的这个庞大的类,以便传递的元素始终在 DOM 中:( 【参考方案1】:

克隆的节点有对 $dom 的引用,但 $dom 没有。当调用上下文发生变化时,内部 php 垃圾收集器会销毁这些节点。只有一种方法可以创建此引用:$dom-&gt;documentElement-&gt;appendChild($node)

所以,使用这样的代码(静态关键字将防止垃圾收集器破坏你的变量):

global $dom;
$dom = new DOMDocument;

function get_div_children () 

global $dom;

$local_dom = new DOMDocument;
$local_dom->loadHTML('<div id="div"><span class="inner"></span></div>');

$div = $local_dom->getElementById('div');
static $nodes;
$nodes = $dom->importNode( $div, true )->childNodes;
return $nodes;


echo get_div_children()->item(0)->tagName;

【讨论】:

以上是关于在原始 DOMDocument 被销毁后访问导入的元素的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript - 原始值包装类型

使用 PHP DOMDocument::loadXML 从 docx 文件导入数学方程和图像

警告:DOMDocument::loadXML():需要开始标记,在实体中找不到“<”

防止 DOMDocument::loadHTML() 转换实体

DOMDocument 保存无法打开流:如果 https [重复],则权限被拒绝

Java Web 浏览器关闭后Session就会被销毁吗?