From 5c26258eeb2741268a59e99c446e1b699bf06b35 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Thu, 13 Jul 2023 15:06:07 +0200
Subject: [PATCH] Handle fragments consisting out of multiple children without
a single root correctly
Closes GH-11698.
---
ext/dom/parentnode.c | 30 +++++++--------
...ragments_multiple_nodes_DOMParentNode.phpt | 38 +++++++++++++++++++
2 files changed, 53 insertions(+), 15 deletions(-)
create mode 100644 ext/dom/tests/fragments_multiple_nodes_DOMParentNode.phpt
diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c
index dba580ead7c..8467fbf0603 100644
--- a/ext/dom/parentnode.c
+++ b/ext/dom/parentnode.c
@@ -183,15 +183,7 @@ xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNod
goto err;
}
- if (newNode->type == XML_DOCUMENT_FRAG_NODE) {
- /* Unpack document fragment nodes, the behaviour differs for different libxml2 versions. */
- newNode = newNode->children;
- if (UNEXPECTED(newNode == NULL)) {
- /* No nodes to add, nothing to do here */
- continue;
- }
- xmlUnlinkNode(newNode);
- } else if (newNode->parent != NULL) {
+ if (newNode->parent != NULL) {
xmlUnlinkNode(newNode);
}
@@ -210,14 +202,23 @@ xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNod
newNode = xmlCopyNode(newNode, 1);
}
- if (!xmlAddChild(fragment, newNode)) {
+ if (newNode->type == XML_DOCUMENT_FRAG_NODE) {
+ /* Unpack document fragment nodes, the behaviour differs for different libxml2 versions. */
+ newNode = newNode->children;
+ while (newNode) {
+ xmlNodePtr next = newNode->next;
+ xmlUnlinkNode(newNode);
+ if (!xmlAddChild(fragment, newNode)) {
+ goto hierarchy_request_err;
+ }
+ newNode = next;
+ }
+ } else if (!xmlAddChild(fragment, newNode)) {
if (will_free) {
xmlFreeNode(newNode);
}
goto hierarchy_request_err;
}
-
- continue;
} else {
zend_argument_type_error(i + 1, "must be of type DOMNode|string, %s given", zend_zval_type_name(&nodes[i]));
goto err;
@@ -363,14 +364,13 @@ static void dom_pre_insert(xmlNodePtr insertion_point, xmlNodePtr parentNode, xm
/* Place it as last node */
if (parentNode->children) {
/* There are children */
- fragment->last->prev = parentNode->last;
- newchild->prev = parentNode->last->prev;
+ 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 = newchild;
}
+ parentNode->last = fragment->last;
} else {
/* Insert fragment before insertion_point */
fragment->last->next = insertion_point;
diff --git a/ext/dom/tests/fragments_multiple_nodes_DOMParentNode.phpt b/ext/dom/tests/fragments_multiple_nodes_DOMParentNode.phpt
new file mode 100644
index 00000000000..3997a23ca97
--- /dev/null
+++ b/ext/dom/tests/fragments_multiple_nodes_DOMParentNode.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Handling fragments of multiple nodes for DOMParentNode
+--EXTENSIONS--
+dom
+--FILE--
+loadXML('
1
2 + + +1
2foo + + +3
41
2foo