Skip to content

Commit

Permalink
Merge branch 'PHP-8.3'
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
nielsdos committed Nov 17, 2023
2 parents 7658220 + 2b42b73 commit 6f215e0
Show file tree
Hide file tree
Showing 5 changed files with 326 additions and 8 deletions.
86 changes: 78 additions & 8 deletions ext/dom/element.c
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,83 @@ PHP_METHOD(DOMElement, setAttributeNS)
}
/* }}} end dom_element_set_attribute_ns */

static void dom_remove_eliminated_ns_single_element(xmlNodePtr node, xmlNsPtr eliminatedNs)
{
ZEND_ASSERT(node->type == XML_ELEMENT_NODE);
if (node->ns == eliminatedNs) {
node->ns = NULL;
}

for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
if (attr->ns == eliminatedNs) {
attr->ns = NULL;
}
}
}

static void dom_remove_eliminated_ns(xmlNodePtr node, xmlNsPtr eliminatedNs)
{
dom_remove_eliminated_ns_single_element(node, eliminatedNs);

xmlNodePtr base = node;
node = node->children;
while (node != NULL) {
ZEND_ASSERT(node != base);

if (node->type == XML_ELEMENT_NODE) {
dom_remove_eliminated_ns_single_element(node, eliminatedNs);

if (node->children) {
node = node->children;
continue;
}
}

if (node->next) {
node = node->next;
} else {
/* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
do {
node = node->parent;
if (node == base) {
return;
}
} while (node->next == NULL);
node = node->next;
}
}
}

static void dom_eliminate_ns(xmlNodePtr nodep, xmlNsPtr nsptr)
{
if (nsptr->href != NULL) {
xmlFree((char *) nsptr->href);
nsptr->href = NULL;
}
if (nsptr->prefix != NULL) {
xmlFree((char *) nsptr->prefix);
nsptr->prefix = NULL;
}

/* Remove it from the list and move it to the old ns list */
xmlNsPtr current_ns = nodep->nsDef;
if (current_ns == nsptr) {
nodep->nsDef = nsptr->next;
} else {
do {
if (current_ns->next == nsptr) {
current_ns->next = nsptr->next;
break;
}
current_ns = current_ns->next;
} while (current_ns != NULL);
}
nsptr->next = NULL;
php_libxml_set_old_ns(nodep->doc, nsptr);

dom_remove_eliminated_ns(nodep, nsptr);
}

/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS
Since: DOM Level 2
*/
Expand All @@ -887,14 +964,7 @@ PHP_METHOD(DOMElement, removeAttributeNS)
nsptr = dom_get_nsdecl(nodep, (xmlChar *)name);
if (nsptr != NULL) {
if (xmlStrEqual((xmlChar *)uri, nsptr->href)) {
if (nsptr->href != NULL) {
xmlFree((char *) nsptr->href);
nsptr->href = NULL;
}
if (nsptr->prefix != NULL) {
xmlFree((char *) nsptr->prefix);
nsptr->prefix = NULL;
}
dom_eliminate_ns(nodep, nsptr);
} else {
RETURN_NULL();
}
Expand Down
36 changes: 36 additions & 0 deletions ext/dom/tests/gh12616_1.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix)
--EXTENSIONS--
dom
--FILE--
<?php

$doc = new DOMDocument();
$doc->loadXML(
<<<XML
<container xmlns="http://symfony.com/schema/dic/services">
CHILDREN
</container>
XML
);

$doc->documentElement->removeAttributeNS('http://symfony.com/schema/dic/services', '');
echo $doc->saveXML();

$new = new DOMDocument();
$new->append(
$new->importNode($doc->documentElement, true)
);

echo $new->saveXML();

?>
--EXPECT--
<?xml version="1.0"?>
<container>
CHILDREN
</container>
<?xml version="1.0"?>
<container>
CHILDREN
</container>
39 changes: 39 additions & 0 deletions ext/dom/tests/gh12616_2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix)
--EXTENSIONS--
dom
--FILE--
<?php

$doc = new DOMDocument();
$doc->loadXML(
<<<XML
<container xmlns:test="urn:test" xmlns:symfony="http://symfony.com/schema/dic/services">
<symfony:services>
<test:service id="hello" />
</symfony:services>
</container>
XML
);

$doc->documentElement->removeAttributeNS('http://symfony.com/schema/dic/services', 'symfony');
$xpath = new DOMXPath($doc);
$xpath->registerNamespace('test', 'urn:test');

echo $doc->saveXML();

$result = $xpath->query('//container/services/test:service[@id="hello"]');
var_dump($result);

?>
--EXPECT--
<?xml version="1.0"?>
<container xmlns:test="urn:test">
<services>
<test:service id="hello"/>
</services>
</container>
object(DOMNodeList)#4 (1) {
["length"]=>
int(1)
}
172 changes: 172 additions & 0 deletions ext/dom/tests/gh12616_3.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
--TEST--
GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix)
--EXTENSIONS--
dom
--FILE--
<?php

$doc = new DOMDocument();
$doc->loadXML(
<<<XML
<container>
<child1 xmlns:x="http://symfony.com/schema/dic/services">
<x:foo x:bar=""/>
<x:foo x:bar=""/>
</child1>
<child2 xmlns:x="http://symfony.com/schema/dic/services">
<x:foo x:bar=""/>
<x:foo x:bar=""/>
</child2>
</container>
XML
);

$doc->documentElement->firstElementChild->removeAttributeNS('http://symfony.com/schema/dic/services', 'x');
echo $doc->saveXML();

$xpath = new DOMXPath($doc);

echo "--- Namespaces of child1 ---\n";

foreach ($xpath->query("/container/child1/namespace::*") as $ns) {
var_dump($ns);
}

echo "--- Namespaces of child1/foo (both nodes) ---\n";

foreach ($xpath->query("/container/child1/foo/namespace::*") as $ns) {
var_dump($ns);
}

echo "--- Namespaces of child2 ---\n";

foreach ($xpath->query("/container/child2/namespace::*") as $ns) {
var_dump($ns);
}

?>
--EXPECT--
<?xml version="1.0"?>
<container>
<child1>
<foo bar=""/>
<foo bar=""/>
</child1>
<child2 xmlns:x="http://symfony.com/schema/dic/services">
<x:foo x:bar=""/>
<x:foo x:bar=""/>
</child2>
</container>
--- Namespaces of child1 ---
object(DOMNameSpaceNode)#4 (10) {
["nodeName"]=>
string(9) "xmlns:xml"
["nodeValue"]=>
string(36) "http://www.w3.org/XML/1998/namespace"
["nodeType"]=>
int(18)
["prefix"]=>
string(3) "xml"
["localName"]=>
string(3) "xml"
["namespaceURI"]=>
string(36) "http://www.w3.org/XML/1998/namespace"
["isConnected"]=>
bool(true)
["ownerDocument"]=>
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
["parentElement"]=>
string(22) "(object value omitted)"
}
--- Namespaces of child1/foo (both nodes) ---
object(DOMNameSpaceNode)#5 (10) {
["nodeName"]=>
string(9) "xmlns:xml"
["nodeValue"]=>
string(36) "http://www.w3.org/XML/1998/namespace"
["nodeType"]=>
int(18)
["prefix"]=>
string(3) "xml"
["localName"]=>
string(3) "xml"
["namespaceURI"]=>
string(36) "http://www.w3.org/XML/1998/namespace"
["isConnected"]=>
bool(true)
["ownerDocument"]=>
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
["parentElement"]=>
string(22) "(object value omitted)"
}
object(DOMNameSpaceNode)#8 (10) {
["nodeName"]=>
string(9) "xmlns:xml"
["nodeValue"]=>
string(36) "http://www.w3.org/XML/1998/namespace"
["nodeType"]=>
int(18)
["prefix"]=>
string(3) "xml"
["localName"]=>
string(3) "xml"
["namespaceURI"]=>
string(36) "http://www.w3.org/XML/1998/namespace"
["isConnected"]=>
bool(true)
["ownerDocument"]=>
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
["parentElement"]=>
string(22) "(object value omitted)"
}
--- Namespaces of child2 ---
object(DOMNameSpaceNode)#9 (10) {
["nodeName"]=>
string(9) "xmlns:xml"
["nodeValue"]=>
string(36) "http://www.w3.org/XML/1998/namespace"
["nodeType"]=>
int(18)
["prefix"]=>
string(3) "xml"
["localName"]=>
string(3) "xml"
["namespaceURI"]=>
string(36) "http://www.w3.org/XML/1998/namespace"
["isConnected"]=>
bool(true)
["ownerDocument"]=>
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
["parentElement"]=>
string(22) "(object value omitted)"
}
object(DOMNameSpaceNode)#5 (10) {
["nodeName"]=>
string(7) "xmlns:x"
["nodeValue"]=>
string(38) "http://symfony.com/schema/dic/services"
["nodeType"]=>
int(18)
["prefix"]=>
string(1) "x"
["localName"]=>
string(1) "x"
["namespaceURI"]=>
string(38) "http://symfony.com/schema/dic/services"
["isConnected"]=>
bool(true)
["ownerDocument"]=>
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
["parentElement"]=>
string(22) "(object value omitted)"
}
1 change: 1 addition & 0 deletions ext/libxml/php_libxml.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extern zend_module_entry libxml_module_entry;

#include "zend_smart_str.h"
#include <libxml/tree.h>
#include <libxml/parser.h>

#define LIBXML_SAVE_NOEMPTYTAG 1<<2

Expand Down

0 comments on commit 6f215e0

Please sign in to comment.