diff --git a/ext/dom/node.c b/ext/dom/node.c
index 880c8cfe3e794..cd92cea2437e6 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -769,17 +769,28 @@ int dom_node_text_content_write(dom_object *obj, zval *newval)
return FAILURE;
}
- if (nodep->type == XML_ELEMENT_NODE || nodep->type == XML_ATTRIBUTE_NODE) {
+ const xmlChar *xmlChars = (const xmlChar *) ZSTR_VAL(str);
+ int type = nodep->type;
+
+ /* We can't directly call xmlNodeSetContent, because it might encode the string through
+ * xmlStringLenGetNodeList for types XML_DOCUMENT_FRAG_NODE, XML_ELEMENT_NODE, XML_ATTRIBUTE_NODE.
+ * See tree.c:xmlNodeSetContent in libxml.
+ * In these cases we need to use a text node to avoid the encoding.
+ * For the other cases, we *can* rely on xmlNodeSetContent because it is either a no-op, or handles
+ * the content without encoding and clears the properties field if needed. */
+ if (type == XML_DOCUMENT_FRAG_NODE || type == XML_ELEMENT_NODE || type == XML_ATTRIBUTE_NODE) {
if (nodep->children) {
node_list_unlink(nodep->children);
php_libxml_node_free_list((xmlNodePtr) nodep->children);
nodep->children = NULL;
}
+
+ xmlNode *textNode = xmlNewText(xmlChars);
+ xmlAddChild(nodep, textNode);
+ } else {
+ xmlNodeSetContent(nodep, xmlChars);
}
- /* we have to use xmlNodeAddContent() to get the same behavior as with xmlNewText() */
- xmlNodeSetContent(nodep, (xmlChar *) "");
- xmlNodeAddContent(nodep, (xmlChar *) ZSTR_VAL(str));
zend_string_release_ex(str, 0);
return SUCCESS;
diff --git a/ext/dom/tests/gh10234.phpt b/ext/dom/tests/gh10234.phpt
new file mode 100644
index 0000000000000..63571edac3160
--- /dev/null
+++ b/ext/dom/tests/gh10234.phpt
@@ -0,0 +1,66 @@
+--TEST--
+GH-10234 (Setting DOMAttr::textContent results in an empty attribute value.)
+--EXTENSIONS--
+dom
+--FILE--
+loadXML('');
+$attribute = $document->documentElement->getAttributeNode('attribute');
+
+var_dump($document->saveHTML());
+var_dump($attribute->textContent);
+
+$attribute->textContent = 'new value';
+var_dump($attribute->textContent);
+var_dump($document->saveHTML());
+
+$attribute->textContent = 'hello & world';
+var_dump($attribute->textContent);
+var_dump($document->saveHTML());
+
+$attribute->textContent = 'hi';
+var_dump($attribute->textContent);
+var_dump($document->saveHTML());
+
+$document->documentElement->textContent = 'hello & world';
+var_dump($document->documentElement->textContent);
+var_dump($document->saveHTML());
+
+$document->documentElement->textContent = 'hi';
+var_dump($document->documentElement->textContent);
+var_dump($document->saveHTML());
+
+$document->documentElement->textContent = 'quote "test"';
+var_dump($document->documentElement->textContent);
+var_dump($document->saveHTML());
+
+$document->documentElement->textContent = "quote 'test'";
+var_dump($document->documentElement->textContent);
+var_dump($document->saveHTML());
+?>
+--EXPECT--
+string(38) "
+"
+string(5) "value"
+string(9) "new value"
+string(42) "
+"
+string(13) "hello & world"
+string(50) "
+"
+string(9) "hi"
+string(54) "
+"
+string(13) "hello & world"
+string(71) "hello & world
+"
+string(9) "hi"
+string(75) "<b>hi</b>
+"
+string(12) "quote "test""
+string(66) "quote "test"
+"
+string(12) "quote 'test'"
+string(66) "quote 'test'
+"