mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix GH-11500: Namespace reuse in createElementNS() generates wrong output
When you construct a DOM tree containing subtrees which are constructed top-down, this won't remove the redundant namespaces. That's because the following conditions hold: 1) The namespace are reused from the doc->oldNs list. 2) Therefore during reconciliation no nsDef field is set, so no redundant namespaces are removed by our reconciliation code. Furthermore, it would only be fixed up automatically if the tree wasn't added in bottom-up way, or if it had been constructed bottom-up from the start. Fix it by setting a flag to remove redundant namespaces in the libxml2 reconciliation call. Since removing redundant namespaces may have a performance cost, we only do this after performing a simple check. Closes GH-11528.
This commit is contained in:
4
NEWS
4
NEWS
@@ -6,6 +6,10 @@ PHP NEWS
|
||||
. Fixed bug GH-11507 (String concatenation performance regression in 8.3).
|
||||
(nielsdos)
|
||||
|
||||
- DOM:
|
||||
. Fixed bug GH-11500 (Namespace reuse in createElementNS() generates wrong
|
||||
output). (nielsdos)
|
||||
|
||||
- Fileinfo:
|
||||
. Fix GH-11408 (Unable to build PHP 8.3.0 alpha 1 / fileinfo extension).
|
||||
(nielsdos)
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
#define PHP_XPATH 1
|
||||
#define PHP_XPTR 2
|
||||
|
||||
/* libxml2 doesn't expose this constant as part of their public API.
|
||||
* See xmlDOMReconcileNSOptions in tree.c */
|
||||
#define PHP_LIBXML2_DOM_RECONNS_REMOVEREDUND (1 << 0)
|
||||
|
||||
/* {{{ class entries */
|
||||
PHP_DOM_EXPORT zend_class_entry *dom_node_class_entry;
|
||||
PHP_DOM_EXPORT zend_class_entry *dom_domexception_class_entry;
|
||||
@@ -1494,7 +1498,8 @@ static void dom_libxml_reconcile_ensure_namespaces_are_declared(xmlNodePtr nodep
|
||||
* Although libxml2 currently does not use this for the reconciliation, it still
|
||||
* makes sense to do this just in case libxml2's internal change in the future. */
|
||||
xmlDOMWrapCtxt dummy_ctxt = {0};
|
||||
xmlDOMWrapReconcileNamespaces(&dummy_ctxt, nodep, /* options */ 0);
|
||||
bool remove_redundant = nodep->nsDef == NULL && nodep->ns != NULL;
|
||||
xmlDOMWrapReconcileNamespaces(&dummy_ctxt, nodep, /* options */ remove_redundant ? PHP_LIBXML2_DOM_RECONNS_REMOVEREDUND : 0);
|
||||
}
|
||||
|
||||
void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */
|
||||
|
||||
156
ext/dom/tests/gh11500.phpt
Normal file
156
ext/dom/tests/gh11500.phpt
Normal file
@@ -0,0 +1,156 @@
|
||||
--TEST--
|
||||
GH-11500 (Namespace reuse in createElementNS() generates wrong output)
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
function api_test_depth2($root_ns) {
|
||||
$dom = new DOMDocument();
|
||||
$root = $dom->createElementNS($root_ns, 'root');
|
||||
$dom->appendChild($root);
|
||||
|
||||
$a1 = $dom->createElementNS('http://example.com', 'a1');
|
||||
$b1 = $a1->appendChild($dom->createElementNS('http://example.com', 'b1'));
|
||||
$root->appendChild($a1);
|
||||
|
||||
$a2 = $dom->createElementNS('http://example.com', 'a2');
|
||||
$b2 = $a2->appendChild($dom->createElementNS('http://example.com', 'b2'));
|
||||
$root->appendChild($a2);
|
||||
|
||||
echo $dom->saveXML();
|
||||
|
||||
var_dump($root->namespaceURI);
|
||||
var_dump($a1->namespaceURI);
|
||||
var_dump($b1->namespaceURI);
|
||||
var_dump($a2->namespaceURI);
|
||||
var_dump($b2->namespaceURI);
|
||||
}
|
||||
|
||||
function api_test_depth3($root_ns, $swapped) {
|
||||
$dom = new DOMDocument();
|
||||
$root = $dom->createElementNS($root_ns, 'root');
|
||||
$dom->appendChild($root);
|
||||
|
||||
$a1 = $dom->createElementNS('http://example.com', 'a1');
|
||||
$b1 = $a1->appendChild($dom->createElementNS('http://example.com', 'b1'));
|
||||
$c1 = $b1->appendChild($dom->createElementNS('http://example.com', 'c1'));
|
||||
$root->appendChild($a1);
|
||||
|
||||
$a2 = $dom->createElementNS('http://example.com', 'a2');
|
||||
if ($swapped) {
|
||||
$b2 = $dom->createElementNS('http://example.com', 'b2');
|
||||
$c2 = $b2->appendChild($dom->createElementNS('http://example.com', 'c2'));
|
||||
$a2->appendChild($b2);
|
||||
} else {
|
||||
$b2 = $a2->appendChild($dom->createElementNS('http://example.com', 'b2'));
|
||||
$c2 = $b2->appendChild($dom->createElementNS('http://example.com', 'c2'));
|
||||
}
|
||||
$root->appendChild($a2);
|
||||
|
||||
echo $dom->saveXML();
|
||||
|
||||
var_dump($root->namespaceURI);
|
||||
var_dump($a1->namespaceURI);
|
||||
var_dump($b1->namespaceURI);
|
||||
var_dump($c1->namespaceURI);
|
||||
var_dump($a2->namespaceURI);
|
||||
var_dump($b2->namespaceURI);
|
||||
var_dump($c2->namespaceURI);
|
||||
}
|
||||
|
||||
echo "-- Constructed from API (depth 2, mismatched root variation) --\n";
|
||||
api_test_depth2('http://example2.com');
|
||||
|
||||
echo "-- Constructed from API (depth 2, matching root variation) --\n";
|
||||
api_test_depth2('http://example.com');
|
||||
|
||||
echo "-- Constructed from API (depth 3, mismatched root variation, non-swapped) --\n";
|
||||
api_test_depth3('http://example2.com', false);
|
||||
|
||||
echo "-- Constructed from API (depth 3, matching root variation, non-swapped) --\n";
|
||||
api_test_depth3('http://example.com', false);
|
||||
|
||||
echo "-- Constructed from API (depth 3, mismatched root variation, swapped) --\n";
|
||||
api_test_depth3('http://example2.com', true);
|
||||
|
||||
echo "-- Constructed from API (depth 3, matching root variation, swapped) --\n";
|
||||
api_test_depth3('http://example.com', true);
|
||||
|
||||
echo "-- Constructed depth 2 from string --\n";
|
||||
$xml = '<?xml version="1.0"?><root xmlns="http://example2.com"><a1 xmlns="http://example.com"><b1/></a1><a2 xmlns="http://example.com"><b2/></a2></root>';
|
||||
$dom = new DOMDocument;
|
||||
$dom->loadXML($xml);
|
||||
echo $dom->saveXML(), "\n";
|
||||
|
||||
var_dump($dom->documentElement->namespaceURI); // root
|
||||
var_dump($dom->documentElement->firstChild->namespaceURI); // a1
|
||||
var_dump($dom->documentElement->firstChild->firstChild->namespaceURI); // b1
|
||||
var_dump($dom->documentElement->firstChild->nextSibling->namespaceURI); // a2
|
||||
var_dump($dom->documentElement->firstChild->nextSibling->firstChild->namespaceURI); // b2
|
||||
?>
|
||||
--EXPECT--
|
||||
-- Constructed from API (depth 2, mismatched root variation) --
|
||||
<?xml version="1.0"?>
|
||||
<root xmlns="http://example2.com"><a1 xmlns="http://example.com"><b1/></a1><a2 xmlns="http://example.com"><b2/></a2></root>
|
||||
string(19) "http://example2.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
-- Constructed from API (depth 2, matching root variation) --
|
||||
<?xml version="1.0"?>
|
||||
<root xmlns="http://example.com"><a1><b1/></a1><a2><b2/></a2></root>
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
-- Constructed from API (depth 3, mismatched root variation, non-swapped) --
|
||||
<?xml version="1.0"?>
|
||||
<root xmlns="http://example2.com"><a1 xmlns="http://example.com"><b1><c1/></b1></a1><a2 xmlns="http://example.com"><b2><c2/></b2></a2></root>
|
||||
string(19) "http://example2.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
-- Constructed from API (depth 3, matching root variation, non-swapped) --
|
||||
<?xml version="1.0"?>
|
||||
<root xmlns="http://example.com"><a1><b1><c1/></b1></a1><a2><b2><c2/></b2></a2></root>
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
-- Constructed from API (depth 3, mismatched root variation, swapped) --
|
||||
<?xml version="1.0"?>
|
||||
<root xmlns="http://example2.com"><a1 xmlns="http://example.com"><b1><c1/></b1></a1><a2 xmlns="http://example.com"><b2><c2/></b2></a2></root>
|
||||
string(19) "http://example2.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
-- Constructed from API (depth 3, matching root variation, swapped) --
|
||||
<?xml version="1.0"?>
|
||||
<root xmlns="http://example.com"><a1><b1><c1/></b1></a1><a2><b2><c2/></b2></a2></root>
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
-- Constructed depth 2 from string --
|
||||
<?xml version="1.0"?>
|
||||
<root xmlns="http://example2.com"><a1 xmlns="http://example.com"><b1/></a1><a2 xmlns="http://example.com"><b2/></a2></root>
|
||||
|
||||
string(19) "http://example2.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
string(18) "http://example.com"
|
||||
Reference in New Issue
Block a user