diff --git a/ext/dom/tests/DOMEntityReference_predefined_free.phpt b/ext/dom/tests/DOMEntityReference_predefined_free.phpt new file mode 100644 index 0000000000000..4b971d83703ed --- /dev/null +++ b/ext/dom/tests/DOMEntityReference_predefined_free.phpt @@ -0,0 +1,46 @@ +--TEST-- +Freeing of a predefined DOMEntityReference +--EXTENSIONS-- +dom +--FILE-- + +--EXPECT-- +object(DOMEntityReference)#1 (17) { + ["nodeName"]=> + string(3) "amp" + ["nodeValue"]=> + NULL + ["nodeType"]=> + int(5) + ["parentNode"]=> + NULL + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + string(22) "(object value omitted)" + ["lastChild"]=> + string(22) "(object value omitted)" + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["attributes"]=> + NULL + ["isConnected"]=> + bool(false) + ["namespaceURI"]=> + NULL + ["prefix"]=> + string(0) "" + ["localName"]=> + NULL + ["baseURI"]=> + NULL + ["textContent"]=> + string(0) "" +} diff --git a/ext/dom/tests/delayed_freeing/entity_declaration.phpt b/ext/dom/tests/delayed_freeing/entity_declaration.phpt index 3e082611c3583..c049be675be38 100644 --- a/ext/dom/tests/delayed_freeing/entity_declaration.phpt +++ b/ext/dom/tests/delayed_freeing/entity_declaration.phpt @@ -9,16 +9,30 @@ $doc->loadXML(<<<'XML' + ]> XML); -$entity = $doc->doctype->entities[0]; -var_dump($entity->nodeName, $entity->parentNode->nodeName); +$ref1 = $doc->createEntityReference("test"); +$ref2 = $doc->createEntityReference("myimage"); +$entity1 = $doc->doctype->entities[0]; +$entity2 = $doc->doctype->entities[1]; +if (strcmp($entity1->nodeName, $entity2->nodeName) < 0) { + // Entity ordering depends on the addresses + [$entity1, $entity2] = [$entity2, $entity1]; +} +var_dump($entity1->nodeName, $entity1->parentNode->nodeName); +var_dump($entity2->nodeName, $entity2->parentNode->nodeName); $doc->removeChild($doc->doctype); -var_dump($entity->nodeName, $entity->parentNode); +var_dump($entity1->nodeName, $entity1->parentNode); +var_dump($entity2->nodeName, $entity2->parentNode); ?> --EXPECT-- string(4) "test" string(5) "books" +string(7) "myimage" +string(5) "books" string(4) "test" NULL +string(7) "myimage" +NULL diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 526aa296aad1a..e6b34b02bf7c1 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -206,12 +206,36 @@ static void php_libxml_node_free(xmlNodePtr node) * dtd is attached to the document. This works around the issue by inspecting the parent directly. */ case XML_ENTITY_DECL: { xmlEntityPtr entity = (xmlEntityPtr) node; - php_libxml_unlink_entity_decl(entity); - if (entity->orig != NULL) { - xmlFree((char *) entity->orig); - entity->orig = NULL; + if (entity->etype != XML_INTERNAL_PREDEFINED_ENTITY) { + php_libxml_unlink_entity_decl(entity); +#if LIBXML_VERSION >= 21200 + xmlFreeEntity(entity); +#else + if (entity->children != NULL && entity->owner && entity == (xmlEntityPtr) entity->children->parent) { + xmlFreeNodeList(entity->children); + } + xmlDictPtr dict = entity->doc != NULL ? entity->doc->dict : NULL; + if (dict == NULL || !xmlDictOwns(dict, entity->name)) { + xmlFree((xmlChar *) entity->name); + } + if (dict == NULL || !xmlDictOwns(dict, entity->ExternalID)) { + xmlFree((xmlChar *) entity->ExternalID); + } + if (dict == NULL || !xmlDictOwns(dict, entity->SystemID)) { + xmlFree((xmlChar *) entity->SystemID); + } + if (dict == NULL || !xmlDictOwns(dict, entity->URI)) { + xmlFree((xmlChar *) entity->URI); + } + if (dict == NULL || !xmlDictOwns(dict, entity->content)) { + xmlFree(entity->content); + } + if (dict == NULL || !xmlDictOwns(dict, entity->orig)) { + xmlFree(entity->orig); + } + xmlFree(entity); +#endif } - xmlFreeNode(node); break; } case XML_NOTATION_NODE: { @@ -1385,6 +1409,15 @@ PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node) case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: break; + case XML_ENTITY_REF_NODE: + /* Entity reference nodes are special: their children point to entity declarations, + * but they don't own the declarations and therefore shouldn't free the children. + * Moreover, there can be N>1 reference nodes for a single entity declarations. */ + php_libxml_unregister_node(node); + if (node->parent == NULL) { + php_libxml_node_free(node); + } + break; default: if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) { php_libxml_node_free_list((xmlNodePtr) node->children);