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


Accessing an imported element after the original DOMDocument is destroyed

我最近一直在摆弄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


我的问题有两个:

  1. 即使在原始文档被销毁后,返回的元素也不应该存在吗?因为它们被克隆到了当前文档中?

  2. 解决方法。由于各种原因,我必须在原始文档被销毁之后,但在实际将它们插入其他DOMDocument的DOM之前,对这些元素进行操作。有什么办法可以做到这一点吗?


澄清:我知道,如果元素被插入到DOM中,它的行为会像预期的那样。但是,如上所述,我的设置要求在将元素插入DOM之前对其进行操作(长话短说)。考虑到这里的第一个例子是有效的,并且在DOM之外操纵元素是JavaScript中的标准过程,这在这里不应该也是可能的吗?

克隆的节点有对$dom的引用,但$dom没有。当调用上下文发生更改时,内部PHP垃圾收集器会销毁此类节点。只有一种方法可以创建此引用:$dom->documentElement->appendChild($node)

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

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;