Skip to content

Commit 4448589

Browse files
committed
Factor out all common code for XML serialization and merge common paths
1 parent de4c9c3 commit 4448589

File tree

9 files changed

+164
-100
lines changed

9 files changed

+164
-100
lines changed

ext/dom/document.c

Lines changed: 14 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,7 @@ PHP_METHOD(DOMDocument, save)
15651565
saveempty = xmlSaveNoEmptyTags;
15661566
xmlSaveNoEmptyTags = 1;
15671567
}
1568+
// TODO: use modern API when necessary
15681569
bytes = xmlSaveFormatFileEnc(file, docp, NULL, format);
15691570
if (options & LIBXML_SAVE_NOEMPTYTAG) {
15701571
xmlSaveNoEmptyTags = saveempty;
@@ -1584,10 +1585,8 @@ static void dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry
15841585
zval *nodep = NULL;
15851586
xmlDoc *docp;
15861587
xmlNode *node;
1587-
xmlBufferPtr buf;
1588-
const xmlChar *mem;
15891588
dom_object *intern, *nodeobj;
1590-
int size, format, old_xml_save_no_empty_tags;
1589+
int old_xml_save_no_empty_tags;
15911590
zend_long options = 0;
15921591

15931592
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!l", &nodep, node_ce, &options) != SUCCESS) {
@@ -1597,9 +1596,9 @@ static void dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry
15971596
DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
15981597

15991598
libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
1600-
format = doc_props->formatoutput;
1599+
bool format = doc_props->formatoutput;
16011600

1602-
int status = -1;
1601+
zend_string *res;
16031602
if (nodep != NULL) {
16041603
/* Dump contents of Node */
16051604
DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
@@ -1608,84 +1607,34 @@ static void dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry
16081607
RETURN_FALSE;
16091608
}
16101609

1611-
buf = xmlBufferCreate();
1612-
if (!buf) {
1613-
php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
1614-
RETURN_FALSE;
1615-
}
1616-
/* Save libxml2 global, override its vaule, and restore after saving. */
1610+
/* Save libxml2 global, override its value, and restore after saving (don't move me or risk breaking the state
1611+
* w.r.t. the implicit return in DOM_GET_OBJ). */
16171612
old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
16181613
xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
1619-
if (php_dom_follow_spec_intern(intern)) {
1620-
xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(buf, (const char *) docp->encoding, XML_SAVE_AS_XML);
1621-
if (EXPECTED(ctxt != NULL)) {
1622-
xmlCharEncodingHandlerPtr handler = xmlFindCharEncodingHandler((const char *) docp->encoding);
1623-
xmlOutputBufferPtr out = xmlOutputBufferCreateBuffer(buf, handler);
1624-
if (EXPECTED(out != NULL)) {
1625-
status = dom_xml_serialize(ctxt, out, node, format);
1626-
status |= xmlOutputBufferFlush(out);
1627-
status |= xmlOutputBufferClose(out);
1628-
}
1629-
(void) xmlSaveClose(ctxt);
1630-
xmlCharEncCloseFunc(handler);
1631-
}
1632-
} else {
1633-
status = xmlNodeDump(buf, docp, node, 0, format);
1634-
}
1614+
res = intern->document->handlers->dump_node_to_str(docp, node, format, (const char *) docp->encoding);
16351615
xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
16361616
} else {
1637-
buf = xmlBufferCreate();
1638-
if (!buf) {
1639-
php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
1640-
RETURN_FALSE;
1641-
}
1642-
16431617
int converted_options = XML_SAVE_AS_XML;
16441618
if (options & XML_SAVE_NO_DECL) {
16451619
converted_options |= XML_SAVE_NO_DECL;
16461620
}
16471621
if (format) {
16481622
converted_options |= XML_SAVE_FORMAT;
16491623
}
1650-
/* Save libxml2 global, override its vaule, and restore after saving. */
1624+
1625+
/* Save libxml2 global, override its value, and restore after saving. */
16511626
old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
16521627
xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
1653-
/* Encoding is handled from the encoding property set on the document */
1654-
xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(buf, (const char *) docp->encoding, converted_options);
1628+
res = intern->document->handlers->dump_doc_to_str(docp, converted_options, (const char *) docp->encoding);
16551629
xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
1656-
if (UNEXPECTED(!ctxt)) {
1657-
xmlBufferFree(buf);
1658-
php_error_docref(NULL, E_WARNING, "Could not create save context");
1659-
RETURN_FALSE;
1660-
}
1661-
if (php_dom_follow_spec_intern(intern)) {
1662-
xmlCharEncodingHandlerPtr handler = xmlFindCharEncodingHandler((const char *) docp->encoding);
1663-
xmlOutputBufferPtr out = xmlOutputBufferCreateBuffer(buf, handler);
1664-
if (EXPECTED(out != NULL)) {
1665-
status = dom_xml_serialize(ctxt, out, (xmlNodePtr) docp, format);
1666-
status |= xmlOutputBufferFlush(out);
1667-
status |= xmlOutputBufferClose(out);
1668-
} else {
1669-
xmlCharEncCloseFunc(handler);
1670-
}
1671-
} else {
1672-
status = xmlSaveDoc(ctxt, docp);
1673-
}
1674-
(void) xmlSaveClose(ctxt);
16751630
}
1676-
if (UNEXPECTED(status < 0)) {
1677-
xmlBufferFree(buf);
1631+
1632+
if (!res) {
16781633
php_error_docref(NULL, E_WARNING, "Could not save document");
16791634
RETURN_FALSE;
1635+
} else {
1636+
RETURN_NEW_STR(res);
16801637
}
1681-
mem = xmlBufferContent(buf);
1682-
if (!mem) {
1683-
xmlBufferFree(buf);
1684-
RETURN_FALSE;
1685-
}
1686-
size = xmlBufferLength(buf);
1687-
RETVAL_STRINGL((const char *) mem, size);
1688-
xmlBufferFree(buf);
16891638
}
16901639

16911640
PHP_METHOD(DOMDocument, saveXML)

ext/dom/domimplementation.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ PHP_METHOD(Dom_Implementation, createDocument)
306306
(xmlNodePtr) document,
307307
NULL
308308
);
309-
intern->document->class_type = PHP_LIBXML_CLASS_MODERN;
309+
dom_set_xml_class(intern->document);
310310
intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
311311

312312
/* 4. If doctype is non-null, append doctype to document. */
@@ -407,7 +407,7 @@ PHP_METHOD(Dom_Implementation, createHTMLDocument)
407407
(xmlNodePtr) doc,
408408
NULL
409409
);
410-
intern->document->class_type = PHP_LIBXML_CLASS_MODERN;
410+
dom_set_xml_class(intern->document);
411411
intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
412412
}
413413
/* }}} */

ext/dom/html_document.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ PHP_METHOD(Dom_HTMLDocument, createEmpty)
756756
(xmlNodePtr) lxml_doc,
757757
NULL
758758
);
759-
intern->document->class_type = PHP_LIBXML_CLASS_MODERN;
759+
dom_set_xml_class(intern->document);
760760
intern->document->private_data = php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper_create());
761761
return;
762762

@@ -916,7 +916,7 @@ PHP_METHOD(Dom_HTMLDocument, createFromString)
916916
(xmlNodePtr) lxml_doc,
917917
NULL
918918
);
919-
intern->document->class_type = PHP_LIBXML_CLASS_MODERN;
919+
dom_set_xml_class(intern->document);
920920
intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
921921
return;
922922

@@ -1136,7 +1136,7 @@ PHP_METHOD(Dom_HTMLDocument, createFromFile)
11361136
(xmlNodePtr) lxml_doc,
11371137
NULL
11381138
);
1139-
intern->document->class_type = PHP_LIBXML_CLASS_MODERN;
1139+
dom_set_xml_class(intern->document);
11401140
intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
11411141
return;
11421142

ext/dom/php_dom.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ static void dom_copy_document_ref(php_libxml_ref_obj *source_doc, php_libxml_ref
246246
}
247247

248248
dest_doc->class_type = source_doc->class_type;
249+
dest_doc->handlers = source_doc->handlers;
249250
}
250251
}
251252

ext/dom/php_dom.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ void dom_document_convert_to_modern(php_libxml_ref_obj *document, xmlDocPtr lxml
173173
dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_entry *ce, xmlNodePtr obj, dom_object *parent);
174174
xmlDocPtr php_dom_create_html_doc(void);
175175
xmlEntityPtr dom_entity_reference_fetch_and_sync_declaration(xmlNodePtr reference);
176-
176+
void dom_set_xml_class(php_libxml_ref_obj *document);
177177
bool dom_compare_value(const xmlAttr *attr, const xmlChar *value);
178178

179179
typedef enum {

ext/dom/xml_document.c

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
2323
#include "php_dom.h"
2424
#include "namespace_compat.h"
25+
#include "xml_serializer.h"
26+
#include <libxml/xmlsave.h>
2527

2628
static bool check_options_validity(uint32_t arg_num, zend_long options)
2729
{
@@ -118,7 +120,7 @@ PHP_METHOD(Dom_XMLDocument, createEmpty)
118120
(xmlNodePtr) lxml_doc,
119121
NULL
120122
);
121-
intern->document->class_type = PHP_LIBXML_CLASS_MODERN;
123+
dom_set_xml_class(intern->document);
122124
intern->document->private_data = php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper_create());
123125
return;
124126

@@ -227,7 +229,7 @@ static void load_from_helper(INTERNAL_FUNCTION_PARAMETERS, int mode)
227229
(xmlNodePtr) lxml_doc,
228230
NULL
229231
);
230-
intern->document->class_type = PHP_LIBXML_CLASS_MODERN;
232+
dom_set_xml_class(intern->document);
231233
dom_document_convert_to_modern(intern->document, lxml_doc);
232234
}
233235

@@ -248,4 +250,58 @@ PHP_METHOD(Dom_XMLDocument, createFromFile)
248250
load_from_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
249251
}
250252

253+
static zend_string *php_new_dom_dump_node_to_str(xmlDocPtr doc, xmlNodePtr node, bool format, const char *encoding)
254+
{
255+
xmlBufferPtr buf = xmlBufferCreate();
256+
if (!buf) {
257+
return NULL;
258+
}
259+
260+
int status = -1;
261+
xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(buf, encoding, XML_SAVE_AS_XML);
262+
if (EXPECTED(ctxt != NULL)) {
263+
xmlCharEncodingHandlerPtr handler = xmlFindCharEncodingHandler(encoding);
264+
xmlOutputBufferPtr out = xmlOutputBufferCreateBuffer(buf, handler);
265+
if (EXPECTED(out != NULL)) {
266+
status = dom_xml_serialize(ctxt, out, node, format);
267+
status |= xmlOutputBufferFlush(out);
268+
status |= xmlOutputBufferClose(out);
269+
}
270+
(void) xmlSaveClose(ctxt);
271+
xmlCharEncCloseFunc(handler);
272+
}
273+
274+
if (UNEXPECTED(status < 0)) {
275+
xmlBufferFree(buf);
276+
return NULL;
277+
}
278+
279+
const xmlChar *content = xmlBufferContent(buf);
280+
if (!content) {
281+
xmlBufferFree(buf);
282+
return NULL;
283+
}
284+
285+
int size = xmlBufferLength(buf);
286+
zend_string *res = zend_string_init((const char *) content, size, false);
287+
xmlBufferFree(buf);
288+
return res;
289+
}
290+
291+
static zend_string *php_new_dom_dump_doc_to_str(xmlDocPtr doc, int options, const char *encoding)
292+
{
293+
return php_new_dom_dump_node_to_str(doc, (xmlNodePtr) doc, options & XML_SAVE_FORMAT, encoding);
294+
}
295+
296+
static const php_libxml_document_handlers php_new_dom_default_document_handlers = {
297+
.dump_node_to_str = php_new_dom_dump_node_to_str,
298+
.dump_doc_to_str = php_new_dom_dump_doc_to_str,
299+
};
300+
301+
void dom_set_xml_class(php_libxml_ref_obj *document)
302+
{
303+
document->class_type = PHP_LIBXML_CLASS_MODERN;
304+
document->handlers = &php_new_dom_default_document_handlers;
305+
}
306+
251307
#endif /* HAVE_LIBXML && HAVE_DOM */

ext/libxml/libxml.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ static PHP_MSHUTDOWN_FUNCTION(libxml);
8181
static PHP_MINFO_FUNCTION(libxml);
8282
static zend_result php_libxml_post_deactivate(void);
8383

84+
static zend_string *php_libxml_default_dump_node_to_str(xmlDocPtr doc, xmlNodePtr node, bool format, const char *encoding);
85+
static zend_string *php_libxml_default_dump_doc_to_str(xmlDocPtr doc, int options, const char *encoding);
86+
8487
/* }}} */
8588

8689
zend_module_entry libxml_module_entry = {
@@ -102,6 +105,11 @@ zend_module_entry libxml_module_entry = {
102105

103106
/* }}} */
104107

108+
static const php_libxml_document_handlers php_libxml_default_document_handlers = {
109+
.dump_node_to_str = php_libxml_default_dump_node_to_str,
110+
.dump_doc_to_str = php_libxml_default_dump_doc_to_str,
111+
};
112+
105113
static void php_libxml_set_old_ns_list(xmlDocPtr doc, xmlNsPtr first, xmlNsPtr last)
106114
{
107115
if (UNEXPECTED(doc == NULL)) {
@@ -1344,6 +1352,7 @@ PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object,
13441352
object->document->cache_tag.modification_nr = 1; /* iterators start at 0, such that they will start in an uninitialised state */
13451353
object->document->private_data = NULL;
13461354
object->document->class_type = PHP_LIBXML_CLASS_UNSET;
1355+
object->document->handlers = &php_libxml_default_document_handlers;
13471356
}
13481357

13491358
return ret_refcount;
@@ -1468,6 +1477,62 @@ PHP_LIBXML_API xmlChar *php_libxml_attr_value(const xmlAttr *attr, bool *free)
14681477
return value;
14691478
}
14701479

1480+
static zend_string *php_libxml_default_dump_doc_to_str(xmlDocPtr doc, int options, const char *encoding)
1481+
{
1482+
xmlBufferPtr buf = xmlBufferCreate();
1483+
if (!buf) {
1484+
return NULL;
1485+
}
1486+
1487+
/* Encoding is handled from the encoding property set on the document */
1488+
xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(buf, encoding, options);
1489+
if (!ctxt) {
1490+
xmlBufferFree(buf);
1491+
return NULL;
1492+
}
1493+
1494+
long status = xmlSaveDoc(ctxt, doc);
1495+
(void) xmlSaveClose(ctxt);
1496+
if (status < 0) {
1497+
xmlBufferFree(buf);
1498+
return NULL;
1499+
}
1500+
1501+
const xmlChar *content = xmlBufferContent(buf);
1502+
if (!content) {
1503+
xmlBufferFree(buf);
1504+
return NULL;
1505+
}
1506+
1507+
int size = xmlBufferLength(buf);
1508+
zend_string *str = zend_string_init((const char *) content, size, false);
1509+
xmlBufferFree(buf);
1510+
return str;
1511+
}
1512+
1513+
static zend_string *php_libxml_default_dump_node_to_str(xmlDocPtr doc, xmlNodePtr node, bool format, const char *encoding)
1514+
{
1515+
// TODO: should this alloc take an encoding? For now keep it NULL for BC.
1516+
xmlOutputBufferPtr buf = xmlAllocOutputBuffer(NULL);
1517+
if (!buf) {
1518+
return NULL;
1519+
}
1520+
1521+
xmlNodeDumpOutput(buf, doc, node, 0, format, encoding);
1522+
1523+
if (xmlOutputBufferFlush(buf) < 0) {
1524+
xmlOutputBufferClose(buf);
1525+
return NULL;
1526+
}
1527+
1528+
const xmlChar *content = xmlOutputBufferGetContent(buf);
1529+
size_t size = xmlOutputBufferGetSize(buf);
1530+
1531+
zend_string *str = zend_string_init((const char *) content, size, false);
1532+
xmlOutputBufferClose(buf);
1533+
return str;
1534+
}
1535+
14711536
#if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML)
14721537
PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
14731538
{

ext/libxml/php_libxml.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ typedef struct _php_libxml_private_data_header {
6767
/* extra fields */
6868
} php_libxml_private_data_header;
6969

70+
/**
71+
* It's possible to set custom handlers for certain actions depending on the type of document.
72+
* For example, there exist multiple ways to serialize an XML document,
73+
* therefore this structure allows setting up a custom handler.
74+
*/
75+
typedef struct php_libxml_document_handlers {
76+
zend_string *(*dump_node_to_str)(xmlDocPtr doc, xmlNodePtr node, bool format, const char *encoding);
77+
zend_string *(*dump_doc_to_str)(xmlDocPtr doc, int options, const char *encoding);
78+
} php_libxml_document_handlers;
79+
7080
/**
7181
* Multiple representations are possible of the same underlying node data.
7282
* This is the case for example when a SimpleXML node is imported into DOM.
@@ -88,6 +98,7 @@ typedef struct _php_libxml_ref_obj {
8898
libxml_doc_props *doc_props;
8999
php_libxml_cache_tag cache_tag;
90100
php_libxml_private_data_header *private_data;
101+
const php_libxml_document_handlers *handlers;
91102
int refcount;
92103
php_libxml_class_type class_type;
93104
} php_libxml_ref_obj;

0 commit comments

Comments
 (0)