php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #28721 appendChild() and insertBefore() unset DOMText node arguments (includes patch)
Submitted: 2004-06-10 07:12 UTC Modified: 2004-06-13 12:14 UTC
From: benjcarson at digitaljunkies dot ca Assigned:
Status: Closed Package: DOM XML related
PHP Version: 5CVS-2004-06-10 (dev) OS: Linux
Private report: No CVE-ID:
 [2004-06-10 07:12 UTC] benjcarson at digitaljunkies dot ca
Description:
------------
If a DOMText node is added to a node using appendChild() or insertBefore(), the node is destroyed (i.e. the parameter's properties are cleared) and a new node is returned instead.  In addition, when a text node is inserted it is collapsed with adjacent text nodes.  This behaviour is inconsistent with other DOM implementations (e.g. Mozilla, gdome).  It means that code like this:

$p->appendChild($textNode);

will unset all of $textNode's properties.


Here is an html version of the reproduce code, for an example of what your browser does:
http://www.digitaljunkies.ca/~benj/dom_insert_demo.php

I've tried to fix this and I've rolled a patch available at:
http://www.digitaljunkies.ca/~benj/node.c.diff

Basically what I've done is remove any special handling of text nodes.  In appendChild() I also manually add the child to the parent's children list.  This is because xmlAddChild  collapses adjacent text nodes.  (I figure that if adjacent text nodes need to be collapsed, users can use DomDocument::normalize().)

In looking at the code, there may be a similar problem with attribute nodes, but since I'm not as familliar with manipulating them, I'm not sure what the desired behaviour should be.

Please indicate if there are any problems with the patch.  Thanks.

Reproduce code:
---------------
#!/usr/bin/php
<?php
$xml = new DomDocument();

$p = $xml->createElement("p");

$p->appendChild($t1 = $xml->createTextNode(" t1 "));
$p->appendChild($b = $xml->createElement("b"));
$b->appendChild($xml->createTextNode("X"));
$p->appendChild($t2 = $xml->createTextNode(""));

$ret = $p->appendChild($t1);
var_dump($t1->nodeName);
var_dump($ret->nodeName);
var_dump( $t1 === $ret );
var_dump( $p->lastChild->nodeValue );

?>

Expected result:
----------------
string(5) "#text"
string(5) "#text"
bool(true)
string(4) " t1 "


Actual result:
--------------
NULL
string(5) "#text"
bool(false)
string(8) " t2  t1 "


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2004-06-10 20:22 UTC] benjcarson at digitaljunkies dot ca
There was a bug with insertBefore() in the patch.  If you downloaded the patch prior to this post, please download it again.
 [2004-06-10 22:34 UTC] rrichards@php.net
Can you try with this patch? The text nodes piece is working fine. Havent had a chance to test out the domfragment stuff yet. I would like to use libxml functions as much as possible any only fall back to custom coding when absolutely necessary.

http://ctindustries.net/dom/dom_node.diff.txt
 [2004-06-11 01:00 UTC] benjcarson at digitaljunkies dot ca
Well, the text node stuff seems to work well, but when I tried adding a document fragment, php segfaulted.  It also seemed to modify other parts of the tree.  Try running the test script below, and note how $p eventually includes nodes from $d.

Here's my test code: 

<?php
function print_node(DomNode $node) {
  echo "name (value): " . $node->nodeName . " (" . $node->nodeValue . ")\n";
}

$xml = new DomDocument();

$p = $xml->createElement("p");

$p->appendChild($p1 = $xml->createTextNode(" p1 "));
$p->appendChild($b = $xml->createElement("b"));
$b->appendChild($xml->createTextNode("p2"));
$p->appendChild($p3 = $xml->createTextNode(" p3 "));
$p->appendChild($xml->createTextNode(" p4 "));


$d = $xml->createElement("div");
$d->appendChild($d1 = $xml->createTextNode(" d1 "));
$d->appendChild($b2 = $xml->createElement("b"));
$b2->appendChild($xml->createTextNode("d2"));
$d->appendChild($d3 = $xml->createTextNode(" d3 "));
$d->appendChild($xml->createTextNode(" d4 "));

$frag = $xml->createDocumentFragment();

$t5 = $frag->appendChild($xml->createTextNode(" frag1 "));
$frag->appendChild($i = $xml->createElement("i"));
$i->appendChild($xml->createTextNode(" frag2 "));
$frag->appendChild($xml->createTextNOde(" frag3 "));

echo "\np:\n";
print_node($p);

echo "\nFragment:\n";
print_node($frag);

echo "\nAppending fragment to p:\n";
$p->appendChild($frag);

print_node($p);
echo "\nFragment:\n";
print_node($frag);

print_node($d);

echo "\nInserting fragment before d1\n";

// If the next line is executed, php segfaults on exit:
$d->insertBefore($frag, $d1);
print_node($d);

echo "\np:\n";
print_node($p);
?>


Here's a gdb backtrace:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 1080672096 (LWP 7486)]
php_libxml_decrement_node_ptr (object=0x84990e8) at /usr/src/php/php5-200406091430/ext/libxml/libxml.c:569
569                     ret_refcount = --obj_node->refcount;
(gdb) bt
#0  php_libxml_decrement_node_ptr (object=0x84990e8) at /usr/src/php/php5-200406091430/ext/libxml/libxml.c:569
#1  0x080873f0 in php_libxml_clear_object (object=0x84990e8) at /usr/src/php/php5-200406091430/ext/libxml/libxml.c:131
#2  0x0808742e in php_libxml_unregister_node (nodep=0x0) at /usr/src/php/php5-200406091430/ext/libxml/libxml.c:144
#3  0x0808759b in php_libxml_node_free_list (node=0x84991d0) at /usr/src/php/php5-200406091430/ext/libxml/libxml.c:222
#4  0x0808823b in php_libxml_node_free_resource (node=0x8498c60) at /usr/src/php/php5-200406091430/ext/libxml/libxml.c:631
#5  0x080882e5 in php_libxml_node_decrement_resource (object=0x406a965c) at /usr/src/php/php5-200406091430/ext/libxml/libxml.c:665
#6  0x080a03b2 in dom_objects_free_storage (object=0x406a965c) at /usr/src/php/php5-200406091430/ext/dom/php_dom.c:773
#7  0x0822349d in zend_objects_store_del_ref (zobject=0x0) at /usr/src/php/php5-200406091430/Zend/zend_objects_API.c:149
#8  0x0820e673 in _zval_dtor (zvalue=0x406a963c) at /usr/src/php/php5-200406091430/Zend/zend_variables.c:61
#9  0x08205508 in _zval_ptr_dtor (zval_ptr=0x406a9720) at /usr/src/php/php5-200406091430/Zend/zend_execute_API.c:391
#10 0x082168f8 in zend_hash_apply_deleter (ht=0x83fadf0, p=0x406a9714) at /usr/src/php/php5-200406091430/Zend/zend_hash.c:568
#11 0x0821699c in zend_hash_graceful_reverse_destroy (ht=0x83fadf0) at /usr/src/php/php5-200406091430/Zend/zend_hash.c:634
#12 0x08205342 in shutdown_executor () at /usr/src/php/php5-200406091430/Zend/zend_execute_API.c:210
#13 0x0820fc64 in zend_deactivate () at /usr/src/php/php5-200406091430/Zend/zend.c:819
#14 0x081d55d8 in php_request_shutdown (dummy=0x0) at /usr/src/php/php5-200406091430/main/main.c:1212
#15 0x08243696 in main (argc=3, argv=0xbffffa54) at /usr/src/php/php5-200406091430/sapi/cli/php_cli.c:1046
 [2004-06-11 14:12 UTC] rrichards@php.net
Can you try again with an updated patch:
http://ctindustries.net/dom/dom_node.diff.txt

 [2004-06-11 17:44 UTC] benjcarson at digitaljunkies dot ca
The new patch seems to work great!  Thanks.  Would you like my test cases for ext/dom/tests?
 [2004-06-13 12:14 UTC] rrichards@php.net
This bug has been fixed in CVS.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.

Can you email me your test cases?
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Mon Apr 21 10:02:10 2014 UTC