Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix "invalid state error" while cloning namespace declarations #11429

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 44 additions & 13 deletions ext/dom/php_dom.c
Expand Up @@ -89,6 +89,7 @@ static HashTable dom_xpath_prop_handlers;

static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type);
static void dom_object_namespace_node_free_storage(zend_object *object);
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original);

typedef int (*dom_read_t)(dom_object *obj, zval *retval);
typedef int (*dom_write_t)(dom_object *obj, zval *newval);
Expand Down Expand Up @@ -477,6 +478,19 @@ PHP_FUNCTION(dom_import_simplexml)

static dom_object* dom_objects_set_class(zend_class_entry *class_type);

static void dom_update_refcount_after_clone(dom_object *original, xmlNodePtr original_node, dom_object *clone, xmlNodePtr cloned_node)
{
/* If we cloned a document then we must create new doc proxy */
if (cloned_node->doc == original_node->doc) {
clone->document = original->document;
}
php_libxml_increment_doc_ref((php_libxml_node_object *)clone, cloned_node->doc);
php_libxml_increment_node_ptr((php_libxml_node_object *)clone, cloned_node, (void *)clone);
if (original->document != clone->document) {
dom_copy_doc_props(original->document, clone->document);
}
}

static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */
{
dom_object *intern = php_dom_obj_from_obj(zobject);
Expand All @@ -489,15 +503,7 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */
if (node != NULL) {
xmlNodePtr cloned_node = xmlDocCopyNode(node, node->doc, 1);
if (cloned_node != NULL) {
/* If we cloned a document then we must create new doc proxy */
if (cloned_node->doc == node->doc) {
clone->document = intern->document;
}
php_libxml_increment_doc_ref((php_libxml_node_object *)clone, cloned_node->doc);
php_libxml_increment_node_ptr((php_libxml_node_object *)clone, cloned_node, (void *)clone);
if (intern->document != clone->document) {
dom_copy_doc_props(intern->document, clone->document);
}
dom_update_refcount_after_clone(intern, node, clone, cloned_node);
}

}
Expand All @@ -509,6 +515,26 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */
}
/* }}} */

static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject)
{
dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject);
zend_object *clone = dom_objects_namespace_node_new(intern->dom.std.ce);
dom_object_namespace_node *clone_intern = php_dom_namespace_node_obj_from_obj(clone);

xmlNodePtr original_node = dom_object_get_node(&intern->dom);
ZEND_ASSERT(original_node->type == XML_NAMESPACE_DECL);
xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(original_node->parent, original_node->ns);

if (intern->parent_intern) {
clone_intern->parent_intern = intern->parent_intern;
GC_ADDREF(&clone_intern->parent_intern->std);
}
dom_update_refcount_after_clone(&intern->dom, original_node, &clone_intern->dom, cloned_node);

zend_objects_clone_members(clone, &intern->dom.std);
return clone;
}

static void dom_copy_prop_handler(zval *zv) /* {{{ */
{
dom_prop_handler *hnd = Z_PTR_P(zv);
Expand Down Expand Up @@ -577,6 +603,7 @@ PHP_MINIT_FUNCTION(dom)
memcpy(&dom_object_namespace_node_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
dom_object_namespace_node_handlers.offset = XtOffsetOf(dom_object_namespace_node, dom.std);
dom_object_namespace_node_handlers.free_obj = dom_object_namespace_node_free_storage;
dom_object_namespace_node_handlers.clone_obj = dom_object_namespace_node_clone_obj;

zend_hash_init(&classes, 0, NULL, NULL, 1);

Expand Down Expand Up @@ -1579,8 +1606,7 @@ xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
}
/* }}} end dom_get_nsdecl */

/* Note: Assumes the additional lifetime was already added in the caller. */
xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern)
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original)
{
xmlNodePtr attrp;
xmlNsPtr curns = xmlNewNs(NULL, original->href, NULL);
Expand All @@ -1593,11 +1619,16 @@ xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr origina
attrp->type = XML_NAMESPACE_DECL;
attrp->parent = nodep;
attrp->ns = curns;
return attrp;
}

/* Note: Assumes the additional lifetime was already added in the caller. */
xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern)
{
xmlNodePtr attrp = php_dom_create_fake_namespace_decl_node_ptr(nodep, original);
php_dom_create_object(attrp, return_value, parent_intern);
/* This object must exist, because we just created an object for it via php_dom_create_object(). */
dom_object *obj = ((php_libxml_node_ptr *)attrp->_private)->_private;
php_dom_namespace_node_obj_from_obj(&obj->std)->parent_intern = parent_intern;
php_dom_namespace_node_obj_from_obj(Z_OBJ_P(return_value))->parent_intern = parent_intern;
return attrp;
}

Expand Down
72 changes: 72 additions & 0 deletions ext/dom/tests/clone_nodes.phpt
@@ -0,0 +1,72 @@
--TEST--
Clone nodes
--EXTENSIONS--
dom
--FILE--
<?php

echo "-- Clone DOMNameSpaceNode --\n";

$doc = new DOMDocument;
$doc->loadXML('<foo xmlns="http://php.net/test" xmlns:foo="urn:foo" />');

$attr = $doc->documentElement->getAttributeNode('xmlns');
var_dump($attr);

$attrClone = clone $attr;
var_dump($attrClone->nodeValue);
var_dump($attrClone->parentNode->nodeName);

unset($doc);
unset($attr);

var_dump($attrClone->nodeValue);
var_dump($attrClone->parentNode->nodeName);

echo "-- Clone DOMNode --\n";

$doc = new DOMDocument;
$doc->loadXML('<foo><bar/></foo>');

$bar = $doc->documentElement->firstChild;
$barClone = clone $bar;
$bar->remove();
unset($bar);

var_dump($barClone->nodeName);

$doc->firstElementChild->remove();
unset($doc);

var_dump($barClone->nodeName);
var_dump($barClone->parentNode);

?>
--EXPECT--
-- Clone DOMNameSpaceNode --
object(DOMNameSpaceNode)#3 (8) {
["nodeName"]=>
string(5) "xmlns"
["nodeValue"]=>
string(19) "http://php.net/test"
["nodeType"]=>
int(18)
["prefix"]=>
string(0) ""
["localName"]=>
string(5) "xmlns"
["namespaceURI"]=>
string(19) "http://php.net/test"
["ownerDocument"]=>
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
}
string(19) "http://php.net/test"
string(3) "foo"
string(19) "http://php.net/test"
string(3) "foo"
-- Clone DOMNode --
string(3) "bar"
string(3) "bar"
NULL