mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Merge branch 'PHP-8.2'
* PHP-8.2: Fix bug #55294 and #47530 and #47847: namespace reconciliation issues
This commit is contained in:
@@ -1451,7 +1451,7 @@ void dom_set_old_ns(xmlDoc *doc, xmlNs *ns) {
|
||||
}
|
||||
/* }}} end dom_set_old_ns */
|
||||
|
||||
static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep)
|
||||
static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr search_parent)
|
||||
{
|
||||
xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL;
|
||||
|
||||
@@ -1461,7 +1461,7 @@ static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep)
|
||||
while (curns) {
|
||||
nsdftptr = curns->next;
|
||||
if (curns->href != NULL) {
|
||||
if((nsptr = xmlSearchNsByHref(doc, nodep->parent, curns->href)) &&
|
||||
if((nsptr = xmlSearchNsByHref(doc, search_parent, curns->href)) &&
|
||||
(curns->prefix == NULL || xmlStrEqual(nsptr->prefix, curns->prefix))) {
|
||||
curns->next = NULL;
|
||||
if (prevns == NULL) {
|
||||
@@ -1482,23 +1482,34 @@ static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep)
|
||||
}
|
||||
}
|
||||
|
||||
static void dom_libxml_reconcile_ensure_namespaces_are_declared(xmlNodePtr nodep)
|
||||
{
|
||||
/* Put on stack to avoid allocation.
|
||||
* 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);
|
||||
}
|
||||
|
||||
void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */
|
||||
{
|
||||
/* Although the node type will be checked by the libxml2 API,
|
||||
* we still want to do the internal reconciliation conditionally. */
|
||||
if (nodep->type == XML_ELEMENT_NODE) {
|
||||
dom_reconcile_ns_internal(doc, nodep);
|
||||
xmlReconciliateNs(doc, nodep);
|
||||
dom_reconcile_ns_internal(doc, nodep, nodep->parent);
|
||||
dom_libxml_reconcile_ensure_namespaces_are_declared(nodep);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void dom_reconcile_ns_list_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last)
|
||||
static void dom_reconcile_ns_list_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last, xmlNodePtr search_parent)
|
||||
{
|
||||
ZEND_ASSERT(nodep != NULL);
|
||||
while (true) {
|
||||
if (nodep->type == XML_ELEMENT_NODE) {
|
||||
dom_reconcile_ns_internal(doc, nodep);
|
||||
dom_reconcile_ns_internal(doc, nodep, search_parent);
|
||||
if (nodep->children) {
|
||||
dom_reconcile_ns_list_internal(doc, nodep->children, nodep->last /* process the whole children list */);
|
||||
dom_reconcile_ns_list_internal(doc, nodep->children, nodep->last /* process the whole children list */, search_parent);
|
||||
}
|
||||
}
|
||||
if (nodep == last) {
|
||||
@@ -1510,10 +1521,12 @@ static void dom_reconcile_ns_list_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlN
|
||||
|
||||
void dom_reconcile_ns_list(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last)
|
||||
{
|
||||
dom_reconcile_ns_list_internal(doc, nodep, last);
|
||||
/* Outside of the recursion above because xmlReconciliateNs() performs its own recursion. */
|
||||
dom_reconcile_ns_list_internal(doc, nodep, last, nodep->parent);
|
||||
/* The loop is outside of the recursion in the above call because
|
||||
* dom_libxml_reconcile_ensure_namespaces_are_declared() performs its own recursion. */
|
||||
while (true) {
|
||||
xmlReconciliateNs(doc, nodep);
|
||||
/* The internal libxml2 call will already check the node type, no need for us to do it here. */
|
||||
dom_libxml_reconcile_ensure_namespaces_are_declared(nodep);
|
||||
if (nodep == last) {
|
||||
break;
|
||||
}
|
||||
|
||||
152
ext/dom/tests/bug47530.phpt
Normal file
152
ext/dom/tests/bug47530.phpt
Normal file
@@ -0,0 +1,152 @@
|
||||
--TEST--
|
||||
Bug #47530 (Importing objects into document fragments creates bogus "default" namespace)
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test_document_fragment_with_import() {
|
||||
$doc = new DOMDocument;
|
||||
$doc->loadXML('<html xmlns="https://php.net/something" xmlns:ns="https://php.net/whatever"><element ns:foo="https://php.net/bar"/></html>');
|
||||
$root = $doc->documentElement;
|
||||
$frag = $doc->createDocumentFragment();
|
||||
$frag->appendChild($doc->importNode($root->firstChild));
|
||||
$root->appendChild($frag);
|
||||
echo $doc->saveXML();
|
||||
}
|
||||
|
||||
function test_document_fragment_without_import() {
|
||||
$doc = new DOMDocument;
|
||||
$doc->loadXML('<html xmlns=""><element xmlns:foo="https://php.net/bar"/></html>');
|
||||
$frag = $doc->createDocumentFragment();
|
||||
$frag->appendChild($doc->createElementNS('https://php.net/bar', 'bar'));
|
||||
$frag->appendChild($doc->createElementNS('', 'bar'));
|
||||
$element = $doc->documentElement->firstChild;
|
||||
$element->appendChild($frag);
|
||||
unset($frag); // Free fragment, should not break getting the namespaceURI below
|
||||
echo $doc->saveXML();
|
||||
unset($doc);
|
||||
var_dump($element->firstChild->tagName);
|
||||
var_dump($element->firstChild->namespaceURI);
|
||||
}
|
||||
|
||||
function test_document_import() {
|
||||
$xml = <<<XML
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p>Test-Text</p>
|
||||
</div>
|
||||
</feed>
|
||||
XML;
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML($xml);
|
||||
|
||||
$dom2 = new DOMDocument();
|
||||
$importedNode = $dom2->importNode($dom->documentElement, true);
|
||||
$dom2->appendChild($importedNode);
|
||||
|
||||
echo $dom2->saveXML();
|
||||
}
|
||||
|
||||
function test_partial_document_import() {
|
||||
$xml = <<<XML
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/1999/xhtml" xmlns:test="https://php.net/test" xmlns:example="https://php.net/example">
|
||||
<div>
|
||||
<p>Test-Text</p>
|
||||
<example:p>More test text</example:p>
|
||||
<test:p>Even more test text</test:p>
|
||||
</div>
|
||||
</feed>
|
||||
XML;
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML($xml);
|
||||
|
||||
$dom2 = new DOMDocument();
|
||||
$dom2->loadXML('<?xml version="1.0"?><container xmlns:test="https://php.net/test" xmlns="https://php.net/example"/>');
|
||||
$importedNode = $dom2->importNode($dom->documentElement, true);
|
||||
$dom2->documentElement->appendChild($importedNode);
|
||||
|
||||
// Freeing the original document shouldn't break the other document
|
||||
unset($importedNode);
|
||||
unset($dom);
|
||||
|
||||
echo $dom2->saveXML();
|
||||
}
|
||||
|
||||
function test_document_import_with_attributes() {
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML('<?xml version="1.0"?><div xmlns="https://php.net/default" xmlns:example="https://php.net/example"><p example:test="test"/><i/></div>');
|
||||
$dom2 = new DOMDocument();
|
||||
$dom2->loadXML('<?xml version="1.0"?><div xmlns:example="https://php.net/somethingelse"/>');
|
||||
$dom2->documentElement->appendChild($dom2->importNode($dom->documentElement->firstChild));
|
||||
echo $dom2->saveXML(), "\n";
|
||||
|
||||
$dom2->documentElement->firstChild->appendChild($dom2->importNode($dom->documentElement->firstChild->nextSibling));
|
||||
echo $dom2->saveXML(), "\n";
|
||||
}
|
||||
|
||||
function test_appendChild_with_shadowing() {
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML('<?xml version="1.0"?><container xmlns:default="http://php.net/default"><a xmlns:foo="http://php.net/bar"/><b xmlns:foo="http://php.net/foo"><default:test foo:bar=""/><foo:test2/></b></container>');
|
||||
|
||||
$a = $dom->documentElement->firstElementChild;
|
||||
$b = $a->nextSibling;
|
||||
$b->remove();
|
||||
$a->appendChild($b);
|
||||
|
||||
echo $dom->saveXML(), "\n";
|
||||
}
|
||||
|
||||
echo "-- Test document fragment with import --\n";
|
||||
test_document_fragment_with_import();
|
||||
echo "-- Test document fragment without import --\n";
|
||||
test_document_fragment_without_import();
|
||||
echo "-- Test document import --\n";
|
||||
test_document_import();
|
||||
echo "-- Test partial document import --\n";
|
||||
test_partial_document_import();
|
||||
echo "-- Test document import with attributes --\n";
|
||||
test_document_import_with_attributes();
|
||||
echo "-- Test appendChild with shadowing --\n";
|
||||
test_appendChild_with_shadowing();
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
-- Test document fragment with import --
|
||||
<?xml version="1.0"?>
|
||||
<html xmlns="https://php.net/something" xmlns:ns="https://php.net/whatever"><element ns:foo="https://php.net/bar"/></html>
|
||||
-- Test document fragment without import --
|
||||
<?xml version="1.0"?>
|
||||
<html xmlns=""><element xmlns:foo="https://php.net/bar"><foo:bar/><bar xmlns=""/></element></html>
|
||||
string(7) "foo:bar"
|
||||
string(19) "https://php.net/bar"
|
||||
-- Test document import --
|
||||
<?xml version="1.0"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p>Test-Text</p>
|
||||
</div>
|
||||
</feed>
|
||||
-- Test partial document import --
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:test="https://php.net/test" xmlns="https://php.net/example"><feed xmlns="http://www.w3.org/1999/xhtml" xmlns:example="https://php.net/example">
|
||||
<div>
|
||||
<p>Test-Text</p>
|
||||
<example:p>More test text</example:p>
|
||||
<test:p>Even more test text</test:p>
|
||||
</div>
|
||||
</feed></container>
|
||||
-- Test document import with attributes --
|
||||
<?xml version="1.0"?>
|
||||
<div xmlns:example="https://php.net/somethingelse"><p xmlns="https://php.net/default" xmlns:example="https://php.net/example" example:test="test"/></div>
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<div xmlns:example="https://php.net/somethingelse"><p xmlns="https://php.net/default" xmlns:example="https://php.net/example" example:test="test"><i/></p></div>
|
||||
|
||||
-- Test appendChild with shadowing --
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:default="http://php.net/default"><a xmlns:foo="http://php.net/bar"><b xmlns:foo="http://php.net/foo"><default:test foo:bar=""/><foo:test2/></b></a></container>
|
||||
27
ext/dom/tests/bug47847.phpt
Normal file
27
ext/dom/tests/bug47847.phpt
Normal file
@@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
Bug #47847 (importNode loses the namespace of an XML element)
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
$fromdom = new DOMDocument();
|
||||
$fromdom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<ns:container xmlns:ns="http://php.net">
|
||||
<ns:inner xmlns="http://php.net">
|
||||
<ns:WATCH-MY-NAMESPACE xmlns=""/>
|
||||
</ns:inner>
|
||||
</ns:container>
|
||||
XML);
|
||||
|
||||
$aDOM = new DOMDocument();
|
||||
$imported = $aDOM->importNode($fromdom->documentElement->firstElementChild, true);
|
||||
$aDOM->appendChild($imported);
|
||||
|
||||
echo $aDOM->saveXML();
|
||||
?>
|
||||
--EXPECT--
|
||||
<?xml version="1.0"?>
|
||||
<ns:inner xmlns="http://php.net" xmlns:ns="http://php.net">
|
||||
<ns:WATCH-MY-NAMESPACE xmlns=""/>
|
||||
</ns:inner>
|
||||
29
ext/dom/tests/bug55294.phpt
Normal file
29
ext/dom/tests/bug55294.phpt
Normal file
@@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
Bug #55294 (DOMDocument::importNode shifts namespaces when "default" namespace exists)
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$aDOM = new DOMDocument();
|
||||
$aDOM->loadXML(<<<EOXML
|
||||
<A xmlns="http://example.com/A">
|
||||
<B>
|
||||
<C xmlns="http://example.com/C" xmlns:default="http://example.com/Z" />
|
||||
</B>
|
||||
</A>
|
||||
EOXML
|
||||
);
|
||||
|
||||
$bDOM = new DOMDocument();
|
||||
$node = $bDOM->importNode($aDOM->getElementsByTagNameNS('http://example.com/A', 'B')->item(0), true);
|
||||
$bDOM->appendChild($node);
|
||||
|
||||
echo $bDOM->saveXML(), "\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
<?xml version="1.0"?>
|
||||
<B xmlns="http://example.com/A">
|
||||
<C xmlns="http://example.com/C" xmlns:default="http://example.com/Z"/>
|
||||
</B>
|
||||
Reference in New Issue
Block a user