diff --git a/ext/dom/node.c b/ext/dom/node.c index 88c058a6ac51..d54822d8077e 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -919,7 +919,7 @@ static bool dom_node_check_legacy_insertion_validity(xmlNodePtr parentp, xmlNode } /* Documents can never be a child. */ - if (child->type == XML_DOCUMENT_NODE || child->type == XML_HTML_DOCUMENT_NODE) { + if (child->type == XML_DOCUMENT_NODE || child->type == XML_HTML_DOCUMENT_NODE || child->type == XML_NOTATION_NODE) { php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror); return false; } diff --git a/ext/dom/parentnode/tree.c b/ext/dom/parentnode/tree.c index aacc991cc735..5d068ee1bbd6 100644 --- a/ext/dom/parentnode/tree.c +++ b/ext/dom/parentnode/tree.c @@ -285,10 +285,10 @@ static bool dom_is_pre_insert_valid_without_step_1(php_libxml_ref_obj *document, dom_hierarchy(parentNode, node) != SUCCESS /* 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException. */ || node->type == XML_ATTRIBUTE_NODE + || node->type == XML_NOTATION_NODE || (php_dom_follow_spec_doc_ref(document) && ( node->type == XML_ENTITY_REF_NODE || node->type == XML_ENTITY_NODE - || node->type == XML_NOTATION_NODE || node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE || node->type >= XML_ELEMENT_DECL))) { diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 0833d9a3c9be..e1d39f41bdc9 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -2629,6 +2629,13 @@ static xmlNodePtr dom_clone_helper(php_dom_libxml_ns_mapper *ns_mapper, xmlNodeP xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive) { + if (node->type == XML_NOTATION_NODE) { + const xmlEntity *notation = (const xmlEntity *) node; + xmlNodePtr clone = create_notation(NULL, notation->name, notation->ExternalID, notation->SystemID); + clone->doc = doc; + return clone; + } + if (node->type == XML_DTD_NODE) { /* The behaviour w.r.t. the internal subset is implementation-defined according to DOM 3. * This follows what e.g. Java and C# do: copy it regardless of the recursiveness. diff --git a/ext/dom/tests/gh25268_notation_insert.phpt b/ext/dom/tests/gh25268_notation_insert.phpt new file mode 100644 index 000000000000..1db202a2e6d2 --- /dev/null +++ b/ext/dom/tests/gh25268_notation_insert.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-25268 (Inserting a DOMNotation clone into a legacy document) +--EXTENSIONS-- +dom +--FILE-- +loadXML(']>'); +$notation = $doc->doctype->notations->getNamedItem('GIF'); +$root = $doc->documentElement; + +$tests = [ + 'appendChild' => fn() => $root->appendChild(clone $notation), + 'insertBefore' => fn() => $root->insertBefore(clone $notation, $root->firstChild), + 'replaceChild' => fn() => $root->replaceChild(clone $notation, $root->firstChild), + 'append' => fn() => $root->append(clone $notation), + 'append original' => fn() => $root->append($notation), +]; + +foreach ($tests as $name => $test) { + try { + $test(); + echo $name, ': inserted', PHP_EOL; + } catch (DOMException $e) { + echo $name, ': ', $e::class, ': ', $e->getMessage(), PHP_EOL; + } +} + +echo $doc->saveXML($root), PHP_EOL; +?> +--EXPECT-- +appendChild: DOMException: Hierarchy Request Error +insertBefore: DOMException: Hierarchy Request Error +replaceChild: DOMException: Hierarchy Request Error +append: DOMException: Hierarchy Request Error +append original: DOMException: Hierarchy Request Error + diff --git a/ext/dom/tests/modern/xml/gh25268_notation.phpt b/ext/dom/tests/modern/xml/gh25268_notation.phpt new file mode 100644 index 000000000000..b535abf44166 --- /dev/null +++ b/ext/dom/tests/modern/xml/gh25268_notation.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-25268 (Cloning Dom\Notation loses its node) +--EXTENSIONS-- +dom +--CREDITS-- +YuanchengJiang +--FILE-- +]>'); +$notation = $dom->doctype->notations->getNamedItem("GIF"); + +$clone = clone $notation; +var_dump($clone->nodeName, $clone->ownerDocument === $dom, $clone->parentNode, $clone->isConnected); + +$clone2 = $notation->cloneNode(); +var_dump($clone2->nodeName, $clone2 === $clone); + +unset($dom, $notation); +var_dump($clone->nodeName, $clone2->nodeName); +?> +--EXPECT-- +string(3) "GIF" +bool(true) +NULL +bool(false) +string(3) "GIF" +bool(false) +string(3) "GIF" +string(3) "GIF"