I've been messing around with DOMDocument lately, and I've noticed that in order to transfer elements from one document to the next, I have to call $DOMDocument->importNode()
on the target DOMDocument
.
However, I'm running into weird issues, where once the originating document is destroyed, the cloned element misbehaves.
For example, here's some lovely working code:
$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"
Here's a demo: http://codepad.viper-7.com/pjd9Ty
The problem arises when I try using the elements after their original document is out of scope:
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;
The above results in the following errors:
PHP Warning: Couldn't fetch
DOMElement
. Node no longer exists in ...
PHP Notice: Undefined property:DOMElement::$tagName
in ...
Here's a demo: http://codepad.viper-7.com/c0kqOA
My question is twofold:
Shouldn't the returned elements exist even after the original document was destroyed, since they were cloned into the current document?
A workaround. For various reasons, I have to manipulate the elements after the original document is destroyed, but before I actually insert them into the DOM of the other DOMDocument
. Is there any way to accomplish this?
Clarification: I understand that if the elements are inserted into the DOM, it behaves as expected. But, as outlined above, my setup calls for the elements to be manipulated before being inserted into the DOM (long story). Given that the first example here works - and that manipulating elements outside of the DOM is standard procedure in JavaScript - shouldn't this be possible here as well?
The cloned node has a reference to $dom, but $dom has not. Internal PHP garbage collector destroys such nodes when the calling context changes. There is only one way to create this reference: $dom->documentElement->appendChild($node)
.
So, use code like this (static keyword will prevent garbage collector from destroying your variable):
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;