Skip to content

Commit 6f215e0

Browse files
committed
Merge branch 'PHP-8.3'
* PHP-8.3: Fix GH-12616: DOM: Removing XMLNS namespace node results in invalid default: prefix Fix GH-12702: libxml2 2.12.0 issue building from src
2 parents 7658220 + 2b42b73 commit 6f215e0

File tree

5 files changed

+326
-8
lines changed

5 files changed

+326
-8
lines changed

ext/dom/element.c

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,83 @@ PHP_METHOD(DOMElement, setAttributeNS)
862862
}
863863
/* }}} end dom_element_set_attribute_ns */
864864

865+
static void dom_remove_eliminated_ns_single_element(xmlNodePtr node, xmlNsPtr eliminatedNs)
866+
{
867+
ZEND_ASSERT(node->type == XML_ELEMENT_NODE);
868+
if (node->ns == eliminatedNs) {
869+
node->ns = NULL;
870+
}
871+
872+
for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
873+
if (attr->ns == eliminatedNs) {
874+
attr->ns = NULL;
875+
}
876+
}
877+
}
878+
879+
static void dom_remove_eliminated_ns(xmlNodePtr node, xmlNsPtr eliminatedNs)
880+
{
881+
dom_remove_eliminated_ns_single_element(node, eliminatedNs);
882+
883+
xmlNodePtr base = node;
884+
node = node->children;
885+
while (node != NULL) {
886+
ZEND_ASSERT(node != base);
887+
888+
if (node->type == XML_ELEMENT_NODE) {
889+
dom_remove_eliminated_ns_single_element(node, eliminatedNs);
890+
891+
if (node->children) {
892+
node = node->children;
893+
continue;
894+
}
895+
}
896+
897+
if (node->next) {
898+
node = node->next;
899+
} else {
900+
/* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
901+
do {
902+
node = node->parent;
903+
if (node == base) {
904+
return;
905+
}
906+
} while (node->next == NULL);
907+
node = node->next;
908+
}
909+
}
910+
}
911+
912+
static void dom_eliminate_ns(xmlNodePtr nodep, xmlNsPtr nsptr)
913+
{
914+
if (nsptr->href != NULL) {
915+
xmlFree((char *) nsptr->href);
916+
nsptr->href = NULL;
917+
}
918+
if (nsptr->prefix != NULL) {
919+
xmlFree((char *) nsptr->prefix);
920+
nsptr->prefix = NULL;
921+
}
922+
923+
/* Remove it from the list and move it to the old ns list */
924+
xmlNsPtr current_ns = nodep->nsDef;
925+
if (current_ns == nsptr) {
926+
nodep->nsDef = nsptr->next;
927+
} else {
928+
do {
929+
if (current_ns->next == nsptr) {
930+
current_ns->next = nsptr->next;
931+
break;
932+
}
933+
current_ns = current_ns->next;
934+
} while (current_ns != NULL);
935+
}
936+
nsptr->next = NULL;
937+
php_libxml_set_old_ns(nodep->doc, nsptr);
938+
939+
dom_remove_eliminated_ns(nodep, nsptr);
940+
}
941+
865942
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS
866943
Since: DOM Level 2
867944
*/
@@ -887,14 +964,7 @@ PHP_METHOD(DOMElement, removeAttributeNS)
887964
nsptr = dom_get_nsdecl(nodep, (xmlChar *)name);
888965
if (nsptr != NULL) {
889966
if (xmlStrEqual((xmlChar *)uri, nsptr->href)) {
890-
if (nsptr->href != NULL) {
891-
xmlFree((char *) nsptr->href);
892-
nsptr->href = NULL;
893-
}
894-
if (nsptr->prefix != NULL) {
895-
xmlFree((char *) nsptr->prefix);
896-
nsptr->prefix = NULL;
897-
}
967+
dom_eliminate_ns(nodep, nsptr);
898968
} else {
899969
RETURN_NULL();
900970
}

ext/dom/tests/gh12616_1.phpt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$doc = new DOMDocument();
9+
$doc->loadXML(
10+
<<<XML
11+
<container xmlns="http://symfony.com/schema/dic/services">
12+
CHILDREN
13+
</container>
14+
XML
15+
);
16+
17+
$doc->documentElement->removeAttributeNS('http://symfony.com/schema/dic/services', '');
18+
echo $doc->saveXML();
19+
20+
$new = new DOMDocument();
21+
$new->append(
22+
$new->importNode($doc->documentElement, true)
23+
);
24+
25+
echo $new->saveXML();
26+
27+
?>
28+
--EXPECT--
29+
<?xml version="1.0"?>
30+
<container>
31+
CHILDREN
32+
</container>
33+
<?xml version="1.0"?>
34+
<container>
35+
CHILDREN
36+
</container>

ext/dom/tests/gh12616_2.phpt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$doc = new DOMDocument();
9+
$doc->loadXML(
10+
<<<XML
11+
<container xmlns:test="urn:test" xmlns:symfony="http://symfony.com/schema/dic/services">
12+
<symfony:services>
13+
<test:service id="hello" />
14+
</symfony:services>
15+
</container>
16+
XML
17+
);
18+
19+
$doc->documentElement->removeAttributeNS('http://symfony.com/schema/dic/services', 'symfony');
20+
$xpath = new DOMXPath($doc);
21+
$xpath->registerNamespace('test', 'urn:test');
22+
23+
echo $doc->saveXML();
24+
25+
$result = $xpath->query('//container/services/test:service[@id="hello"]');
26+
var_dump($result);
27+
28+
?>
29+
--EXPECT--
30+
<?xml version="1.0"?>
31+
<container xmlns:test="urn:test">
32+
<services>
33+
<test:service id="hello"/>
34+
</services>
35+
</container>
36+
object(DOMNodeList)#4 (1) {
37+
["length"]=>
38+
int(1)
39+
}

ext/dom/tests/gh12616_3.phpt

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
--TEST--
2+
GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$doc = new DOMDocument();
9+
$doc->loadXML(
10+
<<<XML
11+
<container>
12+
<child1 xmlns:x="http://symfony.com/schema/dic/services">
13+
<x:foo x:bar=""/>
14+
<x:foo x:bar=""/>
15+
</child1>
16+
<child2 xmlns:x="http://symfony.com/schema/dic/services">
17+
<x:foo x:bar=""/>
18+
<x:foo x:bar=""/>
19+
</child2>
20+
</container>
21+
XML
22+
);
23+
24+
$doc->documentElement->firstElementChild->removeAttributeNS('http://symfony.com/schema/dic/services', 'x');
25+
echo $doc->saveXML();
26+
27+
$xpath = new DOMXPath($doc);
28+
29+
echo "--- Namespaces of child1 ---\n";
30+
31+
foreach ($xpath->query("/container/child1/namespace::*") as $ns) {
32+
var_dump($ns);
33+
}
34+
35+
echo "--- Namespaces of child1/foo (both nodes) ---\n";
36+
37+
foreach ($xpath->query("/container/child1/foo/namespace::*") as $ns) {
38+
var_dump($ns);
39+
}
40+
41+
echo "--- Namespaces of child2 ---\n";
42+
43+
foreach ($xpath->query("/container/child2/namespace::*") as $ns) {
44+
var_dump($ns);
45+
}
46+
47+
?>
48+
--EXPECT--
49+
<?xml version="1.0"?>
50+
<container>
51+
<child1>
52+
<foo bar=""/>
53+
<foo bar=""/>
54+
</child1>
55+
<child2 xmlns:x="http://symfony.com/schema/dic/services">
56+
<x:foo x:bar=""/>
57+
<x:foo x:bar=""/>
58+
</child2>
59+
</container>
60+
--- Namespaces of child1 ---
61+
object(DOMNameSpaceNode)#4 (10) {
62+
["nodeName"]=>
63+
string(9) "xmlns:xml"
64+
["nodeValue"]=>
65+
string(36) "http://www.w3.org/XML/1998/namespace"
66+
["nodeType"]=>
67+
int(18)
68+
["prefix"]=>
69+
string(3) "xml"
70+
["localName"]=>
71+
string(3) "xml"
72+
["namespaceURI"]=>
73+
string(36) "http://www.w3.org/XML/1998/namespace"
74+
["isConnected"]=>
75+
bool(true)
76+
["ownerDocument"]=>
77+
string(22) "(object value omitted)"
78+
["parentNode"]=>
79+
string(22) "(object value omitted)"
80+
["parentElement"]=>
81+
string(22) "(object value omitted)"
82+
}
83+
--- Namespaces of child1/foo (both nodes) ---
84+
object(DOMNameSpaceNode)#5 (10) {
85+
["nodeName"]=>
86+
string(9) "xmlns:xml"
87+
["nodeValue"]=>
88+
string(36) "http://www.w3.org/XML/1998/namespace"
89+
["nodeType"]=>
90+
int(18)
91+
["prefix"]=>
92+
string(3) "xml"
93+
["localName"]=>
94+
string(3) "xml"
95+
["namespaceURI"]=>
96+
string(36) "http://www.w3.org/XML/1998/namespace"
97+
["isConnected"]=>
98+
bool(true)
99+
["ownerDocument"]=>
100+
string(22) "(object value omitted)"
101+
["parentNode"]=>
102+
string(22) "(object value omitted)"
103+
["parentElement"]=>
104+
string(22) "(object value omitted)"
105+
}
106+
object(DOMNameSpaceNode)#8 (10) {
107+
["nodeName"]=>
108+
string(9) "xmlns:xml"
109+
["nodeValue"]=>
110+
string(36) "http://www.w3.org/XML/1998/namespace"
111+
["nodeType"]=>
112+
int(18)
113+
["prefix"]=>
114+
string(3) "xml"
115+
["localName"]=>
116+
string(3) "xml"
117+
["namespaceURI"]=>
118+
string(36) "http://www.w3.org/XML/1998/namespace"
119+
["isConnected"]=>
120+
bool(true)
121+
["ownerDocument"]=>
122+
string(22) "(object value omitted)"
123+
["parentNode"]=>
124+
string(22) "(object value omitted)"
125+
["parentElement"]=>
126+
string(22) "(object value omitted)"
127+
}
128+
--- Namespaces of child2 ---
129+
object(DOMNameSpaceNode)#9 (10) {
130+
["nodeName"]=>
131+
string(9) "xmlns:xml"
132+
["nodeValue"]=>
133+
string(36) "http://www.w3.org/XML/1998/namespace"
134+
["nodeType"]=>
135+
int(18)
136+
["prefix"]=>
137+
string(3) "xml"
138+
["localName"]=>
139+
string(3) "xml"
140+
["namespaceURI"]=>
141+
string(36) "http://www.w3.org/XML/1998/namespace"
142+
["isConnected"]=>
143+
bool(true)
144+
["ownerDocument"]=>
145+
string(22) "(object value omitted)"
146+
["parentNode"]=>
147+
string(22) "(object value omitted)"
148+
["parentElement"]=>
149+
string(22) "(object value omitted)"
150+
}
151+
object(DOMNameSpaceNode)#5 (10) {
152+
["nodeName"]=>
153+
string(7) "xmlns:x"
154+
["nodeValue"]=>
155+
string(38) "http://symfony.com/schema/dic/services"
156+
["nodeType"]=>
157+
int(18)
158+
["prefix"]=>
159+
string(1) "x"
160+
["localName"]=>
161+
string(1) "x"
162+
["namespaceURI"]=>
163+
string(38) "http://symfony.com/schema/dic/services"
164+
["isConnected"]=>
165+
bool(true)
166+
["ownerDocument"]=>
167+
string(22) "(object value omitted)"
168+
["parentNode"]=>
169+
string(22) "(object value omitted)"
170+
["parentElement"]=>
171+
string(22) "(object value omitted)"
172+
}

ext/libxml/php_libxml.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ extern zend_module_entry libxml_module_entry;
3535

3636
#include "zend_smart_str.h"
3737
#include <libxml/tree.h>
38+
#include <libxml/parser.h>
3839

3940
#define LIBXML_SAVE_NOEMPTYTAG 1<<2
4041

0 commit comments

Comments
 (0)