From 0dc6e42920cc8eb69355020ebefb1b5f0233b152 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Tue, 9 Jul 2024 17:08:38 +0200
Subject: [PATCH 01/10] Split off private data from the ns mapper
---
ext/dom/domimplementation.c | 14 +++++++------
ext/dom/html_document.c | 20 ++++++++++--------
ext/dom/namespace_compat.c | 42 ++++++++++++++++++++++++-------------
ext/dom/namespace_compat.h | 17 +++++++++------
ext/dom/node.c | 16 +++++++-------
ext/dom/php_dom.c | 12 +++++------
ext/dom/php_dom.h | 3 +++
ext/dom/xml_common.h | 8 +++----
ext/dom/xml_document.c | 7 ++++---
9 files changed, 83 insertions(+), 56 deletions(-)
diff --git a/ext/dom/domimplementation.c b/ext/dom/domimplementation.c
index 58a50b06d074e..710232d78286e 100644
--- a/ext/dom/domimplementation.c
+++ b/ext/dom/domimplementation.c
@@ -266,7 +266,8 @@ PHP_METHOD(Dom_Implementation, createDocument)
xmlDocPtr document = NULL;
xmlChar *localname = NULL, *prefix = NULL;
- php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create();
+ php_dom_private_data *private_data = php_dom_private_data_create();
+ php_dom_libxml_ns_mapper *ns_mapper = php_dom_ns_mapper_from_private(private_data);
/* 1. Let document be a new XMLDocument. */
document = xmlNewDoc(BAD_CAST "1.0");
@@ -307,7 +308,7 @@ PHP_METHOD(Dom_Implementation, createDocument)
NULL
);
dom_set_xml_class(intern->document);
- intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
+ intern->document->private_data = php_dom_libxml_private_data_header(private_data);
/* 4. If doctype is non-null, append doctype to document. */
if (doctype != NULL) {
@@ -337,7 +338,7 @@ PHP_METHOD(Dom_Implementation, createDocument)
xmlFree(localname);
xmlFree(prefix);
xmlFreeDoc(document);
- php_dom_libxml_ns_mapper_destroy(ns_mapper);
+ php_dom_private_data_destroy(private_data);
RETURN_THROWS();
}
/* }}} end dom_domimplementation_create_document */
@@ -366,7 +367,8 @@ PHP_METHOD(Dom_Implementation, createHTMLDocument)
/* 3. Append a new doctype, with "html" as its name and with its node document set to doc, to doc. */
xmlDtdPtr dtd = xmlCreateIntSubset(doc, BAD_CAST "html", NULL, NULL);
- php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create();
+ php_dom_private_data *private_data = php_dom_private_data_create();
+ php_dom_libxml_ns_mapper *ns_mapper = php_dom_ns_mapper_from_private(private_data);
xmlNsPtr html_ns = php_dom_libxml_ns_mapper_ensure_html_ns(ns_mapper);
/* 4. Append the result of creating an element given doc, html, and the HTML namespace, to doc. */
@@ -396,7 +398,7 @@ PHP_METHOD(Dom_Implementation, createHTMLDocument)
if (UNEXPECTED(dtd == NULL || html_element == NULL || head_element == NULL || (title != NULL && title_element == NULL) || body_element == NULL)) {
php_dom_throw_error(INVALID_STATE_ERR, true);
xmlFreeDoc(doc);
- php_dom_libxml_ns_mapper_destroy(ns_mapper);
+ php_dom_private_data_destroy(private_data);
RETURN_THROWS();
}
@@ -408,7 +410,7 @@ PHP_METHOD(Dom_Implementation, createHTMLDocument)
NULL
);
dom_set_xml_class(intern->document);
- intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
+ intern->document->private_data = php_dom_libxml_private_data_header(private_data);
}
/* }}} */
diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c
index c8a805318c356..4c451f8a525ed 100644
--- a/ext/dom/html_document.c
+++ b/ext/dom/html_document.c
@@ -757,7 +757,7 @@ PHP_METHOD(Dom_HTMLDocument, createEmpty)
NULL
);
dom_set_xml_class(intern->document);
- intern->document->private_data = php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper_create());
+ intern->document->private_data = php_dom_libxml_private_data_header(php_dom_private_data_create());
return;
oom:
@@ -878,7 +878,8 @@ PHP_METHOD(Dom_HTMLDocument, createFromString)
goto fail_oom;
}
- php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create();
+ php_dom_private_data *private_data = php_dom_private_data_create();
+ php_dom_libxml_ns_mapper *ns_mapper = php_dom_ns_mapper_from_private(private_data);
xmlDocPtr lxml_doc;
lexbor_libxml2_bridge_status bridge_status = lexbor_libxml2_bridge_convert_document(
@@ -890,7 +891,7 @@ PHP_METHOD(Dom_HTMLDocument, createFromString)
);
lexbor_libxml2_bridge_copy_observations(parser->tree, &ctx.observations);
if (UNEXPECTED(bridge_status != LEXBOR_LIBXML2_BRIDGE_STATUS_OK)) {
- php_dom_libxml_ns_mapper_destroy(ns_mapper);
+ php_dom_private_data_destroy(private_data);
php_libxml_ctx_error(
NULL,
"%s in %s",
@@ -918,7 +919,7 @@ PHP_METHOD(Dom_HTMLDocument, createFromString)
);
dom_set_xml_class(intern->document);
intern->document->quirks_mode = ctx.observations.quirks_mode;
- intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
+ intern->document->private_data = php_dom_libxml_private_data_header(private_data);
return;
fail_oom:
@@ -930,7 +931,7 @@ PHP_METHOD(Dom_HTMLDocument, createFromString)
PHP_METHOD(Dom_HTMLDocument, createFromFile)
{
const char *filename, *override_encoding = NULL;
- php_dom_libxml_ns_mapper *ns_mapper = NULL;
+ php_dom_private_data *private_data = NULL;
size_t filename_len, override_encoding_len;
zend_long options = 0;
php_stream *stream = NULL;
@@ -1069,7 +1070,8 @@ PHP_METHOD(Dom_HTMLDocument, createFromFile)
goto fail_oom;
}
- ns_mapper = php_dom_libxml_ns_mapper_create();
+ private_data = php_dom_private_data_create();
+ php_dom_libxml_ns_mapper *ns_mapper = php_dom_ns_mapper_from_private(private_data);
xmlDocPtr lxml_doc;
lexbor_libxml2_bridge_status bridge_status = lexbor_libxml2_bridge_convert_document(
@@ -1139,14 +1141,14 @@ PHP_METHOD(Dom_HTMLDocument, createFromFile)
);
dom_set_xml_class(intern->document);
intern->document->quirks_mode = ctx.observations.quirks_mode;
- intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
+ intern->document->private_data = php_dom_libxml_private_data_header(private_data);
return;
fail_oom:
php_dom_throw_error(INVALID_STATE_ERR, true);
fail_general:
- if (ns_mapper != NULL) {
- php_dom_libxml_ns_mapper_destroy(ns_mapper);
+ if (private_data != NULL) {
+ php_dom_private_data_destroy(private_data);
}
lxb_html_document_destroy(document);
php_stream_close(stream);
diff --git a/ext/dom/namespace_compat.c b/ext/dom/namespace_compat.c
index 51df10e2e087b..8472fd36c62b1 100644
--- a/ext/dom/namespace_compat.c
+++ b/ext/dom/namespace_compat.c
@@ -32,7 +32,6 @@ PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_xml_magic_token = (co
PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_xmlns_magic_token = (const php_dom_ns_magic_token *) DOM_XMLNS_NS_URI;
struct php_dom_libxml_ns_mapper {
- php_libxml_private_data_header header;
/* This is used almost all the time for HTML documents, so it makes sense to cache this. */
xmlNsPtr html_ns;
/* Used for every prefixless namespace declaration in XML, so also very common. */
@@ -40,6 +39,11 @@ struct php_dom_libxml_ns_mapper {
HashTable uri_to_prefix_map;
};
+typedef struct php_dom_private_data {
+ php_libxml_private_data_header header;
+ struct php_dom_libxml_ns_mapper ns_mapper;
+} php_dom_private_data;
+
static void php_dom_libxml_ns_mapper_prefix_map_element_dtor(zval *zv)
{
if (DOM_Z_IS_OWNED(zv)) {
@@ -69,25 +73,25 @@ static HashTable *php_dom_libxml_ns_mapper_ensure_prefix_map(php_dom_libxml_ns_m
return prefix_map;
}
-static void php_dom_libxml_ns_mapper_header_destroy(php_libxml_private_data_header *header)
+static void php_dom_libxml_private_data_destroy(php_libxml_private_data_header *header)
{
- php_dom_libxml_ns_mapper_destroy((php_dom_libxml_ns_mapper *) header);
+ php_dom_private_data_destroy((php_dom_private_data *) header);
}
-PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_libxml_ns_mapper_create(void)
+PHP_DOM_EXPORT php_dom_private_data *php_dom_private_data_create(void)
{
- php_dom_libxml_ns_mapper *mapper = emalloc(sizeof(*mapper));
- mapper->header.dtor = php_dom_libxml_ns_mapper_header_destroy;
- mapper->html_ns = NULL;
- mapper->prefixless_xmlns_ns = NULL;
- zend_hash_init(&mapper->uri_to_prefix_map, 0, NULL, ZVAL_PTR_DTOR, false);
+ php_dom_private_data *mapper = emalloc(sizeof(*mapper));
+ mapper->header.dtor = php_dom_libxml_private_data_destroy;
+ mapper->ns_mapper.html_ns = NULL;
+ mapper->ns_mapper.prefixless_xmlns_ns = NULL;
+ zend_hash_init(&mapper->ns_mapper.uri_to_prefix_map, 0, NULL, ZVAL_PTR_DTOR, false);
return mapper;
}
-void php_dom_libxml_ns_mapper_destroy(php_dom_libxml_ns_mapper *mapper)
+void php_dom_private_data_destroy(php_dom_private_data *data)
{
- zend_hash_destroy(&mapper->uri_to_prefix_map);
- efree(mapper);
+ zend_hash_destroy(&data->ns_mapper.uri_to_prefix_map);
+ efree(data);
}
static xmlNsPtr php_dom_libxml_ns_mapper_ensure_cached_ns(php_dom_libxml_ns_mapper *mapper, xmlNsPtr *ptr, const char *uri, size_t length, const php_dom_ns_magic_token *token)
@@ -229,9 +233,19 @@ static xmlNsPtr php_dom_libxml_ns_mapper_store_and_normalize_parsed_ns(php_dom_l
return ns;
}
-PHP_DOM_EXPORT php_libxml_private_data_header *php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper *mapper)
+PHP_DOM_EXPORT php_libxml_private_data_header *php_dom_libxml_private_data_header(php_dom_private_data *private_data)
+{
+ return private_data == NULL ? NULL : &private_data->header;
+}
+
+PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_ns_mapper_from_private(php_dom_private_data *private_data)
+{
+ return private_data == NULL ? NULL : &private_data->ns_mapper;
+}
+
+PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_get_ns_mapper(dom_object *object)
{
- return mapper == NULL ? NULL : &mapper->header;
+ return &php_dom_get_private_data(object)->ns_mapper;
}
typedef struct {
diff --git a/ext/dom/namespace_compat.h b/ext/dom/namespace_compat.h
index bf0a295e1bee6..5a7c3f7b71eae 100644
--- a/ext/dom/namespace_compat.h
+++ b/ext/dom/namespace_compat.h
@@ -33,6 +33,12 @@ typedef struct php_dom_ns_magic_token php_dom_ns_magic_token;
struct php_dom_libxml_ns_mapper;
typedef struct php_dom_libxml_ns_mapper php_dom_libxml_ns_mapper;
+struct php_dom_private_data;
+typedef struct php_dom_private_data php_dom_private_data;
+
+typedef struct php_libxml_private_data_header php_libxml_private_data_header;
+struct php_libxml_private_data_header;
+
PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_html_magic_token;
PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_mathml_magic_token;
PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_svg_magic_token;
@@ -40,21 +46,20 @@ PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_xlink_magic_to
PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_xml_magic_token;
PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_xmlns_magic_token;
-typedef struct php_libxml_private_data_header php_libxml_private_data_header;
-struct php_libxml_private_data_header;
-
/* These functions make it possible to make a namespace declaration also visible as an attribute by
* creating an equivalent attribute node. */
-PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_libxml_ns_mapper_create(void);
-PHP_DOM_EXPORT void php_dom_libxml_ns_mapper_destroy(php_dom_libxml_ns_mapper *mapper);
+PHP_DOM_EXPORT php_dom_private_data *php_dom_private_data_create(void);
+PHP_DOM_EXPORT void php_dom_private_data_destroy(php_dom_private_data *data);
PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_ensure_html_ns(php_dom_libxml_ns_mapper *mapper);
PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_ensure_prefixless_xmlns_ns(php_dom_libxml_ns_mapper *mapper);
PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns(php_dom_libxml_ns_mapper *mapper, zend_string *prefix, zend_string *uri);
PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(php_dom_libxml_ns_mapper *mapper, const xmlChar *prefix, size_t prefix_len, zend_string *uri);
PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(php_dom_libxml_ns_mapper *mapper, const char *prefix, const char *uri);
-PHP_DOM_EXPORT php_libxml_private_data_header *php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper *mapper);
+PHP_DOM_EXPORT php_libxml_private_data_header *php_dom_libxml_private_data_header(php_dom_private_data *private_data);
+PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_get_ns_mapper(dom_object *object);
+PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_ns_mapper_from_private(php_dom_private_data *private_data);
PHP_DOM_EXPORT void php_dom_ns_compat_mark_attribute_list(php_dom_libxml_ns_mapper *mapper, xmlNodePtr node);
PHP_DOM_EXPORT void php_dom_libxml_reconcile_modern(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node);
PHP_DOM_EXPORT void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp);
diff --git a/ext/dom/node.c b/ext/dom/node.c
index ef669963de78b..ff7631825b498 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -1444,21 +1444,21 @@ PHP_METHOD(DOMNode, cloneNode)
DOM_GET_OBJ(n, id, xmlNodePtr, intern);
- php_dom_libxml_ns_mapper *ns_mapper = NULL;
+ php_dom_private_data *private_data = NULL;
bool clone_document = n->type == XML_DOCUMENT_NODE || n->type == XML_HTML_DOCUMENT_NODE;
if (php_dom_follow_spec_intern(intern)) {
if (clone_document) {
- ns_mapper = php_dom_libxml_ns_mapper_create();
+ private_data = php_dom_private_data_create();
} else {
- ns_mapper = php_dom_get_ns_mapper(intern);
+ private_data = php_dom_get_private_data(intern);
}
}
- node = dom_clone_node(ns_mapper, n, n->doc, recursive);
+ node = dom_clone_node(php_dom_ns_mapper_from_private(private_data), n, n->doc, recursive);
if (!node) {
- if (clone_document && ns_mapper != NULL) {
- php_dom_libxml_ns_mapper_destroy(ns_mapper);
+ if (clone_document && private_data != NULL) {
+ php_dom_private_data_destroy(private_data);
}
RETURN_FALSE;
}
@@ -1466,7 +1466,7 @@ PHP_METHOD(DOMNode, cloneNode)
/* If document cloned we want a new document proxy */
if (clone_document) {
dom_object *new_intern;
- if (ns_mapper) {
+ if (private_data) {
/* We have the issue here that we can't create a modern node without an intern.
* Fortunately, it's impossible to have a custom document class for the modern DOM (final base class),
* so we can solve this by invoking the instantiation helper directly. */
@@ -1478,7 +1478,7 @@ PHP_METHOD(DOMNode, cloneNode)
}
php_dom_update_document_after_clone(intern, n, new_intern, node);
ZEND_ASSERT(new_intern->document->private_data == NULL);
- new_intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
+ new_intern->document->private_data = php_dom_libxml_private_data_header(private_data);
} else {
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
diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c
index e6e7a23600d94..cb6439fe263c0 100644
--- a/ext/dom/php_dom.c
+++ b/ext/dom/php_dom.c
@@ -593,21 +593,21 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */
if (instanceof_function(intern->std.ce, dom_node_class_entry) || instanceof_function(intern->std.ce, dom_modern_node_class_entry)) {
xmlNodePtr node = (xmlNodePtr)dom_object_get_node(intern);
if (node != NULL) {
- php_dom_libxml_ns_mapper *ns_mapper = NULL;
+ php_dom_private_data *private_data = NULL;
if (php_dom_follow_spec_intern(intern)) {
if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) {
- ns_mapper = php_dom_libxml_ns_mapper_create();
+ private_data = php_dom_private_data_create();
} else {
- ns_mapper = php_dom_get_ns_mapper(intern);
+ private_data = php_dom_get_private_data(intern);
}
}
- xmlNodePtr cloned_node = dom_clone_node(ns_mapper, node, node->doc, true);
+ xmlNodePtr cloned_node = dom_clone_node(php_dom_ns_mapper_from_private(private_data), node, node->doc, true);
if (cloned_node != NULL) {
dom_update_refcount_after_clone(intern, node, clone, cloned_node);
}
- if (ns_mapper != NULL) {
- clone->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
+ if (private_data != NULL) {
+ clone->document->private_data = php_dom_libxml_private_data_header(private_data);
}
}
}
diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h
index aa1f3316c206a..624e3cbaa5760 100644
--- a/ext/dom/php_dom.h
+++ b/ext/dom/php_dom.h
@@ -114,6 +114,9 @@ typedef enum dom_iterator_type {
DOM_HTMLCOLLECTION,
} dom_iterator_type;
+struct php_dom_libxml_ns_mapper;
+typedef struct php_dom_libxml_ns_mapper php_dom_libxml_ns_mapper;
+
static inline dom_object_namespace_node *php_dom_namespace_node_obj_from_obj(zend_object *obj) {
return (dom_object_namespace_node*)((char*)(obj) - XtOffsetOf(dom_object_namespace_node, dom.std));
}
diff --git a/ext/dom/xml_common.h b/ext/dom/xml_common.h
index 94c753132b893..8e5734d5a914e 100644
--- a/ext/dom/xml_common.h
+++ b/ext/dom/xml_common.h
@@ -80,13 +80,13 @@ PHP_DOM_EXPORT xmlNodePtr dom_object_get_node(dom_object *obj);
__id = ZEND_THIS; \
DOM_GET_OBJ(__ptr, __id, __prtype, __intern);
-struct php_dom_libxml_ns_mapper;
-typedef struct php_dom_libxml_ns_mapper php_dom_libxml_ns_mapper;
+struct php_dom_private_data;
+typedef struct php_dom_private_data php_dom_private_data;
-static zend_always_inline php_dom_libxml_ns_mapper *php_dom_get_ns_mapper(dom_object *intern)
+static zend_always_inline php_dom_private_data *php_dom_get_private_data(dom_object *intern)
{
ZEND_ASSERT(intern->document != NULL);
- return (php_dom_libxml_ns_mapper *) intern->document->private_data;
+ return (php_dom_private_data *) intern->document->private_data;
}
static zend_always_inline xmlNodePtr php_dom_next_in_tree_order(const xmlNode *nodep, const xmlNode *basep)
diff --git a/ext/dom/xml_document.c b/ext/dom/xml_document.c
index e1511b65ef798..d5032e3fb801d 100644
--- a/ext/dom/xml_document.c
+++ b/ext/dom/xml_document.c
@@ -122,7 +122,7 @@ PHP_METHOD(Dom_XMLDocument, createEmpty)
NULL
);
dom_set_xml_class(intern->document);
- intern->document->private_data = php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper_create());
+ intern->document->private_data = php_dom_libxml_private_data_header(php_dom_private_data_create());
return;
oom:
@@ -236,8 +236,9 @@ static void load_from_helper(INTERNAL_FUNCTION_PARAMETERS, int mode)
void dom_document_convert_to_modern(php_libxml_ref_obj *document, xmlDocPtr lxml_doc)
{
- php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create();
- document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
+ php_dom_private_data *private_data = php_dom_private_data_create();
+ php_dom_libxml_ns_mapper *ns_mapper = php_dom_ns_mapper_from_private(private_data);
+ document->private_data = php_dom_libxml_private_data_header(private_data);
dom_mark_namespaces_as_attributes_too(ns_mapper, lxml_doc);
}
From 5b1b6d29e5c0e70d9c408bc2352f04e9d5098dd7 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Wed, 10 Jul 2024 16:19:59 +0200
Subject: [PATCH 02/10] Refactor XML serializer such that passing context is
easier
---
ext/dom/xml_serializer.c | 107 ++++++++++++++++++++-------------------
1 file changed, 55 insertions(+), 52 deletions(-)
diff --git a/ext/dom/xml_serializer.c b/ext/dom/xml_serializer.c
index 0c5f12023aa6d..3d7363b86efca 100644
--- a/ext/dom/xml_serializer.c
+++ b/ext/dom/xml_serializer.c
@@ -66,9 +66,13 @@ typedef struct {
const xmlChar *prefix, *name;
} dom_qname_pair;
+typedef struct dom_xml_serialize_ctx {
+ xmlSaveCtxtPtr ctxt;
+ xmlOutputBufferPtr out;
+} dom_xml_serialize_ctx;
+
static int dom_xml_serialization_algorithm(
- xmlSaveCtxtPtr ctxt,
- xmlOutputBufferPtr out,
+ dom_xml_serialize_ctx *ctx,
dom_xml_ns_prefix_map *namespace_prefix_map,
xmlNodePtr node,
const xmlChar *namespace,
@@ -895,8 +899,7 @@ static int dom_xml_output_indents(xmlOutputBufferPtr out, int indent)
/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serializing-an-element-node */
static int dom_xml_serialize_element_node(
- xmlSaveCtxtPtr ctxt,
- xmlOutputBufferPtr out,
+ dom_xml_serialize_ctx *ctx,
const xmlChar *namespace,
dom_xml_ns_prefix_map *namespace_prefix_map,
xmlNodePtr element,
@@ -916,7 +919,7 @@ static int dom_xml_serialize_element_node(
bool should_format = indent >= 0 && element->children != NULL && dom_xml_should_format_element(element);
/* 2. Let markup be the string "<" (U+003C LESS-THAN SIGN). */
- TRY(xmlOutputBufferWriteLit(out, "<"));
+ TRY(xmlOutputBufferWriteLit(ctx->out, "<"));
/* 3. Let qualified name be an empty string.
* => We're going to do it a bit differently.
@@ -966,7 +969,7 @@ static int dom_xml_serialize_element_node(
}
/* 11.4. Append the value of qualified name to markup. */
- TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name));
+ TRY_OR_CLEANUP(dom_xml_output_qname(ctx->out, &qualified_name));
}
/* 12. Otherwise, inherited ns is not equal to ns */
else {
@@ -1011,7 +1014,7 @@ static int dom_xml_serialize_element_node(
}
/* 12.4.3. Append the value of qualified name to markup. */
- TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name));
+ TRY_OR_CLEANUP(dom_xml_output_qname(ctx->out, &qualified_name));
}
/* 12.5. Otherwise, if prefix is not null, then: */
else if (prefix != NULL) {
@@ -1033,14 +1036,14 @@ static int dom_xml_serialize_element_node(
qualified_name.name = element->name;
/* 12.5.4. Append the value of qualified name to markup. */
- TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name));
+ TRY_OR_CLEANUP(dom_xml_output_qname(ctx->out, &qualified_name));
/* 12.5.5. Append the following to markup, in the order listed: ... */
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, " xmlns:")); /* 12.5.5.1 - 12.5.5.2 */
- TRY_OR_CLEANUP(xmlOutputBufferWriteString(out, (const char *) prefix));
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, "=\""));
- TRY_OR_CLEANUP(dom_xml_common_text_serialization(out, (const char *) ns, true));
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, "\""));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, " xmlns:")); /* 12.5.5.1 - 12.5.5.2 */
+ TRY_OR_CLEANUP(xmlOutputBufferWriteString(ctx->out, (const char *) prefix));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, "=\""));
+ TRY_OR_CLEANUP(dom_xml_common_text_serialization(ctx->out, (const char *) ns, true));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, "\""));
/* 12.5.6. If local default namespace is not null ... (editorial numbering error: https://github.com/w3c/DOM-Parsing/issues/43) */
if (local_default_namespace != NULL) {
@@ -1064,24 +1067,24 @@ static int dom_xml_serialize_element_node(
inherited_ns = ns;
/* 12.6.4. Append the value of qualified name to markup. */
- TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name));
+ TRY_OR_CLEANUP(dom_xml_output_qname(ctx->out, &qualified_name));
/* 12.6.5. Append the following to markup, in the order listed: ... */
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, " xmlns=\"")); /* 12.6.5.1 - 12.6.5.2 */
- TRY_OR_CLEANUP(dom_xml_common_text_serialization(out, (const char *) ns, true));
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, "\""));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, " xmlns=\"")); /* 12.6.5.1 - 12.6.5.2 */
+ TRY_OR_CLEANUP(dom_xml_common_text_serialization(ctx->out, (const char *) ns, true));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, "\""));
}
/* 12.7. Otherwise, the node has a local default namespace that matches ns ... */
else {
qualified_name.name = element->name;
inherited_ns = ns;
- TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name));
+ TRY_OR_CLEANUP(dom_xml_output_qname(ctx->out, &qualified_name));
}
}
/* 13. Append to markup the result of the XML serialization of node's attributes given map, prefix index,
* local prefixes map, ignore namespace definition attribute flag, and require well-formed flag. */
- TRY_OR_CLEANUP(dom_xml_serialize_attributes(out, element, &map, &local_prefixes_map, prefix_index, ignore_namespace_definition_attribute, require_well_formed));
+ TRY_OR_CLEANUP(dom_xml_serialize_attributes(ctx->out, element, &map, &local_prefixes_map, prefix_index, ignore_namespace_definition_attribute, require_well_formed));
/* 14. If ns is the HTML namespace, and the node's list of children is empty, and the node's localName matches
* any one of the following void elements: ... */
@@ -1109,19 +1112,19 @@ static int dom_xml_serialize_element_node(
|| dom_local_name_compare_ex(element, "source", strlen("source"), name_length)
|| dom_local_name_compare_ex(element, "track", strlen("track"), name_length)
|| dom_local_name_compare_ex(element, "wbr", strlen("wbr"), name_length)) {
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, " /"));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, " /"));
skip_end_tag = true;
}
} else {
/* 15. If ns is not the HTML namespace, and the node's list of children is empty,
* then append "/" (U+002F SOLIDUS) to markup and set the skip end tag flag to true. */
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, "/"));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, "/"));
skip_end_tag = true;
}
}
/* 16. Append ">" (U+003E GREATER-THAN SIGN) to markup. */
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, ">"));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, ">"));
/* 17. If the value of skip end tag is true, then return the value of markup and skip the remaining steps. */
if (!skip_end_tag) {
@@ -1136,20 +1139,20 @@ static int dom_xml_serialize_element_node(
/* 19. Otherwise, append to markup the result of running the XML serialization algorithm on each of node's children. */
for (xmlNodePtr child = element->children; child != NULL; child = child->next) {
if (should_format) {
- TRY_OR_CLEANUP(dom_xml_output_indents(out, indent));
+ TRY_OR_CLEANUP(dom_xml_output_indents(ctx->out, indent));
}
- TRY_OR_CLEANUP(dom_xml_serialization_algorithm(ctxt, out, &map, child, inherited_ns, prefix_index, indent, require_well_formed));
+ TRY_OR_CLEANUP(dom_xml_serialization_algorithm(ctx, &map, child, inherited_ns, prefix_index, indent, require_well_formed));
}
if (should_format) {
indent--;
- TRY_OR_CLEANUP(dom_xml_output_indents(out, indent));
+ TRY_OR_CLEANUP(dom_xml_output_indents(ctx->out, indent));
}
/* 20. Append the following to markup, in the order listed: */
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, ""));
- TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name));
- TRY_OR_CLEANUP(xmlOutputBufferWriteLit(out, ">"));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, ""));
+ TRY_OR_CLEANUP(dom_xml_output_qname(ctx->out, &qualified_name));
+ TRY_OR_CLEANUP(xmlOutputBufferWriteLit(ctx->out, ">"));
}
/* 21. Return the value of markup.
@@ -1166,8 +1169,7 @@ static int dom_xml_serialize_element_node(
/* https://w3c.github.io/DOM-Parsing/#xml-serializing-a-documentfragment-node */
static int dom_xml_serializing_a_document_fragment_node(
- xmlSaveCtxtPtr ctxt,
- xmlOutputBufferPtr out,
+ dom_xml_serialize_ctx *ctx,
dom_xml_ns_prefix_map *namespace_prefix_map,
xmlNodePtr node,
const xmlChar *namespace,
@@ -1182,7 +1184,7 @@ static int dom_xml_serializing_a_document_fragment_node(
/* 2. For each child child of node, in tree order, run the XML serialization algorithm on the child ... */
xmlNodePtr child = node->children;
while (child != NULL) {
- TRY(dom_xml_serialization_algorithm(ctxt, out, namespace_prefix_map, child, namespace, prefix_index, indent, require_well_formed));
+ TRY(dom_xml_serialization_algorithm(ctx, namespace_prefix_map, child, namespace, prefix_index, indent, require_well_formed));
child = child->next;
}
@@ -1193,8 +1195,7 @@ static int dom_xml_serializing_a_document_fragment_node(
/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serializing-a-document-node */
static int dom_xml_serializing_a_document_node(
- xmlSaveCtxtPtr ctxt,
- xmlOutputBufferPtr out,
+ dom_xml_serialize_ctx *ctx,
dom_xml_ns_prefix_map *namespace_prefix_map,
xmlNodePtr node,
const xmlChar *namespace,
@@ -1210,16 +1211,16 @@ static int dom_xml_serializing_a_document_node(
node->children = NULL;
/* https://github.com/w3c/DOM-Parsing/issues/50 */
- TRY(xmlOutputBufferFlush(out));
- TRY(xmlSaveDoc(ctxt, (xmlDocPtr) node));
- TRY(xmlSaveFlush(ctxt));
+ TRY(xmlOutputBufferFlush(ctx->out));
+ TRY(xmlSaveDoc(ctx->ctxt, (xmlDocPtr) node));
+ TRY(xmlSaveFlush(ctx->ctxt));
node->children = child;
/* 2. For each child child of node, in tree order, run the XML serialization algorithm on the child passing along the provided arguments,
* and append the result to serialized document. */
while (child != NULL) {
- TRY(dom_xml_serialization_algorithm(ctxt, out, namespace_prefix_map, child, namespace, prefix_index, indent, require_well_formed));
+ TRY(dom_xml_serialization_algorithm(ctx, namespace_prefix_map, child, namespace, prefix_index, indent, require_well_formed));
child = child->next;
}
@@ -1230,8 +1231,7 @@ static int dom_xml_serializing_a_document_node(
/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serialization-algorithm */
static int dom_xml_serialization_algorithm(
- xmlSaveCtxtPtr ctxt,
- xmlOutputBufferPtr out,
+ dom_xml_serialize_ctx *ctx,
dom_xml_ns_prefix_map *namespace_prefix_map,
xmlNodePtr node,
const xmlChar *namespace,
@@ -1243,36 +1243,36 @@ static int dom_xml_serialization_algorithm(
/* If node's interface is: */
switch (node->type) {
case XML_ELEMENT_NODE:
- return dom_xml_serialize_element_node(ctxt, out, namespace, namespace_prefix_map, node, prefix_index, indent, require_well_formed);
+ return dom_xml_serialize_element_node(ctx, namespace, namespace_prefix_map, node, prefix_index, indent, require_well_formed);
case XML_DOCUMENT_FRAG_NODE:
- return dom_xml_serializing_a_document_fragment_node(ctxt, out, namespace_prefix_map, node, namespace, prefix_index, indent, require_well_formed);
+ return dom_xml_serializing_a_document_fragment_node(ctx, namespace_prefix_map, node, namespace, prefix_index, indent, require_well_formed);
case XML_HTML_DOCUMENT_NODE:
case XML_DOCUMENT_NODE:
- return dom_xml_serializing_a_document_node(ctxt, out, namespace_prefix_map, node, namespace, prefix_index, indent, require_well_formed);
+ return dom_xml_serializing_a_document_node(ctx, namespace_prefix_map, node, namespace, prefix_index, indent, require_well_formed);
case XML_TEXT_NODE:
- return dom_xml_serialize_text_node(out, node, require_well_formed);
+ return dom_xml_serialize_text_node(ctx->out, node, require_well_formed);
case XML_COMMENT_NODE:
- return dom_xml_serialize_comment_node(out, node, require_well_formed);
+ return dom_xml_serialize_comment_node(ctx->out, node, require_well_formed);
case XML_PI_NODE:
- return dom_xml_serialize_processing_instruction(out, node, require_well_formed);
+ return dom_xml_serialize_processing_instruction(ctx->out, node, require_well_formed);
case XML_CDATA_SECTION_NODE:
- return dom_xml_serialize_cdata_section_node(out, node);
+ return dom_xml_serialize_cdata_section_node(ctx->out, node);
case XML_ATTRIBUTE_NODE:
- return dom_xml_serialize_attribute_node(out, node);
+ return dom_xml_serialize_attribute_node(ctx->out, node);
default:
- TRY(xmlOutputBufferFlush(out));
- TRY(xmlSaveTree(ctxt, node));
- TRY(xmlSaveFlush(ctxt));
+ TRY(xmlOutputBufferFlush(ctx->out));
+ TRY(xmlSaveTree(ctx->ctxt, node));
+ TRY(xmlSaveFlush(ctx->ctxt));
if (node->type == XML_DTD_NODE) {
- return xmlOutputBufferWriteLit(out, "\n");
+ return xmlOutputBufferWriteLit(ctx->out, "\n");
}
return 0;
}
@@ -1297,8 +1297,11 @@ int dom_xml_serialize(xmlSaveCtxtPtr ctxt, xmlOutputBufferPtr out, xmlNodePtr no
unsigned int prefix_index = 1;
/* 5. Return the result of running the XML serialization algorithm ... */
+ dom_xml_serialize_ctx ctx;
+ ctx.out = out;
+ ctx.ctxt = ctxt;
int indent = format ? 0 : -1;
- int result = dom_xml_serialization_algorithm(ctxt, out, &namespace_prefix_map, node, namespace, &prefix_index, indent, require_well_formed);
+ int result = dom_xml_serialization_algorithm(&ctx, &namespace_prefix_map, node, namespace, &prefix_index, indent, require_well_formed);
dom_xml_ns_prefix_map_dtor(&namespace_prefix_map);
From 0ca7791142a7c549d73375129dd0c0d3b1e6504e Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Wed, 10 Jul 2024 17:07:12 +0200
Subject: [PATCH 03/10] Support templated content
The template element in HTML 5 is special in the sense that it does not
add its contents into the DOM tree, but instead keeps them in a separate
shadow DOM document fragment. Interacting with the DOM tree cannot touch
the elements in the document fragment.
---
ext/dom/config.m4 | 2 +-
ext/dom/config.w32 | 2 +-
ext/dom/domimplementation.c | 1 +
ext/dom/element.c | 6 +
ext/dom/html5_parser.c | 38 ++++--
ext/dom/html5_parser.h | 4 +-
ext/dom/html5_serializer.c | 25 +++-
ext/dom/html5_serializer.h | 2 +
ext/dom/html_document.c | 14 +-
ext/dom/inner_html_mixin.c | 11 +-
ext/dom/internal_helpers.h | 8 ++
ext/dom/namespace_compat.c | 40 +-----
ext/dom/namespace_compat.h | 10 --
ext/dom/node.c | 1 +
ext/dom/php_dom.c | 17 ++-
ext/dom/private_data.c | 127 ++++++++++++++++++
ext/dom/private_data.h | 47 +++++++
.../modern/common/template_participation.phpt | 83 ++++++++++++
.../tests/modern/common/template_rename.phpt | 29 ++++
ext/dom/xml_document.c | 11 +-
ext/dom/xml_serializer.c | 21 ++-
ext/dom/xml_serializer.h | 5 +-
ext/libxml/libxml.c | 6 +-
23 files changed, 424 insertions(+), 86 deletions(-)
create mode 100644 ext/dom/private_data.c
create mode 100644 ext/dom/private_data.h
create mode 100644 ext/dom/tests/modern/common/template_participation.phpt
create mode 100644 ext/dom/tests/modern/common/template_rename.phpt
diff --git a/ext/dom/config.m4 b/ext/dom/config.m4
index f16804d468ba6..8bb41d9ee8118 100644
--- a/ext/dom/config.m4
+++ b/ext/dom/config.m4
@@ -27,7 +27,7 @@ if test "$PHP_DOM" != "no"; then
$LEXBOR_DIR/ns/ns.c \
$LEXBOR_DIR/tag/tag.c"
PHP_NEW_EXTENSION(dom, [php_dom.c attr.c document.c infra.c \
- xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c \
+ xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c private_data.c \
domexception.c \
parentnode/tree.c parentnode/css_selectors.c \
processinginstruction.c cdatasection.c \
diff --git a/ext/dom/config.w32 b/ext/dom/config.w32
index 081190a67f047..8035c5b84f0fc 100644
--- a/ext/dom/config.w32
+++ b/ext/dom/config.w32
@@ -8,7 +8,7 @@ if (PHP_DOM == "yes") {
CHECK_HEADER_ADD_INCLUDE("libxml/parser.h", "CFLAGS_DOM", PHP_PHP_BUILD + "\\include\\libxml2")
) {
EXTENSION("dom", "php_dom.c attr.c document.c infra.c \
- xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c \
+ xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c private_data.c \
domexception.c processinginstruction.c \
cdatasection.c documentfragment.c domimplementation.c element.c inner_html_mixin.c \
node.c characterdata.c documenttype.c \
diff --git a/ext/dom/domimplementation.c b/ext/dom/domimplementation.c
index 710232d78286e..0c2b2c61b308d 100644
--- a/ext/dom/domimplementation.c
+++ b/ext/dom/domimplementation.c
@@ -23,6 +23,7 @@
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "php_dom.h"
#include "namespace_compat.h"
+#include "private_data.h"
/*
* class DOMImplementation
diff --git a/ext/dom/element.c b/ext/dom/element.c
index 9795c0bd7aa8f..54d12252417e1 100644
--- a/ext/dom/element.c
+++ b/ext/dom/element.c
@@ -24,6 +24,7 @@
#include "zend_enum.h"
#include "php_dom.h"
#include "namespace_compat.h"
+#include "private_data.h"
#include "internal_helpers.h"
#include "dom_properties.h"
#include "token_list.h"
@@ -2030,6 +2031,11 @@ PHP_METHOD(Dom_Element, rename)
}
goto cleanup;
}
+
+ /* If we currently have a template but the new element type won't be a template, then throw away the templated content. */
+ if (is_currently_html_ns && xmlStrEqual(nodep->name, BAD_CAST "template") && !xmlStrEqual(localname, BAD_CAST "template")) {
+ php_dom_remove_templated_content(php_dom_get_private_data(intern), nodep);
+ }
}
php_libxml_invalidate_node_list_cache(intern->document);
diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c
index 66c490b1d169f..7632f2303c836 100644
--- a/ext/dom/html5_parser.c
+++ b/ext/dom/html5_parser.c
@@ -22,12 +22,13 @@
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "php_dom.h"
#include "html5_parser.h"
+#include "private_data.h"
#include hello
hello
" +NULL + +hello
+hello
From 4efa3cf77c965076bf139be10587846a30001a8c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 11 Jul 2024 21:31:46 +0200 Subject: [PATCH 07/10] Improve destruction code --- ext/dom/document.c | 1 + ext/dom/element.c | 1 + ext/dom/html5_parser.c | 21 ++++--- ext/dom/html_document.c | 15 +++++ ext/dom/namespace_compat.c | 54 ++++++++-------- ext/dom/php_dom.c | 9 --- ext/dom/php_dom.stub.php | 4 ++ ext/dom/php_dom_arginfo.h | 13 +++- ext/dom/private_data.c | 61 ++++++++++++++----- ext/dom/private_data.h | 19 +++++- .../tests/modern/common/template_cloning.phpt | 14 +++++ .../common/template_indirect_removal.phpt | 22 +++++++ .../modern/common/template_simplexml.phpt | 30 +++++++++ ext/dom/xpath.c | 1 + ext/libxml/libxml.c | 8 ++- ext/libxml/php_libxml.h | 3 + 16 files changed, 212 insertions(+), 64 deletions(-) create mode 100644 ext/dom/tests/modern/common/template_cloning.phpt create mode 100644 ext/dom/tests/modern/common/template_indirect_removal.phpt create mode 100644 ext/dom/tests/modern/common/template_simplexml.phpt diff --git a/ext/dom/document.c b/ext/dom/document.c index 7162550015d29..e315255b43812 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -23,6 +23,7 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" #include "namespace_compat.h" +#include "private_data.h" #include "xml_serializer.h" #include "internal_helpers.h" #include "dom_properties.h" diff --git a/ext/dom/element.c b/ext/dom/element.c index b714c3859c03b..0e1de94c46c69 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -24,6 +24,7 @@ #include "zend_enum.h" #include "php_dom.h" #include "namespace_compat.h" +#include "private_data.h" #include "internal_helpers.h" #include "dom_properties.h" #include "token_list.h" diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c index 7632f2303c836..efb62d1d8cb1f 100644 --- a/ext/dom/html5_parser.c +++ b/ext/dom/html5_parser.c @@ -64,14 +64,20 @@ static unsigned short sanitize_line_nr(size_t line) return (unsigned short) line; } -static const php_dom_ns_magic_token *get_libxml_namespace_href(uintptr_t lexbor_namespace) +struct lxml_ns { + const php_dom_ns_magic_token *token; + const char *href; + size_t href_len; +}; + +static struct lxml_ns get_libxml_namespace_href(uintptr_t lexbor_namespace) { if (lexbor_namespace == LXB_NS_SVG) { - return php_dom_ns_is_svg_magic_token; + return (struct lxml_ns) { php_dom_ns_is_svg_magic_token, ZEND_STRL(DOM_SVG_NS_URI) }; } else if (lexbor_namespace == LXB_NS_MATH) { - return php_dom_ns_is_mathml_magic_token; + return (struct lxml_ns) { php_dom_ns_is_mathml_magic_token, ZEND_STRL(DOM_MATHML_NS_URI) }; } else { - return php_dom_ns_is_html_magic_token; + return (struct lxml_ns) { php_dom_ns_is_html_magic_token, ZEND_STRL(DOM_XHTML_NS_URI) }; } } @@ -148,12 +154,12 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( if (entering_namespace == LXB_NS_HTML) { current_lxml_ns = html_ns; } else { - const php_dom_ns_magic_token *magic_token = get_libxml_namespace_href(entering_namespace); - zend_string *uri = zend_string_init((char *) magic_token, strlen((char *) magic_token), false); + struct lxml_ns ns = get_libxml_namespace_href(entering_namespace); + zend_string *uri = zend_string_init(ns.href, ns.href_len, false); current_lxml_ns = php_dom_libxml_ns_mapper_get_ns(ns_mapper, NULL, uri); zend_string_release_ex(uri, false); if (EXPECTED(current_lxml_ns != NULL)) { - current_lxml_ns->_private = (void *) magic_token; + current_lxml_ns->_private = (void *) ns.token; } } } @@ -172,6 +178,7 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( } lxml_child_parent->parent = lxml_element; + dom_add_element_ns_hook(private_data, lxml_element); php_dom_add_templated_content(private_data, lxml_element, lxml_child_parent); lxb_html_template_element_t *template = lxb_html_interface_template(&element->node); diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c index 79092c8015abd..7ce378aaa96c7 100644 --- a/ext/dom/html_document.c +++ b/ext/dom/html_document.c @@ -1644,4 +1644,19 @@ zend_result dom_html_document_title_write(dom_object *obj, zval *newval) return SUCCESS; } +#if ZEND_DEBUG +PHP_METHOD(Dom_HTMLDocument, debugGetTemplateCount) +{ + xmlDocPtr doc; + dom_object *intern; + + ZEND_PARSE_PARAMETERS_NONE(); + + DOM_GET_OBJ(doc, ZEND_THIS, xmlDocPtr, intern); + ZEND_IGNORE_VALUE(doc); + + RETURN_LONG(php_dom_get_template_count((const php_dom_private_data *) intern->document->private_data)); +} +#endif + #endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/ext/dom/namespace_compat.c b/ext/dom/namespace_compat.c index f9f8108ab0f51..7a3bd68b0111a 100644 --- a/ext/dom/namespace_compat.c +++ b/ext/dom/namespace_compat.c @@ -22,28 +22,25 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" #include "namespace_compat.h" +#include "private_data.h" #include "internal_helpers.h" -PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_html_magic_token = (const php_dom_ns_magic_token *) DOM_XHTML_NS_URI; -PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_mathml_magic_token = (const php_dom_ns_magic_token *) DOM_MATHML_NS_URI; -PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_svg_magic_token = (const php_dom_ns_magic_token *) DOM_SVG_NS_URI; -PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_xlink_magic_token = (const php_dom_ns_magic_token *) DOM_XLINK_NS_URI; -PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_xml_magic_token = (const php_dom_ns_magic_token *) DOM_XML_NS_URI; -PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_xmlns_magic_token = (const php_dom_ns_magic_token *) DOM_XMLNS_NS_URI; - -struct php_dom_libxml_ns_mapper { - /* This is used almost all the time for HTML documents, so it makes sense to cache this. */ - xmlNsPtr html_ns; - /* Used for every prefixless namespace declaration in XML, so also very common. */ - xmlNsPtr prefixless_xmlns_ns; - HashTable uri_to_prefix_map; -}; - -typedef struct php_dom_private_data { - php_libxml_private_data_header header; - struct php_dom_libxml_ns_mapper ns_mapper; - HashTable *template_fragments; -} php_dom_private_data; +/* The actual value of these doesn't matter as long as they serve as a unique ID. + * They need to be pointers because the `_private` field is a pointer, however we can choose the contents ourselves. + * We need keep these at least 4-byte aligned because the pointer may be tagged (although for now 2 byte alignment works too). + * We use a trick: we declare a struct with a double member to force the alignment. */ +#define DECLARE_NS_TOKEN(name, uri) \ + static const struct { \ + char val[sizeof(uri)]; \ + double align; \ + } decl_##name = { uri, 0.0 }; \ + PHP_DOM_EXPORT const php_dom_ns_magic_token *(name) = (const php_dom_ns_magic_token *) &decl_##name; +DECLARE_NS_TOKEN(php_dom_ns_is_html_magic_token, DOM_XHTML_NS_URI); +DECLARE_NS_TOKEN(php_dom_ns_is_mathml_magic_token, DOM_MATHML_NS_URI); +DECLARE_NS_TOKEN(php_dom_ns_is_svg_magic_token, DOM_SVG_NS_URI); +DECLARE_NS_TOKEN(php_dom_ns_is_xlink_magic_token, DOM_XLINK_NS_URI); +DECLARE_NS_TOKEN(php_dom_ns_is_xml_magic_token, DOM_XML_NS_URI); +DECLARE_NS_TOKEN(php_dom_ns_is_xmlns_magic_token, DOM_XMLNS_NS_URI); static void php_dom_libxml_ns_mapper_prefix_map_element_dtor(zval *zv) { @@ -213,11 +210,6 @@ static xmlNsPtr php_dom_libxml_ns_mapper_store_and_normalize_parsed_ns(php_dom_l return ns; } -PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_get_ns_mapper(dom_object *object) -{ - return &php_dom_get_private_data(object)->ns_mapper; -} - typedef struct { /* Fast lookup for created mappings. */ HashTable old_ns_to_new_ns_ptr; @@ -227,6 +219,11 @@ typedef struct { php_dom_libxml_ns_mapper *ns_mapper; } dom_libxml_reconcile_ctx; +PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_get_ns_mapper(dom_object *object) +{ + return &php_dom_get_private_data(object)->ns_mapper; +} + PHP_DOM_EXPORT xmlAttrPtr php_dom_ns_compat_mark_attribute(php_dom_libxml_ns_mapper *mapper, xmlNodePtr node, xmlNsPtr ns) { xmlNsPtr xmlns_ns; @@ -286,13 +283,16 @@ PHP_DOM_EXPORT bool php_dom_ns_is_fast_ex(xmlNsPtr ns, const php_dom_ns_magic_to /* cached for fast checking */ if (ns->_private == magic_token) { return true; - } else if (ns->_private != NULL) { + } else if (ns->_private != NULL && ((uintptr_t) ns->_private & 1) == 0) { /* Other token stored */ return false; } /* Slow path */ if (xmlStrEqual(ns->href, BAD_CAST magic_token)) { - ns->_private = (void *) magic_token; + if (ns->_private == NULL) { + /* Only overwrite the private data if there is no other token stored. */ + ns->_private = (void *) magic_token; + } return true; } return false; diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 6e4461732390d..ce7653198368d 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1395,15 +1395,6 @@ void dom_objects_free_storage(zend_object *object) xmlNodePtr node = ptr->node; if (node->type != XML_DOCUMENT_NODE && node->type != XML_HTML_DOCUMENT_NODE) { - /* Destroy associated template content. */ - php_dom_private_data *private_data; - if (node->type == XML_ELEMENT_NODE - && ptr->refcount == 1 - && intern->document != NULL - && (private_data = php_dom_get_private_data(intern))) { - php_dom_remove_templated_content(private_data, node); - } - php_libxml_node_decrement_resource((php_libxml_node_object *) intern); } else { php_libxml_decrement_node_ptr((php_libxml_node_object *) intern); diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php index 15d639fb27e94..f678c7376d13e 100644 --- a/ext/dom/php_dom.stub.php +++ b/ext/dom/php_dom.stub.php @@ -1643,6 +1643,10 @@ public function saveXmlFile(string $filename, int $options = 0): int|false {} public function saveHtml(?Node $node = null): string {} public function saveHtmlFile(string $filename): int|false {} + +#if ZEND_DEBUG + public function debugGetTemplateCount(): int {} +#endif } final class XMLDocument extends Document diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index fe42290b5f01a..90db1e81af061 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1af73c3b63ebeb5e59948990892dcf6b627a1671 */ + * Stub hash: 9a1e6842b2c5b891e11087d40aa8c9f56a2269a3 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_dom_import_simplexml, 0, 1, DOMElement, 0) ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0) @@ -1059,6 +1059,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Dom_HTMLDocument_saveHtmlF ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) ZEND_END_ARG_INFO() +#if ZEND_DEBUG +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Dom_HTMLDocument_debugGetTemplateCount, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Dom_XMLDocument_createEmpty, 0, 0, Dom\\XMLDocument, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, version, IS_STRING, 0, "\"1.0\"") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 0, "\"UTF-8\"") @@ -1367,6 +1372,9 @@ ZEND_METHOD(Dom_HTMLDocument, createFromString); ZEND_METHOD(Dom_XMLDocument, saveXml); ZEND_METHOD(Dom_HTMLDocument, saveHtml); ZEND_METHOD(Dom_HTMLDocument, saveHtmlFile); +#if ZEND_DEBUG +ZEND_METHOD(Dom_HTMLDocument, debugGetTemplateCount); +#endif ZEND_METHOD(Dom_XMLDocument, createEmpty); ZEND_METHOD(Dom_XMLDocument, createFromFile); ZEND_METHOD(Dom_XMLDocument, createFromString); @@ -1885,6 +1893,9 @@ static const zend_function_entry class_Dom_HTMLDocument_methods[] = { ZEND_RAW_FENTRY("saveXmlFile", zim_DOMDocument_save, arginfo_class_Dom_HTMLDocument_saveXmlFile, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Dom_HTMLDocument, saveHtml, arginfo_class_Dom_HTMLDocument_saveHtml, ZEND_ACC_PUBLIC) ZEND_ME(Dom_HTMLDocument, saveHtmlFile, arginfo_class_Dom_HTMLDocument_saveHtmlFile, ZEND_ACC_PUBLIC) +#if ZEND_DEBUG + ZEND_ME(Dom_HTMLDocument, debugGetTemplateCount, arginfo_class_Dom_HTMLDocument_debugGetTemplateCount, ZEND_ACC_PUBLIC) +#endif ZEND_FE_END }; diff --git a/ext/dom/private_data.c b/ext/dom/private_data.c index 1093fcc66bd5b..35ee82940dc43 100644 --- a/ext/dom/private_data.c +++ b/ext/dom/private_data.c @@ -24,42 +24,46 @@ #include "private_data.h" #include "internal_helpers.h" -typedef struct php_dom_private_data { - php_libxml_private_data_header header; - struct php_dom_libxml_ns_mapper ns_mapper; - HashTable *template_fragments; -} php_dom_private_data; - static void php_dom_libxml_private_data_destroy(php_libxml_private_data_header *header) { php_dom_private_data_destroy((php_dom_private_data *) header); } -PHP_DOM_EXPORT php_libxml_private_data_header *php_dom_libxml_private_data_header(php_dom_private_data *private_data) +static void php_dom_libxml_private_data_ns_hook(php_libxml_private_data_header *header, xmlNodePtr node) +{ + php_dom_remove_templated_content((php_dom_private_data *) header, node); +} + +php_libxml_private_data_header *php_dom_libxml_private_data_header(php_dom_private_data *private_data) { return private_data == NULL ? NULL : &private_data->header; } -PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_ns_mapper_from_private(php_dom_private_data *private_data) +php_dom_libxml_ns_mapper *php_dom_ns_mapper_from_private(php_dom_private_data *private_data) { return private_data == NULL ? NULL : &private_data->ns_mapper; } -PHP_DOM_EXPORT php_dom_private_data *php_dom_private_data_create(void) +php_dom_private_data *php_dom_private_data_create(void) { - php_dom_private_data *mapper = emalloc(sizeof(*mapper)); - mapper->header.dtor = php_dom_libxml_private_data_destroy; - mapper->ns_mapper.html_ns = NULL; - mapper->ns_mapper.prefixless_xmlns_ns = NULL; - zend_hash_init(&mapper->ns_mapper.uri_to_prefix_map, 0, NULL, ZVAL_PTR_DTOR, false); - mapper->template_fragments = NULL; - return mapper; + php_dom_private_data *private_data = emalloc(sizeof(*private_data)); + private_data->header.dtor = php_dom_libxml_private_data_destroy; + private_data->header.ns_hook = php_dom_libxml_private_data_ns_hook; + private_data->ns_mapper.html_ns = NULL; + private_data->ns_mapper.prefixless_xmlns_ns = NULL; + zend_hash_init(&private_data->ns_mapper.uri_to_prefix_map, 0, NULL, ZVAL_PTR_DTOR, false); + private_data->template_fragments = NULL; + return private_data; } void php_dom_private_data_destroy(php_dom_private_data *data) { zend_hash_destroy(&data->ns_mapper.uri_to_prefix_map); if (data->template_fragments != NULL) { + xmlNodePtr node; + ZEND_HASH_MAP_FOREACH_PTR(data->template_fragments, node) { + xmlFreeNode(node); + } ZEND_HASH_FOREACH_END(); zend_hash_destroy(data->template_fragments); FREE_HASHTABLE(data->template_fragments); } @@ -114,6 +118,7 @@ xmlNodePtr php_dom_ensure_templated_content(php_dom_private_data *private_data, result = xmlNewDocFragment(template_node->doc); if (EXPECTED(result != NULL)) { result->parent = template_node; + dom_add_element_ns_hook(private_data, template_node); php_dom_add_templated_content(private_data, template_node, result); } } @@ -137,4 +142,28 @@ void php_dom_remove_templated_content(php_dom_private_data *private_data, const } } +zend_long php_dom_get_template_count(const php_dom_private_data *private_data) +{ + if (private_data->template_fragments != NULL) { + return zend_hash_num_elements(private_data->template_fragments); + } else { + return 0; + } +} + +void dom_add_element_ns_hook(php_dom_private_data *private_data, xmlNodePtr element) +{ + xmlNsPtr ns = pemalloc(sizeof(*ns), true); + + /* The private data is a tagged data structure where only tag 1 is defined by ext/libxml to register a hook. */ + memset(ns, 0, sizeof(*ns)); + ns->prefix = xmlStrdup(element->ns->prefix); + ns->href = xmlStrdup(element->ns->href); + ns->type = XML_LOCAL_NAMESPACE; + ns->_private = (void *) ((uintptr_t) private_data | LIBXML_NS_TAG_HOOK); + element->ns = ns; + + php_libxml_set_old_ns(element->doc, ns); +} + #endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/ext/dom/private_data.h b/ext/dom/private_data.h index b0b71481656ec..a84e3b12ca6c0 100644 --- a/ext/dom/private_data.h +++ b/ext/dom/private_data.h @@ -27,6 +27,17 @@ struct php_dom_libxml_ns_mapper { HashTable uri_to_prefix_map; }; +struct dom_ns_hook_token { + int tag; + struct php_dom_private_data *private_data; +}; + +typedef struct php_dom_private_data { + php_libxml_private_data_header header; + struct php_dom_libxml_ns_mapper ns_mapper; + HashTable *template_fragments; +} php_dom_private_data; + typedef struct php_libxml_private_data_header php_libxml_private_data_header; struct php_libxml_private_data_header; @@ -36,13 +47,15 @@ typedef struct php_dom_private_data php_dom_private_data; struct php_dom_libxml_ns_mapper; typedef struct php_dom_libxml_ns_mapper php_dom_libxml_ns_mapper; -PHP_DOM_EXPORT php_libxml_private_data_header *php_dom_libxml_private_data_header(php_dom_private_data *private_data); -PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_ns_mapper_from_private(php_dom_private_data *private_data); -PHP_DOM_EXPORT php_dom_private_data *php_dom_private_data_create(void); +php_libxml_private_data_header *php_dom_libxml_private_data_header(php_dom_private_data *private_data); +php_dom_libxml_ns_mapper *php_dom_ns_mapper_from_private(php_dom_private_data *private_data); +php_dom_private_data *php_dom_private_data_create(void); void php_dom_private_data_destroy(php_dom_private_data *data); void php_dom_add_templated_content(php_dom_private_data *private_data, const xmlNode *template_node, xmlNodePtr fragment); xmlNodePtr php_dom_retrieve_templated_content(php_dom_private_data *private_data, const xmlNode *template_node); xmlNodePtr php_dom_ensure_templated_content(php_dom_private_data *private_data, xmlNodePtr template_node); void php_dom_remove_templated_content(php_dom_private_data *private_data, const xmlNode *template_node); +zend_long php_dom_get_template_count(const php_dom_private_data *private_data); +void dom_add_element_ns_hook(php_dom_private_data *private_data, xmlNodePtr element); #endif diff --git a/ext/dom/tests/modern/common/template_cloning.phpt b/ext/dom/tests/modern/common/template_cloning.phpt new file mode 100644 index 0000000000000..3ba9c8276495a --- /dev/null +++ b/ext/dom/tests/modern/common/template_cloning.phpt @@ -0,0 +1,14 @@ +--TEST-- +Template cloning +--EXTENSIONS-- +dom +--FILE-- +x', LIBXML_NOERROR); +$a = $dom->head->firstChild->cloneNode(false); +echo $dom->saveXML($a), "\n"; +echo $dom->saveHTML($a), "\n"; +?> +--EXPECT-- + + diff --git a/ext/dom/tests/modern/common/template_indirect_removal.phpt b/ext/dom/tests/modern/common/template_indirect_removal.phpt new file mode 100644 index 0000000000000..b257464e0f2f3 --- /dev/null +++ b/ext/dom/tests/modern/common/template_indirect_removal.phpt @@ -0,0 +1,22 @@ +--TEST-- +template content indirect removal +--EXTENSIONS-- +dom +--SKIPIF-- + +--FILE-- +foonested', LIBXML_NOERROR); +$head = $dom->head; +var_dump($dom->debugGetTemplateCount()); +$head->remove(); +var_dump($dom->debugGetTemplateCount()); +unset($head); +var_dump($dom->debugGetTemplateCount()); +?> +--EXPECT-- +int(2) +int(2) +int(0) diff --git a/ext/dom/tests/modern/common/template_simplexml.phpt b/ext/dom/tests/modern/common/template_simplexml.phpt new file mode 100644 index 0000000000000..52e6aee7d0f10 --- /dev/null +++ b/ext/dom/tests/modern/common/template_simplexml.phpt @@ -0,0 +1,30 @@ +--TEST-- +SimpleXML and template content +--EXTENSIONS-- +dom +simplexml +--SKIPIF-- + +--FILE-- +foonested', LIBXML_NOERROR); +$head = $dom->head; +$head_sxe = simplexml_import_dom($head); +var_dump($head_sxe); +var_dump($dom->debugGetTemplateCount()); +unset($head_sxe->template); +var_dump($head_sxe); +var_dump($dom->debugGetTemplateCount()); +?> +--EXPECTF-- +object(SimpleXMLElement)#%d (1) { + ["template"]=> + object(SimpleXMLElement)#%d (0) { + } +} +int(2) +object(SimpleXMLElement)#%d (0) { +} +int(0) diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index 5709d4f10f3fb..831a11be62ea0 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -23,6 +23,7 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" #include "namespace_compat.h" +#include "private_data.h" #define PHP_DOM_XPATH_QUERY 0 #define PHP_DOM_XPATH_EVALUATE 1 diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 104efc8a1c5f0..e0b3b4eed430d 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -275,7 +275,12 @@ static void php_libxml_node_free(xmlNodePtr node) xmlFreeDtd(dtd); break; } - case XML_ELEMENT_NODE: + case XML_ELEMENT_NODE: { + if (node->ns && (((uintptr_t) node->ns->_private) & 1) == LIBXML_NS_TAG_HOOK) { + /* Special destruction routine hook should be called because it belongs to a "special" namespace. */ + php_libxml_private_data_header *header = (php_libxml_private_data_header *) (((uintptr_t) node->ns->_private) & ~1); + header->ns_hook(header, node); + } if (node->nsDef && node->doc) { /* Make the namespace declaration survive the destruction of the holding element. * This prevents a use-after-free on the namespace declaration. @@ -307,6 +312,7 @@ static void php_libxml_node_free(xmlNodePtr node) } xmlFreeNode(node); break; + } default: xmlFreeNode(node); break; diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index 95f2cd42d8225..9c2682504e18f 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -39,6 +39,8 @@ extern zend_module_entry libxml_module_entry; #define LIBXML_SAVE_NOEMPTYTAG 1<<2 +#define LIBXML_NS_TAG_HOOK 1 + ZEND_BEGIN_MODULE_GLOBALS(libxml) zval stream_context; smart_str error_buffer; @@ -67,6 +69,7 @@ typedef struct { typedef struct php_libxml_private_data_header { void (*dtor)(struct php_libxml_private_data_header *); + void (*ns_hook)(struct php_libxml_private_data_header *, xmlNodePtr); /* extra fields */ } php_libxml_private_data_header; From f98edfa8624111aaef6e96f0401fdca130e7a082 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:34:03 +0200 Subject: [PATCH 08/10] Respect no default ns flag --- ext/dom/html5_parser.c | 18 +++++++------- .../modern/common/template_no_default_ns.phpt | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 ext/dom/tests/modern/common/template_no_default_ns.phpt diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c index efb62d1d8cb1f..0d7d2b9e7249d 100644 --- a/ext/dom/html5_parser.c +++ b/ext/dom/html5_parser.c @@ -171,16 +171,18 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( xmlNodePtr lxml_child_parent = lxml_element; lxb_dom_node_t *child_node = element->node.last_child; if (lxb_html_tree_node_is(&element->node, LXB_TAG_TEMPLATE)) { - lxml_child_parent = xmlNewDocFragment(lxml_doc); - if (UNEXPECTED(lxml_child_parent == NULL)) { - retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; - break; + if (create_default_ns) { + lxml_child_parent = xmlNewDocFragment(lxml_doc); + if (UNEXPECTED(lxml_child_parent == NULL)) { + retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; + break; + } + + lxml_child_parent->parent = lxml_element; + dom_add_element_ns_hook(private_data, lxml_element); + php_dom_add_templated_content(private_data, lxml_element, lxml_child_parent); } - lxml_child_parent->parent = lxml_element; - dom_add_element_ns_hook(private_data, lxml_element); - php_dom_add_templated_content(private_data, lxml_element, lxml_child_parent); - lxb_html_template_element_t *template = lxb_html_interface_template(&element->node); if (template->content != NULL) { child_node = template->content->node.last_child; diff --git a/ext/dom/tests/modern/common/template_no_default_ns.phpt b/ext/dom/tests/modern/common/template_no_default_ns.phpt new file mode 100644 index 0000000000000..fc3cd163044f8 --- /dev/null +++ b/ext/dom/tests/modern/common/template_no_default_ns.phpt @@ -0,0 +1,24 @@ +--TEST-- + element no default namespace +--EXTENSIONS-- +dom +--FILE-- + + + + a