From 38a7cdce76df1e7d256e135348dd333691711253 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 6 Sep 2026 10:58:53 -0400 Subject: [PATCH] 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 --- NEWS | 3 + ext/simplexml/simplexml.c | 7 ++- .../addChild_ns_filter_returned_element.phpt | 56 +++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 ext/simplexml/tests/addChild_ns_filter_returned_element.phpt diff --git a/NEWS b/NEWS index 1cc265f02e8d..ef5037cc1dc0 100644 --- a/NEWS +++ b/NEWS @@ -114,6 +114,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 44fdef5e12d7..9e5f293fe787 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 000000000000..b0452f30182b --- /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-- +'); +$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(''); +$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(''); +$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(''); +$f = $q->addChild('kid'); +$f->addAttribute('id', '7'); +echo trim($q->asXML()), "\n"; +echo (string) $f['id'], "\n"; + +$m = new SimpleXMLElement(''); +$g = $m->addChild('kid', null, 'http://example.com'); +$g->addAttribute('id', '8'); +echo trim($m->asXML()), "\n"; +var_dump(isset($g['id'])); +?> +--EXPECT-- + +v +v +bool(true) + +w +w + +z +z +bool(true) + + +7 + + +bool(false)