diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index 8467fbf0603a4..758014ddedd13 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -545,21 +545,39 @@ void dom_child_node_remove(dom_object *context) void dom_child_replace_with(dom_object *context, zval *nodes, int nodesc) { + /* Spec link: https://dom.spec.whatwg.org/#dom-childnode-replacewith*/ + xmlNodePtr child = dom_object_get_node(context); + + /* Spec step 1 */ xmlNodePtr parentNode = child->parent; + /* Spec step 2 */ + if (!parentNode) { + return; + } int stricterror = dom_get_strict_error(context->document); if (UNEXPECTED(dom_child_removal_preconditions(child, stricterror) != SUCCESS)) { return; } - xmlNodePtr insertion_point = child->next; + /* Spec step 3: find first following child not in nodes; otherwise null */ + xmlNodePtr viable_next_sibling = child->next; + while (viable_next_sibling) { + if (!dom_is_node_in_list(nodes, nodesc, viable_next_sibling)) { + break; + } + viable_next_sibling = viable_next_sibling->next; + } + /* Spec step 4: convert nodes into fragment */ xmlNodePtr fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); if (UNEXPECTED(fragment == NULL)) { return; } + /* Spec step 5: perform the replacement */ + xmlNodePtr newchild = fragment->children; xmlDocPtr doc = parentNode->doc; @@ -571,7 +589,7 @@ void dom_child_replace_with(dom_object *context, zval *nodes, int nodesc) if (newchild) { xmlNodePtr last = fragment->last; - dom_pre_insert(insertion_point, parentNode, newchild, fragment); + dom_pre_insert(viable_next_sibling, parentNode, newchild, fragment); dom_fragment_assign_parent_node(parentNode, fragment); dom_reconcile_ns_list(doc, newchild, last); diff --git a/ext/dom/tests/replaceWith_no_parent.phpt b/ext/dom/tests/replaceWith_no_parent.phpt new file mode 100644 index 0000000000000..923df4affbc5e --- /dev/null +++ b/ext/dom/tests/replaceWith_no_parent.phpt @@ -0,0 +1,28 @@ +--TEST-- +replaceWith() without a parent +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + + + +XML); + +$container = $doc->documentElement; +$child = $container->firstElementChild; + +$test = $doc->createElement('foo'); +$test->replaceWith($child); +echo $doc->saveXML(); +echo $doc->saveXML($test); +?> +--EXPECT-- + + + + + diff --git a/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt b/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt new file mode 100644 index 0000000000000..61458d6cb72dc --- /dev/null +++ b/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt @@ -0,0 +1,36 @@ +--TEST-- +replaceWith() with a non-variable next sibling +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + + + + + +XML); + +$container = $doc->documentElement; +$child = $container->firstElementChild; +$alone = $child->firstElementChild; + +$child->after($alone); +echo $doc->saveXML(); +$child->replaceWith($alone); +echo $doc->saveXML(); +?> +--EXPECT-- + + + + + + + + + +