mirror of
https://github.com/php/php-src.git
synced 2024-11-27 11:53:33 +08:00
Fix cloning attribute with namespace disappearing namespace
Closes GH-12547.
This commit is contained in:
parent
a470c4aeef
commit
3e33eda39a
1
NEWS
1
NEWS
@ -9,6 +9,7 @@ DOM:
|
||||
. Added DOMNode::compareDocumentPosition(). (nielsdos)
|
||||
. Implement #53655 (Improve speed of DOMNode::C14N() on large XML documents).
|
||||
(nielsdos)
|
||||
. Fix cloning attribute with namespace disappearing namespace. (nielsdos)
|
||||
|
||||
Intl:
|
||||
. Added IntlDateFormatter::PATTERN constant. (David Carlier)
|
||||
|
@ -640,6 +640,7 @@ PHP_METHOD(DOMElement, setAttributeNode)
|
||||
}
|
||||
|
||||
xmlAddChild(nodep, (xmlNodePtr) attrp);
|
||||
php_dom_reconcile_attribute_namespace_after_insertion(attrp);
|
||||
|
||||
/* Returns old property if removed otherwise NULL */
|
||||
if (existattrp != NULL) {
|
||||
@ -1012,6 +1013,7 @@ PHP_METHOD(DOMElement, setAttributeNodeNS)
|
||||
}
|
||||
|
||||
xmlAddChild(nodep, (xmlNodePtr) attrp);
|
||||
php_dom_reconcile_attribute_namespace_after_insertion(attrp);
|
||||
|
||||
/* Returns old property if removed otherwise NULL */
|
||||
if (existattrp != NULL) {
|
||||
|
@ -1259,6 +1259,7 @@ PHP_METHOD(DOMNode, appendChild)
|
||||
if (UNEXPECTED(new_child == NULL)) {
|
||||
goto cannot_add;
|
||||
}
|
||||
php_dom_reconcile_attribute_namespace_after_insertion((xmlAttrPtr) new_child);
|
||||
} else if (child->type == XML_DOCUMENT_FRAG_NODE) {
|
||||
xmlNodePtr last = child->last;
|
||||
new_child = _php_dom_insert_fragment(nodep, nodep->last, NULL, child, intern, childobj);
|
||||
@ -1362,6 +1363,13 @@ PHP_METHOD(DOMNode, cloneNode)
|
||||
}
|
||||
}
|
||||
|
||||
if (node->type == XML_ATTRIBUTE_NODE && n->ns != NULL && node->ns == NULL) {
|
||||
/* Let reconciliation deal with this. The lifetime of the namespace poses no problem
|
||||
* because we're increasing the refcount of the document proxy at the return.
|
||||
* libxml2 doesn't set the ns because it can't know that this is safe. */
|
||||
node->ns = n->ns;
|
||||
}
|
||||
|
||||
/* If document cloned we want a new document proxy */
|
||||
if (node->doc != n->doc) {
|
||||
intern = NULL;
|
||||
|
@ -1458,6 +1458,22 @@ static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePt
|
||||
}
|
||||
}
|
||||
|
||||
void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp)
|
||||
{
|
||||
ZEND_ASSERT(attrp != NULL);
|
||||
|
||||
if (attrp->ns != NULL) {
|
||||
/* Try to link to an existing namespace. If that won't work, reconcile. */
|
||||
xmlNodePtr nodep = attrp->parent;
|
||||
xmlNsPtr matching_ns = xmlSearchNs(nodep->doc, nodep, attrp->ns->prefix);
|
||||
if (matching_ns && xmlStrEqual(matching_ns->href, attrp->ns->href)) {
|
||||
attrp->ns = matching_ns;
|
||||
} else {
|
||||
xmlReconciliateNs(nodep->doc, nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dom_libxml_reconcile_ensure_namespaces_are_declared(xmlNodePtr nodep)
|
||||
{
|
||||
/* Ideally we'd use the DOM-wrapped version, but we cannot: https://github.com/php/php-src/pull/12308. */
|
||||
@ -1474,6 +1490,8 @@ static void dom_libxml_reconcile_ensure_namespaces_are_declared(xmlNodePtr nodep
|
||||
|
||||
void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */
|
||||
{
|
||||
ZEND_ASSERT(nodep->type != XML_ATTRIBUTE_NODE);
|
||||
|
||||
/* 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) {
|
||||
|
@ -152,6 +152,7 @@ zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep);
|
||||
bool php_dom_is_node_connected(const xmlNode *node);
|
||||
bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document);
|
||||
xmlNsPtr dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree, const char *uri);
|
||||
void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp);
|
||||
|
||||
/* parentnode */
|
||||
void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc);
|
||||
|
@ -26,7 +26,9 @@ $dom1->loadXML('<?xml version="1.0"?><container xmlns:foo="http://php.net" foo:b
|
||||
$dom2->loadXML('<?xml version="1.0"?><container xmlns="http://php.net" xmlns:foo="http://php.net/2"/>');
|
||||
$attribute = $dom1->documentElement->getAttributeNode('foo:bar');
|
||||
$imported = $dom2->importNode($attribute);
|
||||
var_dump($imported->prefix, $imported->namespaceURI);
|
||||
$dom2->documentElement->setAttributeNodeNS($imported);
|
||||
var_dump($imported->prefix, $imported->namespaceURI);
|
||||
|
||||
echo $dom1->saveXML();
|
||||
echo $dom2->saveXML();
|
||||
@ -54,6 +56,10 @@ echo $dom2->saveXML();
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="http://php.net/2" xmlns:default="http://php.net" default:bar="yes"/>
|
||||
--- Non-default namespace test case with a default namespace in the destination ---
|
||||
string(7) "default"
|
||||
string(14) "http://php.net"
|
||||
string(7) "default"
|
||||
string(14) "http://php.net"
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="http://php.net" foo:bar="yes"/>
|
||||
<?xml version="1.0"?>
|
||||
|
84
ext/dom/tests/clone_attribute_namespace_01.phpt
Normal file
84
ext/dom/tests/clone_attribute_namespace_01.phpt
Normal file
@ -0,0 +1,84 @@
|
||||
--TEST--
|
||||
Cloning an attribute should retain its namespace 01
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function createTestDocument() {
|
||||
$dom = new DOMDocument;
|
||||
$dom->loadXML('<?xml version="1.0"?><container/>');
|
||||
$dom->documentElement->setAttributeNs("some:ns", "foo:bar", "value");
|
||||
|
||||
$attr = $dom->documentElement->getAttributeNodeNs("some:ns", "bar");
|
||||
$clone = $attr->cloneNode(true);
|
||||
|
||||
return [$dom, $clone];
|
||||
}
|
||||
|
||||
[$dom, $clone] = createTestDocument();
|
||||
var_dump($clone->prefix, $clone->namespaceURI);
|
||||
|
||||
echo "--- Re-adding a namespaced attribute ---\n";
|
||||
|
||||
[$dom, $clone] = createTestDocument();
|
||||
$dom->documentElement->removeAttributeNs("some:ns", "bar");
|
||||
echo $dom->saveXML();
|
||||
$dom->documentElement->setAttributeNodeNs($clone);
|
||||
echo $dom->saveXML();
|
||||
|
||||
echo "--- Re-adding a namespaced attribute, with the namespace deleted (setAttributeNodeNs variation) ---\n";
|
||||
|
||||
function readd_test(string $method) {
|
||||
[$dom, $clone] = createTestDocument();
|
||||
$dom->documentElement->removeAttributeNs("some:ns", "bar");
|
||||
$dom->documentElement->removeAttribute("xmlns:foo");
|
||||
echo $dom->saveXML();
|
||||
$child = $dom->documentElement->appendChild($dom->createElement("child"));
|
||||
$child->{$method}($clone);
|
||||
echo $dom->saveXML();
|
||||
}
|
||||
|
||||
readd_test("setAttributeNodeNs");
|
||||
|
||||
echo "--- Re-adding a namespaced attribute, with the namespace deleted (setAttributeNode variation) ---\n";
|
||||
|
||||
readd_test("setAttributeNode");
|
||||
|
||||
echo "--- Re-adding a namespaced attribute, with the namespace deleted (appendChild variation) ---\n";
|
||||
|
||||
readd_test("appendChild");
|
||||
|
||||
echo "--- Removing the document reference should not crash ---\n";
|
||||
|
||||
[$dom, $clone] = createTestDocument();
|
||||
unset($dom);
|
||||
var_dump($clone->prefix, $clone->namespaceURI);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(3) "foo"
|
||||
string(7) "some:ns"
|
||||
--- Re-adding a namespaced attribute ---
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="some:ns"/>
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="some:ns" foo:bar="value"/>
|
||||
--- Re-adding a namespaced attribute, with the namespace deleted (setAttributeNodeNs variation) ---
|
||||
<?xml version="1.0"?>
|
||||
<container/>
|
||||
<?xml version="1.0"?>
|
||||
<container><child xmlns:foo="some:ns" foo:bar="value"/></container>
|
||||
--- Re-adding a namespaced attribute, with the namespace deleted (setAttributeNode variation) ---
|
||||
<?xml version="1.0"?>
|
||||
<container/>
|
||||
<?xml version="1.0"?>
|
||||
<container><child xmlns:foo="some:ns" foo:bar="value"/></container>
|
||||
--- Re-adding a namespaced attribute, with the namespace deleted (appendChild variation) ---
|
||||
<?xml version="1.0"?>
|
||||
<container/>
|
||||
<?xml version="1.0"?>
|
||||
<container><child xmlns:foo="some:ns" foo:bar="value"/></container>
|
||||
--- Removing the document reference should not crash ---
|
||||
string(3) "foo"
|
||||
string(7) "some:ns"
|
26
ext/dom/tests/clone_attribute_namespace_02.phpt
Normal file
26
ext/dom/tests/clone_attribute_namespace_02.phpt
Normal file
@ -0,0 +1,26 @@
|
||||
--TEST--
|
||||
Cloning an attribute should retain its namespace 02
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument;
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="some:ns" foo:bar="1">
|
||||
<child xmlns:foo="some:other"/>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
$clone = $dom->documentElement->getAttributeNodeNs("some:ns", "bar")->cloneNode(true);
|
||||
$dom->documentElement->firstElementChild->setAttributeNodeNs($clone);
|
||||
|
||||
echo $dom->saveXML();
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="some:ns" foo:bar="1">
|
||||
<child xmlns:foo="some:other" xmlns:foo1="some:ns" foo1:bar="1"/>
|
||||
</container>
|
37
ext/dom/tests/import_attribute_namespace.phpt
Normal file
37
ext/dom/tests/import_attribute_namespace.phpt
Normal file
@ -0,0 +1,37 @@
|
||||
--TEST--
|
||||
Cloning an attribute should retain its namespace 02
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument;
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="some:ns" foo:bar="1"/>
|
||||
XML);
|
||||
|
||||
$dom2 = new DOMDocument;
|
||||
$dom2->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="some:other"/>
|
||||
XML);
|
||||
|
||||
$imported = $dom2->importNode($dom->documentElement->getAttributeNodeNs("some:ns", "bar"));
|
||||
var_dump($imported->prefix, $imported->namespaceURI);
|
||||
$dom2->documentElement->setAttributeNodeNs($imported);
|
||||
var_dump($imported->prefix, $imported->namespaceURI);
|
||||
|
||||
echo $dom->saveXML();
|
||||
echo $dom2->saveXML();
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(7) "default"
|
||||
string(7) "some:ns"
|
||||
string(7) "default"
|
||||
string(7) "some:ns"
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="some:ns" foo:bar="1"/>
|
||||
<?xml version="1.0"?>
|
||||
<container xmlns:foo="some:other" xmlns:default="some:ns" default:bar="1"/>
|
@ -2,6 +2,7 @@
|
||||
GH-12532 (PharData created from zip has incorrect timestamp)
|
||||
--EXTENSIONS--
|
||||
phar
|
||||
zlib
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user