From 4bee5743e965b8c1aaa58d1a428eccdddb776304 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 25 Jul 2023 22:22:01 +0200 Subject: [PATCH] Fix GH-11792: LIBXML_NOXMLDECL is not implemented or broken Fixes GH-11792. Closes GH-11794. --- NEWS | 4 + ext/dom/document.c | 74 ++++++++++++------- .../DOMDocument_saveXML_XML_SAVE_NO_DECL.phpt | 24 ++++++ 3 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 ext/dom/tests/DOMDocument_saveXML_XML_SAVE_NO_DECL.phpt diff --git a/NEWS b/NEWS index 90283ae1539..982898615a3 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ PHP NEWS - Core: . Fixed oss-fuzz #60741 (Leak in open_basedir). (ilutov) +- DOM: + . Fixed bug GH-11792 (LIBXML_NOXMLDECL is not implemented or broken). + (nielsdos) + - FFI: . Fix leaking definitions when using FFI::cdef()->new(...). (ilutov) diff --git a/ext/dom/document.c b/ext/dom/document.c index ca136630ed3..2ee5435d8d2 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 +#include #ifdef LIBXML_SCHEMAS_ENABLED #include #include @@ -1462,9 +1463,9 @@ PHP_METHOD(DOMDocument, saveXML) xmlDoc *docp; xmlNode *node; xmlBufferPtr buf; - xmlChar *mem; + const xmlChar *mem; dom_object *intern, *nodeobj; - int size, format, saveempty = 0; + int size, format, old_xml_save_no_empty_tags; zend_long options = 0; id = ZEND_THIS; @@ -1484,42 +1485,59 @@ PHP_METHOD(DOMDocument, saveXML) php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document)); RETURN_FALSE; } + buf = xmlBufferCreate(); if (!buf) { php_error_docref(NULL, E_WARNING, "Could not fetch buffer"); RETURN_FALSE; } - if (options & LIBXML_SAVE_NOEMPTYTAG) { - saveempty = xmlSaveNoEmptyTags; - xmlSaveNoEmptyTags = 1; - } + /* Save libxml2 global, override its vaule, and restore after saving. */ + old_xml_save_no_empty_tags = xmlSaveNoEmptyTags; + xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0; xmlNodeDump(buf, docp, node, 0, format); - if (options & LIBXML_SAVE_NOEMPTYTAG) { - xmlSaveNoEmptyTags = saveempty; - } - mem = (xmlChar*) xmlBufferContent(buf); - if (!mem) { - xmlBufferFree(buf); - RETURN_FALSE; - } - RETVAL_STRING((char *) mem); - xmlBufferFree(buf); + xmlSaveNoEmptyTags = old_xml_save_no_empty_tags; } else { - if (options & LIBXML_SAVE_NOEMPTYTAG) { - saveempty = xmlSaveNoEmptyTags; - xmlSaveNoEmptyTags = 1; - } - /* Encoding is handled from the encoding property set on the document */ - xmlDocDumpFormatMemory(docp, &mem, &size, format); - if (options & LIBXML_SAVE_NOEMPTYTAG) { - xmlSaveNoEmptyTags = saveempty; - } - if (!size || !mem) { + buf = xmlBufferCreate(); + if (!buf) { + php_error_docref(NULL, E_WARNING, "Could not fetch buffer"); RETURN_FALSE; } - RETVAL_STRINGL((char *) mem, size); - xmlFree(mem); + + int converted_options = XML_SAVE_AS_XML; + if (options & XML_SAVE_NO_DECL) { + converted_options |= XML_SAVE_NO_DECL; + } + if (format) { + converted_options |= XML_SAVE_FORMAT; + } + /* Save libxml2 global, override its vaule, and restore after saving. */ + old_xml_save_no_empty_tags = xmlSaveNoEmptyTags; + xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0; + /* Encoding is handled from the encoding property set on the document */ + xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(buf, (const char *) docp->encoding, converted_options); + xmlSaveNoEmptyTags = old_xml_save_no_empty_tags; + if (UNEXPECTED(!ctxt)) { + xmlBufferFree(buf); + php_error_docref(NULL, E_WARNING, "Could not create save context"); + RETURN_FALSE; + } + if (UNEXPECTED(xmlSaveDoc(ctxt, docp) < 0)) { + (void) xmlSaveClose(ctxt); + xmlBufferFree(buf); + php_error_docref(NULL, E_WARNING, "Could not save document"); + RETURN_FALSE; + } + (void) xmlSaveFlush(ctxt); + (void) xmlSaveClose(ctxt); } + mem = xmlBufferContent(buf); + if (!mem) { + xmlBufferFree(buf); + RETURN_FALSE; + } + size = xmlBufferLength(buf); + RETVAL_STRINGL((const char *) mem, size); + xmlBufferFree(buf); } /* }}} end dom_document_savexml */ diff --git a/ext/dom/tests/DOMDocument_saveXML_XML_SAVE_NO_DECL.phpt b/ext/dom/tests/DOMDocument_saveXML_XML_SAVE_NO_DECL.phpt new file mode 100644 index 00000000000..240e5d49848 --- /dev/null +++ b/ext/dom/tests/DOMDocument_saveXML_XML_SAVE_NO_DECL.phpt @@ -0,0 +1,24 @@ +--TEST-- +DOMDocument::saveXML(): XML_SAVE_NO_DECL +--EXTENSIONS-- +dom +--FILE-- +loadXML('é'); + +echo $doc->saveXML(options: 0); +echo $doc->saveXML(options: LIBXML_NOXMLDECL); +$doc->encoding = "BIG5"; +echo $doc->saveXML(options: LIBXML_NOXMLDECL); + +// Edge case +$doc = new DOMDocument(); +var_dump($doc->saveXML(options: LIBXML_NOXMLDECL)); +?> +--EXPECT-- + +é +é +é +string(0) ""