diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index 4f3187db96978..d6b0705545a17 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -280,6 +280,33 @@ static zend_result dom_sanity_check_node_list_for_insertion(php_libxml_ref_obj * return SUCCESS; } +static void dom_pre_insert(xmlNodePtr insertion_point, xmlNodePtr parentNode, xmlNodePtr newchild, xmlNodePtr fragment) +{ + if (!insertion_point) { + /* Place it as last node */ + if (parentNode->children) { + /* There are children */ + newchild->prev = parentNode->last; + parentNode->last->next = newchild; + } else { + /* No children, because they moved out when they became a fragment */ + parentNode->children = newchild; + } + parentNode->last = fragment->last; + } else { + /* Insert fragment before insertion_point */ + fragment->last->next = insertion_point; + if (insertion_point->prev) { + insertion_point->prev->next = newchild; + newchild->prev = insertion_point->prev; + } + insertion_point->prev = fragment->last; + if (parentNode->children == insertion_point) { + parentNode->children = newchild; + } + } +} + void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) { xmlNode *parentNode = dom_object_get_node(context); @@ -331,21 +358,18 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc) return; } - xmlNodePtr newchild, nextsib; xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); if (fragment == NULL) { return; } - newchild = fragment->children; - nextsib = parentNode->children; + xmlNode *newchild = fragment->children; if (newchild) { xmlNodePtr last = fragment->last; - parentNode->children = newchild; - fragment->last->next = nextsib; - nextsib->prev = last; + + dom_pre_insert(parentNode->children, parentNode, newchild, fragment); dom_fragment_assign_parent_node(parentNode, fragment); @@ -355,33 +379,6 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc) xmlFree(fragment); } -static void dom_pre_insert(xmlNodePtr insertion_point, xmlNodePtr parentNode, xmlNodePtr newchild, xmlNodePtr fragment) -{ - if (!insertion_point) { - /* Place it as last node */ - if (parentNode->children) { - /* There are children */ - newchild->prev = parentNode->last; - parentNode->last->next = newchild; - } else { - /* No children, because they moved out when they became a fragment */ - parentNode->children = newchild; - } - parentNode->last = fragment->last; - } else { - /* Insert fragment before insertion_point */ - fragment->last->next = insertion_point; - if (insertion_point->prev) { - insertion_point->prev->next = newchild; - newchild->prev = insertion_point->prev; - } - insertion_point->prev = fragment->last; - if (parentNode->children == insertion_point) { - parentNode->children = newchild; - } - } -} - void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc) { /* Spec link: https://dom.spec.whatwg.org/#dom-childnode-after */ diff --git a/ext/dom/tests/gh11906.phpt b/ext/dom/tests/gh11906.phpt new file mode 100644 index 0000000000000..b971500290c36 --- /dev/null +++ b/ext/dom/tests/gh11906.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-11906 (prepend without children after creating fragment results in segfault) +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + + + +XML); + +$container = $doc->documentElement; +$child = $container->firstElementChild; + +$test = $doc->createElement('foo'); +$test->append($child); +echo $doc->saveXML(); +echo $doc->saveXML($test), "\n"; +$test->prepend($child); +echo $doc->saveXML(); +echo $doc->saveXML($test), "\n"; +$test->append($child); +?> +--EXPECT-- + + + + + + + + + +