@@ -820,11 +820,62 @@ int dom_node_text_content_write(dom_object *obj, zval *newval)
820820
821821/* }}} */
822822
823- static xmlNodePtr _php_dom_insert_fragment (xmlNodePtr nodep , xmlNodePtr prevsib , xmlNodePtr nextsib , xmlNodePtr fragment , dom_object * intern , dom_object * childobj ) /* {{{ */
823+ /* Returns true if the node was changed, false otherwise. */
824+ static bool dom_set_document_ref_obj_single (xmlNodePtr node , xmlDocPtr doc , php_libxml_ref_obj * document )
824825{
825- xmlNodePtr newchild , node ;
826+ dom_object * childobj = php_dom_object_get_data (node );
827+ if (childobj && !childobj -> document ) {
828+ childobj -> document = document ;
829+ document -> refcount ++ ;
830+ return true;
831+ }
832+ return false;
833+ }
834+
835+ /* TODO: on 8.4 replace the loop with the tree walk helper function. */
836+ static void dom_set_document_pointers (xmlNodePtr node , xmlDocPtr doc , php_libxml_ref_obj * document )
837+ {
838+ /* Applies the document to the entire subtree. */
839+ xmlSetTreeDoc (node , doc );
840+
841+ if (!dom_set_document_ref_obj_single (node , doc , document )) {
842+ return ;
843+ }
844+
845+ xmlNodePtr base = node ;
846+ node = node -> children ;
847+ while (node != NULL ) {
848+ ZEND_ASSERT (node != base );
849+
850+ if (!dom_set_document_ref_obj_single (node , doc , document )) {
851+ break ;
852+ }
853+
854+ if (node -> type == XML_ELEMENT_NODE ) {
855+ if (node -> children ) {
856+ node = node -> children ;
857+ continue ;
858+ }
859+ }
860+
861+ if (node -> next ) {
862+ node = node -> next ;
863+ } else {
864+ /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
865+ do {
866+ node = node -> parent ;
867+ if (node == base ) {
868+ return ;
869+ }
870+ } while (node -> next == NULL );
871+ node = node -> next ;
872+ }
873+ }
874+ }
826875
827- newchild = fragment -> children ;
876+ static xmlNodePtr _php_dom_insert_fragment (xmlNodePtr nodep , xmlNodePtr prevsib , xmlNodePtr nextsib , xmlNodePtr fragment , dom_object * intern , dom_object * childobj ) /* {{{ */
877+ {
878+ xmlNodePtr newchild = fragment -> children ;
828879
829880 if (newchild ) {
830881 if (prevsib == NULL ) {
@@ -840,17 +891,10 @@ static xmlNodePtr _php_dom_insert_fragment(xmlNodePtr nodep, xmlNodePtr prevsib,
840891 nextsib -> prev = fragment -> last ;
841892 }
842893
843- node = newchild ;
894+ /* Assign parent node pointer */
895+ xmlNodePtr node = newchild ;
844896 while (node != NULL ) {
845897 node -> parent = nodep ;
846- if (node -> doc != nodep -> doc ) {
847- xmlSetTreeDoc (node , nodep -> doc );
848- if (node -> _private != NULL ) {
849- childobj = node -> _private ;
850- childobj -> document = intern -> document ;
851- php_libxml_increment_doc_ref ((php_libxml_node_object * )childobj , NULL );
852- }
853- }
854898 if (node == fragment -> last ) {
855899 break ;
856900 }
@@ -930,8 +974,7 @@ PHP_METHOD(DOMNode, insertBefore)
930974 }
931975
932976 if (child -> doc == NULL && parentp -> doc != NULL ) {
933- childobj -> document = intern -> document ;
934- php_libxml_increment_doc_ref ((php_libxml_node_object * )childobj , NULL );
977+ dom_set_document_pointers (child , parentp -> doc , intern -> document );
935978 }
936979
937980 php_libxml_invalidate_node_list_cache (intern -> document );
@@ -949,9 +992,6 @@ PHP_METHOD(DOMNode, insertBefore)
949992
950993 if (child -> type == XML_TEXT_NODE && (refp -> type == XML_TEXT_NODE ||
951994 (refp -> prev != NULL && refp -> prev -> type == XML_TEXT_NODE ))) {
952- if (child -> doc == NULL ) {
953- xmlSetTreeDoc (child , parentp -> doc );
954- }
955995 new_child = child ;
956996 new_child -> parent = refp -> parent ;
957997 new_child -> next = refp ;
@@ -1003,9 +1043,6 @@ PHP_METHOD(DOMNode, insertBefore)
10031043 }
10041044 if (child -> type == XML_TEXT_NODE && parentp -> last != NULL && parentp -> last -> type == XML_TEXT_NODE ) {
10051045 child -> parent = parentp ;
1006- if (child -> doc == NULL ) {
1007- xmlSetTreeDoc (child , parentp -> doc );
1008- }
10091046 new_child = child ;
10101047 if (parentp -> children == NULL ) {
10111048 parentp -> children = child ;
@@ -1111,6 +1148,10 @@ PHP_METHOD(DOMNode, replaceChild)
11111148 RETURN_FALSE ;
11121149 }
11131150
1151+ if (newchild -> doc == NULL && nodep -> doc != NULL ) {
1152+ dom_set_document_pointers (newchild , nodep -> doc , intern -> document );
1153+ }
1154+
11141155 if (newchild -> type == XML_DOCUMENT_FRAG_NODE ) {
11151156 xmlNodePtr prevsib , nextsib ;
11161157 prevsib = oldchild -> prev ;
@@ -1127,11 +1168,6 @@ PHP_METHOD(DOMNode, replaceChild)
11271168 xmlDtdPtr intSubset = xmlGetIntSubset (nodep -> doc );
11281169 replacedoctype = (intSubset == (xmlDtd * ) oldchild );
11291170
1130- if (newchild -> doc == NULL && nodep -> doc != NULL ) {
1131- xmlSetTreeDoc (newchild , nodep -> doc );
1132- newchildobj -> document = intern -> document ;
1133- php_libxml_increment_doc_ref ((php_libxml_node_object * )newchildobj , NULL );
1134- }
11351171 xmlReplaceNode (oldchild , newchild );
11361172 dom_reconcile_ns (nodep -> doc , newchild );
11371173
@@ -1216,8 +1252,7 @@ PHP_METHOD(DOMNode, appendChild)
12161252 }
12171253
12181254 if (child -> doc == NULL && nodep -> doc != NULL ) {
1219- childobj -> document = intern -> document ;
1220- php_libxml_increment_doc_ref ((php_libxml_node_object * )childobj , NULL );
1255+ dom_set_document_pointers (child , nodep -> doc , intern -> document );
12211256 }
12221257
12231258 if (child -> parent != NULL ){
@@ -1226,9 +1261,6 @@ PHP_METHOD(DOMNode, appendChild)
12261261
12271262 if (child -> type == XML_TEXT_NODE && nodep -> last != NULL && nodep -> last -> type == XML_TEXT_NODE ) {
12281263 child -> parent = nodep ;
1229- if (child -> doc == NULL ) {
1230- xmlSetTreeDoc (child , nodep -> doc );
1231- }
12321264 new_child = child ;
12331265 if (nodep -> children == NULL ) {
12341266 nodep -> children = child ;
0 commit comments