Commit 8b13fcc13ed for php.net
commit 8b13fcc13edfb49ca32f5a6c8563e7dd71ff5ed2
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Sun Sep 6 10:58:53 2026 -0400
simplexml: fix addChild() namespace filter on the returned element
addChild() passed the caller's prefix to node_as_zval_str() with isprefix
set to 0, so the returned element filtered its children by comparing that
prefix against the namespace href and matched nothing. Take the prefix from
the created node and mark it as one, but only when the caller asked for a
namespace, so a plain addChild() keeps the unfiltered view that its
attributes and non-namespaced children rely on.
Closes GH-23599
diff --git a/NEWS b/NEWS
index 935583e8733..31cb928f0bd 100644
--- a/NEWS
+++ b/NEWS
@@ -133,6 +133,9 @@ PHP NEWS
- SimpleXML:
. Fixed writing to a dimension of the object returned by attributes() not
creating the attribute. (Ilia Alshanetsky)
+ . Fixed child elements of the element returned by
+ SimpleXMLElement::addChild() not being accessible by property name when
+ namespaces are involved. (Ilia Alshanetsky)
- Zip:
. Fixed bug GH-23276 (ZipArchive subclass storing its own stream cannot be
diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c
index 44fdef5e12d..9e5f293fe78 100644
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@ -1679,6 +1679,7 @@ PHP_METHOD(SimpleXMLElement, addChild)
xmlNodePtr node, newnode;
xmlNsPtr nsptr = NULL;
xmlChar *localname, *prefix = NULL;
+ const xmlChar *retprefix = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!s!",
&qname, &qname_len, &value, &value_len, &nsuri, &nsuri_len) == FAILURE) {
@@ -1727,7 +1728,11 @@ PHP_METHOD(SimpleXMLElement, addChild)
}
}
- node_as_zval_str(sxe, newnode, return_value, SXE_ITER_NONE, localname, prefix, 0);
+ if ((prefix != NULL || nsuri != NULL) && newnode->ns != NULL) {
+ retprefix = newnode->ns->prefix;
+ }
+
+ node_as_zval_str(sxe, newnode, return_value, SXE_ITER_NONE, localname, retprefix, 1);
xmlFree(localname);
if (prefix != NULL) {
diff --git a/ext/simplexml/tests/addChild_ns_filter_returned_element.phpt b/ext/simplexml/tests/addChild_ns_filter_returned_element.phpt
new file mode 100644
index 00000000000..b0452f30182
--- /dev/null
+++ b/ext/simplexml/tests/addChild_ns_filter_returned_element.phpt
@@ -0,0 +1,56 @@
+--TEST--
+SimpleXMLElement::addChild() wrong namespace filter on returned element
+--EXTENSIONS--
+simplexml
+--FILE--
+<?php
+$x = new SimpleXMLElement('<r xmlns:a="http://example.com"/>');
+$c = $x->addChild('a:kid', null, 'http://example.com');
+$c->addChild('inner', 'v');
+echo trim($x->asXML()), "\n";
+echo (string) $c->inner, "\n";
+var_dump(isset($c->inner));
+
+$y = new SimpleXMLElement('<r xmlns:a="http://example.com"/>');
+$d = $y->addChild('kid', null, 'http://example.com');
+$d->addChild('inner', 'w');
+echo trim($y->asXML()), "\n";
+echo (string) $d->inner, "\n";
+
+$z = new SimpleXMLElement('<r/>');
+$e = $z->addChild('a:kid');
+$e->addChild('inner', 'z');
+echo trim($z->asXML()), "\n";
+echo (string) $e->inner, "\n";
+var_dump(isset($e->inner));
+
+$q = new SimpleXMLElement('<p:r xmlns:p="http://example.com/p"/>');
+$f = $q->addChild('kid');
+$f->addAttribute('id', '7');
+echo trim($q->asXML()), "\n";
+echo (string) $f['id'], "\n";
+
+$m = new SimpleXMLElement('<r xmlns:a="http://example.com"/>');
+$g = $m->addChild('kid', null, 'http://example.com');
+$g->addAttribute('id', '8');
+echo trim($m->asXML()), "\n";
+var_dump(isset($g['id']));
+?>
+--EXPECT--
+<?xml version="1.0"?>
+<r xmlns:a="http://example.com"><a:kid><a:inner>v</a:inner></a:kid></r>
+v
+bool(true)
+<?xml version="1.0"?>
+<r xmlns:a="http://example.com"><a:kid><a:inner>w</a:inner></a:kid></r>
+w
+<?xml version="1.0"?>
+<r><kid><inner>z</inner></kid></r>
+z
+bool(true)
+<?xml version="1.0"?>
+<p:r xmlns:p="http://example.com/p"><p:kid id="7"/></p:r>
+7
+<?xml version="1.0"?>
+<r xmlns:a="http://example.com"><a:kid id="8"/></r>
+bool(false)