diff --git a/NEWS b/NEWS
index 29896fbe6b33..ab95aab660b0 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,7 @@ DOM:
. Implement #53655 (Improve speed of DOMNode::C14N() on large XML documents).
(nielsdos)
. Fix cloning attribute with namespace disappearing namespace. (nielsdos)
+ . Implement DOM HTML5 parsing and serialization RFC. (nielsdos)
FTP:
. Removed the deprecated inet_ntoa call support. (David Carlier)
diff --git a/UPGRADING b/UPGRADING
index 0e7f671acb94..a43cbf1fcacf 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -80,6 +80,14 @@ PHP 8.4 UPGRADE NOTES
. Added constant DOMNode::DOCUMENT_POSITION_CONTAINS.
. Added constant DOMNode::DOCUMENT_POSITION_CONTAINED_BY.
. Added constant DOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC.
+ . Implemented DOM HTML5 parsing and serialization.
+ RFC: https://wiki.php.net/rfc/domdocument_html5_parser.
+ This RFC adds the new DOM namespace along with class and constant aliases.
+ There are two new classes to handle HTML and XML documents:
+ DOM\HTMLDocument and DOM\XMLDocument.
+ These classes provide a cleaner API to handle HTML and XML documents.
+ Furthermore, the DOM\HTMLDocument class implements spec-compliant HTML5
+ parsing and serialization.
- Phar:
. Added support for the unix timestamp extension for zip archives.
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index dbdda5dd221c..f3c394bf3224 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -52,6 +52,10 @@ PHP 8.4 INTERNALS UPGRADE NOTES
- The function php_xsl_create_object() was removed as it was not used
nor exported.
+ d. ext/libxml
+ - Added php_libxml_pretend_ctx_error_ex() to emit errors as if they had come
+ from libxml.
+
========================
4. OpCode changes
========================
diff --git a/ext/dom/config.m4 b/ext/dom/config.m4
index 6a83d10c8e24..384ea6d5bc9f 100644
--- a/ext/dom/config.m4
+++ b/ext/dom/config.m4
@@ -12,7 +12,21 @@ if test "$PHP_DOM" != "no"; then
PHP_SETUP_LIBXML(DOM_SHARED_LIBADD, [
AC_DEFINE(HAVE_DOM,1,[ ])
+ PHP_LEXBOR_CFLAGS="-I@ext_srcdir@/lexbor -DLEXBOR_STATIC"
+ LEXBOR_DIR="lexbor/lexbor"
+ LEXBOR_SOURCES="$LEXBOR_DIR/ports/posix/lexbor/core/memory.c \
+ $LEXBOR_DIR/core/array_obj.c $LEXBOR_DIR/core/array.c $LEXBOR_DIR/core/avl.c $LEXBOR_DIR/core/bst.c $LEXBOR_DIR/core/diyfp.c $LEXBOR_DIR/core/conv.c $LEXBOR_DIR/core/dobject.c $LEXBOR_DIR/core/dtoa.c $LEXBOR_DIR/core/hash.c $LEXBOR_DIR/core/mem.c $LEXBOR_DIR/core/mraw.c $LEXBOR_DIR/core/print.c $LEXBOR_DIR/core/serialize.c $LEXBOR_DIR/core/shs.c $LEXBOR_DIR/core/str.c $LEXBOR_DIR/core/strtod.c \
+ $LEXBOR_DIR/dom/interface.c $LEXBOR_DIR/dom/interfaces/attr.c $LEXBOR_DIR/dom/interfaces/cdata_section.c $LEXBOR_DIR/dom/interfaces/character_data.c $LEXBOR_DIR/dom/interfaces/comment.c $LEXBOR_DIR/dom/interfaces/document.c $LEXBOR_DIR/dom/interfaces/document_fragment.c $LEXBOR_DIR/dom/interfaces/document_type.c $LEXBOR_DIR/dom/interfaces/element.c $LEXBOR_DIR/dom/interfaces/node.c $LEXBOR_DIR/dom/interfaces/processing_instruction.c $LEXBOR_DIR/dom/interfaces/shadow_root.c $LEXBOR_DIR/dom/interfaces/text.c \
+ $LEXBOR_DIR/html/tokenizer/error.c $LEXBOR_DIR/html/tokenizer/state_comment.c $LEXBOR_DIR/html/tokenizer/state_doctype.c $LEXBOR_DIR/html/tokenizer/state_rawtext.c $LEXBOR_DIR/html/tokenizer/state_rcdata.c $LEXBOR_DIR/html/tokenizer/state_script.c $LEXBOR_DIR/html/tokenizer/state.c \
+ $LEXBOR_DIR/html/tree/active_formatting.c $LEXBOR_DIR/html/tree/error.c $LEXBOR_DIR/html/tree/insertion_mode/after_after_body.c $LEXBOR_DIR/html/tree/insertion_mode/after_after_frameset.c $LEXBOR_DIR/html/tree/insertion_mode/after_body.c $LEXBOR_DIR/html/tree/insertion_mode/after_frameset.c $LEXBOR_DIR/html/tree/insertion_mode/after_head.c $LEXBOR_DIR/html/tree/insertion_mode/before_head.c $LEXBOR_DIR/html/tree/insertion_mode/before_html.c $LEXBOR_DIR/html/tree/insertion_mode/foreign_content.c $LEXBOR_DIR/html/tree/insertion_mode/in_body.c $LEXBOR_DIR/html/tree/insertion_mode/in_caption.c $LEXBOR_DIR/html/tree/insertion_mode/in_cell.c $LEXBOR_DIR/html/tree/insertion_mode/in_column_group.c $LEXBOR_DIR/html/tree/insertion_mode/in_frameset.c $LEXBOR_DIR/html/tree/insertion_mode/in_head.c $LEXBOR_DIR/html/tree/insertion_mode/in_head_noscript.c $LEXBOR_DIR/html/tree/insertion_mode/initial.c $LEXBOR_DIR/html/tree/insertion_mode/in_row.c $LEXBOR_DIR/html/tree/insertion_mode/in_select.c $LEXBOR_DIR/html/tree/insertion_mode/in_select_in_table.c $LEXBOR_DIR/html/tree/insertion_mode/in_table_body.c $LEXBOR_DIR/html/tree/insertion_mode/in_table.c $LEXBOR_DIR/html/tree/insertion_mode/in_table_text.c $LEXBOR_DIR/html/tree/insertion_mode/in_template.c $LEXBOR_DIR/html/tree/insertion_mode/text.c $LEXBOR_DIR/html/tree/open_elements.c \
+ $LEXBOR_DIR/encoding/big5.c $LEXBOR_DIR/encoding/decode.c $LEXBOR_DIR/encoding/encode.c $LEXBOR_DIR/encoding/encoding.c $LEXBOR_DIR/encoding/euc_kr.c $LEXBOR_DIR/encoding/gb18030.c $LEXBOR_DIR/encoding/iso_2022_jp_katakana.c $LEXBOR_DIR/encoding/jis0208.c $LEXBOR_DIR/encoding/jis0212.c $LEXBOR_DIR/encoding/range.c $LEXBOR_DIR/encoding/res.c $LEXBOR_DIR/encoding/single.c \
+ $LEXBOR_DIR/html/encoding.c $LEXBOR_DIR/html/interface.c $LEXBOR_DIR/html/parser.c $LEXBOR_DIR/html/token.c $LEXBOR_DIR/html/token_attr.c $LEXBOR_DIR/html/tokenizer.c $LEXBOR_DIR/html/tree.c \
+ $LEXBOR_DIR/html/interfaces/anchor_element.c $LEXBOR_DIR/html/interfaces/area_element.c $LEXBOR_DIR/html/interfaces/audio_element.c $LEXBOR_DIR/html/interfaces/base_element.c $LEXBOR_DIR/html/interfaces/body_element.c $LEXBOR_DIR/html/interfaces/br_element.c $LEXBOR_DIR/html/interfaces/button_element.c $LEXBOR_DIR/html/interfaces/canvas_element.c $LEXBOR_DIR/html/interfaces/data_element.c $LEXBOR_DIR/html/interfaces/data_list_element.c $LEXBOR_DIR/html/interfaces/details_element.c $LEXBOR_DIR/html/interfaces/dialog_element.c $LEXBOR_DIR/html/interfaces/directory_element.c $LEXBOR_DIR/html/interfaces/div_element.c $LEXBOR_DIR/html/interfaces/d_list_element.c $LEXBOR_DIR/html/interfaces/document.c $LEXBOR_DIR/html/interfaces/element.c $LEXBOR_DIR/html/interfaces/embed_element.c $LEXBOR_DIR/html/interfaces/field_set_element.c $LEXBOR_DIR/html/interfaces/font_element.c $LEXBOR_DIR/html/interfaces/form_element.c $LEXBOR_DIR/html/interfaces/frame_element.c $LEXBOR_DIR/html/interfaces/frame_set_element.c $LEXBOR_DIR/html/interfaces/head_element.c $LEXBOR_DIR/html/interfaces/heading_element.c $LEXBOR_DIR/html/interfaces/hr_element.c $LEXBOR_DIR/html/interfaces/html_element.c $LEXBOR_DIR/html/interfaces/iframe_element.c $LEXBOR_DIR/html/interfaces/image_element.c $LEXBOR_DIR/html/interfaces/input_element.c $LEXBOR_DIR/html/interfaces/label_element.c $LEXBOR_DIR/html/interfaces/legend_element.c $LEXBOR_DIR/html/interfaces/li_element.c $LEXBOR_DIR/html/interfaces/link_element.c $LEXBOR_DIR/html/interfaces/map_element.c $LEXBOR_DIR/html/interfaces/marquee_element.c $LEXBOR_DIR/html/interfaces/media_element.c $LEXBOR_DIR/html/interfaces/menu_element.c $LEXBOR_DIR/html/interfaces/meta_element.c $LEXBOR_DIR/html/interfaces/meter_element.c $LEXBOR_DIR/html/interfaces/mod_element.c $LEXBOR_DIR/html/interfaces/object_element.c $LEXBOR_DIR/html/interfaces/o_list_element.c $LEXBOR_DIR/html/interfaces/opt_group_element.c $LEXBOR_DIR/html/interfaces/option_element.c $LEXBOR_DIR/html/interfaces/output_element.c $LEXBOR_DIR/html/interfaces/paragraph_element.c $LEXBOR_DIR/html/interfaces/param_element.c $LEXBOR_DIR/html/interfaces/picture_element.c $LEXBOR_DIR/html/interfaces/pre_element.c $LEXBOR_DIR/html/interfaces/progress_element.c $LEXBOR_DIR/html/interfaces/quote_element.c $LEXBOR_DIR/html/interfaces/script_element.c $LEXBOR_DIR/html/interfaces/select_element.c $LEXBOR_DIR/html/interfaces/slot_element.c $LEXBOR_DIR/html/interfaces/source_element.c $LEXBOR_DIR/html/interfaces/span_element.c $LEXBOR_DIR/html/interfaces/style_element.c $LEXBOR_DIR/html/interfaces/table_caption_element.c $LEXBOR_DIR/html/interfaces/table_cell_element.c $LEXBOR_DIR/html/interfaces/table_col_element.c $LEXBOR_DIR/html/interfaces/table_element.c $LEXBOR_DIR/html/interfaces/table_row_element.c $LEXBOR_DIR/html/interfaces/table_section_element.c $LEXBOR_DIR/html/interfaces/template_element.c $LEXBOR_DIR/html/interfaces/text_area_element.c $LEXBOR_DIR/html/interfaces/time_element.c $LEXBOR_DIR/html/interfaces/title_element.c $LEXBOR_DIR/html/interfaces/track_element.c $LEXBOR_DIR/html/interfaces/u_list_element.c $LEXBOR_DIR/html/interfaces/unknown_element.c $LEXBOR_DIR/html/interfaces/video_element.c $LEXBOR_DIR/html/interfaces/window.c \
+ $LEXBOR_DIR/selectors/selectors.c \
+ $LEXBOR_DIR/ns/ns.c \
+ $LEXBOR_DIR/tag/tag.c"
PHP_NEW_EXTENSION(dom, [php_dom.c attr.c document.c \
+ xml_document.c html_document.c html5_serializer.c html5_parser.c namespace_compat.c \
domexception.c parentnode.c \
processinginstruction.c cdatasection.c \
documentfragment.c domimplementation.c \
@@ -21,8 +35,9 @@ if test "$PHP_DOM" != "no"; then
nodelist.c text.c comment.c \
entityreference.c \
notation.c xpath.c dom_iterators.c \
- namednodemap.c],
- $ext_shared)
+ namednodemap.c \
+ $LEXBOR_SOURCES],
+ $ext_shared,,$PHP_LEXBOR_CFLAGS)
PHP_SUBST(DOM_SHARED_LIBADD)
PHP_INSTALL_HEADERS([ext/dom/xml_common.h])
PHP_ADD_EXTENSION_DEP(dom, libxml)
diff --git a/ext/dom/config.w32 b/ext/dom/config.w32
index 7795445019e1..a18e8ebe3a60 100644
--- a/ext/dom/config.w32
+++ b/ext/dom/config.w32
@@ -8,13 +8,29 @@ 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 \
+ xml_document.c html_document.c html5_serializer.c html5_parser.c namespace_compat.c \
domexception.c parentnode.c processinginstruction.c \
cdatasection.c documentfragment.c domimplementation.c element.c \
node.c characterdata.c documenttype.c \
entity.c nodelist.c text.c comment.c \
entityreference.c \
notation.c xpath.c dom_iterators.c \
- namednodemap.c");
+ namednodemap.c", null, "-Iext/dom/lexbor");
+
+ ADD_SOURCES("ext/dom/lexbor/lexbor/ports/windows_nt/lexbor/core", "memory.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/core", "array_obj.c array.c avl.c bst.c diyfp.c conv.c dobject.c dtoa.c hash.c mem.c mraw.c print.c serialize.c shs.c str.c strtod.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/dom", "interface.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/dom/interfaces", "attr.c cdata_section.c character_data.c comment.c document.c document_fragment.c document_type.c element.c node.c processing_instruction.c shadow_root.c text.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/html/tokenizer", "error.c state_comment.c state_doctype.c state_rawtext.c state_rcdata.c state_script.c state.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/html/tree", "active_formatting.c open_elements.c error.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/html/tree/insertion_mode", "after_after_body.c after_after_frameset.c after_body.c after_frameset.c after_head.c before_head.c before_html.c foreign_content.c in_body.c in_caption.c in_cell.c in_column_group.c in_frameset.c in_head.c in_head_noscript.c initial.c in_row.c in_select.c in_select_in_table.c in_table_body.c in_table.c in_table_text.c in_template.c text.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/html", "encoding.c interface.c parser.c token.c token_attr.c tokenizer.c tree.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/encoding", "big5.c decode.c encode.c encoding.c euc_kr.c gb18030.c iso_2022_jp_katakana.c jis0208.c jis0212.c range.c res.c single.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/html/interfaces", "anchor_element.c area_element.c audio_element.c base_element.c body_element.c br_element.c button_element.c canvas_element.c data_element.c data_list_element.c details_element.c dialog_element.c directory_element.c div_element.c d_list_element.c document.c element.c embed_element.c field_set_element.c font_element.c form_element.c frame_element.c frame_set_element.c head_element.c heading_element.c hr_element.c html_element.c iframe_element.c image_element.c input_element.c label_element.c legend_element.c li_element.c link_element.c map_element.c marquee_element.c media_element.c menu_element.c meta_element.c meter_element.c mod_element.c object_element.c o_list_element.c opt_group_element.c option_element.c output_element.c paragraph_element.c param_element.c picture_element.c pre_element.c progress_element.c quote_element.c script_element.c select_element.c slot_element.c source_element.c span_element.c style_element.c table_caption_element.c table_cell_element.c table_col_element.c table_element.c table_row_element.c table_section_element.c template_element.c text_area_element.c time_element.c title_element.c track_element.c u_list_element.c unknown_element.c video_element.c window.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/selectors", "selectors.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/ns", "ns.c", "dom");
+ ADD_SOURCES("ext/dom/lexbor/lexbor/tag", "tag.c", "dom");
+ ADD_FLAG("CFLAGS_DOM", "/D LEXBOR_STATIC ");
AC_DEFINE("HAVE_DOM", 1, "DOM support");
diff --git a/ext/dom/document.c b/ext/dom/document.c
index 31b889125269..c4ec263db7fd 100644
--- a/ext/dom/document.c
+++ b/ext/dom/document.c
@@ -35,9 +35,6 @@ struct _idsIterator {
xmlNode *element;
};
-#define DOM_LOAD_STRING 0
-#define DOM_LOAD_FILE 1
-
/*
* class DOMDocument extends DOMNode
*
@@ -486,7 +483,7 @@ zend_result dom_document_config_read(dom_object *obj, zval *retval)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-2141741547
Since:
*/
-PHP_METHOD(DOMDocument, createElement)
+PHP_METHOD(DOM_Document, createElement)
{
zval *id;
xmlNode *node;
@@ -521,7 +518,7 @@ PHP_METHOD(DOMDocument, createElement)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-35CB04B5
Since:
*/
-PHP_METHOD(DOMDocument, createDocumentFragment)
+PHP_METHOD(DOM_Document, createDocumentFragment)
{
zval *id;
xmlNode *node;
@@ -549,7 +546,7 @@ PHP_METHOD(DOMDocument, createDocumentFragment)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1975348127
Since:
*/
-PHP_METHOD(DOMDocument, createTextNode)
+PHP_METHOD(DOM_Document, createTextNode)
{
zval *id;
xmlNode *node;
@@ -579,7 +576,7 @@ PHP_METHOD(DOMDocument, createTextNode)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1334481328
Since:
*/
-PHP_METHOD(DOMDocument, createComment)
+PHP_METHOD(DOM_Document, createComment)
{
zval *id;
xmlNode *node;
@@ -609,7 +606,7 @@ PHP_METHOD(DOMDocument, createComment)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D26C0AF8
Since:
*/
-PHP_METHOD(DOMDocument, createCDATASection)
+PHP_METHOD(DOM_Document, createCDATASection)
{
zval *id;
xmlNode *node;
@@ -639,7 +636,7 @@ PHP_METHOD(DOMDocument, createCDATASection)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-135944439
Since:
*/
-PHP_METHOD(DOMDocument, createProcessingInstruction)
+PHP_METHOD(DOM_Document, createProcessingInstruction)
{
zval *id;
xmlNode *node;
@@ -676,7 +673,7 @@ PHP_METHOD(DOMDocument, createProcessingInstruction)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1084891198
Since:
*/
-PHP_METHOD(DOMDocument, createAttribute)
+PHP_METHOD(DOM_Document, createAttribute)
{
zval *id;
xmlAttrPtr node;
@@ -747,7 +744,7 @@ PHP_METHOD(DOMDocument, createEntityReference)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C9094
Since:
*/
-PHP_METHOD(DOMDocument, getElementsByTagName)
+PHP_METHOD(DOM_Document, getElementsByTagName)
{
size_t name_len;
dom_object *intern, *namednode;
@@ -768,7 +765,7 @@ PHP_METHOD(DOMDocument, getElementsByTagName)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Core-Document-importNode
Since: DOM Level 2
*/
-PHP_METHOD(DOMDocument, importNode)
+PHP_METHOD(DOM_Document, importNode)
{
zval *node;
xmlDocPtr docp;
@@ -776,8 +773,6 @@ PHP_METHOD(DOMDocument, importNode)
dom_object *intern, *nodeobj;
int ret;
bool recursive = 0;
- /* See http://www.xmlsoft.org/html/libxml-tree.html#xmlDocCopyNode for meaning of values */
- int extended_recursive;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &node, dom_node_class_entry, &recursive) == FAILURE) {
RETURN_THROWS();
@@ -796,11 +791,7 @@ PHP_METHOD(DOMDocument, importNode)
if (nodep->doc == docp) {
retnodep = nodep;
} else {
- extended_recursive = recursive;
- if ((recursive == 0) && (nodep->type == XML_ELEMENT_NODE)) {
- extended_recursive = 2;
- }
- retnodep = xmlDocCopyNode(nodep, docp, extended_recursive);
+ retnodep = dom_clone_node(nodep, docp, intern, recursive);
if (!retnodep) {
RETURN_FALSE;
}
@@ -827,7 +818,7 @@ PHP_METHOD(DOMDocument, importNode)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS
Since: DOM Level 2
*/
-PHP_METHOD(DOMDocument, createElementNS)
+PHP_METHOD(DOM_Document, createElementNS)
{
zval *id;
xmlDocPtr docp;
@@ -887,7 +878,7 @@ PHP_METHOD(DOMDocument, createElementNS)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNS
Since: DOM Level 2
*/
-PHP_METHOD(DOMDocument, createAttributeNS)
+PHP_METHOD(DOM_Document, createAttributeNS)
{
zval *id;
xmlDocPtr docp;
@@ -953,7 +944,7 @@ PHP_METHOD(DOMDocument, createAttributeNS)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBTNNS
Since: DOM Level 2
*/
-PHP_METHOD(DOMDocument, getElementsByTagNameNS)
+PHP_METHOD(DOM_Document, getElementsByTagNameNS)
{
size_t uri_len, name_len;
dom_object *intern, *namednode;
@@ -974,7 +965,7 @@ PHP_METHOD(DOMDocument, getElementsByTagNameNS)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBId
Since: DOM Level 2
*/
-PHP_METHOD(DOMDocument, getElementById)
+PHP_METHOD(DOM_Document, getElementById)
{
zval *id;
xmlDocPtr docp;
@@ -1051,7 +1042,7 @@ bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, x
Since: DOM Level 3
Modern spec URL: https://dom.spec.whatwg.org/#dom-document-adoptnode
*/
-PHP_METHOD(DOMDocument, adoptNode)
+PHP_METHOD(DOM_Document, adoptNode)
{
zval *node_zval;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, dom_node_class_entry) == FAILURE) {
@@ -1088,7 +1079,7 @@ PHP_METHOD(DOMDocument, adoptNode)
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocument
Since: DOM Level 3
*/
-PHP_METHOD(DOMDocument, normalizeDocument)
+PHP_METHOD(DOM_Document, normalizeDocument)
{
zval *id;
xmlDocPtr docp;
@@ -1149,11 +1140,11 @@ PHP_METHOD(DOMDocument, __construct)
}
/* }}} end DOMDocument::__construct */
-char *_dom_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len ) /* {{{ */
+const char *_dom_get_valid_file_path(const char *source, char *resolved_path, int resolved_path_len ) /* {{{ */
{
xmlURI *uri;
xmlChar *escsource;
- char *file_dest;
+ const char *file_dest;
int isFileUri = 0;
uri = xmlCreateURI();
@@ -1206,7 +1197,7 @@ char *_dom_get_valid_file_path(char *source, char *resolved_path, int resolved_p
}
/* }}} */
-static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t source_len, size_t options) /* {{{ */
+xmlDocPtr dom_document_parser(zval *id, dom_load_mode mode, const char *source, size_t source_len, size_t options, xmlCharEncodingHandlerPtr encoding) /* {{{ */
{
xmlDocPtr ret;
xmlParserCtxtPtr ctxt = NULL;
@@ -1215,10 +1206,14 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so
int old_error_reporting = 0;
char *directory=NULL, resolved_path[MAXPATHLEN + 1];
- dom_object *intern = Z_DOMOBJ_P(id);
- php_libxml_ref_obj *document = intern->document;
-
- libxml_doc_props const* doc_props = dom_get_doc_props_read_only(document);
+ libxml_doc_props const* doc_props;
+ if (id == NULL) {
+ doc_props = dom_get_doc_props_read_only(NULL);
+ } else {
+ dom_object *intern = Z_DOMOBJ_P(id);
+ php_libxml_ref_obj *document = intern->document;
+ doc_props = dom_get_doc_props_read_only(document);
+ }
validate = doc_props->validateonparse;
resolve_externals = doc_props->resolveexternals;
keep_blanks = doc_props->preservewhitespace;
@@ -1228,12 +1223,11 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so
xmlInitParser();
if (mode == DOM_LOAD_FILE) {
- char *file_dest;
if (CHECK_NULL_PATH(source, source_len)) {
- zend_value_error("Path to document must not contain any null bytes");
+ zend_argument_value_error(1, "must not contain any null bytes");
return NULL;
}
- file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
+ const char *file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
if (file_dest) {
ctxt = xmlCreateFileParserCtxt(file_dest);
}
@@ -1246,6 +1240,8 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so
return(NULL);
}
+ (void) xmlSwitchToEncoding(ctxt, encoding);
+
/* If loading from memory, we need to set the base directory for the document */
if (mode != DOM_LOAD_FILE) {
#ifdef HAVE_GETCWD
@@ -1319,7 +1315,7 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so
}
/* }}} */
-static void dom_finish_loading_document(zval *this, zval *return_value, xmlDocPtr newdoc)
+static void php_dom_finish_loading_document(zval *this, zval *return_value, xmlDocPtr newdoc)
{
if (!newdoc)
RETURN_FALSE;
@@ -1327,11 +1323,13 @@ static void dom_finish_loading_document(zval *this, zval *return_value, xmlDocPt
dom_object *intern = Z_DOMOBJ_P(this);
size_t old_modification_nr = 0;
if (intern != NULL) {
+ bool is_modern_api_class = false;
xmlDocPtr docp = (xmlDocPtr) dom_object_get_node(intern);
dom_doc_propsptr doc_prop = NULL;
if (docp != NULL) {
const php_libxml_ref_obj *doc_ptr = intern->document;
ZEND_ASSERT(doc_ptr != NULL); /* Must exist, we have a document */
+ is_modern_api_class = doc_ptr->is_modern_api_class;
old_modification_nr = doc_ptr->cache_tag.modification_nr;
php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
doc_prop = intern->document->doc_props;
@@ -1346,6 +1344,7 @@ static void dom_finish_loading_document(zval *this, zval *return_value, xmlDocPt
RETURN_FALSE;
}
intern->document->doc_props = doc_prop;
+ intern->document->is_modern_api_class = is_modern_api_class;
}
php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
@@ -1358,8 +1357,8 @@ static void dom_finish_loading_document(zval *this, zval *return_value, xmlDocPt
RETURN_TRUE;
}
-/* {{{ static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) */
-static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) {
+static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode)
+{
char *source;
size_t source_len;
zend_long options = 0;
@@ -1381,11 +1380,9 @@ static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) {
RETURN_FALSE;
}
- xmlDocPtr newdoc = dom_document_parser(ZEND_THIS, mode, source, source_len, options);
-
- dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
+ xmlDocPtr newdoc = dom_document_parser(ZEND_THIS, mode, source, source_len, options, NULL);
+ php_dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
}
-/* }}} end dom_parser_document */
/* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-load
Since: DOM Level 3
@@ -1664,7 +1661,8 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type
zval *id;
xmlDoc *docp;
dom_object *intern;
- char *source = NULL, *valid_file = NULL;
+ char *source = NULL;
+ const char *valid_file = NULL;
size_t source_len = 0;
int valid_opts = 0;
zend_long flags = 0;
@@ -1756,14 +1754,14 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type
/* }}} */
/* {{{ */
-PHP_METHOD(DOMDocument, schemaValidate)
+PHP_METHOD(DOM_Document, schemaValidate)
{
_dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
}
/* }}} end dom_document_schema_validate_file */
/* {{{ */
-PHP_METHOD(DOMDocument, schemaValidateSource)
+PHP_METHOD(DOM_Document, schemaValidateSource)
{
_dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
}
@@ -1774,7 +1772,8 @@ static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int typ
zval *id;
xmlDoc *docp;
dom_object *intern;
- char *source = NULL, *valid_file = NULL;
+ char *source = NULL;
+ const char *valid_file = NULL;
size_t source_len = 0;
xmlRelaxNGParserCtxtPtr parser;
xmlRelaxNGPtr sptr;
@@ -1852,14 +1851,14 @@ static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int typ
/* }}} */
/* {{{ */
-PHP_METHOD(DOMDocument, relaxNGValidate)
+PHP_METHOD(DOM_Document, relaxNGValidate)
{
_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
}
/* }}} end dom_document_relaxNG_validate_file */
/* {{{ */
-PHP_METHOD(DOMDocument, relaxNGValidateSource)
+PHP_METHOD(DOM_Document, relaxNGValidateSource)
{
_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
}
@@ -1923,7 +1922,7 @@ static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
xmlDocPtr newdoc = ctxt->myDoc;
htmlFreeParserCtxt(ctxt);
- dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
+ php_dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
}
/* }}} */
@@ -2065,7 +2064,7 @@ PHP_METHOD(DOMDocument, saveHTML)
#endif /* defined(LIBXML_HTML_ENABLED) */
/* {{{ Register extended class used to create base node type */
-PHP_METHOD(DOMDocument, registerNodeClass)
+PHP_METHOD(DOM_Document, registerNodeClass)
{
zend_class_entry *basece = dom_node_class_entry, *ce = NULL;
dom_object *intern;
@@ -2074,6 +2073,11 @@ PHP_METHOD(DOMDocument, registerNodeClass)
RETURN_THROWS();
}
+ if (basece->ce_flags & ZEND_ACC_ABSTRACT) {
+ zend_argument_value_error(1, "must not be an abstract class");
+ RETURN_THROWS();
+ }
+
if (ce == NULL || instanceof_function(ce, basece)) {
if (UNEXPECTED(ce != NULL && (ce->ce_flags & ZEND_ACC_ABSTRACT))) {
zend_argument_value_error(2, "must not be an abstract class");
@@ -2091,7 +2095,7 @@ PHP_METHOD(DOMDocument, registerNodeClass)
/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
Since:
*/
-PHP_METHOD(DOMDocument, replaceChildren)
+PHP_METHOD(DOM_Document, replaceChildren)
{
uint32_t argc = 0;
zval *args;
diff --git a/ext/dom/dom_ce.h b/ext/dom/dom_ce.h
index b0faf3934df5..5b661b2abbb9 100644
--- a/ext/dom/dom_ce.h
+++ b/ext/dom/dom_ce.h
@@ -23,6 +23,8 @@ extern PHP_DOM_EXPORT zend_class_entry *dom_domexception_class_entry;
extern PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry;
extern PHP_DOM_EXPORT zend_class_entry *dom_documentfragment_class_entry;
extern PHP_DOM_EXPORT zend_class_entry *dom_document_class_entry;
+extern PHP_DOM_EXPORT zend_class_entry *dom_html_document_class_entry;
+extern PHP_DOM_EXPORT zend_class_entry *dom_xml_document_class_entry;
extern PHP_DOM_EXPORT zend_class_entry *dom_nodelist_class_entry;
extern PHP_DOM_EXPORT zend_class_entry *dom_namednodemap_class_entry;
extern PHP_DOM_EXPORT zend_class_entry *dom_characterdata_class_entry;
@@ -36,6 +38,7 @@ extern PHP_DOM_EXPORT zend_class_entry *dom_notation_class_entry;
extern PHP_DOM_EXPORT zend_class_entry *dom_entity_class_entry;
extern PHP_DOM_EXPORT zend_class_entry *dom_entityreference_class_entry;
extern PHP_DOM_EXPORT zend_class_entry *dom_processinginstruction_class_entry;
+extern PHP_DOM_EXPORT zend_class_entry *dom_abstract_base_document_class_entry;
#ifdef LIBXML_XPATH_ENABLED
extern PHP_DOM_EXPORT zend_class_entry *dom_xpath_class_entry;
#endif
diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h
index 5116c310570e..349b604dddcc 100644
--- a/ext/dom/dom_properties.h
+++ b/ext/dom/dom_properties.h
@@ -61,6 +61,9 @@ zend_result dom_document_recover_write(dom_object *obj, zval *newval);
zend_result dom_document_substitue_entities_read(dom_object *obj, zval *retval);
zend_result dom_document_substitue_entities_write(dom_object *obj, zval *newval);
+/* html5 document properties */
+zend_result dom_html_document_encoding_write(dom_object *obj, zval *retval);
+
/* documenttype properties */
zend_result dom_documenttype_name_read(dom_object *obj, zval *retval);
zend_result dom_documenttype_entities_read(dom_object *obj, zval *retval);
diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c
new file mode 100644
index 000000000000..dbe83eb340ee
--- /dev/null
+++ b/ext/dom/html5_parser.c
@@ -0,0 +1,328 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Niels Dossche |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
+#include "html5_parser.h"
+#include "namespace_compat.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define WORK_LIST_INIT_SIZE 128
+/* libxml2 reserves 2 pointer-sized words for interned strings */
+#define LXML_INTERNED_STRINGS_SIZE (sizeof(void *) * 2)
+
+typedef struct {
+ lxb_dom_node_t *node;
+ uintptr_t current_active_namespace;
+ xmlNodePtr lxml_parent;
+ xmlNsPtr lxml_ns;
+} work_list_item;
+
+static void lexbor_libxml2_bridge_work_list_item_push(
+ lexbor_array_obj_t *array,
+ lxb_dom_node_t *node,
+ uintptr_t current_active_namespace,
+ xmlNodePtr lxml_parent,
+ xmlNsPtr lxml_ns
+)
+{
+ work_list_item *item = (work_list_item *) lexbor_array_obj_push_wo_cls(array);
+ item->node = node;
+ item->current_active_namespace = current_active_namespace;
+ item->lxml_parent = lxml_parent;
+ item->lxml_ns = lxml_ns;
+}
+
+static unsigned short sanitize_line_nr(size_t line)
+{
+ if (line > USHRT_MAX) {
+ return USHRT_MAX;
+ }
+ return (unsigned short) line;
+}
+
+static const xmlChar *get_libxml_namespace_href(uintptr_t lexbor_namespace)
+{
+ if (lexbor_namespace == LXB_NS_SVG) {
+ return (const xmlChar *) DOM_SVG_NS_URI;
+ } else if (lexbor_namespace == LXB_NS_MATH) {
+ return (const xmlChar *) DOM_MATHML_NS_URI;
+ } else {
+ return (const xmlChar *) DOM_XHTML_NS_URI;
+ }
+}
+
+static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert(
+ lxb_dom_node_t *start_node,
+ xmlDocPtr lxml_doc,
+ bool compact_text_nodes,
+ bool create_default_ns
+)
+{
+ lexbor_libxml2_bridge_status retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OK;
+
+ lexbor_array_obj_t work_list;
+ lexbor_array_obj_init(&work_list, WORK_LIST_INIT_SIZE, sizeof(work_list_item));
+
+ for (lxb_dom_node_t *node = start_node; node != NULL; node = node->prev) {
+ lexbor_libxml2_bridge_work_list_item_push(&work_list, node, LXB_NS__UNDEF, (xmlNodePtr) lxml_doc, NULL);
+ }
+
+ work_list_item *current_stack_item;
+ while ((current_stack_item = lexbor_array_obj_pop(&work_list)) != NULL) {
+ lxb_dom_node_t *node = current_stack_item->node;
+ xmlNodePtr lxml_parent = current_stack_item->lxml_parent;
+
+ /* CDATA section and processing instructions don't occur in parsed HTML documents.
+ * The historical types are not emitted by the parser either. */
+ if (node->type == LXB_DOM_NODE_TYPE_ELEMENT) {
+ /* Note: HTML isn't exactly XML-namespace-aware; as this is an HTML parser we only care about the local name.
+ * If a prefix:name format is used, then the local name will be "prefix:name" and the prefix will be empty.
+ * There is however still somewhat of a concept of namespaces. There are three: HTML (the default), SVG, and MATHML. */
+ lxb_dom_element_t *element = lxb_dom_interface_element(node);
+ const lxb_char_t *name = lxb_dom_element_local_name(element, NULL);
+ xmlNodePtr lxml_element = xmlNewDocNode(lxml_doc, NULL, name, NULL);
+ if (UNEXPECTED(lxml_element == NULL)) {
+ retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM;
+ goto out;
+ }
+ xmlAddChild(lxml_parent, lxml_element);
+ lxml_element->line = sanitize_line_nr(node->line);
+
+ /* Namespaces, note: namespace switches are uncommon */
+ uintptr_t entering_namespace = element->node.ns;
+ xmlNsPtr current_lxml_ns = current_stack_item->lxml_ns;
+ if (create_default_ns && UNEXPECTED(entering_namespace != current_stack_item->current_active_namespace)) {
+ current_lxml_ns = xmlNewNs(lxml_element, get_libxml_namespace_href(entering_namespace), NULL);
+ }
+ /* Instead of xmlSetNs() because we know the arguments are valid. Prevents overhead. */
+ lxml_element->ns = current_lxml_ns;
+
+ for (lxb_dom_node_t *child_node = element->node.last_child; child_node != NULL; child_node = child_node->prev) {
+ lexbor_libxml2_bridge_work_list_item_push(
+ &work_list,
+ child_node,
+ entering_namespace,
+ lxml_element,
+ current_lxml_ns
+ );
+ }
+
+ for (lxb_dom_attr_t *attr = element->last_attr; attr != NULL; attr = attr->prev) {
+ lexbor_libxml2_bridge_work_list_item_push(
+ &work_list,
+ (lxb_dom_node_t *) attr,
+ entering_namespace,
+ lxml_element,
+ current_lxml_ns
+ );
+ }
+ } else if (node->type == LXB_DOM_NODE_TYPE_TEXT) {
+ lxb_dom_text_t *text = lxb_dom_interface_text(node);
+ const lxb_char_t *data = text->char_data.data.data;
+ size_t data_length = text->char_data.data.length;
+ if (UNEXPECTED(data_length >= INT_MAX)) {
+ retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OVERFLOW;
+ goto out;
+ }
+ xmlNodePtr lxml_text;
+ if (compact_text_nodes && data_length < LXML_INTERNED_STRINGS_SIZE) {
+ /* See xmlSAX2TextNode() in libxml2 */
+ lxml_text = xmlMalloc(sizeof(*lxml_text));
+ if (UNEXPECTED(lxml_text == NULL)) {
+ retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM;
+ goto out;
+ }
+ memset(lxml_text, 0, sizeof(*lxml_text));
+ lxml_text->name = xmlStringText;
+ lxml_text->type = XML_TEXT_NODE;
+ lxml_text->doc = lxml_doc;
+ lxml_text->content = (xmlChar *) &lxml_text->properties;
+ memcpy(lxml_text->content, data, data_length + 1 /* include '\0' */);
+ } else {
+ lxml_text = xmlNewDocTextLen(lxml_doc, (const xmlChar *) data, data_length);
+ if (UNEXPECTED(lxml_text == NULL)) {
+ retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM;
+ goto out;
+ }
+ }
+ xmlAddChild(lxml_parent, lxml_text);
+ if (node->line >= USHRT_MAX) {
+ lxml_text->line = USHRT_MAX;
+ lxml_text->psvi = (void *) (ptrdiff_t) node->line;
+ } else {
+ lxml_text->line = (unsigned short) node->line;
+ }
+ } else if (node->type == LXB_DOM_NODE_TYPE_DOCUMENT_TYPE) {
+ lxb_dom_document_type_t *doctype = lxb_dom_interface_document_type(node);
+ const lxb_char_t *name = lxb_dom_document_type_name(doctype, NULL);
+ size_t public_id_len, system_id_len;
+ const lxb_char_t *public_id = lxb_dom_document_type_public_id(doctype, &public_id_len);
+ const lxb_char_t *system_id = lxb_dom_document_type_system_id(doctype, &system_id_len);
+ xmlDtdPtr lxml_dtd = xmlCreateIntSubset(
+ lxml_doc,
+ name,
+ public_id_len ? public_id : NULL,
+ system_id_len ? system_id : NULL
+ );
+ if (UNEXPECTED(lxml_dtd == NULL)) {
+ retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM;
+ goto out;
+ }
+ /* libxml2 doesn't support line numbers on this anyway, it returns -1 instead, so don't bother */
+ } else if (node->type == LXB_DOM_NODE_TYPE_ATTRIBUTE) {
+ lxb_dom_attr_t *attr = lxb_dom_interface_attr(node);
+ do {
+ /* Same namespace remark as for elements */
+ const lxb_char_t *local_name = lxb_dom_attr_local_name(attr, NULL);
+ const lxb_char_t *value = lxb_dom_attr_value(attr, NULL);
+ xmlAttrPtr lxml_attr = xmlSetNsProp(lxml_parent, NULL, local_name, value);
+ if (UNEXPECTED(lxml_attr == NULL)) {
+ retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM;
+ goto out;
+ }
+ attr = attr->next;
+ /* libxml2 doesn't support line numbers on this anyway, it derives them instead, so don't bother */
+ } while (attr);
+ } else if (node->type == LXB_DOM_NODE_TYPE_COMMENT) {
+ lxb_dom_comment_t *comment = lxb_dom_interface_comment(node);
+ xmlNodePtr lxml_comment = xmlNewDocComment(lxml_doc, comment->char_data.data.data);
+ if (UNEXPECTED(lxml_comment == NULL)) {
+ retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM;
+ goto out;
+ }
+ xmlAddChild(lxml_parent, lxml_comment);
+ lxml_comment->line = sanitize_line_nr(node->line);
+ }
+ }
+
+out:
+ lexbor_array_obj_destroy(&work_list, false);
+ return retval;
+}
+
+void lexbor_libxml2_bridge_parse_context_init(lexbor_libxml2_bridge_parse_context *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+void lexbor_libxml2_bridge_parse_set_error_callbacks(
+ lexbor_libxml2_bridge_parse_context *ctx,
+ lexbor_libxml2_bridge_tokenizer_error_reporter tokenizer_error_reporter,
+ lexbor_libxml2_bridge_tree_error_reporter tree_error_reporter
+)
+{
+ ctx->tokenizer_error_reporter = tokenizer_error_reporter;
+ ctx->tree_error_reporter = tree_error_reporter;
+}
+
+lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert_document(
+ lxb_html_document_t *document,
+ xmlDocPtr *doc_out,
+ bool compact_text_nodes,
+ bool create_default_ns
+)
+{
+#ifdef LIBXML_HTML_ENABLED
+ xmlDocPtr lxml_doc = htmlNewDocNoDtD(NULL, NULL);
+#else
+ /* If HTML support is not enabled, then htmlNewDocNoDtD() is not available.
+ * This code mimics the behaviour. */
+ xmlDocPtr lxml_doc = xmlNewDoc((const xmlChar *) "1.0");
+ lxml_doc->type = XML_HTML_DOCUMENT_NODE;
+#endif
+ if (!lxml_doc) {
+ return LEXBOR_LIBXML2_BRIDGE_STATUS_OOM;
+ }
+ lexbor_libxml2_bridge_status status = lexbor_libxml2_bridge_convert(
+ lxb_dom_interface_node(document)->last_child,
+ lxml_doc,
+ compact_text_nodes,
+ create_default_ns
+ );
+ if (status != LEXBOR_LIBXML2_BRIDGE_STATUS_OK) {
+ xmlFreeDoc(lxml_doc);
+ return status;
+ }
+ *doc_out = lxml_doc;
+ return LEXBOR_LIBXML2_BRIDGE_STATUS_OK;
+}
+
+void lexbor_libxml2_bridge_report_errors(
+ const lexbor_libxml2_bridge_parse_context *ctx,
+ lxb_html_parser_t *parser,
+ const lxb_char_t *input_html,
+ size_t chunk_offset,
+ size_t *error_index_offset_tokenizer,
+ size_t *error_index_offset_tree
+)
+{
+ void *error;
+
+ /* Tokenizer errors */
+ lexbor_array_obj_t *parse_errors = lxb_html_parser_tokenizer(parser)->parse_errors;
+ size_t index = *error_index_offset_tokenizer;
+ while ((error = lexbor_array_obj_get(parse_errors, index)) != NULL) {
+ /* See https://github.com/lexbor/lexbor/blob/master/source/lexbor/html/tokenizer/error.h */
+ lxb_html_tokenizer_error_t *token_error = error;
+ if (ctx->tokenizer_error_reporter) {
+ ctx->tokenizer_error_reporter(
+ ctx->application_data,
+ token_error,
+ token_error->pos - input_html + chunk_offset
+ );
+ }
+ index++;
+ }
+ *error_index_offset_tokenizer = index;
+
+ /* Tree parser errors */
+ parse_errors = lxb_html_parser_tree(parser)->parse_errors;
+ index = *error_index_offset_tree;
+ while ((error = lexbor_array_obj_get(parse_errors, index)) != NULL) {
+ /* See https://github.com/lexbor/lexbor/blob/master/source/lexbor/html/tree/error.h */
+ lxb_html_tree_error_t *tree_error = error;
+ if (ctx->tree_error_reporter) {
+ ctx->tree_error_reporter(
+ ctx->application_data,
+ tree_error,
+ tree_error->line + 1,
+ tree_error->column + 1,
+ tree_error->length
+ );
+ }
+ index++;
+ }
+ *error_index_offset_tree = index;
+}
+
+void lexbor_libxml2_bridge_copy_observations(lxb_html_tree_t *tree, lexbor_libxml2_bridge_extracted_observations *observations)
+{
+ observations->has_explicit_html_tag = tree->has_explicit_html_tag;
+ observations->has_explicit_head_tag = tree->has_explicit_head_tag;
+ observations->has_explicit_body_tag = tree->has_explicit_body_tag;
+}
+
+#endif /* HAVE_LIBXML && HAVE_DOM */
diff --git a/ext/dom/html5_parser.h b/ext/dom/html5_parser.h
new file mode 100644
index 000000000000..a3e7d16c5c4a
--- /dev/null
+++ b/ext/dom/html5_parser.h
@@ -0,0 +1,86 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Niels Dossche |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef HTML5_PARSER_H
+#define HTML5_PARSER_H
+
+#include
+#include
+#include
+
+typedef enum {
+ LEXBOR_LIBXML2_BRIDGE_STATUS_OK = 0,
+ LEXBOR_LIBXML2_BRIDGE_STATUS_CANNOT_INIT,
+ LEXBOR_LIBXML2_BRIDGE_STATUS_FATAL_PARSE,
+ LEXBOR_LIBXML2_BRIDGE_STATUS_OVERFLOW,
+ LEXBOR_LIBXML2_BRIDGE_STATUS_OOM,
+} lexbor_libxml2_bridge_status;
+
+typedef void (*lexbor_libxml2_bridge_tokenizer_error_reporter)(
+ void *application_data,
+ lxb_html_tokenizer_error_t *error,
+ size_t offset
+);
+typedef void (*lexbor_libxml2_bridge_tree_error_reporter)(
+ void *application_data,
+ lxb_html_tree_error_t *error,
+ size_t line,
+ size_t column,
+ size_t len
+);
+
+typedef struct {
+ bool has_explicit_html_tag;
+ bool has_explicit_head_tag;
+ bool has_explicit_body_tag;
+} lexbor_libxml2_bridge_extracted_observations;
+
+typedef struct {
+ /* Private fields */
+ lexbor_libxml2_bridge_tokenizer_error_reporter tokenizer_error_reporter;
+ lexbor_libxml2_bridge_tree_error_reporter tree_error_reporter;
+ /* Public fields */
+ lexbor_libxml2_bridge_extracted_observations observations;
+ /* Application data, do what you want with this */
+ void *application_data;
+} lexbor_libxml2_bridge_parse_context;
+
+void lexbor_libxml2_bridge_parse_context_init(lexbor_libxml2_bridge_parse_context *ctx);
+void lexbor_libxml2_bridge_parse_set_error_callbacks(
+ lexbor_libxml2_bridge_parse_context *ctx,
+ lexbor_libxml2_bridge_tokenizer_error_reporter tokenizer_error_reporter,
+ lexbor_libxml2_bridge_tree_error_reporter tree_error_reporter
+);
+lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert_document(
+ lxb_html_document_t *document,
+ xmlDocPtr *doc_out,
+ bool compact_text_nodes,
+ bool create_default_ns
+);
+void lexbor_libxml2_bridge_report_errors(
+ const lexbor_libxml2_bridge_parse_context *ctx,
+ lxb_html_parser_t *parser,
+ const lxb_char_t *input_html,
+ size_t chunk_offset,
+ size_t *error_index_offset_tokenizer,
+ size_t *error_index_offset_tree
+);
+void lexbor_libxml2_bridge_copy_observations(
+ lxb_html_tree_t *tree,
+ lexbor_libxml2_bridge_extracted_observations *observations
+);
+
+#endif
diff --git a/ext/dom/html5_serializer.c b/ext/dom/html5_serializer.c
new file mode 100644
index 000000000000..f0d43f09afba
--- /dev/null
+++ b/ext/dom/html5_serializer.c
@@ -0,0 +1,356 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Niels Dossche |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
+#include "php_dom.h"
+#include "html5_serializer.h"
+#include "namespace_compat.h"
+#include
+
+#define TRY(x) do { if (UNEXPECTED((x) != SUCCESS)) { return FAILURE; } } while (0)
+
+static bool dom_is_ns(const xmlNode *node, const char *uri)
+{
+ return node->ns != NULL && strcmp((const char *) node->ns->href, uri) == 0;
+}
+
+static bool dom_is_html_ns(const xmlNode *node)
+{
+ return node->ns == NULL || dom_is_ns(node, DOM_XHTML_NS_URI);
+}
+
+static bool dom_local_name_compare_ex(const xmlNode *node, const char *tag, size_t tag_length, size_t name_length)
+{
+ return name_length == tag_length && zend_binary_strcmp((const char *) node->name, name_length, tag, tag_length) == 0;
+}
+
+static zend_result dom_html5_serialize_doctype(dom_html5_serialize_context *ctx, const xmlDtd *dtd)
+{
+ TRY(ctx->write_string_len(ctx->application_data, "write_string(ctx->application_data, (const char *) dtd->name));
+ return ctx->write_string_len(ctx->application_data, ">", strlen(">"));
+}
+
+static zend_result dom_html5_serialize_comment(dom_html5_serialize_context *ctx, const xmlNode *node)
+{
+ TRY(ctx->write_string_len(ctx->application_data, "", strlen("-->"));
+}
+
+static zend_result dom_html5_serialize_processing_instruction(dom_html5_serialize_context *ctx, const xmlNode *node)
+{
+ TRY(ctx->write_string_len(ctx->application_data, "", strlen("")));
+ TRY(ctx->write_string(ctx->application_data, (const char *) node->name));
+ TRY(ctx->write_string_len(ctx->application_data, " ", strlen(" ")));
+ TRY(ctx->write_string(ctx->application_data, (const char *) node->content));
+ return ctx->write_string_len(ctx->application_data, ">", strlen(">"));
+}
+
+/* https://html.spec.whatwg.org/multipage/parsing.html#escapingString */
+static zend_result dom_html5_escape_string(dom_html5_serialize_context *ctx, const char *content, bool attribute_mode)
+{
+ const char *last_output = content;
+
+ while (*content != '\0') {
+ switch (*content) {
+ /* Step 1 */
+ case '&': {
+ TRY(ctx->write_string_len(ctx->application_data, last_output, content - last_output));
+ TRY(ctx->write_string_len(ctx->application_data, "&", strlen("&")));
+ last_output = content + 1;
+ break;
+ }
+
+ /* Step 2 (non-breaking space) (note: uses UTF-8 internally) */
+ case '\xC2': {
+ if (content[1] == '\xA0') {
+ TRY(ctx->write_string_len(ctx->application_data, last_output, content - last_output));
+ TRY(ctx->write_string_len(ctx->application_data, " ", strlen(" ")));
+ content++; /* Consume A0 too */
+ last_output = content + 1;
+ }
+ break;
+ }
+
+ /* Step 3 */
+ case '"': {
+ if (attribute_mode) {
+ TRY(ctx->write_string_len(ctx->application_data, last_output, content - last_output));
+ TRY(ctx->write_string_len(ctx->application_data, """, strlen(""")));
+ last_output = content + 1;
+ }
+ break;
+ }
+
+ /* Step 4 */
+ case '<': {
+ if (!attribute_mode) {
+ TRY(ctx->write_string_len(ctx->application_data, last_output, content - last_output));
+ TRY(ctx->write_string_len(ctx->application_data, "<", strlen("<")));
+ last_output = content + 1;
+ }
+ break;
+ }
+ case '>': {
+ if (!attribute_mode) {
+ TRY(ctx->write_string_len(ctx->application_data, last_output, content - last_output));
+ TRY(ctx->write_string_len(ctx->application_data, ">", strlen(">")));
+ last_output = content + 1;
+ }
+ break;
+ }
+ }
+
+ content++;
+ }
+
+ return ctx->write_string_len(ctx->application_data, last_output, content - last_output);
+}
+
+static zend_result dom_html5_serialize_text_node(dom_html5_serialize_context *ctx, const xmlNode *node)
+{
+ if (node->parent->type == XML_ELEMENT_NODE && dom_is_html_ns(node->parent)) {
+ const xmlNode *parent = node->parent;
+ size_t name_length = strlen((const char *) parent->name);
+ /* Spec tells us to only emit noscript content as-is if scripting is enabled.
+ * However, the user agent (PHP) does not support (JS) scripting.
+ * Furthermore, if actually consumed by a browser then we should err on the safe side and not emit the content as-is. */
+ if (dom_local_name_compare_ex(parent, "style", strlen("style"), name_length)
+ || dom_local_name_compare_ex(parent, "script", strlen("script"), name_length)
+ || dom_local_name_compare_ex(parent, "xmp", strlen("xmp"), name_length)
+ || dom_local_name_compare_ex(parent, "iframe", strlen("iframe"), name_length)
+ || dom_local_name_compare_ex(parent, "noembed", strlen("noembed"), name_length)
+ || dom_local_name_compare_ex(parent, "noframes", strlen("noframes"), name_length)
+ || dom_local_name_compare_ex(parent, "plaintext", strlen("plaintext"), name_length)) {
+ return ctx->write_string(ctx->application_data, (const char *) node->content);
+ }
+ }
+
+ return dom_html5_escape_string(ctx, (const char *) node->content, false);
+}
+
+static zend_result dom_html5_serialize_element_tag_name(dom_html5_serialize_context *ctx, const xmlNode *node)
+{
+ /* Note: it is not the serializer's responsibility to care about uppercase/lowercase (see createElement() note) */
+ if (node->ns != NULL && node->ns->prefix != NULL
+ && !(dom_is_html_ns(node) || dom_is_ns(node, DOM_MATHML_NS_URI) || dom_is_ns(node, DOM_SVG_NS_URI))) {
+ TRY(ctx->write_string(ctx->application_data, (const char *) node->ns->prefix));
+ TRY(ctx->write_string_len(ctx->application_data, ":", strlen(":")));
+ }
+ return ctx->write_string(ctx->application_data, (const char *) node->name);
+}
+
+static zend_result dom_html5_serialize_element_start(dom_html5_serialize_context *ctx, const xmlNode *node)
+{
+ TRY(ctx->write_string_len(ctx->application_data, "<", strlen("<")));
+ TRY(dom_html5_serialize_element_tag_name(ctx, node));
+
+ /* We don't support the "is" value during element creation, so no handling here. */
+
+ /* Some namespace declarations are also attributes (see https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token) */
+ for (const xmlNs *ns = node->nsDef; ns != NULL; ns = ns->next) {
+ if (!dom_ns_is_also_an_attribute(ns)) {
+ continue;
+ }
+
+ if (ns->prefix != NULL) {
+ TRY(ctx->write_string_len(ctx->application_data, " xmlns:", strlen(" xmlns:")));
+ TRY(ctx->write_string(ctx->application_data, (const char *) ns->prefix));
+ TRY(ctx->write_string_len(ctx->application_data, "=\"", strlen("=\"")));
+ } else {
+ TRY(ctx->write_string_len(ctx->application_data, " xmlns=\"", strlen(" xmlns=\"")));
+ }
+ TRY(ctx->write_string(ctx->application_data, (const char *) ns->href));
+ TRY(ctx->write_string_len(ctx->application_data, "\"", strlen("\"")));
+ }
+
+ for (const xmlAttr *attr = node->properties; attr; attr = attr->next) {
+ TRY(ctx->write_string_len(ctx->application_data, " ", strlen(" ")));
+ if (attr->ns == NULL) {
+ TRY(ctx->write_string(ctx->application_data, (const char *) attr->name));
+ } else {
+ if (dom_is_ns((const xmlNode *) attr, DOM_XML_NS_URI)) {
+ TRY(ctx->write_string_len(ctx->application_data, "xml:", strlen("xml:")));
+ TRY(ctx->write_string(ctx->application_data, (const char *) attr->name));
+ } else if (dom_is_ns((const xmlNode *) attr, DOM_XMLNS_NS_URI)) {
+ /* Compatibility for real attributes */
+ if (strcmp((const char *) attr->name, "xmlns") == 0) {
+ TRY(ctx->write_string_len(ctx->application_data, "xmlns", strlen("xmlns")));
+ } else {
+ TRY(ctx->write_string_len(ctx->application_data, "xmlns:", strlen("xmlns:")));
+ TRY(ctx->write_string(ctx->application_data, (const char *) attr->name));
+ }
+ } else if (dom_is_ns((const xmlNode *) attr, DOM_XLINK_NS_URI)) {
+ TRY(ctx->write_string_len(ctx->application_data, "xlink:", strlen("xlink:")));
+ TRY(ctx->write_string(ctx->application_data, (const char *) attr->name));
+ } else if (attr->ns->prefix == NULL) {
+ TRY(ctx->write_string(ctx->application_data, (const char *) attr->name));
+ } else {
+ TRY(ctx->write_string(ctx->application_data, (const char *) attr->ns->prefix));
+ TRY(ctx->write_string_len(ctx->application_data, ":", strlen(":")));
+ TRY(ctx->write_string(ctx->application_data, (const char *) attr->name));
+ }
+ }
+ TRY(ctx->write_string_len(ctx->application_data, "=\"", strlen("=\"")));
+ xmlChar *content = xmlNodeGetContent((const xmlNode *) attr);
+ if (content != NULL) {
+ zend_result result = dom_html5_escape_string(ctx, (const char *) content, true);
+ xmlFree(content);
+ TRY(result);
+ }
+ TRY(ctx->write_string_len(ctx->application_data, "\"", strlen("\"")));
+ }
+
+ return ctx->write_string_len(ctx->application_data, ">", strlen(">"));
+
+ /* Note: "continue on to the next child if the element is void" is handled in the iteration and dom_html5_serialize_element_end() */
+}
+
+/* https://html.spec.whatwg.org/multipage/syntax.html#void-elements
+ * https://html.spec.whatwg.org/multipage/parsing.html#serializes-as-void */
+static bool dom_html5_serializes_as_void(const xmlNode *node)
+{
+ if (dom_is_html_ns(node)) {
+ size_t name_length = strlen((const char *) node->name);
+ if (/* These are the void elements from https://html.spec.whatwg.org/multipage/syntax.html#void-elements */
+ dom_local_name_compare_ex(node, "area", strlen("area"), name_length)
+ || dom_local_name_compare_ex(node, "base", strlen("base"), name_length)
+ || dom_local_name_compare_ex(node, "br", strlen("br"), name_length)
+ || dom_local_name_compare_ex(node, "col", strlen("col"), name_length)
+ || dom_local_name_compare_ex(node, "embed", strlen("embed"), name_length)
+ || dom_local_name_compare_ex(node, "hr", strlen("hr"), name_length)
+ || dom_local_name_compare_ex(node, "img", strlen("img"), name_length)
+ || dom_local_name_compare_ex(node, "input", strlen("input"), name_length)
+ || dom_local_name_compare_ex(node, "link", strlen("link"), name_length)
+ || dom_local_name_compare_ex(node, "meta", strlen("meta"), name_length)
+ || dom_local_name_compare_ex(node, "source", strlen("source"), name_length)
+ || dom_local_name_compare_ex(node, "track", strlen("track"), name_length)
+ || dom_local_name_compare_ex(node, "wbr", strlen("wbr"), name_length)
+ /* These are the additional names from https://html.spec.whatwg.org/multipage/parsing.html#serializes-as-void */
+ || dom_local_name_compare_ex(node, "basefont", strlen("basefont"), name_length)
+ || dom_local_name_compare_ex(node, "bgsound", strlen("bgsound"), name_length)
+ || dom_local_name_compare_ex(node, "frame", strlen("frame"), name_length)
+ || dom_local_name_compare_ex(node, "keygen", strlen("keygen"), name_length)
+ || dom_local_name_compare_ex(node, "param", strlen("param"), name_length)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static zend_result dom_html5_serialize_element_end(dom_html5_serialize_context *ctx, const xmlNode *node)
+{
+ if (!dom_html5_serializes_as_void(node)) {
+ TRY(ctx->write_string_len(ctx->application_data, "", strlen("")));
+ TRY(dom_html5_serialize_element_tag_name(ctx, node));
+ return ctx->write_string_len(ctx->application_data, ">", strlen(">"));
+ }
+ return SUCCESS;
+}
+
+/* https://html.spec.whatwg.org/multipage/parsing.html#html-fragment-serialisation-algorithm */
+static zend_result dom_html5_serialize_node(dom_html5_serialize_context *ctx, const xmlNode *node, const xmlNode *bound)
+{
+ while (node != NULL) {
+ switch (node->type) {
+ case XML_DTD_NODE: {
+ TRY(dom_html5_serialize_doctype(ctx, (const xmlDtd *) node));
+ break;
+ }
+
+ case XML_CDATA_SECTION_NODE:
+ case XML_TEXT_NODE: {
+ TRY(dom_html5_serialize_text_node(ctx, node));
+ break;
+ }
+
+ case XML_PI_NODE: {
+ TRY(dom_html5_serialize_processing_instruction(ctx, node));
+ break;
+ }
+
+ case XML_COMMENT_NODE: {
+ TRY(dom_html5_serialize_comment(ctx, node));
+ break;
+ }
+
+ case XML_ELEMENT_NODE: {
+ TRY(dom_html5_serialize_element_start(ctx, node));
+ if (node->children) {
+ if (!dom_html5_serializes_as_void(node)) {
+ node = node->children;
+ continue;
+ }
+ } else {
+ /* Not descended, so wouldn't put the closing tag as it's normally only done when going back upwards. */
+ TRY(dom_html5_serialize_element_end(ctx, node));
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (node->next) {
+ node = node->next;
+ } else {
+ /* Go upwards, until we find a parent node with a next sibling, or until we hit the bound. */
+ do {
+ node = node->parent;
+ if (node == bound) {
+ return SUCCESS;
+ }
+ if (node->type == XML_ELEMENT_NODE) {
+ TRY(dom_html5_serialize_element_end(ctx, node));
+ }
+ } while (node->next == NULL);
+ node = node->next;
+ }
+ }
+
+ return SUCCESS;
+}
+
+/* https://html.spec.whatwg.org/multipage/parsing.html#serialising-html-fragments (Date 2023-10-18)
+ * Note: this serializes the _children_, excluding the node itself! */
+zend_result dom_html5_serialize(dom_html5_serialize_context *ctx, const xmlNode *node)
+{
+ /* Step 1. Note that this algorithm serializes children. Only elements, documents, and fragments can have children. */
+ if (node->type != XML_ELEMENT_NODE
+ && node->type != XML_DOCUMENT_FRAG_NODE
+ && node->type != XML_DOCUMENT_NODE
+ && node->type != XML_HTML_DOCUMENT_NODE) {
+ return SUCCESS;
+ }
+ if (node->type == XML_ELEMENT_NODE && dom_html5_serializes_as_void(node)) {
+ return SUCCESS;
+ }
+
+ /* Step 2 not needed because we're not using a string to store the serialized data */
+ /* Step 3 not needed because we don't support template contents yet */
+
+ /* Step 4 */
+ return dom_html5_serialize_node(ctx, node->children, node);
+}
+
+#endif /* HAVE_LIBXML && HAVE_DOM */
diff --git a/ext/dom/html5_serializer.h b/ext/dom/html5_serializer.h
new file mode 100644
index 000000000000..a7eb4ee9be0c
--- /dev/null
+++ b/ext/dom/html5_serializer.h
@@ -0,0 +1,31 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Niels Dossche |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef HTML5_SERIALIZER_H
+#define HTML5_SERIALIZER_H
+
+#include
+#include
+
+typedef struct {
+ zend_result (*write_string)(void *application_data, const char *buf);
+ zend_result (*write_string_len)(void *application_data, const char *buf, size_t len);
+ void *application_data;
+} dom_html5_serialize_context;
+
+zend_result dom_html5_serialize(dom_html5_serialize_context *ctx, const xmlNode *node);
+
+#endif
diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c
new file mode 100644
index 000000000000..8c5c8ab48bc9
--- /dev/null
+++ b/ext/dom/html_document.c
@@ -0,0 +1,1330 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Niels Dossche |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
+#include "php_dom.h"
+#include "html5_parser.h"
+#include "html5_serializer.h"
+#include "namespace_compat.h"
+#include
+#include
+#include
+
+/* Implementation defined, but as HTML5 defaults in all other cases to UTF-8, we'll do the same. */
+#define DOM_FALLBACK_ENCODING_NAME "UTF-8"
+#define DOM_FALLBACK_ENCODING_ID LXB_ENCODING_UTF_8
+
+typedef struct {
+ size_t last_line;
+ size_t last_column;
+ size_t last_offset;
+} dom_line_column_cache;
+
+typedef struct {
+ const char *input_name;
+ const lxb_codepoint_t *current_input_codepoints;
+ const char *current_input_characters;
+ size_t current_input_length;
+ size_t current_total_offset;
+ dom_line_column_cache cache_tokenizer;
+ bool html_no_implied;
+} dom_lexbor_libxml2_bridge_application_data;
+
+typedef struct {
+ const lxb_encoding_data_t *encoding_data;
+ size_t bom_shift;
+} dom_character_encoding_data;
+
+typedef zend_result (*dom_write_output)(void*, const char *, size_t);
+
+typedef struct {
+ const lxb_encoding_data_t *encoding_data;
+ const lxb_encoding_data_t *decoding_data;
+ lxb_encoding_encode_t *encode;
+ lxb_encoding_decode_t *decode;
+ lxb_codepoint_t *codepoints;
+ lxb_char_t *encoding_output;
+ void *output_data;
+ dom_write_output write_output;
+} dom_output_ctx;
+
+typedef struct {
+ /* We can skip some conversion if the input and output encoding are both UTF-8,
+ * we only have to validate and substitute replacement characters */
+ bool fast_path; /* Put first, near the encode & decode structures, for cache locality */
+ lxb_encoding_encode_t encode;
+ lxb_encoding_decode_t decode;
+ const lxb_encoding_data_t *encode_data;
+ const lxb_encoding_data_t *decode_data;
+ lxb_char_t encoding_output[4096];
+ lxb_codepoint_t codepoints[4096];
+} dom_decoding_encoding_ctx;
+
+static void dom_decoding_encoding_ctx_init(dom_decoding_encoding_ctx *ctx)
+{
+ ctx->encode_data = lxb_encoding_data(LXB_ENCODING_UTF_8);
+ ctx->decode_data = NULL;
+ /* Set fast path on by default so that the decoder finishing is skipped if this was never initialised properly. */
+ ctx->fast_path = true;
+ (void) lxb_encoding_encode_init(
+ &ctx->encode,
+ ctx->encode_data,
+ ctx->encoding_output,
+ sizeof(ctx->encoding_output) / sizeof(*ctx->encoding_output)
+ );
+ (void) lxb_encoding_encode_replace_set(&ctx->encode, LXB_ENCODING_REPLACEMENT_BYTES, LXB_ENCODING_REPLACEMENT_SIZE);
+}
+
+static const char *dom_lexbor_tokenizer_error_code_to_string(lxb_html_tokenizer_error_id_t id)
+{
+ switch (id) {
+ case LXB_HTML_TOKENIZER_ERROR_ABCLOFEMCO: return "abrupt-closing-of-empty-comment";
+ case LXB_HTML_TOKENIZER_ERROR_ABDOPUID: return "abrupt-doctype-public-identifier";
+ case LXB_HTML_TOKENIZER_ERROR_ABDOSYID: return "abrupt-doctype-system-identifier";
+ case LXB_HTML_TOKENIZER_ERROR_ABOFDIINNUCHRE: return "absence-of-digits-in-numeric-character-reference";
+ case LXB_HTML_TOKENIZER_ERROR_CDINHTCO: return "cdata-in-html-content";
+ case LXB_HTML_TOKENIZER_ERROR_CHREOUUNRA: return "character-reference-outside-unicode-range";
+ case LXB_HTML_TOKENIZER_ERROR_COCHININST: return "control-character-in-input-stream";
+ case LXB_HTML_TOKENIZER_ERROR_COCHRE: return "control-character-reference";
+ case LXB_HTML_TOKENIZER_ERROR_ENTAWIAT: return "end-tag-with-attributes";
+ case LXB_HTML_TOKENIZER_ERROR_DUAT: return "duplicate-attribute";
+ case LXB_HTML_TOKENIZER_ERROR_ENTAWITRSO: return "end-tag-with-trailing-solidus";
+ case LXB_HTML_TOKENIZER_ERROR_EOBETANA: return "eof-before-tag-name";
+ case LXB_HTML_TOKENIZER_ERROR_EOINCD: return "eof-in-cdata";
+ case LXB_HTML_TOKENIZER_ERROR_EOINCO: return "eof-in-comment";
+ case LXB_HTML_TOKENIZER_ERROR_EOINDO: return "eof-in-doctype";
+ case LXB_HTML_TOKENIZER_ERROR_EOINSCHTCOLITE: return "eof-in-script-html-comment-like-text";
+ case LXB_HTML_TOKENIZER_ERROR_EOINTA: return "eof-in-tag";
+ case LXB_HTML_TOKENIZER_ERROR_INCLCO: return "incorrectly-closed-comment";
+ case LXB_HTML_TOKENIZER_ERROR_INOPCO: return "incorrectly-opened-comment";
+ case LXB_HTML_TOKENIZER_ERROR_INCHSEAFDONA: return "invalid-character-sequence-after-doctype-name";
+ case LXB_HTML_TOKENIZER_ERROR_INFICHOFTANA: return "invalid-first-character-of-tag-name";
+ case LXB_HTML_TOKENIZER_ERROR_MIATVA: return "missing-attribute-value";
+ case LXB_HTML_TOKENIZER_ERROR_MIDONA: return "missing-doctype-name";
+ case LXB_HTML_TOKENIZER_ERROR_MIDOPUID: return "missing-doctype-public-identifier";
+ case LXB_HTML_TOKENIZER_ERROR_MIDOSYID: return "missing-doctype-system-identifier";
+ case LXB_HTML_TOKENIZER_ERROR_MIENTANA: return "missing-end-tag-name";
+ case LXB_HTML_TOKENIZER_ERROR_MIQUBEDOPUID: return "missing-quote-before-doctype-public-identifier";
+ case LXB_HTML_TOKENIZER_ERROR_MIQUBEDOSYID: return "missing-quote-before-doctype-system-identifier";
+ case LXB_HTML_TOKENIZER_ERROR_MISEAFCHRE: return "missing-semicolon-after-character-reference";
+ case LXB_HTML_TOKENIZER_ERROR_MIWHAFDOPUKE: return "missing-whitespace-after-doctype-public-keyword";
+ case LXB_HTML_TOKENIZER_ERROR_MIWHAFDOSYKE: return "missing-whitespace-after-doctype-system-keyword";
+ case LXB_HTML_TOKENIZER_ERROR_MIWHBEDONA: return "missing-whitespace-before-doctype-name";
+ case LXB_HTML_TOKENIZER_ERROR_MIWHBEAT: return "missing-whitespace-between-attributes";
+ case LXB_HTML_TOKENIZER_ERROR_MIWHBEDOPUANSYID: return "missing-whitespace-between-doctype-public-and-system-identifiers";
+ case LXB_HTML_TOKENIZER_ERROR_NECO: return "nested-comment";
+ case LXB_HTML_TOKENIZER_ERROR_NOCHRE: return "noncharacter-character-reference";
+ case LXB_HTML_TOKENIZER_ERROR_NOININST: return "noncharacter-in-input-stream";
+ case LXB_HTML_TOKENIZER_ERROR_NOVOHTELSTTAWITRSO: return "non-void-html-element-start-tag-with-trailing-solidus";
+ case LXB_HTML_TOKENIZER_ERROR_NUCHRE: return "null-character-reference";
+ case LXB_HTML_TOKENIZER_ERROR_SUCHRE: return "surrogate-character-reference";
+ case LXB_HTML_TOKENIZER_ERROR_SUININST: return "surrogate-in-input-stream";
+ case LXB_HTML_TOKENIZER_ERROR_UNCHAFDOSYID: return "unexpected-character-after-doctype-system-identifier";
+ case LXB_HTML_TOKENIZER_ERROR_UNCHINATNA: return "unexpected-character-in-attribute-name";
+ case LXB_HTML_TOKENIZER_ERROR_UNCHINUNATVA: return "unexpected-character-in-unquoted-attribute-value";
+ case LXB_HTML_TOKENIZER_ERROR_UNEQSIBEATNA: return "unexpected-equals-sign-before-attribute-name";
+ case LXB_HTML_TOKENIZER_ERROR_UNNUCH: return "unexpected-null-character";
+ case LXB_HTML_TOKENIZER_ERROR_UNQUMAINOFTANA: return "unexpected-question-mark-instead-of-tag-name";
+ case LXB_HTML_TOKENIZER_ERROR_UNSOINTA: return "unexpected-solidus-in-tag";
+ case LXB_HTML_TOKENIZER_ERROR_UNNACHRE: return "unknown-named-character-reference";
+ default: return "unknown error";
+ }
+}
+
+static const char *dom_lexbor_tree_error_code_to_string(lxb_html_tree_error_id_t id)
+{
+ switch (id) {
+ case LXB_HTML_RULES_ERROR_UNTO: return "unexpected-token";
+ case LXB_HTML_RULES_ERROR_UNCLTO: return "unexpected-closed-token";
+ case LXB_HTML_RULES_ERROR_NUCH: return "null-character";
+ case LXB_HTML_RULES_ERROR_UNCHTO: return "unexpected-character-token";
+ case LXB_HTML_RULES_ERROR_UNTOININMO: return "unexpected-token-in-initial-mode";
+ case LXB_HTML_RULES_ERROR_BADOTOININMO: return "bad-doctype-token-in-initial-mode";
+ case LXB_HTML_RULES_ERROR_DOTOINBEHTMO: return "doctype-token-in-before-html-mode";
+ case LXB_HTML_RULES_ERROR_UNCLTOINBEHTMO: return "unexpected-closed-token-in-before-html-mode";
+ case LXB_HTML_RULES_ERROR_DOTOINBEHEMO: return "doctype-token-in-before-head-mode";
+ case LXB_HTML_RULES_ERROR_UNCLTOINBEHEMO: return "unexpected-closed_token-in-before-head-mode";
+ case LXB_HTML_RULES_ERROR_DOTOINHEMO: return "doctype-token-in-head-mode";
+ case LXB_HTML_RULES_ERROR_NOVOHTELSTTAWITRSO: return "non-void-html-element-start-tag-with-trailing-solidus";
+ case LXB_HTML_RULES_ERROR_HETOINHEMO: return "head-token-in-head-mode";
+ case LXB_HTML_RULES_ERROR_UNCLTOINHEMO: return "unexpected-closed-token-in-head-mode";
+ case LXB_HTML_RULES_ERROR_TECLTOWIOPINHEMO: return "template-closed-token-without-opening-in-head-mode";
+ case LXB_HTML_RULES_ERROR_TEELISNOCUINHEMO: return "template-element-is-not-current-in-head-mode";
+ case LXB_HTML_RULES_ERROR_DOTOINHENOMO: return "doctype-token-in-head-noscript-mode";
+ case LXB_HTML_RULES_ERROR_DOTOAFHEMO: return "doctype-token-after-head-mode";
+ case LXB_HTML_RULES_ERROR_HETOAFHEMO: return "head-token-after-head-mode";
+ case LXB_HTML_RULES_ERROR_DOTOINBOMO: return "doctype-token-in-body-mode";
+ case LXB_HTML_RULES_ERROR_BAENOPELISWR: return "bad-ending-open-elements-is-wrong";
+ case LXB_HTML_RULES_ERROR_OPELISWR: return "open-elements-is-wrong";
+ case LXB_HTML_RULES_ERROR_UNELINOPELST: return "unexpected-element-in-open-elements-stack";
+ case LXB_HTML_RULES_ERROR_MIELINOPELST: return "missing-element-in-open-elements-stack";
+ case LXB_HTML_RULES_ERROR_NOBOELINSC: return "no-body-element-in-scope";
+ case LXB_HTML_RULES_ERROR_MIELINSC: return "missing-element-in-scope";
+ case LXB_HTML_RULES_ERROR_UNELINSC: return "unexpected-element-in-scope";
+ case LXB_HTML_RULES_ERROR_UNELINACFOST: return "unexpected-element-in-active-formatting-stack";
+ case LXB_HTML_RULES_ERROR_UNENOFFI: return "unexpected-end-of-file";
+ case LXB_HTML_RULES_ERROR_CHINTATE: return "characters-in-table-text";
+ case LXB_HTML_RULES_ERROR_DOTOINTAMO: return "doctype-token-in-table-mode";
+ case LXB_HTML_RULES_ERROR_DOTOINSEMO: return "doctype-token-in-select-mode";
+ case LXB_HTML_RULES_ERROR_DOTOAFBOMO: return "doctype-token-after-body-mode";
+ case LXB_HTML_RULES_ERROR_DOTOINFRMO: return "doctype-token-in-frameset-mode";
+ case LXB_HTML_RULES_ERROR_DOTOAFFRMO: return "doctype-token-after-frameset-mode";
+ case LXB_HTML_RULES_ERROR_DOTOFOCOMO: return "doctype-token-foreign-content-mode";
+ default: return "unknown error";
+ }
+}
+
+static const char *dom_lexbor_libxml2_bridge_status_code_to_string(lexbor_libxml2_bridge_status status)
+{
+ switch (status) {
+ case LEXBOR_LIBXML2_BRIDGE_STATUS_CANNOT_INIT: return "cannot initialize data structures";
+ case LEXBOR_LIBXML2_BRIDGE_STATUS_FATAL_PARSE: return "fatal error in parsing";
+ case LEXBOR_LIBXML2_BRIDGE_STATUS_OVERFLOW: return "string length overflow";
+ case LEXBOR_LIBXML2_BRIDGE_STATUS_OOM: return "out of memory";
+ default: return "unknown error";
+ }
+}
+
+static void dom_reset_line_column_cache(dom_line_column_cache *cache)
+{
+ cache->last_line = 1;
+ cache->last_column = 1;
+ cache->last_offset = 0;
+}
+
+static void dom_find_line_and_column_using_cache(
+ const dom_lexbor_libxml2_bridge_application_data *application_data,
+ dom_line_column_cache *cache,
+ size_t offset
+)
+{
+ offset -= application_data->current_total_offset;
+ if (offset > application_data->current_input_length) {
+ /* Possible with empty input, also just good for general safety */
+ offset = application_data->current_input_length;
+ }
+
+ /* Either unicode or UTF-8 data */
+ if (application_data->current_input_codepoints != NULL) {
+ while (cache->last_offset < offset) {
+ if (application_data->current_input_codepoints[cache->last_offset] == 0x000A /* Unicode codepoint for line feed */) {
+ cache->last_line++;
+ cache->last_column = 1;
+ } else {
+ cache->last_column++;
+ }
+ cache->last_offset++;
+ }
+ } else {
+ while (cache->last_offset < offset) {
+ const lxb_char_t current = application_data->current_input_characters[cache->last_offset];
+ if (current == '\n') {
+ cache->last_line++;
+ cache->last_column = 1;
+ cache->last_offset++;
+ } else {
+ /* See Lexbor tokenizer patch
+ * Note for future self: branchlessly computing the length and jumping by the length would be nice,
+ * however it takes so many instructions to do so that it is slower than this naive method. */
+ if ((current & 0b11000000) != 0b10000000) {
+ cache->last_column++;
+ }
+ cache->last_offset++;
+ }
+ }
+ }
+}
+
+static void dom_lexbor_libxml2_bridge_tokenizer_error_reporter(
+ void *application_data_voidptr,
+ lxb_html_tokenizer_error_t *error,
+ size_t offset
+)
+{
+ dom_lexbor_libxml2_bridge_application_data *application_data = application_data_voidptr;
+ dom_find_line_and_column_using_cache(application_data, &application_data->cache_tokenizer, offset);
+ php_libxml_pretend_ctx_error_ex(application_data->input_name, application_data->cache_tokenizer.last_line, application_data->cache_tokenizer.last_column, "tokenizer error %s in %s, line: %zu, column: %zu\n", dom_lexbor_tokenizer_error_code_to_string(error->id), application_data->input_name, application_data->cache_tokenizer.last_line, application_data->cache_tokenizer.last_column);
+}
+
+static void dom_lexbor_libxml2_bridge_tree_error_reporter(
+ void *application_data_voidptr,
+ lxb_html_tree_error_t *error,
+ size_t line,
+ size_t column,
+ size_t len
+)
+{
+ dom_lexbor_libxml2_bridge_application_data *application_data = application_data_voidptr;
+
+ if (line == 1 && application_data->html_no_implied && error->id == LXB_HTML_RULES_ERROR_UNTOININMO) {
+ /* For no implied mode, we want to mimick libxml's behaviour of not reporting an error for a lacking doctype. */
+ return;
+ }
+
+ if (UNEXPECTED(len <= 1)) {
+ /* Possible with EOF, or single-character tokens, don't use a range in the error display in this case */
+ php_libxml_pretend_ctx_error_ex(
+ application_data->input_name,
+ line,
+ column,
+ "tree error %s in %s, line: %zu, column: %zu\n",
+ dom_lexbor_tree_error_code_to_string(error->id),
+ application_data->input_name,
+ line,
+ column
+ );
+ } else {
+ php_libxml_pretend_ctx_error_ex(
+ application_data->input_name,
+ line,
+ column,
+ "tree error %s in %s, line: %zu, column: %zu-%zu\n",
+ dom_lexbor_tree_error_code_to_string(error->id),
+ application_data->input_name,
+ line,
+ column,
+ column + len - 1
+ );
+ }
+}
+
+static xmlNodePtr dom_search_child(xmlNodePtr parent, const char *searching_for)
+{
+ xmlNodePtr node = parent->children;
+ while (node != NULL) {
+ if (node->type == XML_ELEMENT_NODE && strcmp((const char *) node->name, searching_for) == 0) {
+ return node;
+ }
+ node = node->next;
+ }
+ return NULL;
+}
+
+static void dom_place_remove_element_and_hoist_children(xmlNodePtr parent, const char *searching_for)
+{
+ xmlNodePtr node = dom_search_child(parent, searching_for);
+ if (node != NULL) {
+ xmlUnlinkNode(node);
+
+ xmlNodePtr child = node->children;
+ while (child != NULL) {
+ xmlUnlinkNode(child);
+ xmlAddChild(parent, child);
+ child = node->children;
+ }
+
+ xmlFreeNode(node);
+ }
+}
+
+static void dom_post_process_html5_loading(
+ xmlDocPtr lxml_doc,
+ zend_long options,
+ const lexbor_libxml2_bridge_extracted_observations *observations
+)
+{
+ if (options & HTML_PARSE_NOIMPLIED) {
+ xmlNodePtr html_node = dom_search_child((xmlNodePtr) lxml_doc, "html");
+ if (!observations->has_explicit_head_tag) {
+ dom_place_remove_element_and_hoist_children(html_node, "head");
+ }
+ if (!observations->has_explicit_body_tag) {
+ dom_place_remove_element_and_hoist_children(html_node, "body");
+ }
+ if (!observations->has_explicit_html_tag) {
+ /* The HTML node has a single namespace declaration, that we must preserve after removing the node.
+ * However, it's possible the namespace is NULL if DOM\HTML_NO_DEFAULT_NS was set. */
+ if (!(options & DOM_HTML_NO_DEFAULT_NS)) {
+ php_libxml_set_old_ns(lxml_doc, html_node->nsDef);
+ html_node->nsDef = NULL;
+ }
+ dom_place_remove_element_and_hoist_children((xmlNodePtr) lxml_doc, "html");
+ if (!(options & DOM_HTML_NO_DEFAULT_NS) && EXPECTED(lxml_doc->children != NULL)) {
+ xmlNodePtr node = lxml_doc->children;
+ while (node) {
+ /* Fine to use the DOM wrap reconciliation here because it's the "modern" world of DOM,
+ * and no user manipulation happened yet. */
+ xmlDOMWrapCtxt dummy_ctxt = {0};
+ xmlDOMWrapReconcileNamespaces(&dummy_ctxt, node, /* options */ 0);
+ node = node->next;
+ }
+ }
+ }
+ }
+}
+
+/* https://html.spec.whatwg.org/multipage/parsing.html#determining-the-character-encoding */
+static dom_character_encoding_data dom_determine_encoding(const char *source, size_t source_len)
+{
+ dom_character_encoding_data result;
+
+ /* BOM sniffing */
+ if (source_len >= 3 && source[0] == '\xEF' && source[1] == '\xBB' && source[2] == '\xBF') {
+ result.encoding_data = lxb_encoding_data(LXB_ENCODING_UTF_8);
+ result.bom_shift = 3;
+ return result;
+ } else if (source_len >= 2) {
+ if (source[0] == '\xFE' && source[1] == '\xFF') {
+ result.encoding_data = lxb_encoding_data(LXB_ENCODING_UTF_16BE);
+ result.bom_shift = 2;
+ return result;
+ } else if (source[0] == '\xFF' && source[1] == '\xFE') {
+ result.encoding_data = lxb_encoding_data(LXB_ENCODING_UTF_16LE);
+ result.bom_shift = 2;
+ return result;
+ }
+ }
+
+ /* Perform prescan */
+ lxb_html_encoding_t encoding;
+ lxb_status_t status = lxb_html_encoding_init(&encoding);
+ if (status != LXB_STATUS_OK) {
+ goto fallback_uninit;
+ }
+ /* This is the "wait either for 1024 bytes or 500ms" part */
+ if (source_len > 1024) {
+ source_len = 1024;
+ }
+ status = lxb_html_encoding_determine(&encoding, (const lxb_char_t *) source, (const lxb_char_t *) source + source_len);
+ if (status != LXB_STATUS_OK) {
+ goto fallback;
+ }
+ lxb_html_encoding_entry_t *entry = lxb_html_encoding_meta_entry(&encoding, 0);
+ if (entry == NULL) {
+ goto fallback;
+ }
+ result.encoding_data = lxb_encoding_data_by_pre_name(entry->name, entry->end - entry->name);
+ if (!result.encoding_data) {
+ goto fallback;
+ }
+ result.bom_shift = 0;
+ lxb_html_encoding_destroy(&encoding, false);
+ return result;
+
+fallback:
+ lxb_html_encoding_destroy(&encoding, false);
+fallback_uninit:
+ result.encoding_data = lxb_encoding_data(DOM_FALLBACK_ENCODING_ID);
+ result.bom_shift = 0;
+ return result;
+}
+
+static void dom_setup_parser_encoding_manually(const lxb_char_t *buf_start, const lxb_encoding_data_t *encoding_data, dom_decoding_encoding_ctx *decoding_encoding_ctx, dom_lexbor_libxml2_bridge_application_data *application_data)
+{
+ static const lxb_codepoint_t replacement_codepoint = LXB_ENCODING_REPLACEMENT_CODEPOINT;
+
+ decoding_encoding_ctx->decode_data = encoding_data;
+
+ (void) lxb_encoding_decode_init(
+ &decoding_encoding_ctx->decode,
+ decoding_encoding_ctx->decode_data,
+ decoding_encoding_ctx->codepoints,
+ sizeof(decoding_encoding_ctx->codepoints) / sizeof(*decoding_encoding_ctx->codepoints)
+ );
+ (void) lxb_encoding_decode_replace_set(
+ &decoding_encoding_ctx->decode,
+ &replacement_codepoint,
+ LXB_ENCODING_REPLACEMENT_BUFFER_LEN
+ );
+ /* Note: encode_data is for UTF-8 */
+ decoding_encoding_ctx->fast_path = decoding_encoding_ctx->decode_data == decoding_encoding_ctx->encode_data;
+
+ if (decoding_encoding_ctx->fast_path) {
+ application_data->current_input_codepoints = NULL;
+ application_data->current_input_characters = (const char *) buf_start;
+ } else {
+ application_data->current_input_codepoints = decoding_encoding_ctx->codepoints;
+ application_data->current_input_characters = NULL;
+ }
+}
+
+static void dom_setup_parser_encoding_implicitly(
+ const lxb_char_t **buf_ref,
+ size_t *read,
+ dom_decoding_encoding_ctx *decoding_encoding_ctx,
+ dom_lexbor_libxml2_bridge_application_data *application_data
+)
+{
+ const char *buf_start = (const char *) *buf_ref;
+ dom_character_encoding_data dom_encoding_data = dom_determine_encoding(buf_start, *read);
+ *buf_ref += dom_encoding_data.bom_shift;
+ *read -= dom_encoding_data.bom_shift;
+ dom_setup_parser_encoding_manually((const lxb_char_t *) buf_start, dom_encoding_data.encoding_data, decoding_encoding_ctx, application_data);
+}
+
+static bool dom_process_parse_chunk(
+ lexbor_libxml2_bridge_parse_context *ctx,
+ lxb_html_document_t *document,
+ lxb_html_parser_t *parser,
+ size_t encoded_length,
+ const lxb_char_t *encoding_output,
+ size_t input_buffer_length,
+ size_t *tokenizer_error_offset,
+ size_t *tree_error_offset
+)
+{
+ dom_lexbor_libxml2_bridge_application_data *application_data = ctx->application_data;
+ application_data->current_input_length = input_buffer_length;
+ lexbor_status_t lexbor_status = lxb_html_document_parse_chunk(document, encoding_output, encoded_length);
+ if (UNEXPECTED(lexbor_status != LXB_STATUS_OK)) {
+ return false;
+ }
+ lexbor_libxml2_bridge_report_errors(ctx, parser, encoding_output, application_data->current_total_offset, tokenizer_error_offset, tree_error_offset);
+ dom_find_line_and_column_using_cache(application_data, &application_data->cache_tokenizer, application_data->current_total_offset + input_buffer_length);
+ application_data->current_total_offset += input_buffer_length;
+ application_data->cache_tokenizer.last_offset = 0;
+ return true;
+}
+
+static bool dom_decode_encode_fast_path(
+ lexbor_libxml2_bridge_parse_context *ctx,
+ lxb_html_document_t *document,
+ lxb_html_parser_t *parser,
+ const lxb_char_t **buf_ref_ref,
+ const lxb_char_t *buf_end,
+ dom_decoding_encoding_ctx *decoding_encoding_ctx,
+ size_t *tokenizer_error_offset,
+ size_t *tree_error_offset
+)
+{
+ const lxb_char_t *buf_ref = *buf_ref_ref;
+ const lxb_char_t *last_output = buf_ref;
+ while (buf_ref != buf_end) {
+ const lxb_char_t *buf_ref_backup = buf_ref;
+ lxb_codepoint_t codepoint = decoding_encoding_ctx->decode_data->decode_single(&decoding_encoding_ctx->decode, &buf_ref, buf_end);
+ if (UNEXPECTED(codepoint > LXB_ENCODING_MAX_CODEPOINT)) {
+ size_t skip = buf_ref - buf_ref_backup; /* Skip invalid data, it's replaced by the UTF-8 replacement bytes */
+ if (!dom_process_parse_chunk(
+ ctx,
+ document,
+ parser,
+ buf_ref - last_output - skip,
+ last_output,
+ buf_ref - last_output,
+ tokenizer_error_offset,
+ tree_error_offset
+ )) {
+ goto fail_oom;
+ }
+ if (!dom_process_parse_chunk(
+ ctx,
+ document,
+ parser,
+ LXB_ENCODING_REPLACEMENT_SIZE,
+ LXB_ENCODING_REPLACEMENT_BYTES,
+ 0,
+ tokenizer_error_offset,
+ tree_error_offset
+ )) {
+ goto fail_oom;
+ }
+ last_output = buf_ref;
+ }
+ }
+ if (buf_ref != last_output
+ && !dom_process_parse_chunk(
+ ctx,
+ document,
+ parser,
+ buf_ref - last_output,
+ last_output,
+ buf_ref - last_output,
+ tokenizer_error_offset,
+ tree_error_offset
+ )) {
+ goto fail_oom;
+ }
+ *buf_ref_ref = buf_ref;
+ return true;
+fail_oom:
+ *buf_ref_ref = buf_ref;
+ return false;
+}
+
+static bool dom_decode_encode_slow_path(
+ lexbor_libxml2_bridge_parse_context *ctx,
+ lxb_html_document_t *document,
+ lxb_html_parser_t *parser,
+ const lxb_char_t **buf_ref_ref,
+ const lxb_char_t *buf_end,
+ dom_decoding_encoding_ctx *decoding_encoding_ctx,
+ size_t *tokenizer_error_offset,
+ size_t *tree_error_offset
+)
+{
+ const lxb_char_t *buf_ref = *buf_ref_ref;
+ lexbor_status_t decode_status, encode_status;
+ do {
+ decode_status = decoding_encoding_ctx->decode_data->decode(&decoding_encoding_ctx->decode, &buf_ref, buf_end);
+
+ const lxb_codepoint_t *codepoints_ref = (const lxb_codepoint_t *) decoding_encoding_ctx->codepoints;
+ size_t decoding_buffer_used = lxb_encoding_decode_buf_used(&decoding_encoding_ctx->decode);
+ const lxb_codepoint_t *codepoints_end = decoding_encoding_ctx->codepoints + decoding_buffer_used;
+ do {
+ encode_status = decoding_encoding_ctx->encode_data->encode(&decoding_encoding_ctx->encode, &codepoints_ref, codepoints_end);
+ ZEND_ASSERT(encode_status != LXB_STATUS_ERROR && "parameters and replacements should be valid");
+ if (!dom_process_parse_chunk(
+ ctx,
+ document,
+ parser,
+ lxb_encoding_encode_buf_used(&decoding_encoding_ctx->encode),
+ decoding_encoding_ctx->encoding_output,
+ decoding_buffer_used,
+ tokenizer_error_offset,
+ tree_error_offset
+ )) {
+ goto fail_oom;
+ }
+ lxb_encoding_encode_buf_used_set(&decoding_encoding_ctx->encode, 0);
+ } while (encode_status == LXB_STATUS_SMALL_BUFFER);
+ lxb_encoding_decode_buf_used_set(&decoding_encoding_ctx->decode, 0);
+ } while (decode_status == LXB_STATUS_SMALL_BUFFER);
+ *buf_ref_ref = buf_ref;
+ return true;
+fail_oom:
+ *buf_ref_ref = buf_ref;
+ return false;
+}
+
+static bool dom_parse_decode_encode_step(
+ lexbor_libxml2_bridge_parse_context *ctx,
+ lxb_html_document_t *document,
+ lxb_html_parser_t *parser,
+ const lxb_char_t **buf_ref_ref,
+ const lxb_char_t *buf_end,
+ dom_decoding_encoding_ctx *decoding_encoding_ctx,
+ size_t *tokenizer_error_offset,
+ size_t *tree_error_offset
+)
+{
+ if (decoding_encoding_ctx->fast_path) {
+ return dom_decode_encode_fast_path(
+ ctx,
+ document,
+ parser,
+ buf_ref_ref,
+ buf_end,
+ decoding_encoding_ctx,
+ tokenizer_error_offset,
+ tree_error_offset
+ );
+ } else {
+ return dom_decode_encode_slow_path(
+ ctx,
+ document,
+ parser,
+ buf_ref_ref,
+ buf_end,
+ decoding_encoding_ctx,
+ tokenizer_error_offset,
+ tree_error_offset
+ );
+ }
+}
+
+static bool dom_parse_decode_encode_finish(
+ lexbor_libxml2_bridge_parse_context *ctx,
+ lxb_html_document_t *document,
+ lxb_html_parser_t *parser,
+ dom_decoding_encoding_ctx *decoding_encoding_ctx,
+ size_t *tokenizer_error_offset,
+ size_t *tree_error_offset
+)
+{
+ if (!decoding_encoding_ctx->fast_path) {
+ /* Fast path handles codepoints one by one, so this part is not applicable in that case */
+ (void) lxb_encoding_decode_finish(&decoding_encoding_ctx->decode);
+ size_t decoding_buffer_size = lxb_encoding_decode_buf_used(&decoding_encoding_ctx->decode);
+ if (decoding_buffer_size > 0) {
+ const lxb_codepoint_t *codepoints_ref = (const lxb_codepoint_t *) decoding_encoding_ctx->codepoints;
+ const lxb_codepoint_t *codepoints_end = codepoints_ref + decoding_buffer_size;
+ (void) decoding_encoding_ctx->encode_data->encode(&decoding_encoding_ctx->encode, &codepoints_ref, codepoints_end);
+ if (!dom_process_parse_chunk(
+ ctx,
+ document,
+ parser,
+ lxb_encoding_encode_buf_used(&decoding_encoding_ctx->encode),
+ decoding_encoding_ctx->encoding_output,
+ decoding_buffer_size,
+ tokenizer_error_offset,
+ tree_error_offset
+ )) {
+ return false;
+ }
+ }
+ }
+ (void) lxb_encoding_encode_finish(&decoding_encoding_ctx->encode);
+ if (lxb_encoding_encode_buf_used(&decoding_encoding_ctx->encode)
+ && !dom_process_parse_chunk(
+ ctx,
+ document,
+ parser,
+ lxb_encoding_encode_buf_used(&decoding_encoding_ctx->encode),
+ decoding_encoding_ctx->encoding_output,
+ lxb_encoding_decode_buf_used(&decoding_encoding_ctx->decode),
+ tokenizer_error_offset,
+ tree_error_offset
+ )) {
+ return false;
+ }
+ return true;
+}
+
+static bool check_options_validity(uint32_t arg_num, zend_long options)
+{
+ const zend_long VALID_OPTIONS = XML_PARSE_NOERROR | XML_PARSE_COMPACT | HTML_PARSE_NOIMPLIED | DOM_HTML_NO_DEFAULT_NS;
+ if ((options & ~VALID_OPTIONS) != 0) {
+ zend_argument_value_error(arg_num, "contains invalid flags (allowed flags: "
+ "LIBXML_NOERROR, "
+ "LIBXML_COMPACT, "
+ "LIBXML_HTML_NOIMPLIED, "
+ "DOM\\NO_DEFAULT_NS)");
+ return false;
+ }
+ return true;
+}
+
+PHP_METHOD(DOM_HTMLDocument, createEmpty)
+{
+ const char *encoding = "UTF-8";
+ size_t encoding_len = strlen("UTF-8");
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p", &encoding, &encoding_len) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ const lxb_encoding_data_t *encoding_data = lxb_encoding_data_by_name((const lxb_char_t *) encoding, encoding_len);
+
+ if (encoding_data == NULL) {
+ zend_argument_value_error(1, "must be a valid document encoding");
+ RETURN_THROWS();
+ }
+
+#ifdef LIBXML_HTML_ENABLED
+ xmlDocPtr lxml_doc = htmlNewDocNoDtD(NULL, NULL);
+ if (UNEXPECTED(lxml_doc == NULL)) {
+ goto oom;
+ }
+#else
+ xmlDocPtr lxml_doc = xmlNewDoc((const xmlChar *) "1.0");
+ if (UNEXPECTED(lxml_doc == NULL)) {
+ goto oom;
+ }
+ lxml_doc->type = XML_HTML_DOCUMENT_NODE;
+#endif
+
+ lxml_doc->encoding = xmlStrdup((const xmlChar *) encoding);
+
+ dom_object *intern = php_dom_instantiate_object_helper(
+ return_value,
+ dom_html_document_class_entry,
+ (xmlNodePtr) lxml_doc,
+ NULL
+ );
+ intern->document->is_modern_api_class = true;
+ return;
+
+oom:
+ php_dom_throw_error(INVALID_STATE_ERR, 1);
+ RETURN_THROWS();
+}
+
+PHP_METHOD(DOM_HTMLDocument, createFromString)
+{
+ const char *source, *override_encoding = NULL;
+ size_t source_len, override_encoding_len;
+ zend_long options = 0;
+ if (zend_parse_parameters(
+ ZEND_NUM_ARGS(),
+ "s|lp!",
+ &source,
+ &source_len,
+ &options,
+ &override_encoding,
+ &override_encoding_len
+ ) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ if (!check_options_validity(2, options)) {
+ RETURN_THROWS();
+ }
+
+ dom_lexbor_libxml2_bridge_application_data application_data;
+ application_data.input_name = "Entity";
+ application_data.current_total_offset = 0;
+ application_data.html_no_implied = options & HTML_PARSE_NOIMPLIED;
+ dom_reset_line_column_cache(&application_data.cache_tokenizer);
+ lexbor_libxml2_bridge_parse_context ctx;
+ lexbor_libxml2_bridge_parse_context_init(&ctx);
+ if (!(options & XML_PARSE_NOERROR)) {
+ lexbor_libxml2_bridge_parse_set_error_callbacks(
+ &ctx,
+ dom_lexbor_libxml2_bridge_tokenizer_error_reporter,
+ dom_lexbor_libxml2_bridge_tree_error_reporter
+ );
+ }
+ ctx.application_data = &application_data;
+
+ size_t tokenizer_error_offset = 0;
+ size_t tree_error_offset = 0;
+
+ /* Setup everything encoding & decoding related */
+ const lxb_char_t *buf_ref = (const lxb_char_t *) source;
+ dom_decoding_encoding_ctx decoding_encoding_ctx;
+ dom_decoding_encoding_ctx_init(&decoding_encoding_ctx);
+ if (override_encoding != NULL) {
+ const lxb_encoding_data_t *encoding_data = lxb_encoding_data_by_name(
+ (const lxb_char_t *) override_encoding,
+ override_encoding_len
+ );
+ if (!encoding_data) {
+ zend_argument_value_error(3, "must be a valid document encoding");
+ RETURN_THROWS();
+ }
+ dom_setup_parser_encoding_manually(buf_ref, encoding_data, &decoding_encoding_ctx, &application_data);
+ } else {
+ dom_setup_parser_encoding_implicitly(&buf_ref, &source_len, &decoding_encoding_ctx, &application_data);
+ }
+
+ lxb_html_document_t *document = lxb_html_document_create();
+ if (UNEXPECTED(document == NULL)) {
+ goto fail_oom;
+ }
+
+ lxb_status_t lexbor_status = lxb_html_document_parse_chunk_begin(document);
+ if (UNEXPECTED(lexbor_status != LXB_STATUS_OK)) {
+ goto fail_oom;
+ }
+
+ lxb_html_parser_t *parser = document->dom_document.parser;
+
+ while (source_len > 0) {
+ size_t chunk_size = source_len;
+ const size_t MAX_CHUNK_SIZE = sizeof(decoding_encoding_ctx.encoding_output) / sizeof(*decoding_encoding_ctx.encoding_output);
+ if (chunk_size > MAX_CHUNK_SIZE) {
+ chunk_size = MAX_CHUNK_SIZE;
+ }
+ source_len -= chunk_size;
+
+ const lxb_char_t *buf_end = buf_ref + chunk_size;
+ bool result = dom_parse_decode_encode_step(
+ &ctx,
+ document,
+ parser,
+ &buf_ref,
+ buf_end,
+ &decoding_encoding_ctx,
+ &tokenizer_error_offset,
+ &tree_error_offset
+ );
+ if (!result) {
+ goto fail_oom;
+ }
+ }
+
+ if (!dom_parse_decode_encode_finish(&ctx, document, parser, &decoding_encoding_ctx, &tokenizer_error_offset, &tree_error_offset)) {
+ goto fail_oom;
+ }
+
+ lexbor_status = lxb_html_document_parse_chunk_end(document);
+ if (lexbor_status != LXB_STATUS_OK) {
+ goto fail_oom;
+ }
+
+ xmlDocPtr lxml_doc;
+ lexbor_libxml2_bridge_status bridge_status = lexbor_libxml2_bridge_convert_document(
+ document,
+ &lxml_doc,
+ options & XML_PARSE_COMPACT,
+ !(options & DOM_HTML_NO_DEFAULT_NS)
+ );
+ lexbor_libxml2_bridge_copy_observations(parser->tree, &ctx.observations);
+ if (UNEXPECTED(bridge_status != LEXBOR_LIBXML2_BRIDGE_STATUS_OK)) {
+ php_libxml_ctx_error(
+ NULL,
+ "%s in %s",
+ dom_lexbor_libxml2_bridge_status_code_to_string(bridge_status),
+ application_data.input_name
+ );
+ lxb_html_document_destroy(document);
+ RETURN_FALSE;
+ }
+ lxb_html_document_destroy(document);
+
+ dom_post_process_html5_loading(lxml_doc, options, &ctx.observations);
+
+ if (decoding_encoding_ctx.decode_data) {
+ lxml_doc->encoding = xmlStrdup((const xmlChar *) decoding_encoding_ctx.decode_data->name);
+ } else {
+ lxml_doc->encoding = xmlStrdup((const xmlChar *) "UTF-8");
+ }
+
+ dom_object *intern = php_dom_instantiate_object_helper(
+ return_value,
+ dom_html_document_class_entry,
+ (xmlNodePtr) lxml_doc,
+ NULL
+ );
+ intern->document->is_modern_api_class = true;
+ return;
+
+fail_oom:
+ lxb_html_document_destroy(document);
+ php_dom_throw_error(INVALID_STATE_ERR, 1);
+ RETURN_THROWS();
+}
+
+PHP_METHOD(DOM_HTMLDocument, createFromFile)
+{
+ const char *filename, *override_encoding = NULL;
+ size_t filename_len, override_encoding_len;
+ zend_long options = 0;
+ php_stream *stream = NULL;
+ if (zend_parse_parameters(
+ ZEND_NUM_ARGS(),
+ "p|lp!",
+ &filename,
+ &filename_len,
+ &options,
+ &override_encoding,
+ &override_encoding_len
+ ) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ /* See php_libxml_streams_IO_open_wrapper(), apparently this caused issues in the past. */
+ if (strstr(filename, "%00")) {
+ zend_argument_value_error(1, "must not contain percent-encoded NUL bytes");
+ RETURN_THROWS();
+ }
+
+ if (!check_options_validity(2, options)) {
+ RETURN_THROWS();
+ }
+
+ dom_lexbor_libxml2_bridge_application_data application_data;
+ application_data.input_name = filename;
+ application_data.current_total_offset = 0;
+ application_data.html_no_implied = options & HTML_PARSE_NOIMPLIED;
+ dom_reset_line_column_cache(&application_data.cache_tokenizer);
+ lexbor_libxml2_bridge_parse_context ctx;
+ lexbor_libxml2_bridge_parse_context_init(&ctx);
+ if (!(options & XML_PARSE_NOERROR)) {
+ lexbor_libxml2_bridge_parse_set_error_callbacks(
+ &ctx,
+ dom_lexbor_libxml2_bridge_tokenizer_error_reporter,
+ dom_lexbor_libxml2_bridge_tree_error_reporter
+ );
+ }
+ ctx.application_data = &application_data;
+
+ char buf[4096];
+
+ /* Setup everything encoding & decoding related */
+ dom_decoding_encoding_ctx decoding_encoding_ctx;
+ dom_decoding_encoding_ctx_init(&decoding_encoding_ctx);
+ bool should_determine_encoding_implicitly = true; /* First read => determine encoding implicitly */
+ if (override_encoding != NULL) {
+ const lxb_encoding_data_t *encoding_data = lxb_encoding_data_by_name(
+ (const lxb_char_t *) override_encoding,
+ override_encoding_len
+ );
+ if (!encoding_data) {
+ zend_argument_value_error(3, "must be a valid document encoding");
+ RETURN_THROWS();
+ }
+ should_determine_encoding_implicitly = false;
+ dom_setup_parser_encoding_manually((const lxb_char_t *) buf, encoding_data, &decoding_encoding_ctx, &application_data);
+ }
+
+ stream = php_stream_open_wrapper_ex(filename, "rb", REPORT_ERRORS, /* opened_path */ NULL, php_libxml_get_stream_context());
+ if (!stream) {
+ if (!EG(exception)) {
+ zend_throw_exception_ex(NULL, 0, "Cannot open file '%s'", filename);
+ }
+ RETURN_THROWS();
+ }
+
+ /* MIME sniff */
+ if (should_determine_encoding_implicitly) {
+ zend_string *charset = php_libxml_sniff_charset_from_stream(stream);
+ if (charset != NULL) {
+ const lxb_encoding_data_t *encoding_data = lxb_encoding_data_by_name(
+ (const lxb_char_t *) ZSTR_VAL(charset),
+ ZSTR_LEN(charset)
+ );
+ if (encoding_data != NULL) {
+ should_determine_encoding_implicitly = false;
+ dom_setup_parser_encoding_manually(
+ (const lxb_char_t *) buf,
+ encoding_data,
+ &decoding_encoding_ctx,
+ &application_data
+ );
+ }
+ zend_string_release_ex(charset, false);
+ }
+ }
+
+ lxb_html_document_t *document = lxb_html_document_create();
+ if (UNEXPECTED(document == NULL)) {
+ goto fail_oom;
+ }
+
+ lxb_status_t lexbor_status = lxb_html_document_parse_chunk_begin(document);
+ if (UNEXPECTED(lexbor_status != LXB_STATUS_OK)) {
+ goto fail_oom;
+ }
+
+ size_t tokenizer_error_offset = 0;
+ size_t tree_error_offset = 0;
+ ssize_t read;
+ lxb_html_parser_t *parser = document->dom_document.parser;
+
+ while ((read = php_stream_read(stream, buf, sizeof(buf))) > 0) {
+ const lxb_char_t *buf_ref = (const lxb_char_t *) buf;
+
+ if (should_determine_encoding_implicitly) {
+ should_determine_encoding_implicitly = false;
+ dom_setup_parser_encoding_implicitly(&buf_ref, (size_t *) &read, &decoding_encoding_ctx, &application_data);
+ }
+
+ const lxb_char_t *buf_end = buf_ref + read;
+ bool result = dom_parse_decode_encode_step(
+ &ctx,
+ document,
+ parser,
+ &buf_ref,
+ buf_end,
+ &decoding_encoding_ctx,
+ &tokenizer_error_offset,
+ &tree_error_offset
+ );
+ if (!result) {
+ goto fail_oom;
+ }
+ }
+
+ if (!dom_parse_decode_encode_finish(&ctx, document, parser, &decoding_encoding_ctx, &tokenizer_error_offset, &tree_error_offset)) {
+ goto fail_oom;
+ }
+
+ lexbor_status = lxb_html_document_parse_chunk_end(document);
+ if (lexbor_status != LXB_STATUS_OK) {
+ goto fail_oom;
+ }
+
+ xmlDocPtr lxml_doc;
+ lexbor_libxml2_bridge_status bridge_status = lexbor_libxml2_bridge_convert_document(
+ document,
+ &lxml_doc,
+ options & XML_PARSE_COMPACT,
+ !(options & DOM_HTML_NO_DEFAULT_NS)
+ );
+ lexbor_libxml2_bridge_copy_observations(parser->tree, &ctx.observations);
+ if (UNEXPECTED(bridge_status != LEXBOR_LIBXML2_BRIDGE_STATUS_OK)) {
+ php_libxml_ctx_error(NULL, "%s in %s", dom_lexbor_libxml2_bridge_status_code_to_string(bridge_status), filename);
+ lxb_html_document_destroy(document);
+ php_stream_close(stream);
+ RETURN_FALSE;
+ }
+ lxb_html_document_destroy(document);
+
+ dom_post_process_html5_loading(lxml_doc, options, &ctx.observations);
+
+ if (decoding_encoding_ctx.decode_data) {
+ lxml_doc->encoding = xmlStrdup((const xmlChar *) decoding_encoding_ctx.decode_data->name);
+ } else {
+ lxml_doc->encoding = xmlStrdup((const xmlChar *) "UTF-8");
+ }
+
+ if (stream->wrapper == &php_plain_files_wrapper) {
+ xmlChar *converted = xmlPathToURI((const xmlChar *) filename);
+ if (UNEXPECTED(!converted)) {
+ goto fail_oom;
+ }
+ /* Check for "file:/" instead of "file://" because of libxml2 quirk */
+ if (strncmp((const char *) converted, "file:/", sizeof("file:/") - 1) != 0) {
+ xmlChar *buffer = xmlStrdup((const xmlChar *) "file://");
+ if (UNEXPECTED(!buffer)) {
+ xmlFree(converted);
+ goto fail_oom;
+ }
+ xmlChar *new_buffer = xmlStrcat(buffer, converted);
+ if (UNEXPECTED(!new_buffer)) {
+ xmlFree(buffer);
+ xmlFree(converted);
+ goto fail_oom;
+ }
+ xmlFree(converted);
+ lxml_doc->URL = new_buffer;
+ } else {
+ lxml_doc->URL = converted;
+ }
+ } else {
+ lxml_doc->URL = xmlStrdup((const xmlChar *) filename);
+ }
+
+ php_stream_close(stream);
+ stream = NULL;
+
+ dom_object *intern = php_dom_instantiate_object_helper(
+ return_value,
+ dom_html_document_class_entry,
+ (xmlNodePtr) lxml_doc,
+ NULL
+ );
+ intern->document->is_modern_api_class = true;
+ return;
+
+fail_oom:
+ php_dom_throw_error(INVALID_STATE_ERR, 1);
+ lxb_html_document_destroy(document);
+ if (stream) {
+ php_stream_close(stream);
+ }
+ RETURN_THROWS();
+}
+
+static zend_result dom_write_output_smart_str(void *ctx, const char *buf, size_t size)
+{
+ smart_str_appendl((smart_str *) ctx, buf, size);
+ return SUCCESS;
+}
+
+static zend_result dom_write_output_stream(void *application_data, const char *buf, size_t len)
+{
+ php_stream *stream = (php_stream *) application_data;
+ if (UNEXPECTED(php_stream_write(stream, buf, len) < 0)) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static zend_result dom_saveHTML_write_string_len(void *application_data, const char *buf, size_t len)
+{
+ dom_output_ctx *output = (dom_output_ctx *) application_data;
+ lxb_status_t decode_status, encode_status;
+ const lxb_char_t *buf_ref = (const lxb_char_t *) buf;
+ const lxb_char_t *buf_end = buf_ref + len;
+
+ do {
+ decode_status = output->decoding_data->decode(output->decode, &buf_ref, buf_end);
+
+ const lxb_codepoint_t *codepoints_ref = output->codepoints;
+ const lxb_codepoint_t *codepoints_end = codepoints_ref + lxb_encoding_decode_buf_used(output->decode);
+ do {
+ encode_status = output->encoding_data->encode(output->encode, &codepoints_ref, codepoints_end);
+ if (UNEXPECTED(output->write_output(
+ output->output_data,
+ (const char *) output->encoding_output,
+ lxb_encoding_encode_buf_used(output->encode)
+ ) != SUCCESS)) {
+ return FAILURE;
+ }
+ lxb_encoding_encode_buf_used_set(output->encode, 0);
+ } while (encode_status == LXB_STATUS_SMALL_BUFFER);
+ lxb_encoding_decode_buf_used_set(output->decode, 0);
+ } while (decode_status == LXB_STATUS_SMALL_BUFFER);
+
+ return SUCCESS;
+}
+
+static zend_result dom_saveHTML_write_string(void *application_data, const char *buf)
+{
+ return dom_saveHTML_write_string_len(application_data, buf, strlen(buf));
+}
+
+static zend_result dom_common_save(dom_output_ctx *output_ctx, const xmlDoc *docp, const xmlNode *node)
+{
+ /* Initialize everything related to encoding & decoding */
+ const lxb_encoding_data_t *decoding_data = lxb_encoding_data(LXB_ENCODING_UTF_8);
+ const lxb_encoding_data_t *encoding_data = lxb_encoding_data_by_name(
+ (const lxb_char_t *) docp->encoding,
+ strlen((const char *) docp->encoding)
+ );
+ lxb_encoding_encode_t encode;
+ lxb_encoding_decode_t decode;
+ lxb_char_t encoding_output[4096];
+ lxb_codepoint_t codepoints[4096];
+ (void) lxb_encoding_encode_init(&encode, encoding_data, encoding_output, sizeof(encoding_output) / sizeof(*encoding_output));
+ (void) lxb_encoding_decode_init(&decode, decoding_data, codepoints, sizeof(codepoints) / sizeof(*codepoints));
+ if (encoding_data->encoding == LXB_ENCODING_UTF_8) {
+ lxb_encoding_encode_replace_set(&encode, LXB_ENCODING_REPLACEMENT_BYTES, LXB_ENCODING_REPLACEMENT_SIZE);
+ } else {
+ /* Fallback if there is no replacement by default */
+ lxb_encoding_encode_replace_set(&encode, (const lxb_char_t *) "?", 1);
+ }
+ lxb_encoding_decode_replace_set(&decode, LXB_ENCODING_REPLACEMENT_BUFFER, LXB_ENCODING_REPLACEMENT_BUFFER_LEN);
+
+ output_ctx->encoding_data = encoding_data;
+ output_ctx->decoding_data = decoding_data;
+ output_ctx->encode = &encode;
+ output_ctx->decode = &decode;
+ output_ctx->codepoints = codepoints;
+ output_ctx->encoding_output = encoding_output;
+
+ dom_html5_serialize_context ctx;
+ ctx.write_string_len = dom_saveHTML_write_string_len;
+ ctx.write_string = dom_saveHTML_write_string;
+ ctx.application_data = output_ctx;
+ if (UNEXPECTED(dom_html5_serialize(&ctx, node) != SUCCESS)) {
+ return FAILURE;
+ }
+
+ (void) lxb_encoding_decode_finish(&decode);
+ if (lxb_encoding_decode_buf_used(&decode)) {
+ const lxb_codepoint_t *codepoints_ref = (const lxb_codepoint_t *) codepoints;
+ (void) encoding_data->encode(&encode, &codepoints_ref, codepoints_ref + lxb_encoding_decode_buf_used(&decode));
+ if (UNEXPECTED(output_ctx->write_output(
+ output_ctx->output_data,
+ (const char *) encoding_output,
+ lxb_encoding_encode_buf_used(&encode)) != SUCCESS
+ )) {
+ return FAILURE;
+ }
+ }
+ (void) lxb_encoding_encode_finish(&encode);
+ if (lxb_encoding_encode_buf_used(&encode)) {
+ if (UNEXPECTED(output_ctx->write_output(
+ output_ctx->output_data,
+ (const char *) encoding_output,
+ lxb_encoding_encode_buf_used(&encode)) != SUCCESS
+ )) {
+ return FAILURE;
+ }
+ }
+
+ return SUCCESS;
+}
+
+PHP_METHOD(DOM_HTMLDocument, saveHTMLFile)
+{
+ zval *id;
+ xmlDoc *docp;
+ size_t file_len;
+ dom_object *intern;
+ char *file;
+
+ id = ZEND_THIS;
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &file, &file_len) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ if (file_len == 0) {
+ zend_argument_value_error(1, "must not be empty");
+ RETURN_THROWS();
+ }
+
+ php_stream *stream = php_stream_open_wrapper_ex(file, "wb", REPORT_ERRORS, /* opened_path */ NULL, php_libxml_get_stream_context());
+ if (!stream) {
+ RETURN_FALSE;
+ }
+
+ DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
+
+ dom_output_ctx output_ctx;
+ output_ctx.output_data = stream;
+ output_ctx.write_output = dom_write_output_stream;
+ if (UNEXPECTED(dom_common_save(&output_ctx, docp, (const xmlNode *) docp) != SUCCESS)) {
+ php_stream_close(stream);
+ RETURN_FALSE;
+ }
+
+ zend_long bytes = php_stream_tell(stream);
+ php_stream_close(stream);
+
+ RETURN_LONG(bytes);
+}
+
+PHP_METHOD(DOM_HTMLDocument, saveHTML)
+{
+ zval *nodep = NULL;
+ const xmlDoc *docp;
+ const xmlNode *node;
+ dom_object *intern, *nodeobj;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &nodep, dom_node_class_entry) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
+
+ if (nodep != NULL) {
+ DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
+ if (node->doc != docp) {
+ php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
+ RETURN_FALSE;
+ }
+ } else {
+ node = (const xmlNode *) docp;
+ }
+
+ smart_str buf = {0};
+ dom_output_ctx output_ctx;
+ output_ctx.output_data = &buf;
+ output_ctx.write_output = dom_write_output_smart_str;
+ /* Can't fail because dom_write_output_smart_str() can't fail. */
+ zend_result result = dom_common_save(&output_ctx, docp, node);
+ ZEND_ASSERT(result == SUCCESS);
+
+ RETURN_STR(smart_str_extract(&buf));
+}
+
+PHP_METHOD(DOM_HTMLDocument, __construct)
+{
+ /* Private constructor cannot be called. */
+ ZEND_UNREACHABLE();
+}
+
+zend_result dom_html_document_encoding_write(dom_object *obj, zval *newval)
+{
+ xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
+ if (docp == NULL) {
+ php_dom_throw_error(INVALID_STATE_ERR, 1);
+ return FAILURE;
+ }
+
+ /* Typed property, can only be IS_STRING or IS_NULL. */
+ ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING || Z_TYPE_P(newval) == IS_NULL);
+
+ if (Z_TYPE_P(newval) == IS_NULL) {
+ goto invalid_encoding;
+ }
+
+ zend_string *str = Z_STR_P(newval);
+ const lxb_encoding_data_t *encoding_data = lxb_encoding_data_by_name((const lxb_char_t *) ZSTR_VAL(str), ZSTR_LEN(str));
+
+ if (encoding_data != NULL) {
+ xmlFree((xmlChar *) docp->encoding);
+ docp->encoding = xmlStrdup((const xmlChar *) encoding_data->name);
+ } else {
+ goto invalid_encoding;
+ }
+
+ return SUCCESS;
+
+invalid_encoding:
+ zend_value_error("Invalid document encoding");
+ return FAILURE;
+}
+
+#endif /* HAVE_LIBXML && HAVE_DOM */
diff --git a/ext/dom/namespace_compat.c b/ext/dom/namespace_compat.c
new file mode 100644
index 000000000000..efd51cd6545d
--- /dev/null
+++ b/ext/dom/namespace_compat.c
@@ -0,0 +1,54 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Niels Dossche |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
+#include "php_dom.h"
+#include "namespace_compat.h"
+
+bool dom_ns_is_also_an_attribute(const xmlNs *ns) {
+ return ns->_private != NULL;
+}
+
+void dom_ns_compat_mark_attribute(xmlNsPtr ns) {
+ ns->_private = (void *) 1;
+}
+
+void dom_ns_compat_mark_attribute_list(xmlNsPtr ns) {
+ while (ns != NULL) {
+ dom_ns_compat_mark_attribute(ns);
+ ns = ns->next;
+ }
+}
+
+void dom_ns_compat_copy_attribute_list_mark(xmlNsPtr copy, const xmlNs *original) {
+ /* It's possible that the original list is shorter than the copy list
+ * because of additional namespace copies from within a fragment. */
+ while (original != NULL) {
+ ZEND_ASSERT(copy != NULL);
+ if (dom_ns_is_also_an_attribute(original)) {
+ dom_ns_compat_mark_attribute(copy);
+ }
+ copy = copy->next;
+ original = original->next;
+ }
+}
+
+#endif /* HAVE_LIBXML && HAVE_DOM */
diff --git a/ext/dom/namespace_compat.h b/ext/dom/namespace_compat.h
new file mode 100644
index 000000000000..ab514a08bbad
--- /dev/null
+++ b/ext/dom/namespace_compat.h
@@ -0,0 +1,39 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Niels Dossche |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef NAMESPACE_COMPAT_H
+#define NAMESPACE_COMPAT_H
+
+#include
+
+/* https://infra.spec.whatwg.org/#namespaces */
+#define DOM_XHTML_NS_URI "http://www.w3.org/1999/xhtml"
+#define DOM_MATHML_NS_URI "http://www.w3.org/1998/Math/MathML"
+#define DOM_SVG_NS_URI "http://www.w3.org/2000/svg"
+#define DOM_XLINK_NS_URI "http://www.w3.org/1999/xlink"
+#define DOM_XML_NS_URI "http://www.w3.org/XML/1998/namespace"
+#define DOM_XMLNS_NS_URI "http://www.w3.org/2000/xmlns/"
+
+/* These functions make it possible to make a namespace declaration also visible as an attribute by
+ * setting a flag that can be checked with dom_ns_is_also_an_attribute().
+ * This is used in the serializer for example. */
+
+bool dom_ns_is_also_an_attribute(const xmlNs *ns);
+void dom_ns_compat_mark_attribute(xmlNsPtr ns);
+void dom_ns_compat_mark_attribute_list(xmlNsPtr ns);
+void dom_ns_compat_copy_attribute_list_mark(xmlNsPtr copy, const xmlNs *original);
+
+#endif
diff --git a/ext/dom/node.c b/ext/dom/node.c
index 5719bdde9999..d053fbceb065 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -1329,40 +1329,12 @@ PHP_METHOD(DOMNode, cloneNode)
DOM_GET_OBJ(n, id, xmlNodePtr, intern);
- node = xmlDocCopyNode(n, n->doc, recursive);
+ node = dom_clone_node(n, n->doc, intern, recursive);
if (!node) {
RETURN_FALSE;
}
- /* When deep is false Element nodes still require the attributes
- Following taken from libxml as xmlDocCopyNode doesn't do this */
- if (n->type == XML_ELEMENT_NODE && recursive == 0) {
- if (n->nsDef != NULL) {
- node->nsDef = xmlCopyNamespaceList(n->nsDef);
- }
- if (n->ns != NULL) {
- xmlNsPtr ns;
- ns = xmlSearchNs(n->doc, node, n->ns->prefix);
- if (ns == NULL) {
- ns = xmlSearchNs(n->doc, n, n->ns->prefix);
- if (ns != NULL) {
- xmlNodePtr root = node;
-
- while (root->parent != NULL) {
- root = root->parent;
- }
- node->ns = xmlNewNs(root, ns->href, ns->prefix);
- }
- } else {
- node->ns = ns;
- }
- }
- if (n->properties != NULL) {
- node->properties = xmlCopyPropList(node, n->properties);
- }
- }
-
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
* because we're increasing the refcount of the document proxy at the return.
diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c
index ce540ad4a3b0..41dbff36d14b 100644
--- a/ext/dom/php_dom.c
+++ b/ext/dom/php_dom.c
@@ -27,6 +27,8 @@
#include "php_dom_arginfo.h"
#include "dom_properties.h"
#include "zend_interfaces.h"
+#include "lexbor/lexbor/core/types.h"
+#include "lexbor/lexbor/core/lexbor.h"
#include "ext/standard/info.h"
#define PHP_XPATH 1
@@ -40,6 +42,8 @@ PHP_DOM_EXPORT zend_class_entry *dom_childnode_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_documentfragment_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_document_class_entry;
+PHP_DOM_EXPORT zend_class_entry *dom_html_document_class_entry;
+PHP_DOM_EXPORT zend_class_entry *dom_xml_document_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_nodelist_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_namednodemap_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_characterdata_class_entry;
@@ -53,6 +57,7 @@ PHP_DOM_EXPORT zend_class_entry *dom_notation_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_entity_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_entityreference_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_processinginstruction_class_entry;
+PHP_DOM_EXPORT zend_class_entry *dom_abstract_base_document_class_entry;
#ifdef LIBXML_XPATH_ENABLED
PHP_DOM_EXPORT zend_class_entry *dom_xpath_class_entry;
#endif
@@ -70,6 +75,8 @@ zend_object_handlers dom_xpath_object_handlers;
static HashTable classes;
/* {{{ prop handler tables */
static HashTable dom_document_prop_handlers;
+static HashTable dom_xml_document_prop_handlers;
+static HashTable dom_html_document_prop_handlers;
static HashTable dom_documentfragment_prop_handlers;
static HashTable dom_node_prop_handlers;
static HashTable dom_nodelist_prop_handlers;
@@ -206,6 +213,7 @@ static void dom_copy_doc_props(php_libxml_ref_obj *source_doc, php_libxml_ref_ob
zend_hash_copy(dest->classmap, source->classmap, NULL);
}
+ dest_doc->is_modern_api_class = source_doc->is_modern_api_class;
}
}
@@ -490,6 +498,12 @@ static void dom_update_refcount_after_clone(dom_object *original, xmlNodePtr ori
php_libxml_increment_node_ptr((php_libxml_node_object *)clone, cloned_node, (void *)clone);
if (original->document != clone->document) {
dom_copy_doc_props(original->document, clone->document);
+ /* Workaround libxml2 bug, see https://gitlab.gnome.org/GNOME/libxml2/-/commit/07920b4381873187c02df53fa9b5d44aff3a7041 */
+#if LIBXML_VERSION < 20911
+ if (original_node->type == XML_HTML_DOCUMENT_NODE) {
+ cloned_node->type = XML_HTML_DOCUMENT_NODE;
+ }
+#endif
}
}
@@ -586,6 +600,38 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject);
void dom_xpath_objects_free_storage(zend_object *object);
#endif
+static void *dom_malloc(size_t size) {
+ return emalloc(size);
+}
+
+static void *dom_realloc(void *dst, size_t size) {
+ return erealloc(dst, size);
+}
+
+static void *dom_calloc(size_t num, size_t size) {
+ return ecalloc(num, size);
+}
+
+static void dom_free(void *ptr) {
+ efree(ptr);
+}
+
+static void register_nondeprecated_xml_props(HashTable *table)
+{
+ dom_register_prop_handler(table, "encoding", sizeof("encoding")-1, dom_document_encoding_read, dom_document_encoding_write);
+ dom_register_prop_handler(table, "xmlEncoding", sizeof("xmlEncoding")-1, dom_document_encoding_read, NULL);
+ dom_register_prop_handler(table, "standalone", sizeof("standalone")-1, dom_document_standalone_read, dom_document_standalone_write);
+ dom_register_prop_handler(table, "xmlStandalone", sizeof("xmlStandalone")-1, dom_document_standalone_read, dom_document_standalone_write);
+ dom_register_prop_handler(table, "version", sizeof("version")-1, dom_document_version_read, dom_document_version_write);
+ dom_register_prop_handler(table, "xmlVersion", sizeof("xmlVersion")-1, dom_document_version_read, dom_document_version_write);
+ dom_register_prop_handler(table, "formatOutput", sizeof("formatOutput")-1, dom_document_format_output_read, dom_document_format_output_write);
+ dom_register_prop_handler(table, "validateOnParse", sizeof("validateOnParse")-1, dom_document_validate_on_parse_read, dom_document_validate_on_parse_write);
+ dom_register_prop_handler(table, "resolveExternals", sizeof("resolveExternals")-1, dom_document_resolve_externals_read, dom_document_resolve_externals_write);
+ dom_register_prop_handler(table, "preserveWhiteSpace", sizeof("preserveWhitespace")-1, dom_document_preserve_whitespace_read, dom_document_preserve_whitespace_write);
+ dom_register_prop_handler(table, "recover", sizeof("recover")-1, dom_document_recover_read, dom_document_recover_write);
+ dom_register_prop_handler(table, "substituteEntities", sizeof("substituteEntities")-1, dom_document_substitue_entities_read, dom_document_substitue_entities_write);
+}
+
/* {{{ PHP_MINIT_FUNCTION(dom) */
PHP_MINIT_FUNCTION(dom)
{
@@ -675,36 +721,49 @@ PHP_MINIT_FUNCTION(dom)
zend_hash_merge(&dom_documentfragment_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0);
zend_hash_add_ptr(&classes, dom_documentfragment_class_entry->name, &dom_documentfragment_prop_handlers);
- dom_document_class_entry = register_class_DOMDocument(dom_node_class_entry, dom_parentnode_class_entry);
+ dom_abstract_base_document_class_entry = register_class_DOM_Document(dom_node_class_entry, dom_parentnode_class_entry);
+ /* No need to set create_object as it's abstract. */
+ HashTable dom_abstract_base_document_prop_handlers;
+ zend_hash_init(&dom_abstract_base_document_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
+ dom_register_prop_handler(&dom_abstract_base_document_prop_handlers, "doctype", sizeof("doctype")-1, dom_document_doctype_read, NULL);
+ dom_register_prop_handler(&dom_abstract_base_document_prop_handlers, "documentElement", sizeof("documentElement")-1, dom_document_document_element_read, NULL);
+ dom_register_prop_handler(&dom_abstract_base_document_prop_handlers, "strictErrorChecking", sizeof("strictErrorChecking")-1, dom_document_strict_error_checking_read, dom_document_strict_error_checking_write);
+ dom_register_prop_handler(&dom_abstract_base_document_prop_handlers, "documentURI", sizeof("documentURI")-1, dom_document_document_uri_read, dom_document_document_uri_write);
+ dom_register_prop_handler(&dom_abstract_base_document_prop_handlers, "firstElementChild", sizeof("firstElementChild")-1, dom_parent_node_first_element_child_read, NULL);
+ dom_register_prop_handler(&dom_abstract_base_document_prop_handlers, "lastElementChild", sizeof("lastElementChild")-1, dom_parent_node_last_element_child_read, NULL);
+ dom_register_prop_handler(&dom_abstract_base_document_prop_handlers, "childElementCount", sizeof("childElementCount")-1, dom_parent_node_child_element_count, NULL);
+ zend_hash_merge(&dom_abstract_base_document_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0);
+ /* No need to register in &classes, because this is only used for merging. This is destroyed down below. */
+
+ dom_document_class_entry = register_class_DOMDocument(dom_abstract_base_document_class_entry);
dom_document_class_entry->create_object = dom_objects_new;
zend_hash_init(&dom_document_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
- dom_register_prop_handler(&dom_document_prop_handlers, "doctype", sizeof("doctype")-1, dom_document_doctype_read, NULL);
dom_register_prop_handler(&dom_document_prop_handlers, "implementation", sizeof("implementation")-1, dom_document_implementation_read, NULL);
- dom_register_prop_handler(&dom_document_prop_handlers, "documentElement", sizeof("documentElement")-1, dom_document_document_element_read, NULL);
dom_register_prop_handler(&dom_document_prop_handlers, "actualEncoding", sizeof("actualEncoding")-1, dom_document_encoding_read, NULL);
- dom_register_prop_handler(&dom_document_prop_handlers, "encoding", sizeof("encoding")-1, dom_document_encoding_read, dom_document_encoding_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "xmlEncoding", sizeof("xmlEncoding")-1, dom_document_encoding_read, NULL);
- dom_register_prop_handler(&dom_document_prop_handlers, "standalone", sizeof("standalone")-1, dom_document_standalone_read, dom_document_standalone_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "xmlStandalone", sizeof("xmlStandalone")-1, dom_document_standalone_read, dom_document_standalone_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "version", sizeof("version")-1, dom_document_version_read, dom_document_version_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "xmlVersion", sizeof("xmlVersion")-1, dom_document_version_read, dom_document_version_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "strictErrorChecking", sizeof("strictErrorChecking")-1, dom_document_strict_error_checking_read, dom_document_strict_error_checking_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "documentURI", sizeof("documentURI")-1, dom_document_document_uri_read, dom_document_document_uri_write);
dom_register_prop_handler(&dom_document_prop_handlers, "config", sizeof("config")-1, dom_document_config_read, NULL);
- dom_register_prop_handler(&dom_document_prop_handlers, "formatOutput", sizeof("formatOutput")-1, dom_document_format_output_read, dom_document_format_output_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "validateOnParse", sizeof("validateOnParse")-1, dom_document_validate_on_parse_read, dom_document_validate_on_parse_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "resolveExternals", sizeof("resolveExternals")-1, dom_document_resolve_externals_read, dom_document_resolve_externals_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "preserveWhiteSpace", sizeof("preserveWhitespace")-1, dom_document_preserve_whitespace_read, dom_document_preserve_whitespace_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "recover", sizeof("recover")-1, dom_document_recover_read, dom_document_recover_write);
- dom_register_prop_handler(&dom_document_prop_handlers, "substituteEntities", sizeof("substituteEntities")-1, dom_document_substitue_entities_read, dom_document_substitue_entities_write);
-
- dom_register_prop_handler(&dom_document_prop_handlers, "firstElementChild", sizeof("firstElementChild")-1, dom_parent_node_first_element_child_read, NULL);
- dom_register_prop_handler(&dom_document_prop_handlers, "lastElementChild", sizeof("lastElementChild")-1, dom_parent_node_last_element_child_read, NULL);
- dom_register_prop_handler(&dom_document_prop_handlers, "childElementCount", sizeof("childElementCount")-1, dom_parent_node_child_element_count, NULL);
-
- zend_hash_merge(&dom_document_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0);
+ register_nondeprecated_xml_props(&dom_document_prop_handlers);
+
+ zend_hash_merge(&dom_document_prop_handlers, &dom_abstract_base_document_prop_handlers, dom_copy_prop_handler, 0);
zend_hash_add_ptr(&classes, dom_document_class_entry->name, &dom_document_prop_handlers);
+ dom_html_document_class_entry = register_class_DOM_HTMLDocument(dom_abstract_base_document_class_entry);
+ dom_document_class_entry->create_object = dom_objects_new;
+ zend_hash_init(&dom_html_document_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
+ dom_register_prop_handler(&dom_html_document_prop_handlers, "encoding", sizeof("encoding")-1, dom_document_encoding_read, dom_html_document_encoding_write);
+
+ zend_hash_merge(&dom_html_document_prop_handlers, &dom_abstract_base_document_prop_handlers, dom_copy_prop_handler, 0);
+ zend_hash_add_ptr(&classes, dom_html_document_class_entry->name, &dom_html_document_prop_handlers);
+
+ dom_xml_document_class_entry = register_class_DOM_XMLDocument(dom_abstract_base_document_class_entry);
+ dom_xml_document_class_entry->create_object = dom_objects_new;
+ zend_hash_init(&dom_xml_document_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
+ register_nondeprecated_xml_props(&dom_xml_document_prop_handlers);
+
+ zend_hash_merge(&dom_xml_document_prop_handlers, &dom_abstract_base_document_prop_handlers, dom_copy_prop_handler, 0);
+ zend_hash_add_ptr(&classes, dom_xml_document_class_entry->name, &dom_xml_document_prop_handlers);
+
+ zend_hash_destroy(&dom_abstract_base_document_prop_handlers);
+
dom_nodelist_class_entry = register_class_DOMNodeList(zend_ce_aggregate, zend_ce_countable);
dom_nodelist_class_entry->create_object = dom_nnodemap_objects_new;
dom_nodelist_class_entry->default_object_handlers = &dom_nodelist_object_handlers;
@@ -845,6 +904,8 @@ PHP_MINIT_FUNCTION(dom)
php_libxml_register_export(dom_node_class_entry, php_dom_export_node);
+ lexbor_memory_setup(dom_malloc, dom_realloc, dom_calloc, dom_free);
+
return SUCCESS;
}
/* }}} */
@@ -876,6 +937,8 @@ PHP_MINFO_FUNCTION(dom)
PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */
{
zend_hash_destroy(&dom_document_prop_handlers);
+ zend_hash_destroy(&dom_html_document_prop_handlers);
+ zend_hash_destroy(&dom_xml_document_prop_handlers);
zend_hash_destroy(&dom_documentfragment_prop_handlers);
zend_hash_destroy(&dom_node_prop_handlers);
zend_hash_destroy(&dom_namespace_node_prop_handlers);
@@ -1172,9 +1235,21 @@ PHP_DOM_EXPORT bool php_dom_create_object(xmlNodePtr obj, zval *return_value, do
switch (obj->type) {
case XML_DOCUMENT_NODE:
+ {
+ if (domobj && domobj->document->is_modern_api_class) {
+ ce = dom_xml_document_class_entry;
+ } else {
+ ce = dom_document_class_entry;
+ }
+ break;
+ }
case XML_HTML_DOCUMENT_NODE:
{
- ce = dom_document_class_entry;
+ if (domobj && domobj->document->is_modern_api_class) {
+ ce = dom_html_document_class_entry;
+ } else {
+ ce = dom_document_class_entry;
+ }
break;
}
case XML_DTD_NODE:
@@ -1249,20 +1324,27 @@ PHP_DOM_EXPORT bool php_dom_create_object(xmlNodePtr obj, zval *return_value, do
if (domobj && domobj->document) {
ce = dom_get_doc_classmap(domobj->document, ce);
}
+ php_dom_instantiate_object_helper(return_value, ce, obj, domobj);
+ return 0;
+}
+/* }}} end php_domobject_new */
+
+dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_entry *ce, xmlNodePtr obj, dom_object *parent)
+{
object_init_ex(return_value, ce);
- intern = Z_DOMOBJ_P(return_value);
+ dom_object *intern = Z_DOMOBJ_P(return_value);
if (obj->doc != NULL) {
- if (domobj != NULL) {
- intern->document = domobj->document;
+ if (parent != NULL) {
+ intern->document = parent->document;
}
php_libxml_increment_doc_ref((php_libxml_node_object *)intern, obj->doc);
}
php_libxml_increment_node_ptr((php_libxml_node_object *)intern, obj, (void *)intern);
- return 0;
+
+ return intern;
}
-/* }}} end php_domobject_new */
void php_dom_create_implementation(zval *retval) {
object_init_ex(retval, dom_domimplementation_class_entry);
@@ -1819,4 +1901,23 @@ static int dom_nodemap_has_dimension(zend_object *object, zval *member, int chec
return offset >= 0 && offset < php_dom_get_namednodemap_length(php_dom_obj_from_obj(object));
} /* }}} end dom_nodemap_has_dimension */
+xmlNodePtr dom_clone_node(xmlNodePtr node, xmlDocPtr doc, const dom_object *intern, bool recursive)
+{
+ /* See http://www.xmlsoft.org/html/libxml-tree.html#xmlDocCopyNode for meaning of values */
+ int extended_recursive = recursive;
+ if (!recursive && node->type == XML_ELEMENT_NODE) {
+ extended_recursive = 2;
+ }
+ xmlNodePtr copy = xmlDocCopyNode(node, doc, extended_recursive);
+ if (UNEXPECTED(!copy)) {
+ return NULL;
+ }
+
+ if (intern->document && intern->document->is_modern_api_class) {
+ dom_mark_namespaces_for_copy_based_on_copy(copy, node);
+ }
+
+ return copy;
+}
+
#endif /* HAVE_DOM */
diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h
index b77036e83c29..dc28d2106110 100644
--- a/ext/dom/php_dom.h
+++ b/ext/dom/php_dom.h
@@ -114,6 +114,8 @@ static inline dom_object_namespace_node *php_dom_namespace_node_obj_from_obj(zen
#include "domexception.h"
+#define DOM_HTML_NO_DEFAULT_NS (1U << 31)
+
dom_object *dom_object_get_data(xmlNodePtr obj);
dom_doc_propsptr dom_get_doc_props(php_libxml_ref_obj *document);
libxml_doc_props const* dom_get_doc_props_read_only(const php_libxml_ref_obj *document);
@@ -154,6 +156,17 @@ bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, x
xmlNsPtr dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree, const char *uri);
void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp);
+void php_dom_document_constructor(INTERNAL_FUNCTION_PARAMETERS);
+
+dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_entry *ce, xmlNodePtr obj, dom_object *parent);
+
+typedef enum {
+ DOM_LOAD_STRING = 0,
+ DOM_LOAD_FILE = 1,
+} dom_load_mode;
+
+xmlDocPtr dom_document_parser(zval *id, dom_load_mode mode, const char *source, size_t source_len, size_t options, xmlCharEncodingHandlerPtr encoding);
+
/* parentnode */
void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc);
void dom_parent_node_append(dom_object *context, zval *nodes, uint32_t nodesc);
@@ -174,6 +187,9 @@ void php_dom_nodelist_get_item_into_zval(dom_nnodemap_object *objmap, zend_long
int php_dom_get_namednodemap_length(dom_object *obj);
int php_dom_get_nodelist_length(dom_object *obj);
+xmlNodePtr dom_clone_node(xmlNodePtr node, xmlDocPtr doc, const dom_object *intern, bool recursive);
+void dom_mark_namespaces_for_copy_based_on_copy(xmlNodePtr copy, const xmlNode *original);
+
#define DOM_GET_INTERN(__id, __intern) { \
__intern = Z_DOMOBJ_P(__id); \
if (UNEXPECTED(__intern->ptr == NULL)) { \
diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php
index cf4fda78a4be..cc355612474c 100644
--- a/ext/dom/php_dom.stub.php
+++ b/ext/dom/php_dom.stub.php
@@ -2,1011 +2,1224 @@
/** @generate-class-entries */
-/**
- * @var int
- * @cvalue XML_ELEMENT_NODE
- */
-const XML_ELEMENT_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_NODE
- */
-const XML_ATTRIBUTE_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_TEXT_NODE
- */
-const XML_TEXT_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_CDATA_SECTION_NODE
- */
-const XML_CDATA_SECTION_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ENTITY_REF_NODE
- */
-const XML_ENTITY_REF_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ENTITY_NODE
- */
-const XML_ENTITY_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_PI_NODE
- */
-const XML_PI_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_COMMENT_NODE
- */
-const XML_COMMENT_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_DOCUMENT_NODE
- */
-const XML_DOCUMENT_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_DOCUMENT_TYPE_NODE
- */
-const XML_DOCUMENT_TYPE_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_DOCUMENT_FRAG_NODE
- */
-const XML_DOCUMENT_FRAG_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_NOTATION_NODE
- */
-const XML_NOTATION_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_HTML_DOCUMENT_NODE
- */
-const XML_HTML_DOCUMENT_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_DTD_NODE
- */
-const XML_DTD_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ELEMENT_DECL
- */
-const XML_ELEMENT_DECL_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_DECL
- */
-const XML_ATTRIBUTE_DECL_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ENTITY_DECL
- */
-const XML_ENTITY_DECL_NODE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_NAMESPACE_DECL
- */
-const XML_NAMESPACE_DECL_NODE = UNKNOWN;
+namespace
+{
+ /**
+ * @var int
+ * @cvalue XML_ELEMENT_NODE
+ */
+ const XML_ELEMENT_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_NODE
+ */
+ const XML_ATTRIBUTE_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_TEXT_NODE
+ */
+ const XML_TEXT_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_CDATA_SECTION_NODE
+ */
+ const XML_CDATA_SECTION_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ENTITY_REF_NODE
+ */
+ const XML_ENTITY_REF_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ENTITY_NODE
+ */
+ const XML_ENTITY_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_PI_NODE
+ */
+ const XML_PI_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_COMMENT_NODE
+ */
+ const XML_COMMENT_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_DOCUMENT_NODE
+ */
+ const XML_DOCUMENT_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_DOCUMENT_TYPE_NODE
+ */
+ const XML_DOCUMENT_TYPE_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_DOCUMENT_FRAG_NODE
+ */
+ const XML_DOCUMENT_FRAG_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_NOTATION_NODE
+ */
+ const XML_NOTATION_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_HTML_DOCUMENT_NODE
+ */
+ const XML_HTML_DOCUMENT_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_DTD_NODE
+ */
+ const XML_DTD_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ELEMENT_DECL
+ */
+ const XML_ELEMENT_DECL_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_DECL
+ */
+ const XML_ATTRIBUTE_DECL_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ENTITY_DECL
+ */
+ const XML_ENTITY_DECL_NODE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_NAMESPACE_DECL
+ */
+ const XML_NAMESPACE_DECL_NODE = UNKNOWN;
#ifdef XML_GLOBAL_NAMESPACE
-/**
- * @var int
- * @cvalue XML_GLOBAL_NAMESPACE
- */
-const XML_GLOBAL_NAMESPACE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_GLOBAL_NAMESPACE
+ */
+ const XML_GLOBAL_NAMESPACE = UNKNOWN;
#endif
-/**
- * @var int
- * @cvalue XML_LOCAL_NAMESPACE
- */
-const XML_LOCAL_NAMESPACE = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_CDATA
- */
-const XML_ATTRIBUTE_CDATA = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_ID
- */
-const XML_ATTRIBUTE_ID = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_IDREF
- */
-const XML_ATTRIBUTE_IDREF = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_IDREFS
- */
-const XML_ATTRIBUTE_IDREFS = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_ENTITIES
- */
-const XML_ATTRIBUTE_ENTITY = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_NMTOKEN
- */
-const XML_ATTRIBUTE_NMTOKEN = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_NMTOKENS
- */
-const XML_ATTRIBUTE_NMTOKENS = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_ENUMERATION
- */
-const XML_ATTRIBUTE_ENUMERATION = UNKNOWN;
-/**
- * @var int
- * @cvalue XML_ATTRIBUTE_NOTATION
- */
-const XML_ATTRIBUTE_NOTATION = UNKNOWN;
-
-/**
- * @var int
- * @cvalue PHP_ERR
- */
-const DOM_PHP_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue INDEX_SIZE_ERR
- */
-const DOM_INDEX_SIZE_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue DOMSTRING_SIZE_ERR
- */
-const DOMSTRING_SIZE_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue HIERARCHY_REQUEST_ERR
- */
-const DOM_HIERARCHY_REQUEST_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue WRONG_DOCUMENT_ERR
- */
-const DOM_WRONG_DOCUMENT_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue INVALID_CHARACTER_ERR
- */
-const DOM_INVALID_CHARACTER_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue NO_DATA_ALLOWED_ERR
- */
-const DOM_NO_DATA_ALLOWED_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue NO_MODIFICATION_ALLOWED_ERR
- */
-const DOM_NO_MODIFICATION_ALLOWED_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue NOT_FOUND_ERR
- */
-const DOM_NOT_FOUND_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue NOT_SUPPORTED_ERR
- */
-const DOM_NOT_SUPPORTED_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue INUSE_ATTRIBUTE_ERR
- */
-const DOM_INUSE_ATTRIBUTE_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue INVALID_STATE_ERR
- */
-const DOM_INVALID_STATE_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue SYNTAX_ERR
- */
-const DOM_SYNTAX_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue INVALID_MODIFICATION_ERR
- */
-const DOM_INVALID_MODIFICATION_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue NAMESPACE_ERR
- */
-const DOM_NAMESPACE_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue INVALID_ACCESS_ERR
- */
-const DOM_INVALID_ACCESS_ERR = UNKNOWN;
-/**
- * @var int
- * @cvalue VALIDATION_ERR
- */
-const DOM_VALIDATION_ERR = UNKNOWN;
-
-class DOMDocumentType extends DOMNode
-{
- /** @readonly */
- public string $name;
+ /**
+ * @var int
+ * @cvalue XML_LOCAL_NAMESPACE
+ */
+ const XML_LOCAL_NAMESPACE = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_CDATA
+ */
+ const XML_ATTRIBUTE_CDATA = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_ID
+ */
+ const XML_ATTRIBUTE_ID = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_IDREF
+ */
+ const XML_ATTRIBUTE_IDREF = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_IDREFS
+ */
+ const XML_ATTRIBUTE_IDREFS = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_ENTITIES
+ */
+ const XML_ATTRIBUTE_ENTITY = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_NMTOKEN
+ */
+ const XML_ATTRIBUTE_NMTOKEN = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_NMTOKENS
+ */
+ const XML_ATTRIBUTE_NMTOKENS = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_ENUMERATION
+ */
+ const XML_ATTRIBUTE_ENUMERATION = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue XML_ATTRIBUTE_NOTATION
+ */
+ const XML_ATTRIBUTE_NOTATION = UNKNOWN;
- /** @readonly */
- public DOMNamedNodeMap $entities;
+ /**
+ * @var int
+ * @cvalue PHP_ERR
+ */
+ const DOM_PHP_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INDEX_SIZE_ERR
+ */
+ const DOM_INDEX_SIZE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue DOMSTRING_SIZE_ERR
+ */
+ const DOMSTRING_SIZE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue HIERARCHY_REQUEST_ERR
+ */
+ const DOM_HIERARCHY_REQUEST_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue WRONG_DOCUMENT_ERR
+ */
+ const DOM_WRONG_DOCUMENT_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INVALID_CHARACTER_ERR
+ */
+ const DOM_INVALID_CHARACTER_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NO_DATA_ALLOWED_ERR
+ */
+ const DOM_NO_DATA_ALLOWED_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NO_MODIFICATION_ALLOWED_ERR
+ */
+ const DOM_NO_MODIFICATION_ALLOWED_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NOT_FOUND_ERR
+ */
+ const DOM_NOT_FOUND_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NOT_SUPPORTED_ERR
+ */
+ const DOM_NOT_SUPPORTED_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INUSE_ATTRIBUTE_ERR
+ */
+ const DOM_INUSE_ATTRIBUTE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INVALID_STATE_ERR
+ */
+ const DOM_INVALID_STATE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue SYNTAX_ERR
+ */
+ const DOM_SYNTAX_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INVALID_MODIFICATION_ERR
+ */
+ const DOM_INVALID_MODIFICATION_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NAMESPACE_ERR
+ */
+ const DOM_NAMESPACE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INVALID_ACCESS_ERR
+ */
+ const DOM_INVALID_ACCESS_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue VALIDATION_ERR
+ */
+ const DOM_VALIDATION_ERR = UNKNOWN;
- /** @readonly */
- public DOMNamedNodeMap $notations;
+ /** @alias DOM\DocumentType */
+ class DOMDocumentType extends DOMNode
+ {
+ /** @readonly */
+ public string $name;
- /** @readonly */
- public string $publicId;
+ /** @readonly */
+ public DOMNamedNodeMap $entities;
- /** @readonly */
- public string $systemId;
+ /** @readonly */
+ public DOMNamedNodeMap $notations;
- /** @readonly */
- public ?string $internalSubset;
-}
+ /** @readonly */
+ public string $publicId;
-class DOMCdataSection extends DOMText
-{
- public function __construct(string $data) {}
-}
+ /** @readonly */
+ public string $systemId;
-class DOMComment extends DOMCharacterData
-{
- public function __construct(string $data = "") {}
-}
+ /** @readonly */
+ public ?string $internalSubset;
+ }
-interface DOMParentNode
-{
- /** @param DOMNode|string $nodes */
- public function append(...$nodes): void;
+ /** @alias DOM\CDATASection */
+ class DOMCdataSection extends DOMText
+ {
+ public function __construct(string $data) {}
+ }
- /** @param DOMNode|string $nodes */
- public function prepend(...$nodes): void;
+ /** @alias DOM\Comment */
+ class DOMComment extends DOMCharacterData
+ {
+ public function __construct(string $data = "") {}
+ }
- /** @param DOMNode|string $nodes */
- public function replaceChildren(...$nodes): void;
-}
+ /** @alias DOM\ParentNode */
+ interface DOMParentNode
+ {
+ /** @param DOMNode|string $nodes */
+ public function append(...$nodes): void;
-interface DOMChildNode
-{
- public function remove(): void;
+ /** @param DOMNode|string $nodes */
+ public function prepend(...$nodes): void;
- /** @param DOMNode|string $nodes */
- public function before(... $nodes): void;
+ /** @param DOMNode|string $nodes */
+ public function replaceChildren(...$nodes): void;
+ }
- /** @param DOMNode|string $nodes */
- public function after(...$nodes): void;
+ /** @alias DOM\ChildNode */
+ interface DOMChildNode
+ {
+ public function remove(): void;
- /** @param DOMNode|string $nodes */
- public function replaceWith(...$nodes): void;
-}
+ /** @param DOMNode|string $nodes */
+ public function before(... $nodes): void;
-class DOMNode
-{
- public const int DOCUMENT_POSITION_DISCONNECTED = 0x01;
- public const int DOCUMENT_POSITION_PRECEDING = 0x02;
- public const int DOCUMENT_POSITION_FOLLOWING = 0x04;
- public const int DOCUMENT_POSITION_CONTAINS = 0x08;
- public const int DOCUMENT_POSITION_CONTAINED_BY = 0x10;
- public const int DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
+ /** @param DOMNode|string $nodes */
+ public function after(...$nodes): void;
- /** @readonly */
- public string $nodeName;
+ /** @param DOMNode|string $nodes */
+ public function replaceWith(...$nodes): void;
+ }
- public ?string $nodeValue;
+ /** @alias DOM\Node */
+ class DOMNode
+ {
+ public const int DOCUMENT_POSITION_DISCONNECTED = 0x01;
+ public const int DOCUMENT_POSITION_PRECEDING = 0x02;
+ public const int DOCUMENT_POSITION_FOLLOWING = 0x04;
+ public const int DOCUMENT_POSITION_CONTAINS = 0x08;
+ public const int DOCUMENT_POSITION_CONTAINED_BY = 0x10;
+ public const int DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
- /** @readonly */
- public int $nodeType;
+ /** @readonly */
+ public string $nodeName;
- /** @readonly */
- public ?DOMNode $parentNode;
+ public ?string $nodeValue;
- /** @readonly */
- public ?DOMElement $parentElement;
+ /** @readonly */
+ public int $nodeType;
- /** @readonly */
- public DOMNodeList $childNodes;
+ /** @readonly */
+ public ?DOMNode $parentNode;
- /** @readonly */
- public ?DOMNode $firstChild;
+ /** @readonly */
+ public ?DOMElement $parentElement;
- /** @readonly */
- public ?DOMNode $lastChild;
+ /** @readonly */
+ public DOMNodeList $childNodes;
- /** @readonly */
- public ?DOMNode $previousSibling;
+ /** @readonly */
+ public ?DOMNode $firstChild;
- /** @readonly */
- public ?DOMNode $nextSibling;
+ /** @readonly */
+ public ?DOMNode $lastChild;
- /** @readonly */
- public ?DOMNamedNodeMap $attributes;
+ /** @readonly */
+ public ?DOMNode $previousSibling;
- /** @readonly */
- public bool $isConnected;
+ /** @readonly */
+ public ?DOMNode $nextSibling;
- /** @readonly */
- public ?DOMDocument $ownerDocument;
+ /** @readonly */
+ public ?DOMNamedNodeMap $attributes;
- /** @readonly */
- public ?string $namespaceURI;
+ /** @readonly */
+ public bool $isConnected;
- public string $prefix;
+ /** @readonly */
+ public ?DOM\Document $ownerDocument;
- /** @readonly */
- public ?string $localName;
+ /** @readonly */
+ public ?string $namespaceURI;
- /** @readonly */
- public ?string $baseURI;
+ public string $prefix;
- public string $textContent;
+ /** @readonly */
+ public ?string $localName;
- public function __sleep(): array {}
+ /** @readonly */
+ public ?string $baseURI;
- public function __wakeup(): void {}
+ public string $textContent;
- /** @return DOMNode|false */
- public function appendChild(DOMNode $node) {}
+ /** @return DOMNode|false */
+ public function appendChild(DOMNode $node) {}
- /** @tentative-return-type */
- public function C14N(bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): string|false {}
+ /** @tentative-return-type */
+ public function C14N(bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): string|false {}
- /** @tentative-return-type */
- public function C14NFile(string $uri, bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): int|false {}
+ /** @tentative-return-type */
+ public function C14NFile(string $uri, bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): int|false {}
- /** @return DOMNode|false */
- public function cloneNode(bool $deep = false) {}
+ /** @return DOMNode|false */
+ public function cloneNode(bool $deep = false) {}
- /** @tentative-return-type */
- public function getLineNo(): int {}
+ /** @tentative-return-type */
+ public function getLineNo(): int {}
- /** @tentative-return-type */
- public function getNodePath(): ?string {}
+ /** @tentative-return-type */
+ public function getNodePath(): ?string {}
- /** @tentative-return-type */
- public function hasAttributes(): bool {}
+ /** @tentative-return-type */
+ public function hasAttributes(): bool {}
- /** @tentative-return-type */
- public function hasChildNodes(): bool {}
+ /** @tentative-return-type */
+ public function hasChildNodes(): bool {}
- /** @return DOMNode|false */
- public function insertBefore(DOMNode $node, ?DOMNode $child = null) {}
+ /** @return DOMNode|false */
+ public function insertBefore(DOMNode $node, ?DOMNode $child = null) {}
- /** @tentative-return-type */
- public function isDefaultNamespace(string $namespace): bool {}
+ /** @tentative-return-type */
+ public function isDefaultNamespace(string $namespace): bool {}
- /** @tentative-return-type */
- public function isSameNode(DOMNode $otherNode): bool {}
+ /** @tentative-return-type */
+ public function isSameNode(DOMNode $otherNode): bool {}
- public function isEqualNode(?DOMNode $otherNode): bool {}
+ public function isEqualNode(?DOMNode $otherNode): bool {}
- /** @tentative-return-type */
- public function isSupported(string $feature, string $version): bool {}
+ /** @tentative-return-type */
+ public function isSupported(string $feature, string $version): bool {}
- /** @tentative-return-type */
- public function lookupNamespaceURI(?string $prefix): ?string {}
+ /** @tentative-return-type */
+ public function lookupNamespaceURI(?string $prefix): ?string {}
- /** @tentative-return-type */
- public function lookupPrefix(string $namespace): ?string {}
+ /** @tentative-return-type */
+ public function lookupPrefix(string $namespace): ?string {}
- /** @tentative-return-type */
- public function normalize(): void {}
+ /** @tentative-return-type */
+ public function normalize(): void {}
- /** @return DOMNode|false */
- public function removeChild(DOMNode $child) {}
+ /** @return DOMNode|false */
+ public function removeChild(DOMNode $child) {}
- /** @return DOMNode|false */
- public function replaceChild(DOMNode $node, DOMNode $child) {}
+ /** @return DOMNode|false */
+ public function replaceChild(DOMNode $node, DOMNode $child) {}
- public function contains(DOMNode|DOMNameSpaceNode|null $other): bool {}
+ public function contains(DOMNode|DOMNameSpaceNode|null $other): bool {}
- public function getRootNode(?array $options = null): DOMNode {}
+ public function getRootNode(?array $options = null): DOMNode {}
- public function compareDocumentPosition(DOMNode $other): int {}
-}
+ public function compareDocumentPosition(DOMNode $other): int {}
-class DOMNameSpaceNode
-{
- /** @readonly */
- public string $nodeName;
+ public function __sleep(): array {}
+
+ public function __wakeup(): void {}
+ }
- /** @readonly */
- public ?string $nodeValue;
+ /** @alias DOM\NameSpaceNode */
+ class DOMNameSpaceNode
+ {
+ /** @readonly */
+ public string $nodeName;
- /** @readonly */
- public int $nodeType;
+ /** @readonly */
+ public ?string $nodeValue;
- /** @readonly */
- public string $prefix;
+ /** @readonly */
+ public int $nodeType;
- /** @readonly */
- public ?string $localName;
+ /** @readonly */
+ public string $prefix;
- /** @readonly */
- public ?string $namespaceURI;
+ /** @readonly */
+ public ?string $localName;
- /** @readonly */
- public bool $isConnected;
+ /** @readonly */
+ public ?string $namespaceURI;
- /** @readonly */
- public ?DOMDocument $ownerDocument;
+ /** @readonly */
+ public bool $isConnected;
- /** @readonly */
- public ?DOMNode $parentNode;
+ /** @readonly */
+ public ?DOMDocument $ownerDocument;
- /** @readonly */
- public ?DOMElement $parentElement;
+ /** @readonly */
+ public ?DOMNode $parentNode;
- /** @implementation-alias DOMNode::__sleep */
- public function __sleep(): array {}
+ /** @readonly */
+ public ?DOMElement $parentElement;
- /** @implementation-alias DOMNode::__wakeup */
- public function __wakeup(): void {}
-}
+ /** @implementation-alias DOMNode::__sleep */
+ public function __sleep(): array {}
+
+ /** @implementation-alias DOMNode::__wakeup */
+ public function __wakeup(): void {}
+ }
-class DOMImplementation
-{
- /** @tentative-return-type */
- public function getFeature(string $feature, string $version): never {}
+ class DOMImplementation
+ {
+ /** @tentative-return-type */
+ public function getFeature(string $feature, string $version): never {}
- /** @tentative-return-type */
- public function hasFeature(string $feature, string $version): bool {}
+ /** @tentative-return-type */
+ public function hasFeature(string $feature, string $version): bool {}
- /** @return DOMDocumentType|false */
- public function createDocumentType(string $qualifiedName, string $publicId = "", string $systemId = "") {}
+ /** @return DOMDocumentType|false */
+ public function createDocumentType(string $qualifiedName, string $publicId = "", string $systemId = "") {}
- /** @return DOMDocument|false */
- public function createDocument(?string $namespace = null, string $qualifiedName = "", ?DOMDocumentType $doctype = null) {}
-}
+ /** @return DOMDocument|false */
+ public function createDocument(?string $namespace = null, string $qualifiedName = "", ?DOMDocumentType $doctype = null) {}
+ }
-class DOMDocumentFragment extends DOMNode implements DOMParentNode
-{
- /** @readonly */
- public ?DOMElement $firstElementChild;
+ /** @alias DOM\DocumentFragment */
+ class DOMDocumentFragment extends DOMNode implements DOMParentNode
+ {
+ /** @readonly */
+ public ?DOMElement $firstElementChild;
- /** @readonly */
- public ?DOMElement $lastElementChild;
+ /** @readonly */
+ public ?DOMElement $lastElementChild;
- /** @readonly */
- public int $childElementCount;
+ /** @readonly */
+ public int $childElementCount;
- public function __construct() {}
+ public function __construct() {}
- /** @tentative-return-type */
- public function appendXML(string $data): bool {}
+ /** @tentative-return-type */
+ public function appendXML(string $data): bool {}
- /**
- * @param DOMNode|string $nodes
- * @implementation-alias DOMElement::append
- */
- public function append(...$nodes): void {}
+ /**
+ * @param DOMNode|string $nodes
+ * @implementation-alias DOMElement::append
+ */
+ public function append(...$nodes): void {}
- /**
- * @param DOMNode|string $nodes
- * @implementation-alias DOMElement::prepend
- */
- public function prepend(...$nodes): void {}
+ /**
+ * @param DOMNode|string $nodes
+ * @implementation-alias DOMElement::prepend
+ */
+ public function prepend(...$nodes): void {}
- /**
- * @param DOMNode|string $nodes
- * @implementation-alias DOMDocument::replaceChildren
- */
- public function replaceChildren(...$nodes): void {}
-}
+ /**
+ * @param DOMNode|string $nodes
+ * @implementation-alias DOM\Document::replaceChildren
+ */
+ public function replaceChildren(...$nodes): void {}
+ }
-class DOMNodeList implements IteratorAggregate, Countable
-{
- /** @readonly */
- public int $length;
+ /** @alias DOM\NodeList */
+ class DOMNodeList implements IteratorAggregate, Countable
+ {
+ /** @readonly */
+ public int $length;
- /** @tentative-return-type */
- public function count(): int {}
+ /** @tentative-return-type */
+ public function count(): int {}
- public function getIterator(): Iterator {}
+ public function getIterator(): Iterator {}
- /** @return DOMElement|DOMNode|DOMNameSpaceNode|null */
- public function item(int $index) {}
-}
+ /** @return DOMElement|DOMNode|DOMNameSpaceNode|null */
+ public function item(int $index) {}
+ }
-class DOMCharacterData extends DOMNode implements DOMChildNode
-{
- public string $data;
+ /** @alias DOM\CharacterData */
+ class DOMCharacterData extends DOMNode implements DOMChildNode
+ {
+ public string $data;
- /** @readonly */
- public int $length;
+ /** @readonly */
+ public int $length;
- /** @readonly */
- public ?DOMElement $previousElementSibling;
+ /** @readonly */
+ public ?DOMElement $previousElementSibling;
- /** @readonly */
- public ?DOMElement $nextElementSibling;
+ /** @readonly */
+ public ?DOMElement $nextElementSibling;
- /** @tentative-return-type */
- public function appendData(string $data): true {}
+ /** @tentative-return-type */
+ public function appendData(string $data): true {}
- /** @return string|false */
- public function substringData(int $offset, int $count) {}
+ /** @return string|false */
+ public function substringData(int $offset, int $count) {}
- /** @tentative-return-type */
- public function insertData(int $offset, string $data): bool {}
+ /** @tentative-return-type */
+ public function insertData(int $offset, string $data): bool {}
- /** @tentative-return-type */
- public function deleteData(int $offset, int $count): bool {}
+ /** @tentative-return-type */
+ public function deleteData(int $offset, int $count): bool {}
- /** @tentative-return-type */
- public function replaceData(int $offset, int $count, string $data): bool {}
+ /** @tentative-return-type */
+ public function replaceData(int $offset, int $count, string $data): bool {}
- /**
- * @param DOMNode|string $nodes
- * @implementation-alias DOMElement::replaceWith
- */
- public function replaceWith(...$nodes): void {}
+ /**
+ * @param DOMNode|string $nodes
+ * @implementation-alias DOMElement::replaceWith
+ */
+ public function replaceWith(...$nodes): void {}
- /** @implementation-alias DOMElement::remove */
- public function remove(): void {}
+ /** @implementation-alias DOMElement::remove */
+ public function remove(): void {}
- /**
- * @param DOMNode|string $nodes
- * @implementation-alias DOMElement::before
- */
- public function before(... $nodes): void {}
+ /**
+ * @param DOMNode|string $nodes
+ * @implementation-alias DOMElement::before
+ */
+ public function before(... $nodes): void {}
- /**
- * @param DOMNode|string $nodes
- * @implementation-alias DOMElement::after
- */
- public function after(...$nodes): void {}
-}
+ /**
+ * @param DOMNode|string $nodes
+ * @implementation-alias DOMElement::after
+ */
+ public function after(...$nodes): void {}
+ }
-class DOMAttr extends DOMNode
-{
- /** @readonly */
- public string $name;
+ /** @alias DOM\Attr */
+ class DOMAttr extends DOMNode
+ {
+ /** @readonly */
+ public string $name;
- /** @readonly */
- public bool $specified = true;
+ /** @readonly */
+ public bool $specified = true;
- public string $value;
+ public string $value;
- /** @readonly */
- public ?DOMElement $ownerElement;
+ /** @readonly */
+ public ?DOMElement $ownerElement;
- /** @readonly */
- public mixed $schemaTypeInfo = null;
+ /** @readonly */
+ public mixed $schemaTypeInfo = null;
- public function __construct(string $name, string $value = "") {}
+ public function __construct(string $name, string $value = "") {}
- /** @tentative-return-type */
- public function isId(): bool {}
-}
+ /** @tentative-return-type */
+ public function isId(): bool {}
+ }
-class DOMElement extends DOMNode implements DOMParentNode, DOMChildNode
-{
- /** @readonly */
- public string $tagName;
+ /** @alias DOM\Element */
+ class DOMElement extends DOMNode implements DOMParentNode, DOMChildNode
+ {
+ /** @readonly */
+ public string $tagName;
- public string $className;
+ public string $className;
- public string $id;
+ public string $id;
- /** @readonly */
- public mixed $schemaTypeInfo = null;
+ /** @readonly */
+ public mixed $schemaTypeInfo = null;
- /** @readonly */
- public ?DOMElement $firstElementChild;
+ /** @readonly */
+ public ?DOMElement $firstElementChild;
- /** @readonly */
- public ?DOMElement $lastElementChild;
+ /** @readonly */
+ public ?DOMElement $lastElementChild;
- /** @readonly */
- public int $childElementCount;
+ /** @readonly */
+ public int $childElementCount;
- /** @readonly */
- public ?DOMElement $previousElementSibling;
+ /** @readonly */
+ public ?DOMElement $previousElementSibling;
- /** @readonly */
- public ?DOMElement $nextElementSibling;
+ /** @readonly */
+ public ?DOMElement $nextElementSibling;
- public function __construct(string $qualifiedName, ?string $value = null, string $namespace = "") {}
+ public function __construct(string $qualifiedName, ?string $value = null, string $namespace = "") {}
- /** @tentative-return-type */
- public function getAttribute(string $qualifiedName): string {}
+ /** @tentative-return-type */
+ public function getAttribute(string $qualifiedName): string {}
- public function getAttributeNames(): array {}
+ public function getAttributeNames(): array {}
- /** @tentative-return-type */
- public function getAttributeNS(?string $namespace, string $localName): string {}
+ /** @tentative-return-type */
+ public function getAttributeNS(?string $namespace, string $localName): string {}
- /** @return DOMAttr|DOMNameSpaceNode|false */
- public function getAttributeNode(string $qualifiedName) {}
+ /** @return DOMAttr|DOMNameSpaceNode|false */
+ public function getAttributeNode(string $qualifiedName) {}
- /** @return DOMAttr|DOMNameSpaceNode|null */
- public function getAttributeNodeNS(?string $namespace, string $localName) {}
+ /** @return DOMAttr|DOMNameSpaceNode|null */
+ public function getAttributeNodeNS(?string $namespace, string $localName) {}
- /** @tentative-return-type */
- public function getElementsByTagName(string $qualifiedName): DOMNodeList {}
+ /** @tentative-return-type */
+ public function getElementsByTagName(string $qualifiedName): DOMNodeList {}
- /** @tentative-return-type */
- public function getElementsByTagNameNS(?string $namespace, string $localName): DOMNodeList {}
+ /** @tentative-return-type */
+ public function getElementsByTagNameNS(?string $namespace, string $localName): DOMNodeList {}
- /** @tentative-return-type */
- public function hasAttribute(string $qualifiedName): bool {}
+ /** @tentative-return-type */
+ public function hasAttribute(string $qualifiedName): bool {}
- /** @tentative-return-type */
- public function hasAttributeNS(?string $namespace, string $localName): bool {}
+ /** @tentative-return-type */
+ public function hasAttributeNS(?string $namespace, string $localName): bool {}
- /** @tentative-return-type */
- public function removeAttribute(string $qualifiedName): bool {}
+ /** @tentative-return-type */
+ public function removeAttribute(string $qualifiedName): bool {}
- /** @tentative-return-type */
- public function removeAttributeNS(?string $namespace, string $localName): void {}
+ /** @tentative-return-type */
+ public function removeAttributeNS(?string $namespace, string $localName): void {}
- /** @return DOMAttr|false */
- public function removeAttributeNode(DOMAttr $attr) {}
+ /** @return DOMAttr|false */
+ public function removeAttributeNode(DOMAttr $attr) {}
- /** @return DOMAttr|bool */
- public function setAttribute(string $qualifiedName, string $value) {} // TODO return type shouldn't depend on the call scope
+ /** @return DOMAttr|bool */
+ public function setAttribute(string $qualifiedName, string $value) {} // TODO return type shouldn't depend on the call scope
- /** @tentative-return-type */
- public function setAttributeNS(?string $namespace, string $qualifiedName, string $value): void {}
+ /** @tentative-return-type */
+ public function setAttributeNS(?string $namespace, string $qualifiedName, string $value): void {}
- /** @return DOMAttr|null|false */
- public function setAttributeNode(DOMAttr $attr) {}
+ /** @return DOMAttr|null|false */
+ public function setAttributeNode(DOMAttr $attr) {}
- /** @return DOMAttr|null|false */
- public function setAttributeNodeNS(DOMAttr $attr) {}
+ /** @return DOMAttr|null|false */
+ public function setAttributeNodeNS(DOMAttr $attr) {}
- /** @tentative-return-type */
- public function setIdAttribute(string $qualifiedName, bool $isId): void {}
+ /** @tentative-return-type */
+ public function setIdAttribute(string $qualifiedName, bool $isId): void {}
- /** @tentative-return-type */
- public function setIdAttributeNS(string $namespace, string $qualifiedName, bool $isId): void {}
+ /** @tentative-return-type */
+ public function setIdAttributeNS(string $namespace, string $qualifiedName, bool $isId): void {}
- /** @tentative-return-type */
- public function setIdAttributeNode(DOMAttr $attr, bool $isId): void {}
+ /** @tentative-return-type */
+ public function setIdAttributeNode(DOMAttr $attr, bool $isId): void {}
- public function toggleAttribute(string $qualifiedName, ?bool $force = null): bool {}
+ public function toggleAttribute(string $qualifiedName, ?bool $force = null): bool {}
- public function remove(): void {}
+ public function remove(): void {}
- /** @param DOMNode|string $nodes */
- public function before(... $nodes): void {}
+ /** @param DOMNode|string $nodes */
+ public function before(... $nodes): void {}
- /** @param DOMNode|string $nodes */
- public function after(...$nodes): void {}
+ /** @param DOMNode|string $nodes */
+ public function after(...$nodes): void {}
- /** @param DOMNode|string $nodes */
- public function replaceWith(...$nodes): void {}
+ /** @param DOMNode|string $nodes */
+ public function replaceWith(...$nodes): void {}
- /** @param DOMNode|string $nodes */
- public function append(...$nodes): void {}
+ /** @param DOMNode|string $nodes */
+ public function append(...$nodes): void {}
- /** @param DOMNode|string $nodes */
- public function prepend(...$nodes): void {}
+ /** @param DOMNode|string $nodes */
+ public function prepend(...$nodes): void {}
- /** @param DOMNode|string $nodes */
- public function replaceChildren(...$nodes): void {}
+ /** @param DOMNode|string $nodes */
+ public function replaceChildren(...$nodes): void {}
- public function insertAdjacentElement(string $where, DOMElement $element): ?DOMElement {}
+ public function insertAdjacentElement(string $where, DOMElement $element): ?DOMElement {}
- public function insertAdjacentText(string $where, string $data): void {}
-}
+ public function insertAdjacentText(string $where, string $data): void {}
+ }
-class DOMDocument extends DOMNode implements DOMParentNode
-{
- /** @readonly */
- public ?DOMDocumentType $doctype;
+ class DOMDocument extends DOM\Document
+ {
+ /** @readonly */
+ public DOMImplementation $implementation;
- /** @readonly */
- public DOMImplementation $implementation;
+ /**
+ * @readonly
+ * @deprecated
+ */
+ public ?string $actualEncoding;
- /** @readonly */
- public ?DOMElement $documentElement;
+ /** @readonly */
+ public ?string $xmlEncoding;
- /**
- * @readonly
- * @deprecated
- */
- public ?string $actualEncoding;
+ public bool $standalone;
- public ?string $encoding;
+ public bool $xmlStandalone;
- /** @readonly */
- public ?string $xmlEncoding;
+ public ?string $version;
- public bool $standalone;
+ public ?string $xmlVersion;
- public bool $xmlStandalone;
+ /**
+ * @readonly
+ * @deprecated
+ */
+ public mixed $config;
- public ?string $version;
+ public bool $formatOutput;
- public ?string $xmlVersion;
+ public bool $validateOnParse;
- public bool $strictErrorChecking;
+ public bool $resolveExternals;
- public ?string $documentURI;
+ public bool $preserveWhiteSpace;
- /**
- * @readonly
- * @deprecated
- */
- public mixed $config;
+ public bool $recover;
- public bool $formatOutput;
+ public bool $substituteEntities;
- public bool $validateOnParse;
+ public function __construct(string $version = "1.0", string $encoding = "") {}
- public bool $resolveExternals;
+ /** @return DOMEntityReference|false */
+ public function createEntityReference(string $name) {}
- public bool $preserveWhiteSpace;
+ /** @tentative-return-type */
+ public function load(string $filename, int $options = 0): bool {}
- public bool $recover;
+ /** @tentative-return-type */
+ public function loadXML(string $source, int $options = 0): bool {}
- public bool $substituteEntities;
+ /** @tentative-return-type */
+ public function save(string $filename, int $options = 0): int|false {}
- /** @readonly */
- public ?DOMElement $firstElementChild;
+#ifdef LIBXML_HTML_ENABLED
+ /** @tentative-return-type */
+ public function loadHTML(string $source, int $options = 0): bool {}
- /** @readonly */
- public ?DOMElement $lastElementChild;
+ /** @tentative-return-type */
+ public function loadHTMLFile(string $filename, int $options = 0): bool {}
- /** @readonly */
- public int $childElementCount;
+ /** @tentative-return-type */
+ public function saveHTML(?DOMNode $node = null): string|false {}
- public function __construct(string $version = "1.0", string $encoding = "") {}
+ /** @tentative-return-type */
+ public function saveHTMLFile(string $filename): int|false {}
+#endif
- /** @return DOMAttr|false */
- public function createAttribute(string $localName) {}
+ /** @tentative-return-type */
+ public function saveXML(?DOMNode $node = null, int $options = 0): string|false {}
+
+ /** @tentative-return-type */
+ public function validate(): bool {}
+
+ /** @tentative-return-type */
+ public function xinclude(int $options = 0): int|false {}
+ }
+
+ /** @alias DOM\DOMException */
+ final class DOMException extends Exception
+ {
+ /**
+ * Intentionally left untyped for BC reasons
+ * @var int
+ */
+ public $code = 0; // TODO add proper type (i.e. int|string)
+ }
+
+ /** @alias DOM\Text */
+ class DOMText extends DOMCharacterData
+ {
+ /** @readonly */
+ public string $wholeText;
+
+ public function __construct(string $data = "") {}
+
+ /** @tentative-return-type */
+ public function isWhitespaceInElementContent(): bool {}
+
+ /**
+ * @tentative-return-type
+ * @alias DOMText::isWhitespaceInElementContent
+ */
+ public function isElementContentWhitespace(): bool {}
+
+ /** @return DOMText|false */
+ public function splitText(int $offset) {}
+ }
+
+ /** @alias DOM\NamedNodeMap */
+ class DOMNamedNodeMap implements IteratorAggregate, Countable
+ {
+ /** @readonly */
+ public int $length;
+
+ /** @tentative-return-type */
+ public function getNamedItem(string $qualifiedName): ?DOMNode {} // TODO DOM spec returns DOMAttr
+
+ /** @tentative-return-type */
+ public function getNamedItemNS(?string $namespace, string $localName): ?DOMNode {} // TODO DOM spec returns DOMAttr
+
+ /** @tentative-return-type */
+ public function item(int $index): ?DOMNode {} // TODO DOM spec returns DOMAttr
+
+ /** @tentative-return-type */
+ public function count(): int {}
+
+ public function getIterator(): Iterator {}
+ }
+
+ /** @alias DOM\Entity */
+ class DOMEntity extends DOMNode
+ {
+ /** @readonly */
+ public ?string $publicId;
+
+ /** @readonly */
+ public ?string $systemId;
+
+ /** @readonly */
+ public ?string $notationName;
+
+ /**
+ * @readonly
+ * @deprecated
+ */
+ public ?string $actualEncoding = null;
+
+ /**
+ * @readonly
+ * @deprecated
+ */
+ public ?string $encoding = null;
+
+ /**
+ * @readonly
+ * @deprecated
+ */
+ public ?string $version = null;
+ }
+
+ /** @alias DOM\EntityReference */
+ class DOMEntityReference extends DOMNode
+ {
+ public function __construct(string $name) {}
+ }
+
+ /** @alias DOM\Notation */
+ class DOMNotation extends DOMNode
+ {
+ /** @readonly */
+ public string $publicId;
+
+ /** @readonly */
+ public string $systemId;
+ }
+
+ /** @alias DOM\ProcessingInstruction */
+ class DOMProcessingInstruction extends DOMNode
+ {
+ /** @readonly */
+ public string $target;
+
+ public string $data;
+
+ public function __construct(string $name, string $value = "") {}
+ }
- /** @return DOMAttr|false */
- public function createAttributeNS(?string $namespace, string $qualifiedName) {}
+#ifdef LIBXML_XPATH_ENABLED
+ /**
+ * @not-serializable
+ * @alias DOM\XPath
+ */
+ class DOMXPath
+ {
+ /** @readonly */
+ public DOM\Document $document;
- /** @return DOMCdataSection|false */
- public function createCDATASection(string $data) {}
+ public bool $registerNodeNamespaces;
- /** @tentative-return-type */
- public function createComment(string $data): DOMComment {}
+ public function __construct(DOM\Document $document, bool $registerNodeNS = true) {}
- /** @tentative-return-type */
- public function createDocumentFragment(): DOMDocumentFragment {}
+ /** @tentative-return-type */
+ public function evaluate(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed {}
- /** @return DOMElement|false */
- public function createElement(string $localName, string $value = "") {}
+ /** @tentative-return-type */
+ public function query(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed {}
- /** @return DOMElement|false */
- public function createElementNS(?string $namespace, string $qualifiedName, string $value = "") {}
+ /** @tentative-return-type */
+ public function registerNamespace(string $prefix, string $namespace): bool {}
- /** @return DOMEntityReference|false */
- public function createEntityReference(string $name) {}
+ /** @tentative-return-type */
+ public function registerPhpFunctions(string|array|null $restrict = null): void {}
+ }
+#endif
- /** @return DOMProcessingInstruction|false */
- public function createProcessingInstruction(string $target, string $data = "") {}
+ function dom_import_simplexml(object $node): DOMElement {}
+}
- /** @tentative-return-type */
- public function createTextNode(string $data): DOMText {}
+namespace DOM
+{
+ /**
+ * @var int
+ * @cvalue PHP_ERR
+ */
+ const PHP_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INDEX_SIZE_ERR
+ */
+ const INDEX_SIZE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue DOMSTRING_SIZE_ERR
+ */
+ const STRING_SIZE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue HIERARCHY_REQUEST_ERR
+ */
+ const HIERARCHY_REQUEST_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue WRONG_DOCUMENT_ERR
+ */
+ const WRONG_DOCUMENT_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INVALID_CHARACTER_ERR
+ */
+ const INVALID_CHARACTER_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NO_DATA_ALLOWED_ERR
+ */
+ const NO_DATA_ALLOWED_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NO_MODIFICATION_ALLOWED_ERR
+ */
+ const NO_MODIFICATION_ALLOWED_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NOT_FOUND_ERR
+ */
+ const NOT_FOUND_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NOT_SUPPORTED_ERR
+ */
+ const NOT_SUPPORTED_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INUSE_ATTRIBUTE_ERR
+ */
+ const INUSE_ATTRIBUTE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INVALID_STATE_ERR
+ */
+ const INVALID_STATE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue SYNTAX_ERR
+ */
+ const SYNTAX_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INVALID_MODIFICATION_ERR
+ */
+ const INVALID_MODIFICATION_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue NAMESPACE_ERR
+ */
+ const NAMESPACE_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue INVALID_ACCESS_ERR
+ */
+ const INVALID_ACCESS_ERR = UNKNOWN;
+ /**
+ * @var int
+ * @cvalue VALIDATION_ERR
+ */
+ const VALIDATION_ERR = UNKNOWN;
- /** @tentative-return-type */
- public function getElementById(string $elementId): ?DOMElement {}
+ /**
+ * @var int
+ * @cvalue DOM_HTML_NO_DEFAULT_NS
+ */
+ const HTML_NO_DEFAULT_NS = UNKNOWN;
- /** @tentative-return-type */
- public function getElementsByTagName(string $qualifiedName): DOMNodeList {}
+ abstract class Document extends DOMNode implements DOMParentNode
+ {
+ /** @readonly */
+ public ?DocumentType $doctype;
- /** @tentative-return-type */
- public function getElementsByTagNameNS(?string $namespace, string $localName): DOMNodeList {}
+ /** @readonly */
+ public ?Element $documentElement;
- /** @return DOMNode|false */
- public function importNode(DOMNode $node, bool $deep = false) {}
+ public ?string $encoding;
- /** @tentative-return-type */
- public function load(string $filename, int $options = 0): bool {}
+ public bool $strictErrorChecking;
- /** @tentative-return-type */
- public function loadXML(string $source, int $options = 0): bool {}
+ public ?string $documentURI;
- /** @tentative-return-type */
- public function normalizeDocument(): void {}
+ /** @readonly */
+ public ?Element $firstElementChild;
- /** @tentative-return-type */
- public function registerNodeClass(string $baseClass, ?string $extendedClass): bool {}
+ /** @readonly */
+ public ?Element $lastElementChild;
- /** @tentative-return-type */
- public function save(string $filename, int $options = 0): int|false {}
+ /** @readonly */
+ public int $childElementCount;
-#ifdef LIBXML_HTML_ENABLED
- /** @tentative-return-type */
- public function loadHTML(string $source, int $options = 0): bool {}
+ /** @return Attr|false */
+ public function createAttribute(string $localName) {}
- /** @tentative-return-type */
- public function loadHTMLFile(string $filename, int $options = 0): bool {}
+ /** @return Attr|false */
+ public function createAttributeNS(?string $namespace, string $qualifiedName) {}
- /** @tentative-return-type */
- public function saveHTML(?DOMNode $node = null): string|false {}
+ /** @return CDATASection|false */
+ public function createCDATASection(string $data) {}
- /** @tentative-return-type */
- public function saveHTMLFile(string $filename): int|false {}
-#endif
+ /** @tentative-return-type */
+ public function createComment(string $data): Comment {}
- /** @tentative-return-type */
- public function saveXML(?DOMNode $node = null, int $options = 0): string|false {}
+ /** @tentative-return-type */
+ public function createDocumentFragment(): DocumentFragment {}
-#ifdef LIBXML_SCHEMAS_ENABLED
- /** @tentative-return-type */
- public function schemaValidate(string $filename, int $flags = 0): bool {}
+ /** @return Element|false */
+ public function createElement(string $localName, string $value = "") {}
- /** @tentative-return-type */
- public function schemaValidateSource(string $source, int $flags = 0): bool {}
+ /** @return Element|false */
+ public function createElementNS(?string $namespace, string $qualifiedName, string $value = "") {}
- /** @tentative-return-type */
- public function relaxNGValidate(string $filename): bool {}
+ /** @return ProcessingInstruction|false */
+ public function createProcessingInstruction(string $target, string $data = "") {}
- /** @tentative-return-type */
- public function relaxNGValidateSource(string $source): bool {}
-#endif
+ /** @tentative-return-type */
+ public function createTextNode(string $data): Text {}
- /** @tentative-return-type */
- public function validate(): bool {}
+ /** @tentative-return-type */
+ public function getElementById(string $elementId): ?Element {}
- /** @tentative-return-type */
- public function xinclude(int $options = 0): int|false {}
+ /** @tentative-return-type */
+ public function getElementsByTagName(string $qualifiedName): NodeList {}
- /** @tentative-return-type */
- public function adoptNode(DOMNode $node): DOMNode|false {}
+ /** @tentative-return-type */
+ public function getElementsByTagNameNS(?string $namespace, string $localName): NodeList {}
- /**
- * @param DOMNode|string $nodes
- * @implementation-alias DOMElement::append
- */
- public function append(...$nodes): void {}
+ /** @return Node|false */
+ public function importNode(Node $node, bool $deep = false) {}
- /**
- * @param DOMNode|string $nodes
- * @implementation-alias DOMElement::prepend
- */
- public function prepend(...$nodes): void {}
+ /** @tentative-return-type */
+ public function normalizeDocument(): void {}
- /** @param DOMNode|string $nodes */
- public function replaceChildren(...$nodes): void {}
-}
+ /** @tentative-return-type */
+ public function registerNodeClass(string $baseClass, ?string $extendedClass): bool {}
-final class DOMException extends Exception
-{
- /**
- * Intentionally left untyped for BC reasons
- * @var int
- */
- public $code = 0; // TODO add proper type (i.e. int|string)
-}
+#ifdef LIBXML_SCHEMAS_ENABLED
+ /** @tentative-return-type */
+ public function schemaValidate(string $filename, int $flags = 0): bool {}
-class DOMText extends DOMCharacterData
-{
- /** @readonly */
- public string $wholeText;
+ /** @tentative-return-type */
+ public function schemaValidateSource(string $source, int $flags = 0): bool {}
- public function __construct(string $data = "") {}
+ /** @tentative-return-type */
+ public function relaxNGValidate(string $filename): bool {}
- /** @tentative-return-type */
- public function isWhitespaceInElementContent(): bool {}
+ /** @tentative-return-type */
+ public function relaxNGValidateSource(string $source): bool {}
+#endif
- /**
- * @tentative-return-type
- * @alias DOMText::isWhitespaceInElementContent
- */
- public function isElementContentWhitespace(): bool {}
+ /** @tentative-return-type */
+ public function adoptNode(Node $node): Node|false {}
- /** @return DOMText|false */
- public function splitText(int $offset) {}
-}
+ /**
+ * @param Node|string $nodes
+ * @implementation-alias DOMElement::append
+ */
+ public function append(...$nodes): void {}
-class DOMNamedNodeMap implements IteratorAggregate, Countable
-{
- /** @readonly */
- public int $length;
+ /**
+ * @param Node|string $nodes
+ * @implementation-alias DOMElement::prepend
+ */
+ public function prepend(...$nodes): void {}
- /** @tentative-return-type */
- public function getNamedItem(string $qualifiedName): ?DOMNode {} // TODO DOM spec returns DOMAttr
+ /** @param Node|string $nodes */
+ public function replaceChildren(...$nodes): void {}
+ }
- /** @tentative-return-type */
- public function getNamedItemNS(?string $namespace, string $localName): ?DOMNode {} // TODO DOM spec returns DOMAttr
+ /** @strict-properties */
+ final class HTMLDocument extends DOM\Document
+ {
+ private function __construct() {}
- /** @tentative-return-type */
- public function item(int $index): ?DOMNode {} // TODO DOM spec returns DOMAttr
+ public static function createEmpty(string $encoding = "UTF-8"): HTMLDocument {}
- /** @tentative-return-type */
- public function count(): int {}
+ public static function createFromFile(string $path, int $options = 0, ?string $overrideEncoding = null): HTMLDocument {}
- public function getIterator(): Iterator {}
-}
+ public static function createFromString(string $source, int $options = 0, ?string $overrideEncoding = null): HTMLDocument {}
-class DOMEntity extends DOMNode
-{
- /** @readonly */
- public ?string $publicId;
+ /** @implementation-alias DOMDocument::saveXML */
+ public function saveXML(?Node $node = null, int $options = 0): string|false {}
- /** @readonly */
- public ?string $systemId;
+ /** @implementation-alias DOMDocument::save */
+ public function saveXMLFile(string $filename, int $options = 0): int|false {}
- /** @readonly */
- public ?string $notationName;
+ public function saveHTML(?Node $node = null): string|false {}
- /**
- * @readonly
- * @deprecated
- */
- public ?string $actualEncoding = null;
+ public function saveHTMLFile(string $filename): int|false {}
+ }
- /**
- * @readonly
- * @deprecated
- */
- public ?string $encoding = null;
+ /** @strict-properties */
+ final class XMLDocument extends DOM\Document
+ {
+ /** @implementation-alias DOM\HTMLDocument::__construct */
+ private function __construct() {}
- /**
- * @readonly
- * @deprecated
- */
- public ?string $version = null;
-}
+ public static function createEmpty(string $version = "1.0", string $encoding = "UTF-8"): XMLDocument {}
-class DOMEntityReference extends DOMNode
-{
- public function __construct(string $name) {}
-}
+ public static function createFromFile(string $path, int $options = 0, ?string $overrideEncoding = null): XMLDocument {}
-class DOMNotation extends DOMNode
-{
- /** @readonly */
- public string $publicId;
+ public static function createFromString(string $source, int $options = 0, ?string $overrideEncoding = null): XMLDocument {}
- /** @readonly */
- public string $systemId;
-}
+ /** @readonly */
+ public ?string $xmlEncoding;
-class DOMProcessingInstruction extends DOMNode
-{
- /** @readonly */
- public string $target;
+ public bool $standalone;
- public string $data;
+ public bool $xmlStandalone;
- public function __construct(string $name, string $value = "") {}
-}
+ public ?string $version;
-#ifdef LIBXML_XPATH_ENABLED
-/** @not-serializable */
-class DOMXPath
-{
- /** @readonly */
- public DOMDocument $document;
+ public ?string $xmlVersion;
- public bool $registerNodeNamespaces;
+ public bool $formatOutput;
- public function __construct(DOMDocument $document, bool $registerNodeNS = true) {}
+ public bool $validateOnParse;
- /** @tentative-return-type */
- public function evaluate(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed {}
+ public bool $resolveExternals;
- /** @tentative-return-type */
- public function query(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed {}
+ public bool $preserveWhiteSpace;
- /** @tentative-return-type */
- public function registerNamespace(string $prefix, string $namespace): bool {}
+ public bool $recover;
- /** @tentative-return-type */
- public function registerPhpFunctions(string|array|null $restrict = null): void {}
-}
-#endif
+ public bool $substituteEntities;
+
+ /**
+ * @implementation-alias DOMDocument::createEntityReference
+ * @return EntityReference|false
+ */
+ public function createEntityReference(string $name) {}
+
+ /**
+ * @implementation-alias DOMDocument::validate
+ */
+ public function validate(): bool {}
-function dom_import_simplexml(object $node): DOMElement {}
+ /**
+ * @tentative-return-type
+ * @implementation-alias DOMDocument::xinclude
+ */
+ public function xinclude(int $options = 0): int|false {}
+
+ /**
+ * @tentative-return-type
+ * @implementation-alias DOMDocument::saveXML
+ */
+ public function saveXML(?Node $node = null, int $options = 0): string|false {}
+
+ /**
+ * @tentative-return-type
+ * @implementation-alias DOMDocument::save
+ */
+ public function saveXMLFile(string $filename, int $options = 0): int|false {}
+ }
+
+ /** @implementation-alias dom_import_simplexml */
+ function import_simplexml(object $node): Element {}
+}
diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h
index 694275e5e728..eb5e55c2cf28 100644
--- a/ext/dom/php_dom_arginfo.h
+++ b/ext/dom/php_dom_arginfo.h
@@ -1,10 +1,14 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 134f9c3e73943f3f6815fa0e3a6784fdc9e70600 */
+ * Stub hash: fbdcec5b706fe7dc0e757edfa262d96b44bd510f */
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)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_DOM_import_simplexml, 0, 1, DOM\\Element, 0)
+ ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMCdataSection___construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
ZEND_END_ARG_INFO()
@@ -30,11 +34,6 @@ ZEND_END_ARG_INFO()
#define arginfo_class_DOMChildNode_replaceWith arginfo_class_DOMParentNode_append
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOMNode___sleep, 0, 0, IS_ARRAY, 0)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_DOMNode___wakeup arginfo_class_DOMChildNode_remove
-
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMNode_appendChild, 0, 0, 1)
ZEND_ARG_OBJ_INFO(0, node, DOMNode, 0)
ZEND_END_ARG_INFO()
@@ -123,6 +122,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOMNode_compareDocumentPos
ZEND_ARG_OBJ_INFO(0, other, DOMNode, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOMNode___sleep, 0, 0, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_DOMNode___wakeup arginfo_class_DOMChildNode_remove
+
#define arginfo_class_DOMNameSpaceNode___sleep arginfo_class_DOMNode___sleep
#define arginfo_class_DOMNameSpaceNode___wakeup arginfo_class_DOMChildNode_remove
@@ -328,61 +332,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMDocument___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 0, "\"\"")
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMDocument_createAttribute, 0, 0, 1)
- ZEND_ARG_TYPE_INFO(0, localName, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMDocument_createAttributeNS, 0, 0, 2)
- ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1)
- ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_DOMDocument_createCDATASection arginfo_class_DOMCdataSection___construct
-
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOMDocument_createComment, 0, 1, DOMComment, 0)
- ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOMDocument_createDocumentFragment, 0, 0, DOMDocumentFragment, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMDocument_createElement, 0, 0, 1)
- ZEND_ARG_TYPE_INFO(0, localName, IS_STRING, 0)
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 0, "\"\"")
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMDocument_createElementNS, 0, 0, 2)
- ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1)
- ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0)
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 0, "\"\"")
-ZEND_END_ARG_INFO()
-
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMDocument_createEntityReference, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMDocument_createProcessingInstruction, 0, 0, 1)
- ZEND_ARG_TYPE_INFO(0, target, IS_STRING, 0)
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data, IS_STRING, 0, "\"\"")
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOMDocument_createTextNode, 0, 1, DOMText, 0)
- ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOMDocument_getElementById, 0, 1, DOMElement, 1)
- ZEND_ARG_TYPE_INFO(0, elementId, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_DOMDocument_getElementsByTagName arginfo_class_DOMElement_getElementsByTagName
-
-#define arginfo_class_DOMDocument_getElementsByTagNameNS arginfo_class_DOMElement_getElementsByTagNameNS
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMDocument_importNode, 0, 0, 1)
- ZEND_ARG_OBJ_INFO(0, node, DOMNode, 0)
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, deep, _IS_BOOL, 0, "false")
-ZEND_END_ARG_INFO()
-
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMDocument_load, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
@@ -393,13 +346,6 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMDocument_load
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
ZEND_END_ARG_INFO()
-#define arginfo_class_DOMDocument_normalizeDocument arginfo_class_DOMNode_normalize
-
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMDocument_registerNodeClass, 0, 2, _IS_BOOL, 0)
- ZEND_ARG_TYPE_INFO(0, baseClass, IS_STRING, 0)
- ZEND_ARG_TYPE_INFO(0, extendedClass, IS_STRING, 1)
-ZEND_END_ARG_INFO()
-
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DOMDocument_save, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
@@ -436,48 +382,12 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DOMDocument_save
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
ZEND_END_ARG_INFO()
-#if defined(LIBXML_SCHEMAS_ENABLED)
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMDocument_schemaValidate, 0, 1, _IS_BOOL, 0)
- ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
-ZEND_END_ARG_INFO()
-#endif
-
-#if defined(LIBXML_SCHEMAS_ENABLED)
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMDocument_schemaValidateSource, 0, 1, _IS_BOOL, 0)
- ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0)
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
-ZEND_END_ARG_INFO()
-#endif
-
-#if defined(LIBXML_SCHEMAS_ENABLED)
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMDocument_relaxNGValidate, 0, 1, _IS_BOOL, 0)
- ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-#endif
-
-#if defined(LIBXML_SCHEMAS_ENABLED)
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMDocument_relaxNGValidateSource, 0, 1, _IS_BOOL, 0)
- ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-#endif
-
#define arginfo_class_DOMDocument_validate arginfo_class_DOMNode_hasAttributes
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DOMDocument_xinclude, 0, 0, MAY_BE_LONG|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DOMDocument_adoptNode, 0, 1, DOMNode, MAY_BE_FALSE)
- ZEND_ARG_OBJ_INFO(0, node, DOMNode, 0)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_DOMDocument_append arginfo_class_DOMParentNode_append
-
-#define arginfo_class_DOMDocument_prepend arginfo_class_DOMParentNode_append
-
-#define arginfo_class_DOMDocument_replaceChildren arginfo_class_DOMParentNode_append
-
#define arginfo_class_DOMText___construct arginfo_class_DOMComment___construct
#define arginfo_class_DOMText_isWhitespaceInElementContent arginfo_class_DOMNode_hasAttributes
@@ -511,7 +421,7 @@ ZEND_END_ARG_INFO()
#if defined(LIBXML_XPATH_ENABLED)
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMXPath___construct, 0, 0, 1)
- ZEND_ARG_OBJ_INFO(0, document, DOMDocument, 0)
+ ZEND_ARG_OBJ_INFO(0, document, DOM\\Document, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, registerNodeNS, _IS_BOOL, 0, "true")
ZEND_END_ARG_INFO()
#endif
@@ -541,12 +451,178 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMXPath_registe
ZEND_END_ARG_INFO()
#endif
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOM_Document_createAttribute, 0, 0, 1)
+ ZEND_ARG_TYPE_INFO(0, localName, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOM_Document_createAttributeNS, 0, 0, 2)
+ ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1)
+ ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_DOM_Document_createCDATASection arginfo_class_DOMCdataSection___construct
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createComment, 0, 1, DOM\\Comment, 0)
+ ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createDocumentFragment, 0, 0, DOM\\DocumentFragment, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOM_Document_createElement, 0, 0, 1)
+ ZEND_ARG_TYPE_INFO(0, localName, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 0, "\"\"")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOM_Document_createElementNS, 0, 0, 2)
+ ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1)
+ ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 0, "\"\"")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOM_Document_createProcessingInstruction, 0, 0, 1)
+ ZEND_ARG_TYPE_INFO(0, target, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data, IS_STRING, 0, "\"\"")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createTextNode, 0, 1, DOM\\Text, 0)
+ ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_getElementById, 0, 1, DOM\\Element, 1)
+ ZEND_ARG_TYPE_INFO(0, elementId, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_getElementsByTagName, 0, 1, DOM\\\116odeList, 0)
+ ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_getElementsByTagNameNS, 0, 2, DOM\\\116odeList, 0)
+ ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1)
+ ZEND_ARG_TYPE_INFO(0, localName, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOM_Document_importNode, 0, 0, 1)
+ ZEND_ARG_OBJ_INFO(0, node, DOM\\\116ode, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, deep, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_DOM_Document_normalizeDocument arginfo_class_DOMNode_normalize
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Document_registerNodeClass, 0, 2, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, baseClass, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, extendedClass, IS_STRING, 1)
+ZEND_END_ARG_INFO()
+
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Document_schemaValidate, 0, 1, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+#endif
+
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Document_schemaValidateSource, 0, 1, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+#endif
+
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Document_relaxNGValidate, 0, 1, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+#endif
+
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Document_relaxNGValidateSource, 0, 1, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DOM_Document_adoptNode, 0, 1, DOM\\\116ode, MAY_BE_FALSE)
+ ZEND_ARG_OBJ_INFO(0, node, DOM\\\116ode, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_DOM_Document_append arginfo_class_DOMParentNode_append
+
+#define arginfo_class_DOM_Document_prepend arginfo_class_DOMParentNode_append
+
+#define arginfo_class_DOM_Document_replaceChildren arginfo_class_DOMParentNode_append
+
+#define arginfo_class_DOM_HTMLDocument___construct arginfo_class_DOMDocumentFragment___construct
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_HTMLDocument_createEmpty, 0, 0, DOM\\HTMLDocument, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 0, "\"UTF-8\"")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_HTMLDocument_createFromFile, 0, 1, DOM\\HTMLDocument, 0)
+ ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, overrideEncoding, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_HTMLDocument_createFromString, 0, 1, DOM\\HTMLDocument, 0)
+ ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, overrideEncoding, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_DOM_HTMLDocument_saveXML, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)
+ ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, node, DOM\\\116ode, 1, "null")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_DOM_HTMLDocument_saveXMLFile, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)
+ ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_DOM_HTMLDocument_saveHTML, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)
+ ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, node, DOM\\\116ode, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_DOM_HTMLDocument_saveHTMLFile, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)
+ ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_DOM_XMLDocument___construct arginfo_class_DOMDocumentFragment___construct
+
+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\"")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_XMLDocument_createFromFile, 0, 1, DOM\\XMLDocument, 0)
+ ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, overrideEncoding, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_XMLDocument_createFromString, 0, 1, DOM\\XMLDocument, 0)
+ ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, overrideEncoding, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_DOM_XMLDocument_createEntityReference arginfo_class_DOMDocument_createEntityReference
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_XMLDocument_validate, 0, 0, _IS_BOOL, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_DOM_XMLDocument_xinclude arginfo_class_DOMDocument_xinclude
+
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DOM_XMLDocument_saveXML, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)
+ ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, node, DOM\\\116ode, 1, "null")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_DOM_XMLDocument_saveXMLFile arginfo_class_DOMDocument_save
+
ZEND_FUNCTION(dom_import_simplexml);
ZEND_METHOD(DOMCdataSection, __construct);
ZEND_METHOD(DOMComment, __construct);
-ZEND_METHOD(DOMNode, __sleep);
-ZEND_METHOD(DOMNode, __wakeup);
ZEND_METHOD(DOMNode, appendChild);
ZEND_METHOD(DOMNode, C14N);
ZEND_METHOD(DOMNode, C14NFile);
@@ -568,6 +644,8 @@ ZEND_METHOD(DOMNode, replaceChild);
ZEND_METHOD(DOMNode, contains);
ZEND_METHOD(DOMNode, getRootNode);
ZEND_METHOD(DOMNode, compareDocumentPosition);
+ZEND_METHOD(DOMNode, __sleep);
+ZEND_METHOD(DOMNode, __wakeup);
ZEND_METHOD(DOMImplementation, getFeature);
ZEND_METHOD(DOMImplementation, hasFeature);
ZEND_METHOD(DOMImplementation, createDocumentType);
@@ -576,7 +654,7 @@ ZEND_METHOD(DOMDocumentFragment, __construct);
ZEND_METHOD(DOMDocumentFragment, appendXML);
ZEND_METHOD(DOMElement, append);
ZEND_METHOD(DOMElement, prepend);
-ZEND_METHOD(DOMDocument, replaceChildren);
+ZEND_METHOD(DOM_Document, replaceChildren);
ZEND_METHOD(DOMNodeList, count);
ZEND_METHOD(DOMNodeList, getIterator);
ZEND_METHOD(DOMNodeList, item);
@@ -616,24 +694,9 @@ ZEND_METHOD(DOMElement, replaceChildren);
ZEND_METHOD(DOMElement, insertAdjacentElement);
ZEND_METHOD(DOMElement, insertAdjacentText);
ZEND_METHOD(DOMDocument, __construct);
-ZEND_METHOD(DOMDocument, createAttribute);
-ZEND_METHOD(DOMDocument, createAttributeNS);
-ZEND_METHOD(DOMDocument, createCDATASection);
-ZEND_METHOD(DOMDocument, createComment);
-ZEND_METHOD(DOMDocument, createDocumentFragment);
-ZEND_METHOD(DOMDocument, createElement);
-ZEND_METHOD(DOMDocument, createElementNS);
ZEND_METHOD(DOMDocument, createEntityReference);
-ZEND_METHOD(DOMDocument, createProcessingInstruction);
-ZEND_METHOD(DOMDocument, createTextNode);
-ZEND_METHOD(DOMDocument, getElementById);
-ZEND_METHOD(DOMDocument, getElementsByTagName);
-ZEND_METHOD(DOMDocument, getElementsByTagNameNS);
-ZEND_METHOD(DOMDocument, importNode);
ZEND_METHOD(DOMDocument, load);
ZEND_METHOD(DOMDocument, loadXML);
-ZEND_METHOD(DOMDocument, normalizeDocument);
-ZEND_METHOD(DOMDocument, registerNodeClass);
ZEND_METHOD(DOMDocument, save);
#if defined(LIBXML_HTML_ENABLED)
ZEND_METHOD(DOMDocument, loadHTML);
@@ -648,21 +711,8 @@ ZEND_METHOD(DOMDocument, saveHTML);
ZEND_METHOD(DOMDocument, saveHTMLFile);
#endif
ZEND_METHOD(DOMDocument, saveXML);
-#if defined(LIBXML_SCHEMAS_ENABLED)
-ZEND_METHOD(DOMDocument, schemaValidate);
-#endif
-#if defined(LIBXML_SCHEMAS_ENABLED)
-ZEND_METHOD(DOMDocument, schemaValidateSource);
-#endif
-#if defined(LIBXML_SCHEMAS_ENABLED)
-ZEND_METHOD(DOMDocument, relaxNGValidate);
-#endif
-#if defined(LIBXML_SCHEMAS_ENABLED)
-ZEND_METHOD(DOMDocument, relaxNGValidateSource);
-#endif
ZEND_METHOD(DOMDocument, validate);
ZEND_METHOD(DOMDocument, xinclude);
-ZEND_METHOD(DOMDocument, adoptNode);
ZEND_METHOD(DOMText, __construct);
ZEND_METHOD(DOMText, isWhitespaceInElementContent);
ZEND_METHOD(DOMText, splitText);
@@ -688,10 +738,48 @@ ZEND_METHOD(DOMXPath, registerNamespace);
#if defined(LIBXML_XPATH_ENABLED)
ZEND_METHOD(DOMXPath, registerPhpFunctions);
#endif
+ZEND_METHOD(DOM_Document, createAttribute);
+ZEND_METHOD(DOM_Document, createAttributeNS);
+ZEND_METHOD(DOM_Document, createCDATASection);
+ZEND_METHOD(DOM_Document, createComment);
+ZEND_METHOD(DOM_Document, createDocumentFragment);
+ZEND_METHOD(DOM_Document, createElement);
+ZEND_METHOD(DOM_Document, createElementNS);
+ZEND_METHOD(DOM_Document, createProcessingInstruction);
+ZEND_METHOD(DOM_Document, createTextNode);
+ZEND_METHOD(DOM_Document, getElementById);
+ZEND_METHOD(DOM_Document, getElementsByTagName);
+ZEND_METHOD(DOM_Document, getElementsByTagNameNS);
+ZEND_METHOD(DOM_Document, importNode);
+ZEND_METHOD(DOM_Document, normalizeDocument);
+ZEND_METHOD(DOM_Document, registerNodeClass);
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ZEND_METHOD(DOM_Document, schemaValidate);
+#endif
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ZEND_METHOD(DOM_Document, schemaValidateSource);
+#endif
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ZEND_METHOD(DOM_Document, relaxNGValidate);
+#endif
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ZEND_METHOD(DOM_Document, relaxNGValidateSource);
+#endif
+ZEND_METHOD(DOM_Document, adoptNode);
+ZEND_METHOD(DOM_HTMLDocument, __construct);
+ZEND_METHOD(DOM_HTMLDocument, createEmpty);
+ZEND_METHOD(DOM_HTMLDocument, createFromFile);
+ZEND_METHOD(DOM_HTMLDocument, createFromString);
+ZEND_METHOD(DOM_HTMLDocument, saveHTML);
+ZEND_METHOD(DOM_HTMLDocument, saveHTMLFile);
+ZEND_METHOD(DOM_XMLDocument, createEmpty);
+ZEND_METHOD(DOM_XMLDocument, createFromFile);
+ZEND_METHOD(DOM_XMLDocument, createFromString);
static const zend_function_entry ext_functions[] = {
ZEND_FE(dom_import_simplexml, arginfo_dom_import_simplexml)
+ ZEND_NS_FALIAS("DOM", import_simplexml, dom_import_simplexml, arginfo_DOM_import_simplexml)
ZEND_FE_END
};
@@ -731,8 +819,6 @@ static const zend_function_entry class_DOMChildNode_methods[] = {
static const zend_function_entry class_DOMNode_methods[] = {
- ZEND_ME(DOMNode, __sleep, arginfo_class_DOMNode___sleep, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMNode, __wakeup, arginfo_class_DOMNode___wakeup, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNode, appendChild, arginfo_class_DOMNode_appendChild, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNode, C14N, arginfo_class_DOMNode_C14N, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNode, C14NFile, arginfo_class_DOMNode_C14NFile, ZEND_ACC_PUBLIC)
@@ -754,6 +840,8 @@ static const zend_function_entry class_DOMNode_methods[] = {
ZEND_ME(DOMNode, contains, arginfo_class_DOMNode_contains, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNode, getRootNode, arginfo_class_DOMNode_getRootNode, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNode, compareDocumentPosition, arginfo_class_DOMNode_compareDocumentPosition, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOMNode, __sleep, arginfo_class_DOMNode___sleep, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOMNode, __wakeup, arginfo_class_DOMNode___wakeup, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
@@ -779,7 +867,7 @@ static const zend_function_entry class_DOMDocumentFragment_methods[] = {
ZEND_ME(DOMDocumentFragment, appendXML, arginfo_class_DOMDocumentFragment_appendXML, ZEND_ACC_PUBLIC)
ZEND_MALIAS(DOMElement, append, append, arginfo_class_DOMDocumentFragment_append, ZEND_ACC_PUBLIC)
ZEND_MALIAS(DOMElement, prepend, prepend, arginfo_class_DOMDocumentFragment_prepend, ZEND_ACC_PUBLIC)
- ZEND_MALIAS(DOMDocument, replaceChildren, replaceChildren, arginfo_class_DOMDocumentFragment_replaceChildren, ZEND_ACC_PUBLIC)
+ ZEND_MALIAS(DOM_Document, replaceChildren, replaceChildren, arginfo_class_DOMDocumentFragment_replaceChildren, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
@@ -850,24 +938,9 @@ static const zend_function_entry class_DOMElement_methods[] = {
static const zend_function_entry class_DOMDocument_methods[] = {
ZEND_ME(DOMDocument, __construct, arginfo_class_DOMDocument___construct, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, createAttribute, arginfo_class_DOMDocument_createAttribute, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, createAttributeNS, arginfo_class_DOMDocument_createAttributeNS, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, createCDATASection, arginfo_class_DOMDocument_createCDATASection, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, createComment, arginfo_class_DOMDocument_createComment, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, createDocumentFragment, arginfo_class_DOMDocument_createDocumentFragment, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, createElement, arginfo_class_DOMDocument_createElement, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, createElementNS, arginfo_class_DOMDocument_createElementNS, ZEND_ACC_PUBLIC)
ZEND_ME(DOMDocument, createEntityReference, arginfo_class_DOMDocument_createEntityReference, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, createProcessingInstruction, arginfo_class_DOMDocument_createProcessingInstruction, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, createTextNode, arginfo_class_DOMDocument_createTextNode, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, getElementById, arginfo_class_DOMDocument_getElementById, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, getElementsByTagName, arginfo_class_DOMDocument_getElementsByTagName, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, getElementsByTagNameNS, arginfo_class_DOMDocument_getElementsByTagNameNS, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, importNode, arginfo_class_DOMDocument_importNode, ZEND_ACC_PUBLIC)
ZEND_ME(DOMDocument, load, arginfo_class_DOMDocument_load, ZEND_ACC_PUBLIC)
ZEND_ME(DOMDocument, loadXML, arginfo_class_DOMDocument_loadXML, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, normalizeDocument, arginfo_class_DOMDocument_normalizeDocument, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, registerNodeClass, arginfo_class_DOMDocument_registerNodeClass, ZEND_ACC_PUBLIC)
ZEND_ME(DOMDocument, save, arginfo_class_DOMDocument_save, ZEND_ACC_PUBLIC)
#if defined(LIBXML_HTML_ENABLED)
ZEND_ME(DOMDocument, loadHTML, arginfo_class_DOMDocument_loadHTML, ZEND_ACC_PUBLIC)
@@ -882,24 +955,8 @@ static const zend_function_entry class_DOMDocument_methods[] = {
ZEND_ME(DOMDocument, saveHTMLFile, arginfo_class_DOMDocument_saveHTMLFile, ZEND_ACC_PUBLIC)
#endif
ZEND_ME(DOMDocument, saveXML, arginfo_class_DOMDocument_saveXML, ZEND_ACC_PUBLIC)
-#if defined(LIBXML_SCHEMAS_ENABLED)
- ZEND_ME(DOMDocument, schemaValidate, arginfo_class_DOMDocument_schemaValidate, ZEND_ACC_PUBLIC)
-#endif
-#if defined(LIBXML_SCHEMAS_ENABLED)
- ZEND_ME(DOMDocument, schemaValidateSource, arginfo_class_DOMDocument_schemaValidateSource, ZEND_ACC_PUBLIC)
-#endif
-#if defined(LIBXML_SCHEMAS_ENABLED)
- ZEND_ME(DOMDocument, relaxNGValidate, arginfo_class_DOMDocument_relaxNGValidate, ZEND_ACC_PUBLIC)
-#endif
-#if defined(LIBXML_SCHEMAS_ENABLED)
- ZEND_ME(DOMDocument, relaxNGValidateSource, arginfo_class_DOMDocument_relaxNGValidateSource, ZEND_ACC_PUBLIC)
-#endif
ZEND_ME(DOMDocument, validate, arginfo_class_DOMDocument_validate, ZEND_ACC_PUBLIC)
ZEND_ME(DOMDocument, xinclude, arginfo_class_DOMDocument_xinclude, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, adoptNode, arginfo_class_DOMDocument_adoptNode, ZEND_ACC_PUBLIC)
- ZEND_MALIAS(DOMElement, append, append, arginfo_class_DOMDocument_append, ZEND_ACC_PUBLIC)
- ZEND_MALIAS(DOMElement, prepend, prepend, arginfo_class_DOMDocument_prepend, ZEND_ACC_PUBLIC)
- ZEND_ME(DOMDocument, replaceChildren, arginfo_class_DOMDocument_replaceChildren, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
@@ -961,6 +1018,69 @@ static const zend_function_entry class_DOMXPath_methods[] = {
};
#endif
+
+static const zend_function_entry class_DOM_Document_methods[] = {
+ ZEND_ME(DOM_Document, createAttribute, arginfo_class_DOM_Document_createAttribute, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, createAttributeNS, arginfo_class_DOM_Document_createAttributeNS, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, createCDATASection, arginfo_class_DOM_Document_createCDATASection, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, createComment, arginfo_class_DOM_Document_createComment, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, createDocumentFragment, arginfo_class_DOM_Document_createDocumentFragment, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, createElement, arginfo_class_DOM_Document_createElement, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, createElementNS, arginfo_class_DOM_Document_createElementNS, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, createProcessingInstruction, arginfo_class_DOM_Document_createProcessingInstruction, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, createTextNode, arginfo_class_DOM_Document_createTextNode, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, getElementById, arginfo_class_DOM_Document_getElementById, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, getElementsByTagName, arginfo_class_DOM_Document_getElementsByTagName, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, getElementsByTagNameNS, arginfo_class_DOM_Document_getElementsByTagNameNS, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, importNode, arginfo_class_DOM_Document_importNode, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, normalizeDocument, arginfo_class_DOM_Document_normalizeDocument, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, registerNodeClass, arginfo_class_DOM_Document_registerNodeClass, ZEND_ACC_PUBLIC)
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ ZEND_ME(DOM_Document, schemaValidate, arginfo_class_DOM_Document_schemaValidate, ZEND_ACC_PUBLIC)
+#endif
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ ZEND_ME(DOM_Document, schemaValidateSource, arginfo_class_DOM_Document_schemaValidateSource, ZEND_ACC_PUBLIC)
+#endif
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ ZEND_ME(DOM_Document, relaxNGValidate, arginfo_class_DOM_Document_relaxNGValidate, ZEND_ACC_PUBLIC)
+#endif
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ ZEND_ME(DOM_Document, relaxNGValidateSource, arginfo_class_DOM_Document_relaxNGValidateSource, ZEND_ACC_PUBLIC)
+#endif
+ ZEND_ME(DOM_Document, adoptNode, arginfo_class_DOM_Document_adoptNode, ZEND_ACC_PUBLIC)
+ ZEND_MALIAS(DOMElement, append, append, arginfo_class_DOM_Document_append, ZEND_ACC_PUBLIC)
+ ZEND_MALIAS(DOMElement, prepend, prepend, arginfo_class_DOM_Document_prepend, ZEND_ACC_PUBLIC)
+ ZEND_ME(DOM_Document, replaceChildren, arginfo_class_DOM_Document_replaceChildren, ZEND_ACC_PUBLIC)
+ ZEND_FE_END
+};
+
+
+static const zend_function_entry class_DOM_HTMLDocument_methods[] = {
+ ZEND_ME(DOM_HTMLDocument, __construct, arginfo_class_DOM_HTMLDocument___construct, ZEND_ACC_PRIVATE)
+ ZEND_ME(DOM_HTMLDocument, createEmpty, arginfo_class_DOM_HTMLDocument_createEmpty, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(DOM_HTMLDocument, createFromFile, arginfo_class_DOM_HTMLDocument_createFromFile, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(DOM_HTMLDocument, createFromString, arginfo_class_DOM_HTMLDocument_createFromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_MALIAS(DOMDocument, saveXML, saveXML, arginfo_class_DOM_HTMLDocument_saveXML, ZEND_ACC_PUBLIC)
+ ZEND_MALIAS(DOMDocument, saveXMLFile, save, arginfo_class_DOM_HTMLDocument_saveXMLFile, ZEND_ACC_PUBLIC)
+ 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)
+ ZEND_FE_END
+};
+
+
+static const zend_function_entry class_DOM_XMLDocument_methods[] = {
+ ZEND_MALIAS(DOM_HTMLDocument, __construct, __construct, arginfo_class_DOM_XMLDocument___construct, ZEND_ACC_PRIVATE)
+ ZEND_ME(DOM_XMLDocument, createEmpty, arginfo_class_DOM_XMLDocument_createEmpty, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(DOM_XMLDocument, createFromFile, arginfo_class_DOM_XMLDocument_createFromFile, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(DOM_XMLDocument, createFromString, arginfo_class_DOM_XMLDocument_createFromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_MALIAS(DOMDocument, createEntityReference, createEntityReference, arginfo_class_DOM_XMLDocument_createEntityReference, ZEND_ACC_PUBLIC)
+ ZEND_MALIAS(DOMDocument, validate, validate, arginfo_class_DOM_XMLDocument_validate, ZEND_ACC_PUBLIC)
+ ZEND_MALIAS(DOMDocument, xinclude, xinclude, arginfo_class_DOM_XMLDocument_xinclude, ZEND_ACC_PUBLIC)
+ ZEND_MALIAS(DOMDocument, saveXML, saveXML, arginfo_class_DOM_XMLDocument_saveXML, ZEND_ACC_PUBLIC)
+ ZEND_MALIAS(DOMDocument, saveXMLFile, save, arginfo_class_DOM_XMLDocument_saveXMLFile, ZEND_ACC_PUBLIC)
+ ZEND_FE_END
+};
+
static void register_php_dom_symbols(int module_number)
{
REGISTER_LONG_CONSTANT("XML_ELEMENT_NODE", XML_ELEMENT_NODE, CONST_PERSISTENT);
@@ -1011,6 +1131,24 @@ static void register_php_dom_symbols(int module_number)
REGISTER_LONG_CONSTANT("DOM_NAMESPACE_ERR", NAMESPACE_ERR, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("DOM_INVALID_ACCESS_ERR", INVALID_ACCESS_ERR, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("DOM_VALIDATION_ERR", VALIDATION_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\PHP_ERR", PHP_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\INDEX_SIZE_ERR", INDEX_SIZE_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\STRING_SIZE_ERR", DOMSTRING_SIZE_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\HIERARCHY_REQUEST_ERR", HIERARCHY_REQUEST_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\WRONG_DOCUMENT_ERR", WRONG_DOCUMENT_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\INVALID_CHARACTER_ERR", INVALID_CHARACTER_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\NO_DATA_ALLOWED_ERR", NO_DATA_ALLOWED_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\NO_MODIFICATION_ALLOWED_ERR", NO_MODIFICATION_ALLOWED_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\NOT_FOUND_ERR", NOT_FOUND_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\NOT_SUPPORTED_ERR", NOT_SUPPORTED_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\INUSE_ATTRIBUTE_ERR", INUSE_ATTRIBUTE_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\INVALID_STATE_ERR", INVALID_STATE_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\SYNTAX_ERR", SYNTAX_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\INVALID_MODIFICATION_ERR", INVALID_MODIFICATION_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\NAMESPACE_ERR", NAMESPACE_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\INVALID_ACCESS_ERR", INVALID_ACCESS_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\VALIDATION_ERR", VALIDATION_ERR, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DOM\\HTML_NO_DEFAULT_NS", DOM_HTML_NO_DEFAULT_NS, CONST_PERSISTENT);
}
static zend_class_entry *register_class_DOMDocumentType(zend_class_entry *class_entry_DOMNode)
@@ -1019,6 +1157,7 @@ static zend_class_entry *register_class_DOMDocumentType(zend_class_entry *class_
INIT_CLASS_ENTRY(ce, "DOMDocumentType", class_DOMDocumentType_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
+ zend_register_class_alias("DOM\\DocumentType", class_entry);
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
@@ -1067,6 +1206,7 @@ static zend_class_entry *register_class_DOMCdataSection(zend_class_entry *class_
INIT_CLASS_ENTRY(ce, "DOMCdataSection", class_DOMCdataSection_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMText);
+ zend_register_class_alias("DOM\\CDATASection", class_entry);
return class_entry;
}
@@ -1077,6 +1217,7 @@ static zend_class_entry *register_class_DOMComment(zend_class_entry *class_entry
INIT_CLASS_ENTRY(ce, "DOMComment", class_DOMComment_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMCharacterData);
+ zend_register_class_alias("DOM\\Comment", class_entry);
return class_entry;
}
@@ -1087,6 +1228,7 @@ static zend_class_entry *register_class_DOMParentNode(void)
INIT_CLASS_ENTRY(ce, "DOMParentNode", class_DOMParentNode_methods);
class_entry = zend_register_internal_interface(&ce);
+ zend_register_class_alias("DOM\\ParentNode", class_entry);
return class_entry;
}
@@ -1097,6 +1239,7 @@ static zend_class_entry *register_class_DOMChildNode(void)
INIT_CLASS_ENTRY(ce, "DOMChildNode", class_DOMChildNode_methods);
class_entry = zend_register_internal_interface(&ce);
+ zend_register_class_alias("DOM\\ChildNode", class_entry);
return class_entry;
}
@@ -1107,6 +1250,7 @@ static zend_class_entry *register_class_DOMNode(void)
INIT_CLASS_ENTRY(ce, "DOMNode", class_DOMNode_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL);
+ zend_register_class_alias("DOM\\Node", class_entry);
zval const_DOCUMENT_POSITION_DISCONNECTED_value;
ZVAL_LONG(&const_DOCUMENT_POSITION_DISCONNECTED_value, 0x1);
@@ -1227,8 +1371,8 @@ static zend_class_entry *register_class_DOMNode(void)
zval property_ownerDocument_default_value;
ZVAL_UNDEF(&property_ownerDocument_default_value);
zend_string *property_ownerDocument_name = zend_string_init("ownerDocument", sizeof("ownerDocument") - 1, 1);
- zend_string *property_ownerDocument_class_DOMDocument = zend_string_init("DOMDocument", sizeof("DOMDocument")-1, 1);
- zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_ownerDocument_class_DOMDocument, 0, MAY_BE_NULL));
+ zend_string *property_ownerDocument_class_DOM_Document = zend_string_init("DOM\\Document", sizeof("DOM\\Document")-1, 1);
+ zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_ownerDocument_class_DOM_Document, 0, MAY_BE_NULL));
zend_string_release(property_ownerDocument_name);
zval property_namespaceURI_default_value;
@@ -1270,6 +1414,7 @@ static zend_class_entry *register_class_DOMNameSpaceNode(void)
INIT_CLASS_ENTRY(ce, "DOMNameSpaceNode", class_DOMNameSpaceNode_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL);
+ zend_register_class_alias("DOM\\NameSpaceNode", class_entry);
zval property_nodeName_default_value;
ZVAL_UNDEF(&property_nodeName_default_value);
@@ -1354,6 +1499,7 @@ static zend_class_entry *register_class_DOMDocumentFragment(zend_class_entry *cl
INIT_CLASS_ENTRY(ce, "DOMDocumentFragment", class_DOMDocumentFragment_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
zend_class_implements(class_entry, 1, class_entry_DOMParentNode);
+ zend_register_class_alias("DOM\\DocumentFragment", class_entry);
zval property_firstElementChild_default_value;
ZVAL_UNDEF(&property_firstElementChild_default_value);
@@ -1385,6 +1531,7 @@ static zend_class_entry *register_class_DOMNodeList(zend_class_entry *class_entr
INIT_CLASS_ENTRY(ce, "DOMNodeList", class_DOMNodeList_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL);
zend_class_implements(class_entry, 2, class_entry_IteratorAggregate, class_entry_Countable);
+ zend_register_class_alias("DOM\\NodeList", class_entry);
zval property_length_default_value;
ZVAL_UNDEF(&property_length_default_value);
@@ -1402,6 +1549,7 @@ static zend_class_entry *register_class_DOMCharacterData(zend_class_entry *class
INIT_CLASS_ENTRY(ce, "DOMCharacterData", class_DOMCharacterData_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
zend_class_implements(class_entry, 1, class_entry_DOMChildNode);
+ zend_register_class_alias("DOM\\CharacterData", class_entry);
zval property_data_default_value;
ZVAL_UNDEF(&property_data_default_value);
@@ -1438,6 +1586,7 @@ static zend_class_entry *register_class_DOMAttr(zend_class_entry *class_entry_DO
INIT_CLASS_ENTRY(ce, "DOMAttr", class_DOMAttr_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
+ zend_register_class_alias("DOM\\Attr", class_entry);
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
@@ -1480,6 +1629,7 @@ static zend_class_entry *register_class_DOMElement(zend_class_entry *class_entry
INIT_CLASS_ENTRY(ce, "DOMElement", class_DOMElement_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
zend_class_implements(class_entry, 2, class_entry_DOMParentNode, class_entry_DOMChildNode);
+ zend_register_class_alias("DOM\\Element", class_entry);
zval property_tagName_default_value;
ZVAL_UNDEF(&property_tagName_default_value);
@@ -1542,20 +1692,12 @@ static zend_class_entry *register_class_DOMElement(zend_class_entry *class_entry
return class_entry;
}
-static zend_class_entry *register_class_DOMDocument(zend_class_entry *class_entry_DOMNode, zend_class_entry *class_entry_DOMParentNode)
+static zend_class_entry *register_class_DOMDocument(zend_class_entry *class_entry_DOM_Document)
{
zend_class_entry ce, *class_entry;
INIT_CLASS_ENTRY(ce, "DOMDocument", class_DOMDocument_methods);
- class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
- zend_class_implements(class_entry, 1, class_entry_DOMParentNode);
-
- zval property_doctype_default_value;
- ZVAL_UNDEF(&property_doctype_default_value);
- zend_string *property_doctype_name = zend_string_init("doctype", sizeof("doctype") - 1, 1);
- zend_string *property_doctype_class_DOMDocumentType = zend_string_init("DOMDocumentType", sizeof("DOMDocumentType")-1, 1);
- zend_declare_typed_property(class_entry, property_doctype_name, &property_doctype_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_doctype_class_DOMDocumentType, 0, MAY_BE_NULL));
- zend_string_release(property_doctype_name);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Document);
zval property_implementation_default_value;
ZVAL_UNDEF(&property_implementation_default_value);
@@ -1564,25 +1706,12 @@ static zend_class_entry *register_class_DOMDocument(zend_class_entry *class_entr
zend_declare_typed_property(class_entry, property_implementation_name, &property_implementation_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_implementation_class_DOMImplementation, 0, 0));
zend_string_release(property_implementation_name);
- zval property_documentElement_default_value;
- ZVAL_UNDEF(&property_documentElement_default_value);
- zend_string *property_documentElement_name = zend_string_init("documentElement", sizeof("documentElement") - 1, 1);
- zend_string *property_documentElement_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1);
- zend_declare_typed_property(class_entry, property_documentElement_name, &property_documentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_documentElement_class_DOMElement, 0, MAY_BE_NULL));
- zend_string_release(property_documentElement_name);
-
zval property_actualEncoding_default_value;
ZVAL_UNDEF(&property_actualEncoding_default_value);
zend_string *property_actualEncoding_name = zend_string_init("actualEncoding", sizeof("actualEncoding") - 1, 1);
zend_declare_typed_property(class_entry, property_actualEncoding_name, &property_actualEncoding_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
zend_string_release(property_actualEncoding_name);
- zval property_encoding_default_value;
- ZVAL_UNDEF(&property_encoding_default_value);
- zend_string *property_encoding_name = zend_string_init("encoding", sizeof("encoding") - 1, 1);
- zend_declare_typed_property(class_entry, property_encoding_name, &property_encoding_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
- zend_string_release(property_encoding_name);
-
zval property_xmlEncoding_default_value;
ZVAL_UNDEF(&property_xmlEncoding_default_value);
zend_string *property_xmlEncoding_name = zend_string_init("xmlEncoding", sizeof("xmlEncoding") - 1, 1);
@@ -1613,18 +1742,6 @@ static zend_class_entry *register_class_DOMDocument(zend_class_entry *class_entr
zend_declare_typed_property(class_entry, property_xmlVersion_name, &property_xmlVersion_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
zend_string_release(property_xmlVersion_name);
- zval property_strictErrorChecking_default_value;
- ZVAL_UNDEF(&property_strictErrorChecking_default_value);
- zend_string *property_strictErrorChecking_name = zend_string_init("strictErrorChecking", sizeof("strictErrorChecking") - 1, 1);
- zend_declare_typed_property(class_entry, property_strictErrorChecking_name, &property_strictErrorChecking_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
- zend_string_release(property_strictErrorChecking_name);
-
- zval property_documentURI_default_value;
- ZVAL_UNDEF(&property_documentURI_default_value);
- zend_string *property_documentURI_name = zend_string_init("documentURI", sizeof("documentURI") - 1, 1);
- zend_declare_typed_property(class_entry, property_documentURI_name, &property_documentURI_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
- zend_string_release(property_documentURI_name);
-
zval property_config_default_value;
ZVAL_UNDEF(&property_config_default_value);
zend_string *property_config_name = zend_string_init("config", sizeof("config") - 1, 1);
@@ -1667,26 +1784,6 @@ static zend_class_entry *register_class_DOMDocument(zend_class_entry *class_entr
zend_declare_typed_property(class_entry, property_substituteEntities_name, &property_substituteEntities_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
zend_string_release(property_substituteEntities_name);
- zval property_firstElementChild_default_value;
- ZVAL_UNDEF(&property_firstElementChild_default_value);
- zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1);
- zend_string *property_firstElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1);
- zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_DOMElement, 0, MAY_BE_NULL));
- zend_string_release(property_firstElementChild_name);
-
- zval property_lastElementChild_default_value;
- ZVAL_UNDEF(&property_lastElementChild_default_value);
- zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1);
- zend_string *property_lastElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1);
- zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_DOMElement, 0, MAY_BE_NULL));
- zend_string_release(property_lastElementChild_name);
-
- zval property_childElementCount_default_value;
- ZVAL_UNDEF(&property_childElementCount_default_value);
- zend_string *property_childElementCount_name = zend_string_init("childElementCount", sizeof("childElementCount") - 1, 1);
- zend_declare_typed_property(class_entry, property_childElementCount_name, &property_childElementCount_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(property_childElementCount_name);
-
return class_entry;
}
@@ -1697,6 +1794,7 @@ static zend_class_entry *register_class_DOMException(zend_class_entry *class_ent
INIT_CLASS_ENTRY(ce, "DOMException", class_DOMException_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_Exception);
class_entry->ce_flags |= ZEND_ACC_FINAL;
+ zend_register_class_alias("DOM\\DOMException", class_entry);
zval property_code_default_value;
ZVAL_LONG(&property_code_default_value, 0);
@@ -1713,6 +1811,7 @@ static zend_class_entry *register_class_DOMText(zend_class_entry *class_entry_DO
INIT_CLASS_ENTRY(ce, "DOMText", class_DOMText_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMCharacterData);
+ zend_register_class_alias("DOM\\Text", class_entry);
zval property_wholeText_default_value;
ZVAL_UNDEF(&property_wholeText_default_value);
@@ -1730,6 +1829,7 @@ static zend_class_entry *register_class_DOMNamedNodeMap(zend_class_entry *class_
INIT_CLASS_ENTRY(ce, "DOMNamedNodeMap", class_DOMNamedNodeMap_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL);
zend_class_implements(class_entry, 2, class_entry_IteratorAggregate, class_entry_Countable);
+ zend_register_class_alias("DOM\\NamedNodeMap", class_entry);
zval property_length_default_value;
ZVAL_UNDEF(&property_length_default_value);
@@ -1746,6 +1846,7 @@ static zend_class_entry *register_class_DOMEntity(zend_class_entry *class_entry_
INIT_CLASS_ENTRY(ce, "DOMEntity", class_DOMEntity_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
+ zend_register_class_alias("DOM\\Entity", class_entry);
zval property_publicId_default_value;
ZVAL_UNDEF(&property_publicId_default_value);
@@ -1792,6 +1893,7 @@ static zend_class_entry *register_class_DOMEntityReference(zend_class_entry *cla
INIT_CLASS_ENTRY(ce, "DOMEntityReference", class_DOMEntityReference_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
+ zend_register_class_alias("DOM\\EntityReference", class_entry);
return class_entry;
}
@@ -1802,6 +1904,7 @@ static zend_class_entry *register_class_DOMNotation(zend_class_entry *class_entr
INIT_CLASS_ENTRY(ce, "DOMNotation", class_DOMNotation_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
+ zend_register_class_alias("DOM\\Notation", class_entry);
zval property_publicId_default_value;
ZVAL_UNDEF(&property_publicId_default_value);
@@ -1824,6 +1927,7 @@ static zend_class_entry *register_class_DOMProcessingInstruction(zend_class_entr
INIT_CLASS_ENTRY(ce, "DOMProcessingInstruction", class_DOMProcessingInstruction_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_DOMNode);
+ zend_register_class_alias("DOM\\ProcessingInstruction", class_entry);
zval property_target_default_value;
ZVAL_UNDEF(&property_target_default_value);
@@ -1848,12 +1952,13 @@ static zend_class_entry *register_class_DOMXPath(void)
INIT_CLASS_ENTRY(ce, "DOMXPath", class_DOMXPath_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
+ zend_register_class_alias("DOM\\XPath", class_entry);
zval property_document_default_value;
ZVAL_UNDEF(&property_document_default_value);
zend_string *property_document_name = zend_string_init("document", sizeof("document") - 1, 1);
- zend_string *property_document_class_DOMDocument = zend_string_init("DOMDocument", sizeof("DOMDocument")-1, 1);
- zend_declare_typed_property(class_entry, property_document_name, &property_document_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_document_class_DOMDocument, 0, 0));
+ zend_string *property_document_class_DOM_Document = zend_string_init("DOM\\Document", sizeof("DOM\\Document")-1, 1);
+ zend_declare_typed_property(class_entry, property_document_name, &property_document_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_document_class_DOM_Document, 0, 0));
zend_string_release(property_document_name);
zval property_registerNodeNamespaces_default_value;
@@ -1865,3 +1970,155 @@ static zend_class_entry *register_class_DOMXPath(void)
return class_entry;
}
#endif
+
+static zend_class_entry *register_class_DOM_Document(zend_class_entry *class_entry_DOM_DOMNode, zend_class_entry *class_entry_DOM_DOMParentNode)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_NS_CLASS_ENTRY(ce, "DOM", "Document", class_DOM_Document_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_DOMNode);
+ class_entry->ce_flags |= ZEND_ACC_ABSTRACT;
+ zend_class_implements(class_entry, 1, class_entry_DOM_DOMParentNode);
+
+ zval property_doctype_default_value;
+ ZVAL_UNDEF(&property_doctype_default_value);
+ zend_string *property_doctype_name = zend_string_init("doctype", sizeof("doctype") - 1, 1);
+ zend_string *property_doctype_class_DOM_DocumentType = zend_string_init("DOM\\DocumentType", sizeof("DOM\\DocumentType")-1, 1);
+ zend_declare_typed_property(class_entry, property_doctype_name, &property_doctype_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_doctype_class_DOM_DocumentType, 0, MAY_BE_NULL));
+ zend_string_release(property_doctype_name);
+
+ zval property_documentElement_default_value;
+ ZVAL_UNDEF(&property_documentElement_default_value);
+ zend_string *property_documentElement_name = zend_string_init("documentElement", sizeof("documentElement") - 1, 1);
+ zend_string *property_documentElement_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1);
+ zend_declare_typed_property(class_entry, property_documentElement_name, &property_documentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_documentElement_class_DOM_Element, 0, MAY_BE_NULL));
+ zend_string_release(property_documentElement_name);
+
+ zval property_encoding_default_value;
+ ZVAL_UNDEF(&property_encoding_default_value);
+ zend_string *property_encoding_name = zend_string_init("encoding", sizeof("encoding") - 1, 1);
+ zend_declare_typed_property(class_entry, property_encoding_name, &property_encoding_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
+ zend_string_release(property_encoding_name);
+
+ zval property_strictErrorChecking_default_value;
+ ZVAL_UNDEF(&property_strictErrorChecking_default_value);
+ zend_string *property_strictErrorChecking_name = zend_string_init("strictErrorChecking", sizeof("strictErrorChecking") - 1, 1);
+ zend_declare_typed_property(class_entry, property_strictErrorChecking_name, &property_strictErrorChecking_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_string_release(property_strictErrorChecking_name);
+
+ zval property_documentURI_default_value;
+ ZVAL_UNDEF(&property_documentURI_default_value);
+ zend_string *property_documentURI_name = zend_string_init("documentURI", sizeof("documentURI") - 1, 1);
+ zend_declare_typed_property(class_entry, property_documentURI_name, &property_documentURI_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
+ zend_string_release(property_documentURI_name);
+
+ zval property_firstElementChild_default_value;
+ ZVAL_UNDEF(&property_firstElementChild_default_value);
+ zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1);
+ zend_string *property_firstElementChild_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1);
+ zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_DOM_Element, 0, MAY_BE_NULL));
+ zend_string_release(property_firstElementChild_name);
+
+ zval property_lastElementChild_default_value;
+ ZVAL_UNDEF(&property_lastElementChild_default_value);
+ zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1);
+ zend_string *property_lastElementChild_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1);
+ zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_DOM_Element, 0, MAY_BE_NULL));
+ zend_string_release(property_lastElementChild_name);
+
+ zval property_childElementCount_default_value;
+ ZVAL_UNDEF(&property_childElementCount_default_value);
+ zend_string *property_childElementCount_name = zend_string_init("childElementCount", sizeof("childElementCount") - 1, 1);
+ zend_declare_typed_property(class_entry, property_childElementCount_name, &property_childElementCount_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
+ zend_string_release(property_childElementCount_name);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_DOM_HTMLDocument(zend_class_entry *class_entry_DOM_DOM_Document)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_NS_CLASS_ENTRY(ce, "DOM", "HTMLDocument", class_DOM_HTMLDocument_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_DOM_Document);
+ class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES;
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_DOM_XMLDocument(zend_class_entry *class_entry_DOM_DOM_Document)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_NS_CLASS_ENTRY(ce, "DOM", "XMLDocument", class_DOM_XMLDocument_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_DOM_Document);
+ class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES;
+
+ zval property_xmlEncoding_default_value;
+ ZVAL_UNDEF(&property_xmlEncoding_default_value);
+ zend_string *property_xmlEncoding_name = zend_string_init("xmlEncoding", sizeof("xmlEncoding") - 1, 1);
+ zend_declare_typed_property(class_entry, property_xmlEncoding_name, &property_xmlEncoding_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
+ zend_string_release(property_xmlEncoding_name);
+
+ zval property_standalone_default_value;
+ ZVAL_UNDEF(&property_standalone_default_value);
+ zend_string *property_standalone_name = zend_string_init("standalone", sizeof("standalone") - 1, 1);
+ zend_declare_typed_property(class_entry, property_standalone_name, &property_standalone_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_string_release(property_standalone_name);
+
+ zval property_xmlStandalone_default_value;
+ ZVAL_UNDEF(&property_xmlStandalone_default_value);
+ zend_string *property_xmlStandalone_name = zend_string_init("xmlStandalone", sizeof("xmlStandalone") - 1, 1);
+ zend_declare_typed_property(class_entry, property_xmlStandalone_name, &property_xmlStandalone_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_string_release(property_xmlStandalone_name);
+
+ zval property_version_default_value;
+ ZVAL_UNDEF(&property_version_default_value);
+ zend_string *property_version_name = zend_string_init("version", sizeof("version") - 1, 1);
+ zend_declare_typed_property(class_entry, property_version_name, &property_version_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
+ zend_string_release(property_version_name);
+
+ zval property_xmlVersion_default_value;
+ ZVAL_UNDEF(&property_xmlVersion_default_value);
+ zend_string *property_xmlVersion_name = zend_string_init("xmlVersion", sizeof("xmlVersion") - 1, 1);
+ zend_declare_typed_property(class_entry, property_xmlVersion_name, &property_xmlVersion_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
+ zend_string_release(property_xmlVersion_name);
+
+ zval property_formatOutput_default_value;
+ ZVAL_UNDEF(&property_formatOutput_default_value);
+ zend_string *property_formatOutput_name = zend_string_init("formatOutput", sizeof("formatOutput") - 1, 1);
+ zend_declare_typed_property(class_entry, property_formatOutput_name, &property_formatOutput_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_string_release(property_formatOutput_name);
+
+ zval property_validateOnParse_default_value;
+ ZVAL_UNDEF(&property_validateOnParse_default_value);
+ zend_string *property_validateOnParse_name = zend_string_init("validateOnParse", sizeof("validateOnParse") - 1, 1);
+ zend_declare_typed_property(class_entry, property_validateOnParse_name, &property_validateOnParse_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_string_release(property_validateOnParse_name);
+
+ zval property_resolveExternals_default_value;
+ ZVAL_UNDEF(&property_resolveExternals_default_value);
+ zend_string *property_resolveExternals_name = zend_string_init("resolveExternals", sizeof("resolveExternals") - 1, 1);
+ zend_declare_typed_property(class_entry, property_resolveExternals_name, &property_resolveExternals_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_string_release(property_resolveExternals_name);
+
+ zval property_preserveWhiteSpace_default_value;
+ ZVAL_UNDEF(&property_preserveWhiteSpace_default_value);
+ zend_string *property_preserveWhiteSpace_name = zend_string_init("preserveWhiteSpace", sizeof("preserveWhiteSpace") - 1, 1);
+ zend_declare_typed_property(class_entry, property_preserveWhiteSpace_name, &property_preserveWhiteSpace_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_string_release(property_preserveWhiteSpace_name);
+
+ zval property_recover_default_value;
+ ZVAL_UNDEF(&property_recover_default_value);
+ zend_string *property_recover_name = zend_string_init("recover", sizeof("recover") - 1, 1);
+ zend_declare_typed_property(class_entry, property_recover_name, &property_recover_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_string_release(property_recover_name);
+
+ zval property_substituteEntities_default_value;
+ ZVAL_UNDEF(&property_substituteEntities_default_value);
+ zend_string *property_substituteEntities_name = zend_string_init("substituteEntities", sizeof("substituteEntities") - 1, 1);
+ zend_declare_typed_property(class_entry, property_substituteEntities_name, &property_substituteEntities_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_string_release(property_substituteEntities_name);
+
+ return class_entry;
+}
diff --git a/ext/dom/tests/DOMDocument_adoptNode.phpt b/ext/dom/tests/DOMDocument_adoptNode.phpt
index 2382cabd5136..81fe4c564971 100644
--- a/ext/dom/tests/DOMDocument_adoptNode.phpt
+++ b/ext/dom/tests/DOMDocument_adoptNode.phpt
@@ -141,7 +141,7 @@ string(27) "
Not Supported Error
-- Adopt a document (strict error off) --
-Warning: DOMDocument::adoptNode(): Not Supported Error in %s on line %d
+Warning: DOM\Document::adoptNode(): Not Supported Error in %s on line %d
-- Adopt an attribute --
bool(true)
bool(true)
diff --git a/ext/dom/tests/DOMDocument_load_error6.phpt b/ext/dom/tests/DOMDocument_load_error6.phpt
index 948780f18965..a4f9bc4a02a8 100644
--- a/ext/dom/tests/DOMDocument_load_error6.phpt
+++ b/ext/dom/tests/DOMDocument_load_error6.phpt
@@ -23,5 +23,5 @@ var_dump($dom->load(str_repeat(" ", PHP_MAXPATHLEN + 1)));
?>
--EXPECT--
DOMDocument::load(): Argument #1 ($filename) must not be empty
-Path to document must not contain any null bytes
+DOMDocument::load(): Argument #1 ($filename) must not contain any null bytes
bool(false)
diff --git a/ext/dom/tests/DOMDocument_relaxNGValidateSource_error1.phpt b/ext/dom/tests/DOMDocument_relaxNGValidateSource_error1.phpt
index fd476749fe20..3baa24c50c65 100644
--- a/ext/dom/tests/DOMDocument_relaxNGValidateSource_error1.phpt
+++ b/ext/dom/tests/DOMDocument_relaxNGValidateSource_error1.phpt
@@ -35,5 +35,5 @@ var_dump($result);
?>
--EXPECTF--
-Warning: DOMDocument::relaxNGValidateSource(): Did not expect element pear there in %s on line %d
+Warning: DOM\Document::relaxNGValidateSource(): Did not expect element pear there in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_relaxNGValidateSource_error2.phpt b/ext/dom/tests/DOMDocument_relaxNGValidateSource_error2.phpt
index da00943c7caf..76aa3a04939d 100644
--- a/ext/dom/tests/DOMDocument_relaxNGValidateSource_error2.phpt
+++ b/ext/dom/tests/DOMDocument_relaxNGValidateSource_error2.phpt
@@ -31,7 +31,7 @@ var_dump($result);
?>
--EXPECTF--
-Warning: DOMDocument::relaxNGValidateSource(): xmlRelaxNGParseElement: element has no content in %s on line %d
+Warning: DOM\Document::relaxNGValidateSource(): xmlRelaxNGParseElement: element has no content in %s on line %d
-Warning: DOMDocument::relaxNGValidateSource(): Invalid RelaxNG in %s on line %d
+Warning: DOM\Document::relaxNGValidateSource(): Invalid RelaxNG in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_relaxNGValidate_error1.phpt b/ext/dom/tests/DOMDocument_relaxNGValidate_error1.phpt
index aa38ca5c1ed2..aff2680b3dae 100644
--- a/ext/dom/tests/DOMDocument_relaxNGValidate_error1.phpt
+++ b/ext/dom/tests/DOMDocument_relaxNGValidate_error1.phpt
@@ -20,5 +20,5 @@ $result = $doc->relaxNGValidate($rng);
var_dump($result);
?>
--EXPECTF--
-Warning: DOMDocument::relaxNGValidate(): Did not expect element pear there in %s on line %d
+Warning: DOM\Document::relaxNGValidate(): Did not expect element pear there in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt b/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt
index 1ad46e014a0a..5dde5e8ced92 100644
--- a/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt
+++ b/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt
@@ -20,9 +20,9 @@ $result = $doc->relaxNGValidate($rng);
var_dump($result);
?>
--EXPECTF--
-Warning: DOMDocument::relaxNGValidate(): I/O warning : failed to load external entity "%s/foo.rng" in %s on line %d
+Warning: DOM\Document::relaxNGValidate(): I/O warning : failed to load external entity "%s/foo.rng" in %s on line %d
-Warning: DOMDocument::relaxNGValidate(): xmlRelaxNGParse: could not load %s/foo.rng in %s on line %d
+Warning: DOM\Document::relaxNGValidate(): xmlRelaxNGParse: could not load %s/foo.rng in %s on line %d
-Warning: DOMDocument::relaxNGValidate(): Invalid RelaxNG in %s on line %d
+Warning: DOM\Document::relaxNGValidate(): Invalid RelaxNG in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_schemaValidateSource_error1.phpt b/ext/dom/tests/DOMDocument_schemaValidateSource_error1.phpt
index 10b5d07664d5..f71d192a616e 100644
--- a/ext/dom/tests/DOMDocument_schemaValidateSource_error1.phpt
+++ b/ext/dom/tests/DOMDocument_schemaValidateSource_error1.phpt
@@ -17,13 +17,13 @@ var_dump($result);
?>
--EXPECTF--
-Warning: DOMDocument::schemaValidateSource(): Entity: line 1: parser error : Start tag expected, '<' not found in %s.php on line %d
+Warning: DOM\Document::schemaValidateSource(): Entity: line 1: parser error : Start tag expected, '<' not found in %s on line %d
-Warning: DOMDocument::schemaValidateSource(): string that is not a schema in %s.php on line %d
+Warning: DOM\Document::schemaValidateSource(): string that is not a schema in %s on line %d
-Warning: DOMDocument::schemaValidateSource(): ^ in %s.php on line %d
+Warning: DOM\Document::schemaValidateSource(): ^ in %s on line %d
-Warning: DOMDocument::schemaValidateSource(): Failed to parse the XML resource 'in_memory_buffer'. in %s.php on line %d
+Warning: DOM\Document::schemaValidateSource(): Failed to parse the XML resource 'in_memory_buffer'. in %s on line %d
-Warning: DOMDocument::schemaValidateSource(): Invalid Schema in %s.php on line %d
+Warning: DOM\Document::schemaValidateSource(): Invalid Schema in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_schemaValidateSource_error2.phpt b/ext/dom/tests/DOMDocument_schemaValidateSource_error2.phpt
index d099afa779c6..b9fea75807a4 100644
--- a/ext/dom/tests/DOMDocument_schemaValidateSource_error2.phpt
+++ b/ext/dom/tests/DOMDocument_schemaValidateSource_error2.phpt
@@ -19,5 +19,5 @@ var_dump($result);
?>
--EXPECTF--
-Warning: DOMDocument::schemaValidateSource(): Element 'books': No matching global declaration available for the validation root. in %s.php on line %d
+Warning: DOM\Document::schemaValidateSource(): Element 'books': No matching global declaration available for the validation root. in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_schemaValidateSource_error3.phpt b/ext/dom/tests/DOMDocument_schemaValidateSource_error3.phpt
index ec295a55e339..bdeb74ff6c2c 100644
--- a/ext/dom/tests/DOMDocument_schemaValidateSource_error3.phpt
+++ b/ext/dom/tests/DOMDocument_schemaValidateSource_error3.phpt
@@ -20,4 +20,4 @@ try {
?>
--EXPECT--
-DOMDocument::schemaValidateSource(): Argument #1 ($source) must not be empty
+DOM\Document::schemaValidateSource(): Argument #1 ($source) must not be empty
diff --git a/ext/dom/tests/DOMDocument_schemaValidate_error1.phpt b/ext/dom/tests/DOMDocument_schemaValidate_error1.phpt
index baa8debbdb88..2923943a3c8f 100644
--- a/ext/dom/tests/DOMDocument_schemaValidate_error1.phpt
+++ b/ext/dom/tests/DOMDocument_schemaValidate_error1.phpt
@@ -17,13 +17,13 @@ var_dump($result);
?>
--EXPECTF--
-Warning: DOMDocument::schemaValidate(): %sbook-not-a-schema.xsd:1: parser error : Start tag expected, '<' not found in %s.php on line %d
+Warning: DOM\Document::schemaValidate(): %s/book-not-a-schema.xsd:1: parser error : Start tag expected, '<' not found in %s on line %d
-Warning: DOMDocument::schemaValidate(): Let's see what happens upon parsing a file that doesn't contain a schema. in %s.php on line %d
+Warning: DOM\Document::schemaValidate(): Let's see what happens upon parsing a file that doesn't contain a schema. in %s on line %d
-Warning: DOMDocument::schemaValidate(): ^ in %s.php on line %d
+Warning: DOM\Document::schemaValidate(): ^ in %s on line %d
-Warning: DOMDocument::schemaValidate(): Failed to parse the XML resource '%sbook-not-a-schema.xsd'. in %s.php on line %d
+Warning: DOM\Document::schemaValidate(): Failed to parse the XML resource '%s/book-not-a-schema.xsd'. in %s on line %d
-Warning: DOMDocument::schemaValidate(): Invalid Schema in %s.php on line %d
+Warning: DOM\Document::schemaValidate(): Invalid Schema in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_schemaValidate_error2.phpt b/ext/dom/tests/DOMDocument_schemaValidate_error2.phpt
index 3c4f6f4ff5ed..ddc491c70ca3 100644
--- a/ext/dom/tests/DOMDocument_schemaValidate_error2.phpt
+++ b/ext/dom/tests/DOMDocument_schemaValidate_error2.phpt
@@ -17,5 +17,5 @@ var_dump($result);
?>
--EXPECTF--
-Warning: DOMDocument::schemaValidate(): Element 'books': No matching global declaration available for the validation root. in %s.php on line %d
+Warning: DOM\Document::schemaValidate(): Element 'books': No matching global declaration available for the validation root. in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_schemaValidate_error3.phpt b/ext/dom/tests/DOMDocument_schemaValidate_error3.phpt
index 274463e62e13..d48ed3d1963b 100644
--- a/ext/dom/tests/DOMDocument_schemaValidate_error3.phpt
+++ b/ext/dom/tests/DOMDocument_schemaValidate_error3.phpt
@@ -20,4 +20,4 @@ try {
?>
--EXPECT--
-DOMDocument::schemaValidate(): Argument #1 ($filename) must not be empty
+DOM\Document::schemaValidate(): Argument #1 ($filename) must not be empty
diff --git a/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt b/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt
index 2feda5d1e1f8..2c179ed35bd4 100644
--- a/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt
+++ b/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt
@@ -17,9 +17,9 @@ var_dump($result);
?>
--EXPECTF--
-Warning: DOMDocument::schemaValidate(): I/O warning : failed to load external entity "%snon-existent-file" in %s.php on line %d
+Warning: DOM\Document::schemaValidate(): I/O warning : failed to load external entity "%s/non-existent-file" in %s on line %d
-Warning: DOMDocument::schemaValidate(): Failed to locate the main schema resource at '%s/non-existent-file'. in %s.php on line %d
+Warning: DOM\Document::schemaValidate(): Failed to locate the main schema resource at '%s/non-existent-file'. in %s on line %d
-Warning: DOMDocument::schemaValidate(): Invalid Schema in %s.php on line %d
+Warning: DOM\Document::schemaValidate(): Invalid Schema in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_schemaValidate_error6.phpt b/ext/dom/tests/DOMDocument_schemaValidate_error6.phpt
index c5d99b20a3fe..3668921264cf 100644
--- a/ext/dom/tests/DOMDocument_schemaValidate_error6.phpt
+++ b/ext/dom/tests/DOMDocument_schemaValidate_error6.phpt
@@ -19,7 +19,7 @@ var_dump($doc->schemaValidate(str_repeat(" ", PHP_MAXPATHLEN + 1)));
?>
--EXPECTF--
-DOMDocument::schemaValidate(): Argument #1 ($filename) must not contain any null bytes
+DOM\Document::schemaValidate(): Argument #1 ($filename) must not contain any null bytes
-Warning: DOMDocument::schemaValidate(): Invalid Schema file source in %s on line %d
+Warning: DOM\Document::schemaValidate(): Invalid Schema file source in %s on line %d
bool(false)
diff --git a/ext/dom/tests/DOMDocument_strictErrorChecking_variation.phpt b/ext/dom/tests/DOMDocument_strictErrorChecking_variation.phpt
index 60027f5117d9..3def63da9b5a 100644
--- a/ext/dom/tests/DOMDocument_strictErrorChecking_variation.phpt
+++ b/ext/dom/tests/DOMDocument_strictErrorChecking_variation.phpt
@@ -56,4 +56,4 @@ See if strictErrorChecking is off
bool(false)
Should raise PHP error because strictErrorChecking is off
-Warning: DOMDocument::createAttribute(): Invalid Character Error in %sDOMDocument_strictErrorChecking_variation.php on line %d
+Warning: DOM\Document::createAttribute(): Invalid Character Error in %s on line %d
diff --git a/ext/dom/tests/domobject_debug_handler.phpt b/ext/dom/tests/domobject_debug_handler.phpt
index 98a9a72315ac..c97655a63a14 100644
--- a/ext/dom/tests/domobject_debug_handler.phpt
+++ b/ext/dom/tests/domobject_debug_handler.phpt
@@ -20,14 +20,12 @@ object(DOMDocument)#1 (41) {
["dynamicProperty"]=>
object(stdClass)#2 (0) {
}
- ["doctype"]=>
- NULL
["implementation"]=>
string(22) "(object value omitted)"
- ["documentElement"]=>
- string(22) "(object value omitted)"
["actualEncoding"]=>
NULL
+ ["config"]=>
+ NULL
["encoding"]=>
NULL
["xmlEncoding"]=>
@@ -40,12 +38,6 @@ object(DOMDocument)#1 (41) {
string(3) "1.0"
["xmlVersion"]=>
string(3) "1.0"
- ["strictErrorChecking"]=>
- bool(true)
- ["documentURI"]=>
- string(%d) %s
- ["config"]=>
- NULL
["formatOutput"]=>
bool(false)
["validateOnParse"]=>
@@ -58,6 +50,14 @@ object(DOMDocument)#1 (41) {
bool(false)
["substituteEntities"]=>
bool(false)
+ ["doctype"]=>
+ NULL
+ ["documentElement"]=>
+ string(22) "(object value omitted)"
+ ["strictErrorChecking"]=>
+ bool(true)
+ ["documentURI"]=>
+ string(%d) "%s"
["firstElementChild"]=>
string(22) "(object value omitted)"
["lastElementChild"]=>
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_GB18030.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_GB18030.phpt
new file mode 100644
index 000000000000..c0139661e307
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_GB18030.phpt
@@ -0,0 +1,36 @@
+--TEST--
+DOM\HTMLDocument GB18030 encoding test
+--EXTENSIONS--
+dom
+--FILE--
+encoding);
+$dom->documentElement->firstChild->nextElementSibling->textContent = "é";
+$output = $dom->saveHTML();
+echo $output, "\n";
+$dom->saveHTMLFile(__DIR__ . "/gb18030_output.tmp");
+var_dump(file_get_contents(__DIR__ . "/gb18030_output.tmp") === $output);
+
+echo "--- After changing encoding to UTF-8 ---\n";
+$dom->encoding = "UTF-8";
+echo $dom->saveHTML(), "\n";
+
+?>
+--CLEAN--
+
+--EXPECT--
+string(7) "gb18030"
+
+
+
+��
+bool(true)
+--- After changing encoding to UTF-8 ---
+
+
+
+é
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_Shift_JIS.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_Shift_JIS.phpt
new file mode 100644
index 000000000000..c021d7022f27
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_Shift_JIS.phpt
@@ -0,0 +1,40 @@
+--TEST--
+DOM\HTMLDocument Shift JIS encoding test
+--EXTENSIONS--
+dom
+--FILE--
+encoding);
+$dom->documentElement->firstChild->nextElementSibling->textContent .= "é";
+$output = $dom->saveHTML();
+echo $output, "\n";
+$dom->saveHTMLFile(__DIR__ . "/shift_jis.tmp");
+var_dump(file_get_contents(__DIR__ . "/shift_jis.tmp") === $output);
+
+echo "--- After changing encoding to UTF-8 ---\n";
+$dom->encoding = "UTF-8";
+echo $dom->saveHTML(), "\n";
+
+?>
+--CLEAN--
+
+--EXPECT--
+string(9) "Shift_JIS"
+
+
+
+
+ �₠
+?
+bool(true)
+--- After changing encoding to UTF-8 ---
+
+
+
+
+ やあ
+é
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16BE_BOM.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16BE_BOM.phpt
new file mode 100644
index 000000000000..9e10859f1914
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16BE_BOM.phpt
@@ -0,0 +1,38 @@
+--TEST--
+DOM\HTMLDocument UTF-16BE BOM encoding test
+--EXTENSIONS--
+dom
+--FILE--
+encoding);
+$dom->documentElement->firstChild->nextElementSibling->textContent = "é";
+$output = $dom->saveHTML();
+echo $output, "\n";
+$dom->saveHTMLFile(__DIR__ . "/utf16be_bom_output.tmp");
+var_dump(file_get_contents(__DIR__ . "/utf16be_bom_output.tmp") === $output);
+
+echo "--- After changing encoding to UTF-8 ---\n";
+$dom->encoding = "UTF-8";
+echo $dom->saveHTML(), "\n";
+
+?>
+--CLEAN--
+
+--EXPECTF--
+string(8) "UTF-16BE"
+%0<%0!%0D%0O%0C%0T%0Y%0P%0E%0 %0h%0t%0m%0l%0>%0<%0h%0t%0m%0l%0>%0<%0h%0e%0a%0d%0>%0
+%0<%0!%0-%0-%0 %0i%0n%0t%0e%0n%0t%0i%0o%0n%0a%0l%0 %0l%0i%0e%0s%0 %0a%0n%0d%0 %0d%0e%0c%0e%0i%0t%0 %0-%0-%0>%0
+%0<%0m%0e%0t%0a%0 %0c%0h%0a%0r%0s%0e%0t%0=%0"%0u%0t%0f%0-%08%0"%0>%0
+%0<%0/%0h%0e%0a%0d%0>%0
+%0<%0b%0o%0d%0y%0>%0�%0<%0/%0b%0o%0d%0y%0>%0<%0/%0h%0t%0m%0l%0>
+bool(true)
+--- After changing encoding to UTF-8 ---
+
+
+
+
+é
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16LE_BOM.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16LE_BOM.phpt
new file mode 100644
index 000000000000..7006163e3de0
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16LE_BOM.phpt
@@ -0,0 +1,38 @@
+--TEST--
+DOM\HTMLDocument UTF-16LE BOM encoding test
+--EXTENSIONS--
+dom
+--FILE--
+encoding);
+$dom->documentElement->firstChild->nextElementSibling->textContent = "é";
+$output = $dom->saveHTML();
+echo $output, "\n";
+$dom->saveHTMLFile(__DIR__ . "/utf16le_bom_output.tmp");
+var_dump(file_get_contents(__DIR__ . "/utf16le_bom_output.tmp") === $output);
+
+echo "--- After changing encoding to UTF-8 ---\n";
+$dom->encoding = "UTF-8";
+echo $dom->saveHTML(), "\n";
+
+?>
+--CLEAN--
+
+--EXPECTF--
+string(8) "UTF-16LE"
+<%0!%0D%0O%0C%0T%0Y%0P%0E%0 %0h%0t%0m%0l%0>%0<%0h%0t%0m%0l%0>%0<%0h%0e%0a%0d%0>%0
+%0<%0!%0-%0-%0 %0i%0n%0t%0e%0n%0t%0i%0o%0n%0a%0l%0 %0l%0i%0e%0s%0 %0a%0n%0d%0 %0d%0e%0c%0e%0i%0t%0 %0-%0-%0>%0
+%0<%0m%0e%0t%0a%0 %0c%0h%0a%0r%0s%0e%0t%0=%0"%0u%0t%0f%0-%08%0"%0>%0
+%0<%0/%0h%0e%0a%0d%0>%0
+%0<%0b%0o%0d%0y%0>%0�%0<%0/%0b%0o%0d%0y%0>%0<%0/%0h%0t%0m%0l%0>%0
+bool(true)
+--- After changing encoding to UTF-8 ---
+
+
+
+
+é
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF8_BOM.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF8_BOM.phpt
new file mode 100644
index 000000000000..65326ad6b037
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF8_BOM.phpt
@@ -0,0 +1,38 @@
+--TEST--
+DOM\HTMLDocument UTF-8 BOM encoding test
+--EXTENSIONS--
+dom
+--FILE--
+encoding);
+$dom->documentElement->firstChild->nextElementSibling->textContent = "é";
+$output = $dom->saveHTML();
+echo $output, "\n";
+$dom->saveHTMLFile(__DIR__ . "/utf8_bom_output.tmp");
+var_dump(file_get_contents(__DIR__ . "/utf8_bom_output.tmp") === $output);
+
+echo "--- After changing encoding to UTF-8 ---\n";
+$dom->encoding = "UTF-8";
+echo $dom->saveHTML(), "\n";
+
+?>
+--CLEAN--
+
+--EXPECT--
+string(5) "UTF-8"
+
+
+
+
+é
+bool(true)
+--- After changing encoding to UTF-8 ---
+
+
+
+
+é
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_Windows1251.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_Windows1251.phpt
new file mode 100644
index 000000000000..5b85639996d9
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_Windows1251.phpt
@@ -0,0 +1,40 @@
+--TEST--
+DOM\HTMLDocument Windows-1251 encoding test
+--EXTENSIONS--
+dom
+--FILE--
+encoding);
+$dom->documentElement->firstChild->nextElementSibling->textContent .= "é"; // Note: won't show up in Windows 1251 because it doesn't exist there
+$output = $dom->saveHTML();
+echo $output, "\n";
+$dom->saveHTMLFile(__DIR__ . "/windows1251_output.tmp");
+var_dump(file_get_contents(__DIR__ . "/windows1251_output.tmp") === $output);
+
+echo "--- After changing encoding to UTF-8 ---\n";
+$dom->encoding = "UTF-8";
+echo $dom->saveHTML(), "\n";
+
+?>
+--CLEAN--
+
+--EXPECT--
+string(12) "windows-1251"
+
+
+
+
+ A � B � C
+?
+bool(true)
+--- After changing encoding to UTF-8 ---
+
+
+
+
+ A ф B б C
+é
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_http_header.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_http_header.phpt
new file mode 100644
index 000000000000..3209c3e5d6ae
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_http_header.phpt
@@ -0,0 +1,96 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() HTTP header Content-Type
+--EXTENSIONS--
+dom
+--SKIPIF--
+
+--FILE--
+ [
+ "/html; Charset=\"ISO-8859-1\"",
+ "text/; Charset=\"ISO-8859-1\"",
+ "tex°t/html; Charset=\"ISO-8859-1\"",
+ "/; Charset=\"ISO-8859-1\"",
+ "$/€; Charset=\"ISO-8859-1\"",
+ "; Charset=\"ISO-8859-1\"",
+ ";",
+ "",
+ " \t",
+ ],
+ "Valid type/subtype without charset" => [
+ "text/html; x=ISO-8859-1",
+ "text/html; x=\"ISO-8859-1\"",
+ "text/html; charet=\"ISO-8859-1\"",
+ "text/html; chars et=\"ISO-8859-1\"",
+ ],
+ "All valid inputs" => [
+ "text/html; charset=ISO-8859-1",
+ "\t\r text/html; charset=ISO-8859-1 \t",
+ "text/html; foo=bar;charset=ISO-8859-1",
+ "text/html; foo=bar;charset=ISO-8859-1;bar=\"foooooo\"",
+ "text/html;;;; charset=ISO-8859-1",
+ "text/html; Charset=\"ISO-8859-1\"",
+ "text/html; Charset=\"ISO\\-8859-1\"",
+ "text/html; ;; ; ;; Charset=\"ISO-8859-1\"",
+ "text/html;Charset=\"ISO-8859-1",
+ "tex.t/h#\$%!&'*%2B-.^_`|~tml;Charset=\"ISO-8859-1\"", // Note: have to encode + as 2B because of implementation details of http_server()
+ ],
+ "Valid input, but invalid encoding name" => [
+ "text/html;Charset=\"ISO-8859-1\\",
+ "text/html;Charset=\"ISO-8859-1\\\"",
+ "text/html;Charset=\"foobar\\\"",
+ "text/html;Charset=\"\\\"",
+ "text/html;Charset=",
+ ],
+];
+
+foreach ($tests as $name => $headers) {
+ echo "--- $name ---\n";
+ $responses = array_map(fn ($header) => "data://text/plain,HTTP/1.1 200 OK\r\nContent-Type: " . $header . "\r\n\r\n" . "\xE4\xF6\xFC
\n", $headers);
+ ['pid' => $pid, 'uri' => $uri] = http_server($responses);
+ for ($i = 0; $i < count($responses); $i++) {
+ $result = DOM\HTMLDocument::createFromFile($uri, LIBXML_NOERROR);
+ echo $result->textContent;
+ }
+ http_server_kill($pid);
+}
+?>
+--EXPECT--
+--- Invalid type/subtype ---
+���
+���
+���
+���
+���
+���
+���
+���
+���
+--- Valid type/subtype without charset ---
+���
+���
+���
+���
+--- All valid inputs ---
+äöü
+äöü
+äöü
+äöü
+äöü
+äöü
+äöü
+äöü
+äöü
+äöü
+--- Valid input, but invalid encoding name ---
+���
+���
+���
+���
+���
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_override_encoding.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_override_encoding.phpt
new file mode 100644
index 000000000000..4f6f9943d2b6
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_override_encoding.phpt
@@ -0,0 +1,33 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() with overrideEncoding
+--EXTENSIONS--
+dom
+--FILE--
+getMessage(), "\n";
+}
+
+// The override encoding matches with the document encoding attribute
+$dom = DOM\HTMLDocument::createFromFile(__DIR__ . '/gb18030_without_charset.html', overrideEncoding: 'GB18030');
+var_dump($dom->documentElement->lastChild->textContent);
+var_dump($dom->encoding);
+
+// The override encoding mismatches with the document encoding attribute
+$dom = DOM\HTMLDocument::createFromFile(__DIR__ . '/fallback_encoding.html', overrideEncoding: 'Windows-1252');
+var_dump($dom->documentElement->lastChild->textContent);
+var_dump($dom->encoding);
+
+?>
+--EXPECT--
+DOM\HTMLDocument::createFromFile(): Argument #3 ($overrideEncoding) must be a valid document encoding
+string(20) "
+ Héllo, world!
+"
+string(7) "gb18030"
+string(1) "
+"
+string(12) "windows-1252"
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromString_override_encoding.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromString_override_encoding.phpt
new file mode 100644
index 000000000000..b276be453eec
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromString_override_encoding.phpt
@@ -0,0 +1,33 @@
+--TEST--
+DOM\HTMLDocument::createFromString() with overrideEncoding
+--EXTENSIONS--
+dom
+--FILE--
+getMessage(), "\n";
+}
+
+// The override encoding matches with the document encoding attribute
+$dom = DOM\HTMLDocument::createFromString(file_get_contents(__DIR__ . '/gb18030_without_charset.html'), overrideEncoding: 'GB18030');
+var_dump($dom->documentElement->lastChild->textContent);
+var_dump($dom->encoding);
+
+// The override encoding mismatches with the document encoding attribute
+$dom = DOM\HTMLDocument::createFromString(file_get_contents(__DIR__ . '/fallback_encoding.html'), overrideEncoding: 'Windows-1252');
+var_dump($dom->documentElement->lastChild->textContent);
+var_dump($dom->encoding);
+
+?>
+--EXPECT--
+DOM\HTMLDocument::createFromString(): Argument #3 ($overrideEncoding) must be a valid document encoding
+string(20) "
+ Héllo, world!
+"
+string(7) "gb18030"
+string(1) "
+"
+string(12) "windows-1252"
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_01.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_01.phpt
new file mode 100644
index 000000000000..3988767e5806
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_01.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\HTMLDocument edge case encoding 01
+--EXTENSIONS--
+dom
+--FILE--
+ UTF-8
+// Create a UTF-8 string where a UTF-8 byte sequence falls over the boundary of the 4096 byte buffer
+$dom = DOM\HTMLDocument::createEmpty();
+$dom->append(str_repeat("A", 4096 - 2) . "\xf0\x90\x8d\x88AA");
+var_dump($dom->saveHTML());
+
+?>
+--EXPECT--
+string(4100) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA𐍈AA"
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_02.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_02.phpt
new file mode 100644
index 000000000000..5ac5694a89dc
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_02.phpt
@@ -0,0 +1,27 @@
+--TEST--
+DOM\HTMLDocument edge case encoding 02
+--EXTENSIONS--
+dom
+--FILE--
+ GB18030
+$dom = DOM\HTMLDocument::createEmpty("GB18030");
+// Create a UTF-8 string where a UTF-8 byte sequence falls over the boundary of the 4096 byte buffer
+// *and* the sequence also falls over the boundary for the result
+$dom->append(str_repeat("A", 4096 - 2) . "\xf0\x90\x8d\x88AA");
+var_dump($output = $dom->saveHTML());
+
+// GB18030 encoding of the above UTF-8 symbol
+var_dump($output[4094] == "\x90");
+var_dump($output[4095] == "\x30");
+var_dump($output[4096] == "\xd5");
+var_dump($output[4097] == "\x30");
+
+?>
+--EXPECT--
+string(4100) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�0�0AA"
+bool(true)
+bool(true)
+bool(true)
+bool(true)
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_03.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_03.phpt
new file mode 100644
index 000000000000..d68257ca80fe
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_03.phpt
@@ -0,0 +1,17 @@
+--TEST--
+DOM\HTMLDocument edge case encoding 03
+--EXTENSIONS--
+dom
+--FILE--
+ GB18030
+$dom = DOM\HTMLDocument::createEmpty("GB18030");
+// Create a UTF-8 string where an invalid UTF-8 byte sequence falls over the boundary of the 4096 byte buffer
+// Note: the strange ?1?7 sequence is the GB18030 encoding for the unicode replacement character
+$dom->append(str_repeat("A", 4096 - 2) . "\xff\xff\xff");
+var_dump($dom->saveHTML());
+
+?>
+--EXPECT--
+string(4106) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�1�7�1�7�1�7"
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_04.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_04.phpt
new file mode 100644
index 000000000000..ccff90a83320
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_04.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\HTMLDocument edge case encoding 04
+--EXTENSIONS--
+dom
+--FILE--
+ UTF-8
+$dom = DOM\HTMLDocument::createEmpty();
+// Create a UTF-8 string where an invalid UTF-8 byte sequence falls over the boundary of the 4096 byte buffer
+$dom->append(str_repeat("A", 4096 - 2) . "\xff\xff\xff");
+var_dump($dom->saveHTML());
+
+?>
+--EXPECT--
+string(4103) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���"
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_05.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_05.phpt
new file mode 100644
index 000000000000..e7da1e439b70
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_05.phpt
@@ -0,0 +1,21 @@
+--TEST--
+DOM\HTMLDocument edge case encoding 05
+--EXTENSIONS--
+dom
+--FILE--
+ UTF-8
+$header = " ";
+$padding_required_until_4094 = 4094 - strlen($header);
+$trailer = "\x90\x30\xd5\x30";
+$dom = DOM\HTMLDocument::createFromString($header . str_repeat("A", $padding_required_until_4094) . $trailer);
+// GB18030 byte sequence crossing the 4096 boundary
+var_dump($dom->encoding);
+$dom->encoding = "UTF-8";
+var_dump($dom->saveHTML());
+
+?>
+--EXPECT--
+string(7) "gb18030"
+string(4112) " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA𐍈"
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_06.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_06.phpt
new file mode 100644
index 000000000000..0b472b466fbe
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_06.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\HTMLDocument edge case encoding 06
+--EXTENSIONS--
+dom
+--FILE--
+ UTF-8
+$dom = DOM\HTMLDocument::createEmpty();
+// Create a UTF-8 string where a *broken* UTF-8 byte sequence falls over the boundary of the 4096 byte buffer
+$dom->append(str_repeat("A", 4096 - 1) . "\xf0\x90");
+var_dump($dom->saveHTML());
+
+?>
+--EXPECT--
+string(4101) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��"
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_07.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_07.phpt
new file mode 100644
index 000000000000..6cf2d1d8f5e4
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_07.phpt
@@ -0,0 +1,21 @@
+--TEST--
+DOM\HTMLDocument edge case encoding 07
+--EXTENSIONS--
+dom
+--FILE--
+ UTF-8
+$header = " ";
+$padding_required_until_4095 = 4095 - strlen($header);
+$trailer = "\x90\x30";
+$dom = DOM\HTMLDocument::createFromString($header . str_repeat("A", $padding_required_until_4095) . $trailer);
+// GB18030 *broken* byte sequence crossing the 4096 boundary
+var_dump($dom->encoding);
+$dom->encoding = "UTF-8";
+var_dump($dom->saveHTML());
+
+?>
+--EXPECT--
+string(7) "gb18030"
+string(4115) " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��"
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_field_test.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_field_test.phpt
new file mode 100644
index 000000000000..bab3532565b8
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_field_test.phpt
@@ -0,0 +1,43 @@
+--TEST--
+DOM\HTMLDocument test values for encoding field
+--EXTENSIONS--
+dom
+--FILE--
+encoding);
+$dom->encoding = "CSeuckr";
+var_dump($dom->encoding);
+try {
+ $dom->encoding = "nope";
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+var_dump($dom->encoding);
+$dom->encoding = "Windows-1251";
+var_dump($dom->encoding);
+try {
+ $dom->encoding = NULL;
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+var_dump($dom->encoding);
+echo $dom->saveHTML();
+
+try {
+ $dom = DOM\HTMLDocument::createEmpty("bogus");
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+string(5) "UTF-8"
+string(6) "EUC-KR"
+Invalid document encoding
+string(6) "EUC-KR"
+string(12) "windows-1251"
+Invalid document encoding
+string(12) "windows-1251"
+DOM\HTMLDocument::createEmpty(): Argument #1 ($encoding) must be a valid document encoding
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_unicode_error.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_unicode_error.phpt
new file mode 100644
index 000000000000..7885f68ecf50
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_unicode_error.phpt
@@ -0,0 +1,26 @@
+--TEST--
+DOM\HTMLDocument loading with unicode codepoints resulting in an error
+--EXTENSIONS--
+dom
+--FILE--
+
+--EXPECTF--
+--- createFromFile ---
+
+Warning: DOM\HTMLDocument::createFromFile(): tokenizer error missing-end-tag-name in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromFile(): tree error unexpected-token in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromFile(): tree error unexpected-token in %s on line %d
+--- createFromString ---
+
+Warning: DOM\HTMLDocument::createFromString(): tokenizer error missing-end-tag-name in Entity, line: 7, column: 29 in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromString(): tree error unexpected-token in Entity, line: 7, column: 14-17 in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromString(): tree error unexpected-token in Entity, line: 8, column: 7-10 in %s on line %d
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_fallback_encoding.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_fallback_encoding.phpt
new file mode 100644
index 000000000000..9ffd02dc5d7d
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_fallback_encoding.phpt
@@ -0,0 +1,23 @@
+--TEST--
+DOM\HTMLDocument fallback encoding test
+--EXTENSIONS--
+dom
+--FILE--
+encoding);
+echo $dom->saveHTML();
+
+?>
+--CLEAN--
+
+--EXPECT--
+string(5) "UTF-8"
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_override_encoding_incompatible_charset.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_override_encoding_incompatible_charset.phpt
new file mode 100644
index 000000000000..737f6aca4285
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_override_encoding_incompatible_charset.phpt
@@ -0,0 +1,36 @@
+--TEST--
+DOM\HTMLDocument: overrideEncoding with incompatible charset
+--EXTENSIONS--
+iconv
+dom
+--FILE--
+
+
+
+
+ ���
+
+
+ ���
+
+
+ DOC,
+ ),
+ overrideEncoding: 'utf-8'
+);
+
+var_dump(iconv('UTF-8', 'ISO-8859-1', $doc->getElementsByTagName('title')->item(0)->textContent));
+var_dump(iconv('UTF-8', 'ISO-8859-1', $doc->getElementsByTagName('body')->item(0)->textContent));
+?>
+--EXPECT--
+string(3) "���"
+string(9) "
+ ���
+
+"
diff --git a/ext/dom/tests/modern/html/encoding/fallback_encoding.html b/ext/dom/tests/modern/html/encoding/fallback_encoding.html
new file mode 100644
index 000000000000..4191de914d2d
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/fallback_encoding.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/encoding/gb18030.html b/ext/dom/tests/modern/html/encoding/gb18030.html
new file mode 100644
index 000000000000..423711cd5f33
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/gb18030.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+ H��llo, world!
+
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/encoding/gb18030_without_charset.html b/ext/dom/tests/modern/html/encoding/gb18030_without_charset.html
new file mode 100644
index 000000000000..4140d3381f3f
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/gb18030_without_charset.html
@@ -0,0 +1,7 @@
+
+
+No charset!
+
+
+ H��llo, world!
+
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/encoding/shift_jis.html b/ext/dom/tests/modern/html/encoding/shift_jis.html
new file mode 100644
index 000000000000..3da08c8f24b1
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/shift_jis.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+ �₠
+
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/encoding/utf16be_bom.html b/ext/dom/tests/modern/html/encoding/utf16be_bom.html
new file mode 100644
index 000000000000..9a50e89a5772
Binary files /dev/null and b/ext/dom/tests/modern/html/encoding/utf16be_bom.html differ
diff --git a/ext/dom/tests/modern/html/encoding/utf16le_bom.html b/ext/dom/tests/modern/html/encoding/utf16le_bom.html
new file mode 100644
index 000000000000..73a6e5f89b21
Binary files /dev/null and b/ext/dom/tests/modern/html/encoding/utf16le_bom.html differ
diff --git a/ext/dom/tests/modern/html/encoding/utf16le_error.html b/ext/dom/tests/modern/html/encoding/utf16le_error.html
new file mode 100644
index 000000000000..3f3d22b0c25b
Binary files /dev/null and b/ext/dom/tests/modern/html/encoding/utf16le_error.html differ
diff --git a/ext/dom/tests/modern/html/encoding/utf8_bom.html b/ext/dom/tests/modern/html/encoding/utf8_bom.html
new file mode 100644
index 000000000000..45319b3bda3a
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/utf8_bom.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/encoding/windows1251.html b/ext/dom/tests/modern/html/encoding/windows1251.html
new file mode 100644
index 000000000000..928f5922c097
--- /dev/null
+++ b/ext/dom/tests/modern/html/encoding/windows1251.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+ A � B � C
+
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_adopt_DOMDocument.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_adopt_DOMDocument.phpt
new file mode 100644
index 000000000000..cff51dac28f6
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_adopt_DOMDocument.phpt
@@ -0,0 +1,30 @@
+--TEST--
+DOM\HTMLDocument adopts a DOMDocument
+--EXTENSIONS--
+dom
+--FILE--
+loadHTML(<<
+
+
+
+
+
+
+HTML);
+
+$dom2 = DOM\HTMLDocument::createEmpty();
+$dom2->appendChild($dom2->adoptNode($dom->documentElement));
+echo $dom2->saveHTML();
+
+?>
+--EXPECT--
+
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_clone.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_clone.phpt
new file mode 100644
index 000000000000..1a04dd7beae2
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_clone.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Cloning a DOM\HTMLDocument
+--EXTENSIONS--
+dom
+--FILE--
+foo
");
+
+$dom2 = clone $dom;
+var_dump($dom2->firstChild->tagName);
+var_dump($dom2->firstChild->textContent);
+
+$element = $dom2->firstChild;
+unset($dom2);
+var_dump(get_class($element->ownerDocument));
+
+?>
+--EXPECTF--
+Warning: DOM\HTMLDocument::createFromString(): tree error unexpected-token-in-initial-mode in Entity, line: 1, column: 2 in %s on line %d
+string(4) "html"
+string(3) "foo"
+string(16) "DOM\HTMLDocument"
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_documentURI.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_documentURI.phpt
new file mode 100644
index 000000000000..9430aac9da30
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_documentURI.phpt
@@ -0,0 +1,47 @@
+--TEST--
+DOM\HTMLDocument::documentURI
+--EXTENSIONS--
+dom
+--FILE--
+documentURI);
+
+$memory = fopen("php://memory", "w+");
+fwrite($memory, "foobar");
+rewind($memory);
+$dom = DOM\HTMLDocument::createFromFile("php://memory");
+var_dump($dom->documentURI);
+fclose($memory);
+
+class DummyWrapper {
+ public $context;
+
+ public function stream_open($path, $mode, $options, &$opened_path) {
+ return true;
+ }
+
+ public function stream_read($count) {
+ return "";
+ }
+
+ public function stream_eof() {
+ return true;
+ }
+
+ public function stream_close() {
+ return true;
+ }
+}
+
+stream_wrapper_register("dummy", DummyWrapper::class);
+
+$dom = DOM\HTMLDocument::createFromFile("dummy://foo/ bar");
+var_dump($dom->documentURI);
+
+?>
+--EXPECTF--
+string(%d) "file:/%stest%20foo.html"
+string(12) "php://memory"
+string(16) "dummy://foo/ bar"
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_getElementsByTagName.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_getElementsByTagName.phpt
new file mode 100644
index 000000000000..3af5c2d01eb7
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_getElementsByTagName.phpt
@@ -0,0 +1,98 @@
+--TEST--
+Test DOM\HTMLDocument::getElementsByTagName(NS)
+--EXTENSIONS--
+dom
+--FILE--
+
+
+
+ Test
+
+
+ Test
+ Test
+
+
+
+
+
+
+
+
+HTML);
+
+echo "--- getElementsByTagName ---\n";
+
+var_dump($dom->getElementsByTagName("p")[0]?->nodeName);
+var_dump($dom->getElementsByTagName("math")[0]?->nodeName);
+var_dump($dom->getElementsByTagName("mtable")[0]?->nodeName);
+var_dump($dom->getElementsByTagName("svg")[0]?->nodeName);
+var_dump($dom->getElementsByTagName("circle")[0]?->nodeName);
+
+echo "--- getElementsByTagNameNS (*) ---\n";
+
+var_dump($dom->getElementsByTagNameNS("*", "p")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("*", "math")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("*", "mtable")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("*", "svg")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("*", "circle")[0]?->nodeName);
+
+echo "--- getElementsByTagNameNS (xhtml) ---\n";
+
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "p")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "math")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "mtable")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "svg")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "circle")[0]?->nodeName);
+
+echo "--- getElementsByTagNameNS (svg) ---\n";
+
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/2000/svg", "p")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/2000/svg", "math")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/2000/svg", "mtable")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/2000/svg", "circle")[0]?->nodeName);
+
+echo "--- getElementsByTagNameNS (math) ---\n";
+
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML", "p")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML", "math")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML", "mtable")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML", "svg")[0]?->nodeName);
+var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML", "circle")[0]?->nodeName);
+
+?>
+--EXPECT--
+--- getElementsByTagName ---
+string(1) "p"
+string(4) "math"
+string(6) "mtable"
+string(3) "svg"
+string(6) "circle"
+--- getElementsByTagNameNS (*) ---
+string(1) "p"
+string(4) "math"
+string(6) "mtable"
+string(3) "svg"
+string(6) "circle"
+--- getElementsByTagNameNS (xhtml) ---
+string(1) "p"
+NULL
+NULL
+NULL
+NULL
+--- getElementsByTagNameNS (svg) ---
+NULL
+NULL
+NULL
+string(3) "svg"
+string(6) "circle"
+--- getElementsByTagNameNS (math) ---
+NULL
+string(4) "math"
+string(6) "mtable"
+NULL
+NULL
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_01.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_01.phpt
new file mode 100644
index 000000000000..e7f4fb630f54
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_01.phpt
@@ -0,0 +1,17 @@
+--TEST--
+DOM\HTMLDocument::registerNodeClass 01
+--EXTENSIONS--
+dom
+--FILE--
+registerNodeClass("DOM\\HTMLDocument", "DOMDocument");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: DOM\Document::registerNodeClass(): Argument #2 ($extendedClass) must be a class name derived from DOM\HTMLDocument or null, DOMDocument given in %s:%d
+Stack trace:
+#0 %s(%d): DOM\Document->registerNodeClass('DOM\\HTMLDocumen...', 'DOMDocument')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_02.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_02.phpt
new file mode 100644
index 000000000000..f9b8493cda34
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_02.phpt
@@ -0,0 +1,36 @@
+--TEST--
+DOM\HTMLDocument::registerNodeClass 02
+--EXTENSIONS--
+dom
+--FILE--
+registerNodeClass("DOM\\Document", "Custom");
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+
+$element = $dom->appendChild($dom->createElement("foo"));
+unset($dom);
+
+var_dump(get_class($element->ownerDocument));
+
+// Should fail
+$element->ownerDocument->foo();
+
+?>
+--EXPECTF--
+DOM\Document::registerNodeClass(): Argument #1 ($baseClass) must not be an abstract class
+string(11) "DOMDocument"
+
+Fatal error: Uncaught Error: Call to undefined method DOMDocument::foo() in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_saveHTMLFile_empty_path.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_saveHTMLFile_empty_path.phpt
new file mode 100644
index 000000000000..02514e54e07e
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_saveHTMLFile_empty_path.phpt
@@ -0,0 +1,18 @@
+--TEST--
+DOM\HTMLDocument::saveHTMLFile() empty path
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createElement("root"));
+$dom->saveHTMLFile("");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ValueError: DOM\HTMLDocument::saveHTMLFile(): Argument #1 ($filename) must not be empty in %s:%d
+Stack trace:
+#0 %s(%d): DOM\HTMLDocument->saveHTMLFile('')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_saveHTML_wrong_document.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_saveHTML_wrong_document.phpt
new file mode 100644
index 000000000000..d2356068b4ec
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_saveHTML_wrong_document.phpt
@@ -0,0 +1,17 @@
+--TEST--
+DOM\HTMLDocument::saveHTML() wrong document
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(DOM\HTMLDocument::createEmpty());
+
+?>
+--EXPECTF--
+Fatal error: Uncaught DOMException: Wrong Document Error in %s:%d
+Stack trace:
+#0 %s(%d): DOM\HTMLDocument->saveHTML(Object(DOM\HTMLDocument))
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt
new file mode 100644
index 000000000000..4a6e81631322
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt
@@ -0,0 +1,78 @@
+--TEST--
+DOM\HTMLDocument should retain properties and ownerDocument relation 01
+--EXTENSIONS--
+dom
+--FILE--
+foo", LIBXML_NOERROR);
+$dom->strictErrorChecking = false;
+
+// Destroy reference to the DOM
+$child = $dom->documentElement;
+unset($dom);
+
+// Regain reference using the ownerDocument property
+// Should be a DOM\HTML5Document
+$dom = $child->ownerDocument;
+var_dump($dom);
+// Test if property is preserved (any random doc_props property will do)
+var_dump($dom->strictErrorChecking);
+
+?>
+--EXPECT--
+object(DOM\HTMLDocument)#1 (26) {
+ ["encoding"]=>
+ string(5) "UTF-8"
+ ["doctype"]=>
+ NULL
+ ["documentElement"]=>
+ string(22) "(object value omitted)"
+ ["strictErrorChecking"]=>
+ bool(false)
+ ["documentURI"]=>
+ NULL
+ ["firstElementChild"]=>
+ string(22) "(object value omitted)"
+ ["lastElementChild"]=>
+ string(22) "(object value omitted)"
+ ["childElementCount"]=>
+ int(1)
+ ["nodeName"]=>
+ string(9) "#document"
+ ["nodeValue"]=>
+ NULL
+ ["nodeType"]=>
+ int(13)
+ ["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(true)
+ ["ownerDocument"]=>
+ NULL
+ ["namespaceURI"]=>
+ NULL
+ ["prefix"]=>
+ string(0) ""
+ ["localName"]=>
+ NULL
+ ["baseURI"]=>
+ NULL
+ ["textContent"]=>
+ string(3) "foo"
+}
+bool(false)
diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt
new file mode 100644
index 000000000000..9c4d016fef81
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt
@@ -0,0 +1,78 @@
+--TEST--
+DOM\HTMLDocument should retain properties and ownerDocument relation 02
+--EXTENSIONS--
+dom
+--FILE--
+foo", LIBXML_NOERROR);
+$dom->strictErrorChecking = false;
+$child = $dom->appendChild($dom->createElement('html'));
+
+// Destroy reference to the DOM
+unset($dom);
+
+// Regain reference using the ownerDocument property
+// Should be a DOM\HTMLDocument
+$dom = $child->ownerDocument;
+var_dump($dom);
+// Test if property is preserved (any random doc_props property will do)
+var_dump($dom->strictErrorChecking);
+
+?>
+--EXPECT--
+object(DOM\HTMLDocument)#1 (26) {
+ ["encoding"]=>
+ string(5) "UTF-8"
+ ["doctype"]=>
+ NULL
+ ["documentElement"]=>
+ string(22) "(object value omitted)"
+ ["strictErrorChecking"]=>
+ bool(false)
+ ["documentURI"]=>
+ NULL
+ ["firstElementChild"]=>
+ string(22) "(object value omitted)"
+ ["lastElementChild"]=>
+ string(22) "(object value omitted)"
+ ["childElementCount"]=>
+ int(2)
+ ["nodeName"]=>
+ string(9) "#document"
+ ["nodeValue"]=>
+ NULL
+ ["nodeType"]=>
+ int(13)
+ ["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(true)
+ ["ownerDocument"]=>
+ NULL
+ ["namespaceURI"]=>
+ NULL
+ ["prefix"]=>
+ string(0) ""
+ ["localName"]=>
+ NULL
+ ["baseURI"]=>
+ NULL
+ ["textContent"]=>
+ string(3) "foo"
+}
+bool(false)
diff --git a/ext/dom/tests/modern/html/interactions/getLineNo_65536.phpt b/ext/dom/tests/modern/html/interactions/getLineNo_65536.phpt
new file mode 100644
index 000000000000..122bcc8aaaae
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/getLineNo_65536.phpt
@@ -0,0 +1,20 @@
+--TEST--
+getLineNo() returns the line number of the node >= 65536
+--EXTENSIONS--
+dom
+--FILE--
+
+
+ hello
+
+EOF;
+
+$dom = DOM\HTMLDocument::createFromString($html);
+var_dump($dom->documentElement->firstChild->nextSibling->firstChild->nextSibling->getLineNo());
+
+?>
+--EXPECT--
+int(65538)
diff --git a/ext/dom/tests/modern/html/interactions/noscript.phpt b/ext/dom/tests/modern/html/interactions/noscript.phpt
new file mode 100644
index 000000000000..839845e330f2
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/noscript.phpt
@@ -0,0 +1,46 @@
+--TEST--
+noscript behaviour
+--EXTENSIONS--
+dom
+--FILE--
+hi
", DOM\HTML_NO_DEFAULT_NS);
+var_dump($dom->documentElement->textContent);
+echo $dom->saveHTML(), "\n";
+echo $dom->saveXML();
+
+echo "--- Modifying the text content: tag ---\n";
+
+$xpath = new DOMXPath($dom);
+$noscript = $xpath->query("//noscript")[0];
+$noscript->textContent = "bye
";
+echo $dom->saveHTML(), "\n";
+echo $dom->saveXML();
+
+echo "--- Modifying the text content: trick ---\n";
+
+$noscript->textContent = "";
+echo $dom->saveHTML(), "\n";
+echo $dom->saveXML();
+
+?>
+--EXPECT--
+--- Parsing ---
+string(2) "hi"
+hi
+
+
+hi
+--- Modifying the text content: tag ---
+<p>bye</p>
+
+
+<p>bye</p>
+--- Modifying the text content: trick ---
+<!-- </noscript> -->
+
+
+<!-- </noscript> -->
diff --git a/ext/dom/tests/modern/html/interactions/test foo.html b/ext/dom/tests/modern/html/interactions/test foo.html
new file mode 100644
index 000000000000..19102815663d
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/test foo.html
@@ -0,0 +1 @@
+foo
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/interactions/without_constructor.phpt b/ext/dom/tests/modern/html/interactions/without_constructor.phpt
new file mode 100644
index 000000000000..606ebfb8b286
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/without_constructor.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Tests without running the constructor
+--EXTENSIONS--
+dom
+--FILE--
+newInstanceWithoutConstructor();
+ } catch (ReflectionException $e) {
+ echo $e->getMessage(), "\n";
+ }
+}
+
+?>
+--EXPECT--
+Class DOM\HTMLDocument is an internal class marked as final that cannot be instantiated without invoking its constructor
+Class DOM\XMLDocument is an internal class marked as final that cannot be instantiated without invoking its constructor
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_createFromFile_createFromString_BOM_buffer_edge.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_createFromFile_createFromString_BOM_buffer_edge.phpt
new file mode 100644
index 000000000000..8d7d70e6d9f8
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_createFromFile_createFromString_BOM_buffer_edge.phpt
@@ -0,0 +1,26 @@
+--TEST--
+DOM\HTMLDocument::createFromFile()/createFromString() BOM with a buffer on the edge
+--EXTENSIONS--
+dom
+--FILE--
+";
+$trailer = "";
+$data = $header . str_repeat("a", 4096 - strlen($header) - strlen($trailer)) . $trailer;
+
+$dom = DOM\HTMLDocument::createFromString($header . str_repeat("a", 4096 - strlen($header) - strlen($trailer)) . $trailer);
+var_dump($dom->textContent);
+
+file_put_contents(__DIR__ . "/BOM_edge.tmp", $data);
+$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/BOM_edge.tmp");
+var_dump($dom->textContent);
+
+?>
+--CLEAN--
+
+--EXPECT--
+string(4052) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+string(4052) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_DOM_HTML_NO_DEFAULT_NS.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_DOM_HTML_NO_DEFAULT_NS.phpt
new file mode 100644
index 000000000000..55089d8fac34
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_DOM_HTML_NO_DEFAULT_NS.phpt
@@ -0,0 +1,37 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() with DOM\HTML_NO_DEFAULT_NS
+--EXTENSIONS--
+dom
+--FILE--
+registerNamespace("x", "http://www.w3.org/1999/xhtml");
+var_dump($xpath->query("//p"));
+var_dump($xpath->query("//x:p"));
+
+$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/paragraph.html", LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR | DOM\HTML_NO_DEFAULT_NS);
+$xpath = new DOMXPath($dom);
+$xpath->registerNamespace("x", "http://www.w3.org/1999/xhtml");
+var_dump($xpath->query("//p"));
+var_dump($xpath->query("//x:p"));
+
+?>
+--EXPECT--
+object(DOMNodeList)#3 (1) {
+ ["length"]=>
+ int(0)
+}
+object(DOMNodeList)#4 (1) {
+ ["length"]=>
+ int(1)
+}
+object(DOMNodeList)#3 (1) {
+ ["length"]=>
+ int(1)
+}
+object(DOMNodeList)#3 (1) {
+ ["length"]=>
+ int(0)
+}
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_empty_path.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_empty_path.phpt
new file mode 100644
index 000000000000..73e55c1f09aa
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_empty_path.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() - empty path
+--EXTENSIONS--
+dom
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught ValueError: Path cannot be empty in %s:%d
+Stack trace:
+#0 %s(%d): DOM\HTMLDocument::createFromFile('')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_local_existing_file.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_local_existing_file.phpt
new file mode 100644
index 000000000000..0e9e2b60e2bc
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_local_existing_file.phpt
@@ -0,0 +1,22 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() - local existing file
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(), "\n";
+
+?>
+--EXPECTF--
+Warning: DOM\HTMLDocument::createFromFile(): tree error unexpected-token-in-initial-mode in %s on line %d
+
+Hello world
+
+
+This is a not well-formed
+html files with undeclared entities
+
+
+
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_local_file_does_not_exist.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_local_file_does_not_exist.phpt
new file mode 100644
index 000000000000..8bb91cf8ee7b
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_local_file_does_not_exist.phpt
@@ -0,0 +1,19 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() - local file that does not exist
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(), "\n";
+
+?>
+--EXPECTF--
+Warning: DOM\HTMLDocument::createFromFile(%s): Failed to open stream: No such file or directory in %s on line %d
+
+Fatal error: Uncaught Exception: Cannot open file '%s' in %s:%d
+Stack trace:
+#0 %s(%d): DOM\HTMLDocument::createFromFile('%s')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_nul_terminator_cases_path.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_nul_terminator_cases_path.phpt
new file mode 100644
index 000000000000..6e79c2f12a94
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_nul_terminator_cases_path.phpt
@@ -0,0 +1,22 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() - NUL terminator cases path
+--EXTENSIONS--
+dom
+--FILE--
+getMessage(), "\n";
+}
+try {
+ DOM\HTMLDocument::createFromFile('%00');
+} catch (ValueError $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+DOM\HTMLDocument::createFromFile(): Argument #1 ($path) must not contain any null bytes
+DOM\HTMLDocument::createFromFile(): Argument #1 ($path) must not contain percent-encoded NUL bytes
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_01.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_01.phpt
new file mode 100644
index 000000000000..9c77ebeadd4d
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_01.phpt
@@ -0,0 +1,19 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() - parser warning 01
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(), "\n";
+
+?>
+--EXPECT--
+foo
+
+
+
+
+
+error
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_02.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_02.phpt
new file mode 100644
index 000000000000..9d3dd944e7ba
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_02.phpt
@@ -0,0 +1,20 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() - parser warning 02
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+ foo
+
+
+ -->
+
+
+
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_03.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_03.phpt
new file mode 100644
index 000000000000..f5cada6c18f2
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_03.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() - parser warning 03
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+
+
+
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_libxml_get_last_error.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_libxml_get_last_error.phpt
new file mode 100644
index 000000000000..3de37d94890b
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_parser_warning_libxml_get_last_error.phpt
@@ -0,0 +1,30 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() - parser warning libxml_get_last_error()
+--EXTENSIONS--
+dom
+--FILE--
+x> ';
+$dom = DOM\HTMLDocument::createFromFile(__DIR__."/parser_warning_01.html");
+
+var_dump(libxml_get_last_error());
+
+?>
+--EXPECTF--
+object(LibXMLError)#2 (6) {
+ ["level"]=>
+ int(2)
+ ["code"]=>
+ int(1)
+ ["column"]=>
+ int(2)
+ ["message"]=>
+ string(%d) "tree error unexpected-token-in-initial-mode in %sparser_warning_01.html, line: 1, column: 2-6"
+ ["file"]=>
+ string(%d) "%sparser_warning_01.html"
+ ["line"]=>
+ int(1)
+}
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_with_failing_stream_wrapper.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_with_failing_stream_wrapper.phpt
new file mode 100644
index 000000000000..78e07b72bbef
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_with_failing_stream_wrapper.phpt
@@ -0,0 +1,44 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() with failing stream wrapper
+--EXTENSIONS--
+dom
+--FILE--
+fail) {
+ throw new Error("fail");
+ }
+ $this->fail = true;
+ return str_repeat("X", $count);
+ }
+
+ public function stream_eof() {
+ return false;
+ }
+
+ public function stream_close() {
+ return true;
+ }
+}
+
+stream_wrapper_register("fail", FailingWrapper::class, 0);
+
+DOM\HTMLDocument::createFromFile("fail://x");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: fail in %s:%d
+Stack trace:
+#0 [internal function]: FailingWrapper->stream_read(8192)
+#1 %s(%d): DOM\HTMLDocument::createFromFile('fail://x')
+#2 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_with_working_stream_wrapper.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_with_working_stream_wrapper.phpt
new file mode 100644
index 000000000000..e2e24fdba272
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromFile_with_working_stream_wrapper.phpt
@@ -0,0 +1,60 @@
+--TEST--
+DOM\HTMLDocument::createFromFile() with working stream wrapper
+--EXTENSIONS--
+dom
+--FILE--
+data = substr($path, 6);
+ return true;
+ }
+
+ public function stream_read($count) {
+ $ret = substr($this->data, $this->position, $count);
+ $this->position += $count;
+ return $ret;
+ }
+
+ public function stream_eof() {
+ return $this->position >= strlen($this->data);
+ }
+
+ public function stream_close() {
+ return true;
+ }
+}
+
+stream_wrapper_register("euw", EchoUriWrapper::class, 0);
+
+echo "--- Stream wrapper case ---\n";
+
+$dom = DOM\HTMLDocument::createFromFile("euw://hello
");
+echo $dom->saveHTML(), "\n";
+
+echo "--- Stream wrapper in two chunks case ---\n";
+
+libxml_use_internal_errors(true);
+// To properly test this, keep the 4096 in sync with document.c's input stream buffer size.
+$dom = DOM\HTMLDocument::createFromFile("euw://" . str_repeat("\n", 4096-22) . "<>");
+echo $dom->saveHTML(), "\n";
+
+foreach (libxml_get_errors() as $error) {
+ var_dump($error->line, $error->column);
+}
+
+?>
+--EXPECTF--
+--- Stream wrapper case ---
+
+Warning: DOM\HTMLDocument::createFromFile(): tree error unexpected-token-in-initial-mode in euw://hello
, line: 1, column: 2 in %s on line %d
+hello
+--- Stream wrapper in two chunks case ---
+<>
+int(4075)
+int(2)
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_DOM_HTML_NO_DEFAULT_NS.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_DOM_HTML_NO_DEFAULT_NS.phpt
new file mode 100644
index 000000000000..0e6839007fc8
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_DOM_HTML_NO_DEFAULT_NS.phpt
@@ -0,0 +1,37 @@
+--TEST--
+DOM\HTMLDocument::createFromString() with DOM\HTML_NO_DEFAULT_NS
+--EXTENSIONS--
+dom
+--FILE--
+registerNamespace("x", "http://www.w3.org/1999/xhtml");
+var_dump($xpath->query("//p"));
+var_dump($xpath->query("//x:p"));
+
+$dom = DOM\HTMLDocument::createFromString(file_get_contents(__DIR__ . "/paragraph.html"), LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR | DOM\HTML_NO_DEFAULT_NS);
+$xpath = new DOMXPath($dom);
+$xpath->registerNamespace("x", "http://www.w3.org/1999/xhtml");
+var_dump($xpath->query("//p"));
+var_dump($xpath->query("//x:p"));
+
+?>
+--EXPECT--
+object(DOMNodeList)#3 (1) {
+ ["length"]=>
+ int(0)
+}
+object(DOMNodeList)#4 (1) {
+ ["length"]=>
+ int(1)
+}
+object(DOMNodeList)#3 (1) {
+ ["length"]=>
+ int(1)
+}
+object(DOMNodeList)#3 (1) {
+ ["length"]=>
+ int(0)
+}
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_COMPACT.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_COMPACT.phpt
new file mode 100644
index 000000000000..28146903105e
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_COMPACT.phpt
@@ -0,0 +1,40 @@
+--TEST--
+DOM\HTMLDocument::createFromString() with LIBXML_COMPACT
+--EXTENSIONS--
+dom
+--FILE--
+
+
+
+
+
+ x
+ foo
+ foox
+ fooxx
+ fooxxx
+ fooxxxx
+ fooxxxxx
+ this does not fit
+
+
+HTML, LIBXML_COMPACT);
+
+$xpath = new DOMXPath($dom);
+foreach ($xpath->query("//*[name()='p']") as $p) {
+ echo $p->textContent, "\n";
+}
+
+?>
+--EXPECT--
+x
+foo
+foox
+fooxx
+fooxxx
+fooxxxx
+fooxxxxx
+this does not fit
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_HTML_NOIMPLIED_error.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_HTML_NOIMPLIED_error.phpt
new file mode 100644
index 000000000000..26f322f7127e
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_HTML_NOIMPLIED_error.phpt
@@ -0,0 +1,13 @@
+--TEST--
+DOM\HTMLDocument::createFromString() with LIBXML_HTML_NOIMPLIED - tree error should not happen
+--EXTENSIONS--
+dom
+--FILE--
+foo", LIBXML_HTML_NOIMPLIED);
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+foo
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_HTML_NOIMPLIED_namespace.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_HTML_NOIMPLIED_namespace.phpt
new file mode 100644
index 000000000000..ea384bb7885f
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_HTML_NOIMPLIED_namespace.phpt
@@ -0,0 +1,45 @@
+--TEST--
+DOM\HTMLDocument::createFromString() with LIBXML_HTML_NOIMPLIED namespace check
+--EXTENSIONS--
+dom
+--FILE--
+saveXML();
+
+echo "--- Single element ---\n";
+
+$dom = DOM\HTMLDocument::createFromString("foo
", LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR);
+echo $dom->saveXML();
+var_dump($dom->documentElement->namespaceURI);
+var_dump($dom->documentElement->prefix);
+
+echo "--- Multiple elements ---\n";
+
+$dom = DOM\HTMLDocument::createFromString("foo
bar ", LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR);
+echo $dom->saveXML();
+var_dump($dom->documentElement->namespaceURI);
+var_dump($dom->documentElement->prefix);
+var_dump($dom->documentElement->nextSibling->namespaceURI);
+var_dump($dom->documentElement->nextSibling->prefix);
+
+?>
+--EXPECT--
+--- No elements ---
+
+--- Single element ---
+
+foo
+string(28) "http://www.w3.org/1999/xhtml"
+string(0) ""
+--- Multiple elements ---
+
+foo
+bar
+string(28) "http://www.w3.org/1999/xhtml"
+string(0) ""
+string(28) "http://www.w3.org/1999/xhtml"
+string(0) ""
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_empty.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_empty.phpt
new file mode 100644
index 000000000000..898ae43fabb2
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_empty.phpt
@@ -0,0 +1,13 @@
+--TEST--
+DOM\HTMLDocument::createFromString() - empty document
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(), "\n";
+
+?>
+--EXPECT--
+
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_fromFile_LIBXML_HTML_NOIMPLIED.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_fromFile_LIBXML_HTML_NOIMPLIED.phpt
new file mode 100644
index 000000000000..3e26fc35b950
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_fromFile_LIBXML_HTML_NOIMPLIED.phpt
@@ -0,0 +1,90 @@
+--TEST--
+DOM\HTMLDocument::createFromString()/createFromFile() with LIBXML_HTML_NOIMPLIED
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML();
+ echo $output, "\n";
+
+ // Also test the loadHTMLFile variation. We won't print out the result, just checking the result is the same.
+ $temp = fopen(__DIR__."/DOM_HTMLDocument_loadHTML_LIBXML_HTML_NOIMPLIED_input.tmp", "w");
+ fwrite($temp, $html);
+ fclose($temp);
+ $dom = DOM\HTMLDocument::createFromFile(__DIR__."/DOM_HTMLDocument_loadHTML_LIBXML_HTML_NOIMPLIED_input.tmp", LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR);
+ var_dump($output === $dom->saveHTML());
+}
+
+echo "--- Missing html, head, body ---\n";
+test("");
+test("foobarbaz");
+test("foo
");
+echo "--- Missing html, head ---\n";
+test("foo
");
+test("x foo
");
+echo "--- Missing html, body ---\n";
+test("x foo
");
+echo "--- Missing html ---\n";
+test("x foo
");
+echo "--- Missing head, body ---\n";
+test("foobar");
+test("a foo
");
+echo "--- Missing head ---\n";
+test("hi");
+echo "--- Missing nothing ---\n";
+test("x foo
");
+echo "--- Malformed document ---\n";
+test("foo");
+?>
+--CLEAN--
+
+--EXPECT--
+--- Missing html, head, body ---
+Testing:
+
+bool(true)
+Testing: foobarbaz
+foobarbaz
+bool(true)
+Testing: foo
+foo
+bool(true)
+--- Missing html, head ---
+Testing: foo
+foo
+bool(true)
+Testing: x foo
+x foo
+bool(true)
+--- Missing html, body ---
+Testing: x foo
+x foo
+bool(true)
+--- Missing html ---
+Testing: x foo
+x foo
+bool(true)
+--- Missing head, body ---
+Testing: foobar
+foobar
+bool(true)
+Testing: a foo
+a foo
+bool(true)
+--- Missing head ---
+Testing: hi
+hi
+bool(true)
+--- Missing nothing ---
+Testing: x foo
+x foo
+bool(true)
+--- Malformed document ---
+Testing: foo
+foo
+bool(true)
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_line_column.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_line_column.phpt
new file mode 100644
index 000000000000..ba47a715fb39
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_line_column.phpt
@@ -0,0 +1,57 @@
+--TEST--
+DOM\HTMLDocument::createFromString() - line and column test
+--EXTENSIONS--
+dom
+--FILE--
+
+
+
+ foo
+
+
+
+
+ This is my paragraph
+
+
+
+
+
+HTML);
+
+$xpath = new DOMXPath($dom);
+
+foreach ($xpath->query("//*") as $element) {
+ echo "Element: '", $element->tagName, "', ", $element->getLineNo(), "\n";
+}
+
+foreach ($xpath->query("//*[name()='strong']") as $element) {
+ echo "Text: '", $element->textContent, "', ", $element->firstChild->getLineNo(), "\n";
+}
+
+foreach ($xpath->query("//*[name()='div']") as $element) {
+ foreach ($element->attributes as $attribute) {
+ echo "Attribute: '", $attribute->nodeName, "', ", $attribute->getLineNo(), "\n";
+ }
+}
+
+foreach ($xpath->query("//comment()") as $comment) {
+ echo "Comment: '", $comment->data, "', ", $comment->getLineNo(), "\n";
+}
+
+?>
+--EXPECT--
+Element: 'html', 1
+Element: 'head', 2
+Element: 'title', 3
+Element: 'body', 5
+Element: 'div', 6
+Element: 'p', 7
+Element: 'strong', 8
+Text: 'This is my paragraph', 8
+Attribute: 'id', 6
+Attribute: 'x', 6
+Comment: ' my comment ', 9
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_normal_no_error.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_normal_no_error.phpt
new file mode 100644
index 000000000000..b4a778a68d3e
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_normal_no_error.phpt
@@ -0,0 +1,39 @@
+--TEST--
+DOM\HTMLDocument::createFromString() - normal document, no error
+--EXTENSIONS--
+dom
+--FILE--
+
+
+
+
+ foo
+
+
+
+ bar
+
+
+HTML;
+$dom = DOM\HTMLDocument::createFromString($html);
+echo $dom->saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+
+ foo
+
+
+
+ bar
+
+
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_old_dtd.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_old_dtd.phpt
new file mode 100644
index 000000000000..f140b8b1f349
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_old_dtd.phpt
@@ -0,0 +1,39 @@
+--TEST--
+DOM\HTMLDocument::createFromString(): Old DTD
+--EXTENSIONS--
+dom
+--FILE--
+
+
+
+
+
+
+
+HTML);
+
+echo "--- HTML serialization ---\n";
+echo $dom->saveHTML(), "\n";
+echo "--- XML serialization ---\n";
+echo $dom->saveXML();
+
+?>
+--EXPECTF--
+Warning: DOM\HTMLDocument::createFromString(): tree error bad-doctype-token-in-initial-mode in Entity, line: 1, column: 3-9 in %s on line %d
+--- HTML serialization ---
+
+
+
+
+
+--- XML serialization ---
+
+
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_01.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_01.phpt
new file mode 100644
index 000000000000..4469ee890ee7
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_01.phpt
@@ -0,0 +1,23 @@
+--TEST--
+DOM\HTMLDocument::createFromString() - parser warning 01
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(), "\n";
+
+?>
+--EXPECTF--
+Warning: DOM\HTMLDocument::createFromString(): tokenizer error missing-end-tag-name in Entity, line: 7, column: 11 in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromString(): tree error unexpected-token-in-initial-mode in Entity, line: 1, column: 2-6 in %s on line %d
+foo
+
+
+
+
+
+error
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_02.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_02.phpt
new file mode 100644
index 000000000000..08abc826af8d
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_02.phpt
@@ -0,0 +1,32 @@
+--TEST--
+DOM\HTMLDocument::createFromString() - parser warning 02
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(), "\n";
+
+?>
+--EXPECTF--
+Warning: DOM\HTMLDocument::createFromString(): tokenizer error unexpected-null-character in Entity, line: 4, column: 11 in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromString(): tokenizer error missing-whitespace-between-attributes in Entity, line: 5, column: 20 in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromString(): tokenizer error incorrectly-opened-comment in Entity, line: 6, column: 11 in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromString(): tokenizer error nested-comment in Entity, line: 7, column: 18 in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromString(): tree error unexpected-closed-token in Entity, line: 4, column: 18 in %s on line %d
+
+Warning: DOM\HTMLDocument::createFromString(): tree error doctype-token-in-body-mode in Entity, line: 8, column: 11-17 in %s on line %d
+
+ foo
+
+
+ -->
+
+
+
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_03.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_03.phpt
new file mode 100644
index 000000000000..2de4b36e2ef4
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_03.phpt
@@ -0,0 +1,17 @@
+--TEST--
+DOM\HTMLDocument::createFromString() - parser warning 03
+--EXTENSIONS--
+dom
+--FILE--
+saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+
+
+
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_internal_error.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_internal_error.phpt
new file mode 100644
index 000000000000..44dac9eb5392
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_parser_warning_internal_error.phpt
@@ -0,0 +1,45 @@
+--TEST--
+DOM\HTMLDocument::createFromString() - parser warning via internal error
+--EXTENSIONS--
+dom
+--FILE--
+x> ';
+$dom = DOM\HTMLDocument::createFromString($html);
+foreach (libxml_get_errors() as $error) {
+ var_dump($error->message, $error->line, $error->column);
+}
+
+var_dump(libxml_get_last_error());
+
+?>
+--EXPECT--
+string(81) "tokenizer error invalid-first-character-of-tag-name in Entity, line: 1, column: 2"
+int(1)
+int(2)
+string(66) "tokenizer error missing-end-tag-name in Entity, line: 1, column: 6"
+int(1)
+int(6)
+string(75) "tree error unexpected-token-in-initial-mode in Entity, line: 1, column: 1-7"
+int(1)
+int(1)
+string(71) "tree error doctype-token-in-body-mode in Entity, line: 1, column: 10-16"
+int(1)
+int(10)
+object(LibXMLError)#4 (6) {
+ ["level"]=>
+ int(2)
+ ["code"]=>
+ int(1)
+ ["column"]=>
+ int(10)
+ ["message"]=>
+ string(71) "tree error doctype-token-in-body-mode in Entity, line: 1, column: 10-16"
+ ["file"]=>
+ string(6) "Entity"
+ ["line"]=>
+ int(1)
+}
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_without_body.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_without_body.phpt
new file mode 100644
index 000000000000..047c18640849
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_without_body.phpt
@@ -0,0 +1,13 @@
+--TEST--
+DOM\HTMLDocument::createFromString() - document without body
+--EXTENSIONS--
+dom
+--FILE--
+foo '
');
+echo $dom->saveHTML(), "\n";
+
+?>
+--EXPECT--
+foo '
diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_parse_options.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_parse_options.phpt
new file mode 100644
index 000000000000..c1e281b5331a
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/HTMLDocument_parse_options.phpt
@@ -0,0 +1,107 @@
+--TEST--
+DOM\HTMLDocument: loading $options check
+--EXTENSIONS--
+dom
+--FILE--
+getMessage(), "\n";
+ }
+ }
+}
+
+?>
+--EXPECTF--
+--- Method createFromString ---
+int(%d)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(4194304)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(524288)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(8)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(4)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(16)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(4)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(256)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(16384)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(4)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(2)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(1024)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(1)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(2048)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(64)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(128)
+DOM\HTMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+--- Method createFromFile ---
+int(%d)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(4194304)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(524288)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(8)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(4)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(16)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(4)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(256)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(16384)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(4)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(2)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(1024)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(1)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(2048)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(64)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
+int(128)
+DOM\HTMLDocument::createFromFile(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOERROR, LIBXML_COMPACT, LIBXML_HTML_NOIMPLIED, DOM\NO_DEFAULT_NS)
diff --git a/ext/dom/tests/modern/html/parser/paragraph.html b/ext/dom/tests/modern/html/parser/paragraph.html
new file mode 100644
index 000000000000..998ea4094d49
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/paragraph.html
@@ -0,0 +1 @@
+foo
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/parser/parser_warning_01.html b/ext/dom/tests/modern/html/parser/parser_warning_01.html
new file mode 100644
index 000000000000..cc16fe40eb14
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/parser_warning_01.html
@@ -0,0 +1,7 @@
+foo
+
+
+
+
+
+error>
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/parser/parser_warning_02.html b/ext/dom/tests/modern/html/parser/parser_warning_02.html
new file mode 100644
index 000000000000..cb448e3f35d7
Binary files /dev/null and b/ext/dom/tests/modern/html/parser/parser_warning_02.html differ
diff --git a/ext/dom/tests/modern/html/parser/parser_warning_03.html b/ext/dom/tests/modern/html/parser/parser_warning_03.html
new file mode 100644
index 000000000000..b97e130faa68
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/parser_warning_03.html
@@ -0,0 +1,6 @@
+
+
+
+ >
+
+
\ No newline at end of file
diff --git a/ext/dom/tests/modern/html/parser/predefined_namespaces.phpt b/ext/dom/tests/modern/html/parser/predefined_namespaces.phpt
new file mode 100644
index 000000000000..39e8bf0131d0
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/predefined_namespaces.phpt
@@ -0,0 +1,100 @@
+--TEST--
+HTMLDocument: Predefined namespaces
+--EXTENSIONS--
+dom
+--FILE--
+
+
+
+ Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+HTML);
+
+echo "--- Namespaces ---\n";
+$xpath = new DOMXPath($dom);
+foreach ($xpath->query("//*[name()='body']//*") as $node) {
+ echo $node->nodeName, " ", $node->namespaceURI ?? "(NONE)", "\n";
+ foreach ($node->attributes as $attribute) {
+ echo " Attribute: ", $attribute->nodeName, " ", $attribute->namespaceURI ?? "(NONE)", "\n";
+ }
+}
+
+echo "--- HTML serialization ---\n";
+echo $dom->saveHTML(), "\n";
+echo "--- XML serialization ---\n";
+echo $dom->saveXML();
+
+?>
+--EXPECT--
+--- Namespaces ---
+svg http://www.w3.org/2000/svg
+ Attribute: width (NONE)
+ Attribute: height (NONE)
+ Attribute: viewbox (NONE)
+rect http://www.w3.org/2000/svg
+ Attribute: id (NONE)
+ Attribute: x (NONE)
+ Attribute: y (NONE)
+ Attribute: width (NONE)
+ Attribute: height (NONE)
+div http://www.w3.org/1999/xhtml
+p http://www.w3.org/1999/xhtml
+math http://www.w3.org/1998/Math/MathML
+mtable http://www.w3.org/1998/Math/MathML
+ Attribute: id (NONE)
+svg http://www.w3.org/1998/Math/MathML
+--- HTML serialization ---
+
+ Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+--- XML serialization ---
+
+
+
+ Test
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/parser/xml_style_namespace.phpt b/ext/dom/tests/modern/html/parser/xml_style_namespace.phpt
new file mode 100644
index 000000000000..08c2756f2cdf
--- /dev/null
+++ b/ext/dom/tests/modern/html/parser/xml_style_namespace.phpt
@@ -0,0 +1,57 @@
+--TEST--
+HTMLDocument: XML-style namespace
+--EXTENSIONS--
+dom
+--FILE--
+
+
+
+ Test
+
+
+
+
+
+HTML);
+
+echo "--- Namespaces ---\n";
+$xpath = new DOMXPath($dom);
+foreach ($xpath->query("//*[name()='body']//*") as $node) {
+ echo $node->nodeName, " ", $node->namespaceURI ?? "(NONE)", "\n";
+ echo "prefix: \"", $node->prefix, "\"\n";
+ foreach ($node->attributes as $attribute) {
+ echo " Attribute: ", $attribute->nodeName, " ", $attribute->namespaceURI ?? "(NONE)", "\n";
+ }
+}
+
+echo "--- HTML serialization ---\n";
+echo $dom->saveHTML(), "\n";
+echo "--- XML serialization ---\n";
+echo $dom->saveXML();
+
+?>
+--EXPECT--
+--- Namespaces ---
+foo:bar http://www.w3.org/1999/xhtml
+prefix: ""
+--- HTML serialization ---
+
+ Test
+
+
+
+
+
+--- XML serialization ---
+
+
+
+ Test
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_escape_attribute.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_escape_attribute.phpt
new file mode 100644
index 000000000000..d4a888bffe82
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_escape_attribute.phpt
@@ -0,0 +1,15 @@
+--TEST--
+DOM\HTMLDocument serialization escape attribute
+--EXTENSIONS--
+dom
+--FILE--
+", LIBXML_NOERROR);
+$p = $dom->documentElement->firstChild->nextSibling->firstChild;
+$p->setAttribute("foo", "\"'&");
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_escape_nbsp.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_escape_nbsp.phpt
new file mode 100644
index 000000000000..c7b11a7a4c74
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_escape_nbsp.phpt
@@ -0,0 +1,13 @@
+--TEST--
+DOM\HTMLDocument serialization escape nbsp
+--EXTENSIONS--
+dom
+--FILE--
+these must transform: \xc2\xa0\xc2\xa0 but these not: \xa0|\xc2... ", LIBXML_NOERROR);
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+these must transform: but these not: �|�...
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_attribute_ns.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_attribute_ns.phpt
new file mode 100644
index 000000000000..9e8865f9971f
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_attribute_ns.phpt
@@ -0,0 +1,22 @@
+--TEST--
+DOM\HTMLDocument serialization of an attribute in a namespace
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createElement("root"));
+$root->setAttributeNodeNS($dom->createAttributeNS("http://php.net", "x:foo"));
+$root->setAttributeNodeNS($dom->createAttributeNS("http://www.w3.org/XML/1998/namespace", "y:id"));
+// Can't test the following because its behaviour is broken in combination with the live spec
+//$root->setAttributeNodeNS($dom->createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns"));
+$root->setAttributeNodeNS($dom->createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:f"));
+$root->setAttributeNodeNS($dom->createAttributeNS("http://www.w3.org/1999/xlink", "z:f"));
+
+// Note: XML declarations are not emitted in HTML5
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_cdata.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_cdata.phpt
new file mode 100644
index 000000000000..2951ff59e9d1
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_cdata.phpt
@@ -0,0 +1,14 @@
+--TEST--
+DOM\HTMLDocument serialization of CData
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createCDATASection("foobaré\"<>-&"));
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+foobaré"<>-&
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_comment.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_comment.phpt
new file mode 100644
index 000000000000..4abc382a3ae1
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_comment.phpt
@@ -0,0 +1,14 @@
+--TEST--
+DOM\HTMLDocument serialization of comment
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createComment("foobaré\"<>-&"));
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_doctype.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_doctype.phpt
new file mode 100644
index 000000000000..72823bb5b4bb
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_doctype.phpt
@@ -0,0 +1,39 @@
+--TEST--
+DOM\HTMLDocument serialization of document type
+--EXTENSIONS--
+dom
+--FILE--
+
+
+
+
+
+
+
+HTML, LIBXML_NOERROR);
+
+echo "--- XML encoding ---\n";
+echo $dom->saveXML();
+echo "--- HTML encoding ---\n";
+// We don't expect to see the public ID and the system ID because the serialization algorithm doesn't serialize those
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+--- XML encoding ---
+
+
+
+
+
+
+
+--- HTML encoding ---
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_element_ns.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_element_ns.phpt
new file mode 100644
index 000000000000..03813a625208
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_element_ns.phpt
@@ -0,0 +1,29 @@
+--TEST--
+DOM\HTMLDocument serialization of element in a namespace
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createElement("root"));
+
+$root->append("\n");
+$root->append($dom->createElementNS("http://php.net", "noprefix"), "\n");
+$root->append($dom->createElementNS("http://php.net", "with:prefix"), "\n");
+$root->append($dom->createElementNS("http://www.w3.org/1999/xhtml", "xhtml:br"), "\n");
+$root->append($dom->createElementNS("http://www.w3.org/2000/svg", "s:svg"), "\n");
+$root->append($dom->createElementNS("http://www.w3.org/1998/Math/MathML", "m:math"), "\n");
+
+// Note: XML declarations are not emitted in HTML5
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+
+
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_failing_stream.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_failing_stream.phpt
new file mode 100644
index 000000000000..7241c34f7754
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_failing_stream.phpt
@@ -0,0 +1,49 @@
+--TEST--
+DOM\HTMLDocument serialization with a failing stream
+--EXTENSIONS--
+dom
+--FILE--
+fail) {
+ throw new Error("fail");
+ }
+ $this->fail = true;
+ var_dump($data);
+ return strlen($data);
+ }
+
+ public function stream_eof() {
+ return false;
+ }
+
+ public function stream_close() {
+ return true;
+ }
+}
+
+stream_wrapper_register("failing", "FailingWrapper");
+
+$dom = DOM\HTMLDocument::createEmpty();
+$root = $dom->appendChild($dom->createElement("root"));
+$dom->saveHTMLFile("failing://foo");
+
+?>
+--EXPECTF--
+string(1) "<"
+
+Fatal error: Uncaught Error: fail in %s:%d
+Stack trace:
+#0 [internal function]: FailingWrapper->stream_write('root')
+#1 %s(%d): DOM\HTMLDocument->saveHTMLFile('failing://foo')
+#2 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_fragment.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_fragment.phpt
new file mode 100644
index 000000000000..69c47eca4ace
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_fragment.phpt
@@ -0,0 +1,18 @@
+--TEST--
+DOM\HTMLDocument serialization of document fragment
+--EXTENSIONS--
+dom
+--FILE--
+createDocumentFragment();
+$fragment->appendChild($dom->createElement("foo"));
+$bar = $fragment->appendChild($dom->createElement("bar"));
+$fragment->appendChild($dom->createElement("baz"));
+$bar->appendChild($dom->createElement("inner"));
+echo $dom->saveHTML($fragment);
+
+?>
+--EXPECT--
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_full_document.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_full_document.phpt
new file mode 100644
index 000000000000..28fb38024a7b
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_full_document.phpt
@@ -0,0 +1,51 @@
+--TEST--
+DOM\HTMLDocument serialization of full document
+--EXTENSIONS--
+dom
+--FILE--
+
+
+
+ This is my épic title!
+
+
+
+
+
+
+
+
+
+
+
+
+HTML);
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+
+ This is my épic title!
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_01.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_01.phpt
new file mode 100644
index 000000000000..1395fe72f1e9
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_01.phpt
@@ -0,0 +1,30 @@
+--TEST--
+DOM\HTMLDocument serialization with an imported namespace node 01
+--EXTENSIONS--
+dom
+--FILE--
+ ');
+$xml->documentElement->setAttributeNS("http://foo/", "foo:bar", "value");
+$xml->documentElement->appendChild($xml->createElementNS('some:ns2', 'child'));
+echo $xml->saveXML();
+
+echo "--- After import into HTML ---\n";
+
+$html = DOM\HTMLDocument::createFromString('foo
', LIBXML_NOERROR);
+
+$p = $html->documentElement->firstChild->nextSibling->firstChild;
+$p->appendChild($html->importNode($xml->documentElement, true));
+
+echo $html->saveXML();
+echo $html->saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+
+--- After import into HTML ---
+
+ foo
+foo
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_02.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_02.phpt
new file mode 100644
index 000000000000..c7297805b0a3
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_02.phpt
@@ -0,0 +1,59 @@
+--TEST--
+DOM\HTMLDocument serialization with an imported namespace node 02
+--EXTENSIONS--
+dom
+--FILE--
+documentElement->appendChild($xml->createElementNS('some:ns2', 'child'));
+echo $xml->saveXML();
+
+echo "--- After import into HTML ---\n";
+
+$html = DOM\HTMLDocument::createFromString('foo
', LIBXML_NOERROR);
+
+$p = $html->documentElement->firstChild->nextSibling->firstChild;
+$p->appendChild($html->importNode($xml->documentElement, true));
+
+echo $html->saveXML();
+echo $html->saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+
+
+
+
+
+
+
+
+
+
+
+--- After import into HTML ---
+
+foo
+
+
+
+
+
+
+
+
+
+
+foo
+
+
+
+
+
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_03.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_03.phpt
new file mode 100644
index 000000000000..f5a51c16b01e
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_03.phpt
@@ -0,0 +1,39 @@
+--TEST--
+DOM\HTMLDocument serialization with an imported namespace node 03
+--EXTENSIONS--
+dom
+--FILE--
+documentElement->appendChild($xml->createElementNS('some:ns2', 'child'));
+echo $xml->saveXML();
+
+echo "--- After import into HTML ---\n";
+
+$html = DOM\HTMLDocument::createFromString('foo
', LIBXML_NOERROR);
+
+$p = $html->documentElement->firstChild->nextSibling->firstChild;
+$p->appendChild($html->importNode($xml->documentElement, false));
+
+echo $html->saveXML();
+echo $html->saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+
+
+
+
+
+
+
+
+
+
+
+--- After import into HTML ---
+
+foo
+foo
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_04.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_04.phpt
new file mode 100644
index 000000000000..9793043568c2
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_04.phpt
@@ -0,0 +1,39 @@
+--TEST--
+DOM\HTMLDocument serialization with an imported namespace node 04
+--EXTENSIONS--
+dom
+--FILE--
+documentElement->appendChild($xml->createElementNS('some:ns2', 'child'));
+echo $xml->saveXML();
+
+echo "--- After import into HTML ---\n";
+
+$html = DOM\HTMLDocument::createFromString('foo
', LIBXML_NOERROR);
+
+$p = $html->documentElement->firstChild->nextSibling->firstChild;
+$p->appendChild($html->importNode($xml->documentElement, false));
+
+echo $html->saveXML();
+echo $html->saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+
+
+
+
+
+
+
+
+
+
+
+--- After import into HTML ---
+
+foo
+foo
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_05.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_05.phpt
new file mode 100644
index 000000000000..09810be1e13e
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_05.phpt
@@ -0,0 +1,59 @@
+--TEST--
+DOM\HTMLDocument serialization with an imported namespace node 05
+--EXTENSIONS--
+dom
+--FILE--
+documentElement->appendChild($xml->createElementNS('some:ns2', 'child'));
+echo $xml->saveXML();
+
+echo "--- After adoption into HTML ---\n";
+
+$html = DOM\HTMLDocument::createFromString('foo
', LIBXML_NOERROR);
+
+$p = $html->documentElement->firstChild->nextSibling->firstChild;
+$p->appendChild($html->adoptNode($xml->documentElement));
+
+echo $html->saveXML();
+echo $html->saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+
+
+
+
+
+
+
+
+
+
+
+--- After adoption into HTML ---
+
+foo
+
+
+
+
+
+
+
+
+
+
+foo
+
+
+
+
+
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_06.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_06.phpt
new file mode 100644
index 000000000000..5df88add914e
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_ns_imported_06.phpt
@@ -0,0 +1,55 @@
+--TEST--
+DOM\HTMLDocument serialization with an imported namespace node 06
+--EXTENSIONS--
+dom
+--FILE--
+documentElement->firstChild->appendChild($xml->createElementNS('some:ns2', 'child'));
+echo $xml->saveXML();
+
+echo "--- After clone + import into HTML ---\n";
+
+$html = DOM\HTMLDocument::createFromString('foo
', LIBXML_NOERROR);
+
+$p = $html->documentElement->firstChild->nextSibling->firstChild;
+$p->appendChild($html->adoptNode($xml->documentElement->firstElementChild->cloneNode(true)));
+
+echo $html->saveXML();
+echo $html->saveHTML(), "\n";
+
+?>
+--EXPECT--
+
+
+
+
+
+
+
+
+
+
+
+
+--- After clone + import into HTML ---
+
+foo
+
+
+
+
+
+
+
+
+foo
+
+
+
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_processing_instruction.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_processing_instruction.phpt
new file mode 100644
index 000000000000..c45bef620faa
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_processing_instruction.phpt
@@ -0,0 +1,15 @@
+--TEST--
+DOM\HTMLDocument serialization of processing instruction
+--EXTENSIONS--
+dom
+--FILE--
+ in a processing instruction element but that breaks (as expected)
+$dom->appendChild($dom->createProcessingInstruction("target", "foobaré\"&<\xc2\xa0"));
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_roots_test_empty.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_roots_test_empty.phpt
new file mode 100644
index 000000000000..fd33e67d7e48
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_roots_test_empty.phpt
@@ -0,0 +1,30 @@
+--TEST--
+DOM\HTMLDocument serialization of different roots resulting in an empty result
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createComment("comment"));
+$cdata = $dom->appendChild($dom->createCDATASection("cdata"));
+$emptyElement = $dom->appendChild($dom->createElement("empty"));
+$text = $dom->appendChild($dom->createTextNode("text"));
+$pi = $dom->appendChild($dom->createProcessingInstruction("target", "data"));
+$fragment = $dom->createDocumentFragment();
+
+var_dump($dom->saveHTML($comment));
+var_dump($dom->saveHTML($cdata));
+var_dump($dom->saveHTML($emptyElement));
+var_dump($dom->saveHTML($text));
+var_dump($dom->saveHTML($pi));
+var_dump($dom->saveHTML($fragment));
+
+?>
+--EXPECT--
+string(0) ""
+string(0) ""
+string(0) ""
+string(0) ""
+string(0) ""
+string(0) ""
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_01.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_01.phpt
new file mode 100644
index 000000000000..025bee90ced7
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_01.phpt
@@ -0,0 +1,15 @@
+--TEST--
+DOM\HTMLDocument serialization escape text 01
+--EXTENSIONS--
+dom
+--FILE--
+", LIBXML_NOERROR);
+$p = $dom->documentElement->firstChild->nextSibling->firstChild;
+$p->textContent = "this is &text! \"\"";
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+this is <some> &text! ""
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_02.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_02.phpt
new file mode 100644
index 000000000000..31e12bbd6ef4
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_02.phpt
@@ -0,0 +1,27 @@
+--TEST--
+DOM\HTMLDocument serialization escape text 02 - special tags in html namespace
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createElement("body"));
+foreach (["style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript"] as $tag) {
+ $tag = $body->appendChild($dom->createElement($tag));
+ $tag->textContent = "&\"<>\xc2\xa0 foobar";
+ $body->appendChild(new DOMText("\n"));
+}
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+
+
+&"<> foobar
+
+&"<> foobar
+&"<> foobar
+&"<> foobar
+&"<> foobar
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_03.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_03.phpt
new file mode 100644
index 000000000000..fee3c34b3c5d
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_03.phpt
@@ -0,0 +1,27 @@
+--TEST--
+DOM\HTMLDocument serialization escape text 03 - special tags in namespace should encode content
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createElement("body"));
+foreach (["style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript"] as $tag) {
+ $tag = $body->appendChild($dom->createElementNS("some:ns", $tag));
+ $tag->textContent = "&\"<>\xc2\xa0 foobar";
+ $body->appendChild(new DOMText("\n"));
+}
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+
+
+&"<> foobar
+
+&"<> foobar
+&"<> foobar
+&"<> foobar
+&"<> foobar
+
diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_void_elements.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_void_elements.phpt
new file mode 100644
index 000000000000..c5ac9a6d71a7
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_void_elements.phpt
@@ -0,0 +1,100 @@
+--TEST--
+DOM\HTMLDocument serialization of void elements
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createElement($tag));
+ $element->appendChild($dom->createElement("inner"));
+ $element->after("\n");
+ echo "$tag: ";
+ var_dump(strlen($dom->saveHTML($element)) === 0);
+
+ $element = $dom->appendChild($dom->createElementNS("http://php.net/foo", "x:$tag"));
+ $element->appendChild($dom->createElement("inner"));
+ $element->after("\n");
+}
+
+echo $dom->saveHTML();
+
+?>
+--EXPECT--
+area: bool(true)
+base: bool(true)
+br: bool(true)
+col: bool(true)
+embed: bool(true)
+hr: bool(true)
+img: bool(true)
+input: bool(true)
+link: bool(true)
+meta: bool(true)
+source: bool(true)
+track: bool(true)
+wbr: bool(true)
+basefont: bool(true)
+bgsound: bool(true)
+frame: bool(true)
+keygen: bool(true)
+param: bool(true)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ext/dom/tests/modern/html/serializer/sample.xml b/ext/dom/tests/modern/html/serializer/sample.xml
new file mode 100644
index 000000000000..e1c3c58a829b
--- /dev/null
+++ b/ext/dom/tests/modern/html/serializer/sample.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ext/dom/tests/modern/xml/XMLDocument_createFromFile_override_encoding.phpt b/ext/dom/tests/modern/xml/XMLDocument_createFromFile_override_encoding.phpt
new file mode 100644
index 000000000000..9ca93b56d587
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_createFromFile_override_encoding.phpt
@@ -0,0 +1,30 @@
+--TEST--
+DOM\XMLDocument::createFromFile() with overrideEncoding
+--EXTENSIONS--
+dom
+--FILE--
+getMessage(), "\n";
+}
+
+// The override encoding matches with the document encoding attribute
+$dom = DOM\XMLDocument::createFromFile(__DIR__ . '/dummy.xml', overrideEncoding: 'UTF-8');
+var_dump($dom->documentElement->lastChild->textContent);
+var_dump($dom->encoding);
+
+// The override encoding mismatches with the document encoding attribute
+$dom = DOM\XMLDocument::createFromFile(__DIR__ . '/dummy.xml', overrideEncoding: 'Windows-1252');
+var_dump($dom->documentElement->lastChild->textContent);
+var_dump($dom->encoding);
+
+?>
+--EXPECT--
+DOM\XMLDocument::createFromFile(): Argument #3 ($overrideEncoding) must be a valid document encoding
+string(2) "é"
+string(5) "UTF-8"
+string(4) "é"
+string(12) "Windows-1252"
diff --git a/ext/dom/tests/modern/xml/XMLDocument_createFromString_override_encoding.phpt b/ext/dom/tests/modern/xml/XMLDocument_createFromString_override_encoding.phpt
new file mode 100644
index 000000000000..29ce548292e9
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_createFromString_override_encoding.phpt
@@ -0,0 +1,30 @@
+--TEST--
+DOM\XMLDocument::createFromString() with overrideEncoding
+--EXTENSIONS--
+dom
+--FILE--
+getMessage(), "\n";
+}
+
+// The override encoding matches with the document encoding attribute
+$dom = DOM\XMLDocument::createFromString(file_get_contents(__DIR__ . '/dummy.xml'), overrideEncoding: 'UTF-8');
+var_dump($dom->documentElement->lastChild->textContent);
+var_dump($dom->encoding);
+
+// The override encoding mismatches with the document encoding attribute
+$dom = DOM\XMLDocument::createFromString(file_get_contents(__DIR__ . '/dummy.xml'), overrideEncoding: 'Windows-1252');
+var_dump($dom->documentElement->lastChild->textContent);
+var_dump($dom->encoding);
+
+?>
+--EXPECT--
+DOM\XMLDocument::createFromString(): Argument #3 ($overrideEncoding) must be a valid document encoding
+string(2) "é"
+string(5) "UTF-8"
+string(4) "é"
+string(12) "Windows-1252"
diff --git a/ext/dom/tests/modern/xml/XMLDocument_debug.phpt b/ext/dom/tests/modern/xml/XMLDocument_debug.phpt
new file mode 100644
index 000000000000..2fbe33cd73e3
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_debug.phpt
@@ -0,0 +1,88 @@
+--TEST--
+DOM\XMLDocument debug
+--EXTENSIONS--
+dom
+--FILE--
+
+--EXPECT--
+object(DOM\XMLDocument)#1 (37) {
+ ["encoding"]=>
+ string(5) "UTF-8"
+ ["xmlEncoding"]=>
+ string(5) "UTF-8"
+ ["standalone"]=>
+ bool(false)
+ ["xmlStandalone"]=>
+ bool(false)
+ ["version"]=>
+ string(3) "1.0"
+ ["xmlVersion"]=>
+ string(3) "1.0"
+ ["formatOutput"]=>
+ bool(false)
+ ["validateOnParse"]=>
+ bool(false)
+ ["resolveExternals"]=>
+ bool(false)
+ ["preserveWhiteSpace"]=>
+ bool(true)
+ ["recover"]=>
+ bool(false)
+ ["substituteEntities"]=>
+ bool(false)
+ ["doctype"]=>
+ NULL
+ ["documentElement"]=>
+ NULL
+ ["strictErrorChecking"]=>
+ bool(true)
+ ["documentURI"]=>
+ NULL
+ ["firstElementChild"]=>
+ NULL
+ ["lastElementChild"]=>
+ NULL
+ ["childElementCount"]=>
+ int(0)
+ ["nodeName"]=>
+ string(9) "#document"
+ ["nodeValue"]=>
+ NULL
+ ["nodeType"]=>
+ int(9)
+ ["parentNode"]=>
+ NULL
+ ["parentElement"]=>
+ NULL
+ ["childNodes"]=>
+ string(22) "(object value omitted)"
+ ["firstChild"]=>
+ NULL
+ ["lastChild"]=>
+ NULL
+ ["previousSibling"]=>
+ NULL
+ ["nextSibling"]=>
+ NULL
+ ["attributes"]=>
+ NULL
+ ["isConnected"]=>
+ bool(true)
+ ["ownerDocument"]=>
+ NULL
+ ["namespaceURI"]=>
+ NULL
+ ["prefix"]=>
+ string(0) ""
+ ["localName"]=>
+ NULL
+ ["baseURI"]=>
+ NULL
+ ["textContent"]=>
+ string(0) ""
+}
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_01.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_01.phpt
new file mode 100644
index 000000000000..cc8f3f159b08
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_01.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\XMLDocument::createEmpty 01
+--EXTENSIONS--
+dom
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught ValueError: DOM\XMLDocument::createEmpty(): Argument #2 ($encoding) is not a valid document encoding in %s:%d
+Stack trace:
+#0 %s(%d): DOM\XMLDocument::createEmpty('1.0', 'foo')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt
new file mode 100644
index 000000000000..5f7a604bb12b
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt
@@ -0,0 +1,88 @@
+--TEST--
+DOM\XMLDocument::createEmpty 02
+--EXTENSIONS--
+dom
+--FILE--
+
+--EXPECT--
+object(DOM\XMLDocument)#1 (37) {
+ ["encoding"]=>
+ string(5) "UTF-8"
+ ["xmlEncoding"]=>
+ string(5) "UTF-8"
+ ["standalone"]=>
+ bool(false)
+ ["xmlStandalone"]=>
+ bool(false)
+ ["version"]=>
+ string(3) "1.1"
+ ["xmlVersion"]=>
+ string(3) "1.1"
+ ["formatOutput"]=>
+ bool(false)
+ ["validateOnParse"]=>
+ bool(false)
+ ["resolveExternals"]=>
+ bool(false)
+ ["preserveWhiteSpace"]=>
+ bool(true)
+ ["recover"]=>
+ bool(false)
+ ["substituteEntities"]=>
+ bool(false)
+ ["doctype"]=>
+ NULL
+ ["documentElement"]=>
+ NULL
+ ["strictErrorChecking"]=>
+ bool(true)
+ ["documentURI"]=>
+ NULL
+ ["firstElementChild"]=>
+ NULL
+ ["lastElementChild"]=>
+ NULL
+ ["childElementCount"]=>
+ int(0)
+ ["nodeName"]=>
+ string(9) "#document"
+ ["nodeValue"]=>
+ NULL
+ ["nodeType"]=>
+ int(9)
+ ["parentNode"]=>
+ NULL
+ ["parentElement"]=>
+ NULL
+ ["childNodes"]=>
+ string(22) "(object value omitted)"
+ ["firstChild"]=>
+ NULL
+ ["lastChild"]=>
+ NULL
+ ["previousSibling"]=>
+ NULL
+ ["nextSibling"]=>
+ NULL
+ ["attributes"]=>
+ NULL
+ ["isConnected"]=>
+ bool(true)
+ ["ownerDocument"]=>
+ NULL
+ ["namespaceURI"]=>
+ NULL
+ ["prefix"]=>
+ string(0) ""
+ ["localName"]=>
+ NULL
+ ["baseURI"]=>
+ NULL
+ ["textContent"]=>
+ string(0) ""
+}
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_03.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_03.phpt
new file mode 100644
index 000000000000..590fdd976bd0
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_03.phpt
@@ -0,0 +1,15 @@
+--TEST--
+DOM\XMLDocument::createEmpty 03
+--EXTENSIONS--
+dom
+--FILE--
+append("foo");
+echo $dom->saveXML();
+
+?>
+--EXPECT--
+
+foo
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromFile_01.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromFile_01.phpt
new file mode 100644
index 000000000000..5e4243795f7c
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromFile_01.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\XMLDocument::createFromFile 01
+--EXTENSIONS--
+dom
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught ValueError: DOM\XMLDocument::createFromString(): Argument #1 ($source) must not be empty in %s:%d
+Stack trace:
+#0 %s(%d): DOM\XMLDocument::createFromString('')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromFile_02.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromFile_02.phpt
new file mode 100644
index 000000000000..2d593c124ab8
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromFile_02.phpt
@@ -0,0 +1,18 @@
+--TEST--
+DOM\XMLDocument::createFromFile 02
+--EXTENSIONS--
+dom
+--FILE--
+
+--EXPECTF--
+Warning: DOM\XMLDocument::createFromFile(): I/O warning : failed to load external entity "%s" in %s on line %d
+
+Fatal error: Uncaught Exception: Cannot open file '\0' in %s:%d
+Stack trace:
+#0 %s(%d): DOM\XMLDocument::createFromFile('\\0')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromFile_03.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromFile_03.phpt
new file mode 100644
index 000000000000..f129450b38c9
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromFile_03.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\XMLDocument::createFromFile 03
+--EXTENSIONS--
+dom
+--FILE--
+getMessage();
+}
+
+?>
+--EXPECT--
+DOM\XMLDocument::createFromFile(): Argument #1 ($path) must not contain percent-encoded NUL bytes
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromFile_04.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromFile_04.phpt
new file mode 100644
index 000000000000..fcf8709e954c
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromFile_04.phpt
@@ -0,0 +1,23 @@
+--TEST--
+DOM\XMLDocument::createFromFile 04
+--EXTENSIONS--
+dom
+--FILE--
+saveXML();
+
+?>
+--EXPECT--
+
+
+
+ The Grapes of Wrath
+ John Steinbeck
+
+
+ The Pearl
+ John Steinbeck
+
+
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromString_01.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromString_01.phpt
new file mode 100644
index 000000000000..f4b7771e852c
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromString_01.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\XMLDocument::createFromString 01
+--EXTENSIONS--
+dom
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught ValueError: DOM\XMLDocument::createFromString(): Argument #1 ($source) must not be empty in %s:%d
+Stack trace:
+#0 %s(%d): DOM\XMLDocument::createFromString('')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromString_02.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromString_02.phpt
new file mode 100644
index 000000000000..3576af5f6a62
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromString_02.phpt
@@ -0,0 +1,15 @@
+--TEST--
+DOM\XMLDocument::createFromString 02
+--EXTENSIONS--
+dom
+--FILE--
+ ');
+var_dump($dom->saveXMLFile("php://stdout"));
+
+?>
+--EXPECT--
+
+
+int(52)
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromString_03.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromString_03.phpt
new file mode 100644
index 000000000000..359f7086efce
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromString_03.phpt
@@ -0,0 +1,38 @@
+--TEST--
+DOM\XMLDocument::createFromString 03
+--EXTENSIONS--
+dom
+--FILE--
+ ', -1);
+} catch (ValueError $e) {
+ echo $e->getMessage();
+}
+
+foreach ($flags as $flag) {
+ var_dump(DOM\XMLDocument::createFromString(' ', $flag) instanceof DOM\XMLDocument);
+}
+
+?>
+--EXPECT--
+DOM\XMLDocument::createFromString(): Argument #2 ($options) contains invalid flags (allowed flags: LIBXML_NOENT, LIBXML_DTDLOAD, LIBXML_DTDATTR, LIBXML_DTDVALID, LIBXML_NOERROR, LIBXML_NOWARNING, LIBXML_NOBLANKS, LIBXML_XINCLUDE, LIBXML_NSCLEAN, LIBXML_NOCDATA, LIBXML_NONET, LIBXML_PEDANTIC, LIBXML_COMPACT, LIBXML_PARSEHUGE, LIBXML_BIGLINES)bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromString_04.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromString_04.phpt
new file mode 100644
index 000000000000..f3a7cd669027
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_fromString_04.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\XMLDocument::createFromString 04
+--EXTENSIONS--
+dom
+--FILE--
+ ');
+var_dump($dom->encoding);
+echo $dom->saveXML();
+
+?>
+--EXPECT--
+string(12) "Windows-1251"
+
+
diff --git a/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt b/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt
new file mode 100644
index 000000000000..eacdbedd67ef
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt
@@ -0,0 +1,91 @@
+--TEST--
+DOM\XMLDocument getting ownerDocument from a node in an XML document should yield a DOM\XMLDocument
+--EXTENSIONS--
+dom
+--FILE--
+ ');
+
+$element = $dom->documentElement;
+unset($dom);
+var_dump($element->ownerDocument);
+
+?>
+--EXPECTF--
+object(DOM\XMLDocument)#1 (37) {
+ ["encoding"]=>
+ string(5) "UTF-8"
+ ["xmlEncoding"]=>
+ string(5) "UTF-8"
+ ["standalone"]=>
+ bool(false)
+ ["xmlStandalone"]=>
+ bool(false)
+ ["version"]=>
+ string(3) "1.0"
+ ["xmlVersion"]=>
+ string(3) "1.0"
+ ["formatOutput"]=>
+ bool(false)
+ ["validateOnParse"]=>
+ bool(false)
+ ["resolveExternals"]=>
+ bool(false)
+ ["preserveWhiteSpace"]=>
+ bool(true)
+ ["recover"]=>
+ bool(false)
+ ["substituteEntities"]=>
+ bool(false)
+ ["doctype"]=>
+ NULL
+ ["documentElement"]=>
+ string(22) "(object value omitted)"
+ ["strictErrorChecking"]=>
+ bool(true)
+ ["documentURI"]=>
+ string(%d) "%s"
+ ["firstElementChild"]=>
+ string(22) "(object value omitted)"
+ ["lastElementChild"]=>
+ string(22) "(object value omitted)"
+ ["childElementCount"]=>
+ int(1)
+ ["nodeName"]=>
+ string(9) "#document"
+ ["nodeValue"]=>
+ NULL
+ ["nodeType"]=>
+ int(9)
+ ["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(true)
+ ["ownerDocument"]=>
+ NULL
+ ["namespaceURI"]=>
+ NULL
+ ["prefix"]=>
+ string(0) ""
+ ["localName"]=>
+ NULL
+ ["baseURI"]=>
+ string(%d) %s
+ ["textContent"]=>
+ string(0) ""
+}
diff --git a/ext/dom/tests/modern/xml/XMLDocument_saveXML_node.phpt b/ext/dom/tests/modern/xml/XMLDocument_saveXML_node.phpt
new file mode 100644
index 000000000000..73a48eb454dd
--- /dev/null
+++ b/ext/dom/tests/modern/xml/XMLDocument_saveXML_node.phpt
@@ -0,0 +1,16 @@
+--TEST--
+DOM\XMLDocument::saveXML(File) node
+--EXTENSIONS--
+dom
+--FILE--
+appendChild($dom->createElement("root"));
+$child1 = $root->appendChild($dom->createElement("child1"));
+$child2 = $root->appendChild($dom->createElement("child2"));
+echo $dom->saveXML($child1);
+
+?>
+--EXPECT--
+
diff --git a/ext/dom/tests/modern/xml/dummy.xml b/ext/dom/tests/modern/xml/dummy.xml
new file mode 100644
index 000000000000..305c2f435d62
--- /dev/null
+++ b/ext/dom/tests/modern/xml/dummy.xml
@@ -0,0 +1,2 @@
+
+é
diff --git a/ext/dom/tests/registerNodeClass_abstract_class.phpt b/ext/dom/tests/registerNodeClass_abstract_class.phpt
index 24124d712ea0..3c384f3f5d52 100644
--- a/ext/dom/tests/registerNodeClass_abstract_class.phpt
+++ b/ext/dom/tests/registerNodeClass_abstract_class.phpt
@@ -21,4 +21,4 @@ $dom->createElement("foo");
?>
--EXPECT--
-ValueError: DOMDocument::registerNodeClass(): Argument #2 ($extendedClass) must not be an abstract class
+ValueError: DOM\Document::registerNodeClass(): Argument #2 ($extendedClass) must not be an abstract class
diff --git a/ext/dom/xml_document.c b/ext/dom/xml_document.c
new file mode 100644
index 000000000000..c45e2ccfda01
--- /dev/null
+++ b/ext/dom/xml_document.c
@@ -0,0 +1,258 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Niels Dossche |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
+#include "php_dom.h"
+#include "namespace_compat.h"
+
+static bool check_options_validity(uint32_t arg_num, zend_long options)
+{
+ const zend_long VALID_OPTIONS = XML_PARSE_NOENT
+ | XML_PARSE_DTDLOAD
+ | XML_PARSE_DTDATTR
+ | XML_PARSE_DTDVALID
+ | XML_PARSE_NOERROR
+ | XML_PARSE_NOWARNING
+ | XML_PARSE_NOBLANKS
+ | XML_PARSE_XINCLUDE
+ | XML_PARSE_NSCLEAN
+ | XML_PARSE_NOCDATA
+ | XML_PARSE_NONET
+ | XML_PARSE_PEDANTIC
+ | XML_PARSE_COMPACT
+ | XML_PARSE_HUGE
+ | XML_PARSE_BIG_LINES;
+ if ((options & ~VALID_OPTIONS) != 0) {
+ zend_argument_value_error(2, "contains invalid flags (allowed flags: "
+ "LIBXML_NOENT, "
+ "LIBXML_DTDLOAD, "
+ "LIBXML_DTDATTR, "
+ "LIBXML_DTDVALID, "
+ "LIBXML_NOERROR, "
+ "LIBXML_NOWARNING, "
+ "LIBXML_NOBLANKS, "
+ "LIBXML_XINCLUDE, "
+ "LIBXML_NSCLEAN, "
+ "LIBXML_NOCDATA, "
+ "LIBXML_NONET, "
+ "LIBXML_PEDANTIC, "
+ "LIBXML_COMPACT, "
+ "LIBXML_PARSEHUGE, "
+ "LIBXML_BIGLINES)");
+ return false;
+ }
+ return true;
+}
+
+/* Living spec never creates explicit namespace declaration nodes.
+ * They are only written upon serialization but never appear in the tree.
+ * So in principle we could just ignore them outright.
+ * However, step 10 in https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token
+ * requires us to have the declaration as an attribute available */
+static void dom_mark_namespaces_as_attributes_too(xmlDocPtr doc)
+{
+ xmlNodePtr node = doc->children;
+ while (node != NULL) {
+ if (node->type == XML_ELEMENT_NODE) {
+ dom_ns_compat_mark_attribute_list(node->nsDef);
+
+ if (node->children) {
+ node = node->children;
+ continue;
+ }
+ }
+
+ if (node->next) {
+ node = node->next;
+ } else {
+ /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
+ do {
+ node = node->parent;
+ if (node == NULL) {
+ return;
+ }
+ } while (node->next == NULL);
+ node = node->next;
+ }
+ }
+}
+
+void dom_mark_namespaces_for_copy_based_on_copy(xmlNodePtr copy, const xmlNode *original)
+{
+ xmlNodePtr copy_current = copy;
+ const xmlNode *original_current = original;
+ while (copy_current != NULL) {
+ ZEND_ASSERT(original_current != NULL);
+
+ if (copy_current->type == XML_ELEMENT_NODE) {
+ dom_ns_compat_copy_attribute_list_mark(copy_current->nsDef, original_current->nsDef);
+
+ if (copy_current->children) {
+ copy_current = copy_current->children;
+ original_current = original_current->children;
+ continue;
+ }
+ }
+
+ if (copy_current->next) {
+ copy_current = copy_current->next;
+ original_current = original_current->next;
+ } else {
+ /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
+ do {
+ copy_current = copy_current->parent;
+ if (copy_current == NULL) {
+ return;
+ }
+ original_current = original_current->parent;
+ } while (copy_current->next == NULL);
+ copy_current = copy_current->next;
+ original_current = original_current->next;
+ }
+ }
+}
+
+PHP_METHOD(DOM_XMLDocument, createEmpty)
+{
+ const char *version = NULL;
+ size_t encoding_len = strlen("UTF-8");
+ const char *encoding = "UTF-8";
+ size_t version_len;
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sp", &version, &version_len, &encoding, &encoding_len) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ xmlCharEncodingHandlerPtr handler = xmlFindCharEncodingHandler(encoding);
+
+ if (handler != NULL) {
+ xmlCharEncCloseFunc(handler);
+ } else {
+ zend_argument_value_error(2, "is not a valid document encoding");
+ RETURN_THROWS();
+ }
+
+ xmlDocPtr lxml_doc = xmlNewDoc((const xmlChar *) version);
+ if (UNEXPECTED(lxml_doc == NULL)) {
+ goto oom;
+ }
+
+ lxml_doc->encoding = xmlStrdup((const xmlChar *) encoding);
+
+ dom_object *intern = php_dom_instantiate_object_helper(
+ return_value,
+ dom_xml_document_class_entry,
+ (xmlNodePtr) lxml_doc,
+ NULL
+ );
+ intern->document->is_modern_api_class = true;
+ return;
+
+oom:
+ php_dom_throw_error(INVALID_STATE_ERR, 1);
+ RETURN_THROWS();
+}
+
+static void load_from_helper(INTERNAL_FUNCTION_PARAMETERS, int mode)
+{
+ const char *source, *override_encoding = NULL;
+ size_t source_len, override_encoding_len;
+ zend_long options = 0;
+ if (zend_parse_parameters(
+ ZEND_NUM_ARGS(),
+ "s|lp!",
+ &source,
+ &source_len,
+ &options,
+ &override_encoding,
+ &override_encoding_len
+ ) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ if (!source_len) {
+ zend_argument_value_error(1, "must not be empty");
+ RETURN_THROWS();
+ }
+
+ if (ZEND_SIZE_T_INT_OVFL(source_len)) {
+ zend_argument_value_error(1, "is too long");
+ RETURN_THROWS();
+ }
+
+ /* See php_libxml_streams_IO_open_wrapper(), apparently this caused issues in the past. */
+ if (mode == DOM_LOAD_FILE && strstr(source, "%00")) {
+ zend_argument_value_error(1, "must not contain percent-encoded NUL bytes");
+ RETURN_THROWS();
+ }
+
+ if (!check_options_validity(2, options)) {
+ RETURN_THROWS();
+ }
+
+ xmlCharEncodingHandlerPtr encoding = NULL;
+ if (override_encoding != NULL) {
+ encoding = xmlFindCharEncodingHandler(override_encoding);
+ if (!encoding) {
+ zend_argument_value_error(3, "must be a valid document encoding");
+ RETURN_THROWS();
+ }
+ options |= XML_PARSE_IGNORE_ENC;
+ }
+
+ xmlDocPtr lxml_doc = dom_document_parser(NULL, mode, source, source_len, options, encoding);
+ if (UNEXPECTED(lxml_doc == NULL)) {
+ if (!EG(exception)) {
+ if (mode == DOM_LOAD_FILE) {
+ zend_throw_exception_ex(NULL, 0, "Cannot open file '%s'", source);
+ } else {
+ php_dom_throw_error(INVALID_STATE_ERR, 1);
+ }
+ }
+ RETURN_THROWS();
+ }
+ if (lxml_doc->encoding == NULL) {
+ if (override_encoding) {
+ lxml_doc->encoding = xmlStrdup((const xmlChar *) override_encoding);
+ } else {
+ lxml_doc->encoding = xmlStrdup((const xmlChar *) "UTF-8");
+ }
+ }
+ dom_object *intern = php_dom_instantiate_object_helper(
+ return_value,
+ dom_xml_document_class_entry,
+ (xmlNodePtr) lxml_doc,
+ NULL
+ );
+ intern->document->is_modern_api_class = true;
+ dom_mark_namespaces_as_attributes_too(lxml_doc);
+}
+
+PHP_METHOD(DOM_XMLDocument, createFromString)
+{
+ load_from_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
+}
+
+PHP_METHOD(DOM_XMLDocument, createFromFile)
+{
+ load_from_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
+}
+
+#endif /* HAVE_LIBXML && HAVE_DOM */
diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c
index 0ef0fdfa8c09..7522ec3f1df8 100644
--- a/ext/dom/xpath.c
+++ b/ext/dom/xpath.c
@@ -215,7 +215,7 @@ PHP_METHOD(DOMXPath, __construct)
dom_xpath_object *intern;
xmlXPathContextPtr ctx, oldctx;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &doc, dom_document_class_entry, ®ister_node_ns) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &doc, dom_abstract_base_document_class_entry, ®ister_node_ns) == FAILURE) {
RETURN_THROWS();
}
@@ -383,7 +383,6 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
}
}
-
ctxp->namespaces = ns;
ctxp->nsNr = nsnbr;
diff --git a/ext/libxml/config.w32 b/ext/libxml/config.w32
index b11c57bc44a7..3a2a707f3e4b 100644
--- a/ext/libxml/config.w32
+++ b/ext/libxml/config.w32
@@ -9,7 +9,7 @@ if (PHP_LIBXML == "yes") {
CHECK_HEADER_ADD_INCLUDE("libxml/tree.h", "CFLAGS_LIBXML", PHP_PHP_BUILD + "\\include\\libxml2") &&
ADD_EXTENSION_DEP('libxml', 'iconv')) {
- EXTENSION("libxml", "libxml.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
+ EXTENSION("libxml", "libxml.c mime_sniff.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
AC_DEFINE("HAVE_LIBXML", 1, "LibXML support");
ADD_FLAG("CFLAGS_LIBXML", "/D LIBXML_STATIC /D LIBXML_STATIC_FOR_DLL /D HAVE_WIN32_THREADS ");
if (!PHP_LIBXML_SHARED) {
diff --git a/ext/libxml/config0.m4 b/ext/libxml/config0.m4
index 044a58fa6246..a594e350e149 100644
--- a/ext/libxml/config0.m4
+++ b/ext/libxml/config0.m4
@@ -11,7 +11,7 @@ if test "$PHP_LIBXML" != "no"; then
PHP_SETUP_LIBXML(LIBXML_SHARED_LIBADD, [
AC_DEFINE(HAVE_LIBXML,1,[ ])
- PHP_NEW_EXTENSION(libxml, [libxml.c], $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
+ PHP_NEW_EXTENSION(libxml, [libxml.c mime_sniff.c], $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
PHP_INSTALL_HEADERS([ext/libxml/php_libxml.h])
])
fi
diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c
index 6ddbdff5fb80..b3798107abd9 100644
--- a/ext/libxml/libxml.c
+++ b/ext/libxml/libxml.c
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
#ifdef LIBXML_SCHEMAS_ENABLED
#include
#include
@@ -369,6 +370,11 @@ static PHP_GINIT_FUNCTION(libxml)
libxml_globals->entity_loader_callback = empty_fcall_info_cache;
}
+PHP_LIBXML_API php_stream_context *php_libxml_get_stream_context(void)
+{
+ return php_stream_context_from_zval(Z_ISUNDEF(LIBXML(stream_context)) ? NULL : &LIBXML(stream_context), false);
+}
+
/* Channel libxml file io layer through the PHP streams subsystem.
* This allows use of ftps:// and https:// urls */
@@ -436,7 +442,7 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char
}
}
- context = php_stream_context_from_zval(Z_ISUNDEF(LIBXML(stream_context))? NULL : &LIBXML(stream_context), 0);
+ context = php_libxml_get_stream_context();
ret_val = php_stream_open_wrapper_ex(path_to_open, (char *)mode, REPORT_ERRORS, NULL, context);
if (ret_val) {
@@ -496,47 +502,13 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc)
/* Check if there's been an external transport protocol with an encoding information */
if (enc == XML_CHAR_ENCODING_NONE) {
php_stream *s = (php_stream *) context;
-
- if (Z_TYPE(s->wrapperdata) == IS_ARRAY) {
- zval *header;
-
- ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) {
- const char buf[] = "Content-Type:";
- if (Z_TYPE_P(header) == IS_STRING &&
- !zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) {
- char needle[] = "charset=";
- char *haystack = estrndup(Z_STRVAL_P(header), Z_STRLEN_P(header));
- char *encoding = php_stristr(haystack, needle, Z_STRLEN_P(header), strlen(needle));
-
- if (encoding) {
- char *end;
-
- encoding += sizeof("charset=")-1;
- if (*encoding == '"') {
- encoding++;
- }
- end = strchr(encoding, ';');
- if (end == NULL) {
- end = encoding + strlen(encoding);
- }
- end--; /* end == encoding-1 isn't a buffer underrun */
- while (*end == ' ' || *end == '\t') {
- end--;
- }
- if (*end == '"') {
- end--;
- }
- if (encoding >= end) continue;
- *(end+1) = '\0';
- enc = xmlParseCharEncoding(encoding);
- if (enc <= XML_CHAR_ENCODING_NONE) {
- enc = XML_CHAR_ENCODING_NONE;
- }
- }
- efree(haystack);
- break; /* found content-type */
- }
- } ZEND_HASH_FOREACH_END();
+ zend_string *charset = php_libxml_sniff_charset_from_stream(s);
+ if (charset != NULL) {
+ enc = xmlParseCharEncoding(ZSTR_VAL(charset));
+ if (enc <= XML_CHAR_ENCODING_NONE) {
+ enc = XML_CHAR_ENCODING_NONE;
+ }
+ zend_string_release_ex(charset, false);
}
}
@@ -608,7 +580,7 @@ static void _php_libxml_free_error(void *ptr)
xmlResetError((xmlErrorPtr) ptr);
}
-static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
+static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg, int line, int column)
{
xmlError error_copy;
int ret;
@@ -621,6 +593,8 @@ static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
} else {
error_copy.code = XML_ERR_INTERNAL_ERROR;
error_copy.level = XML_ERR_ERROR;
+ error_copy.line = line;
+ error_copy.int2 = column;
error_copy.message = (char*)xmlStrdup((const xmlChar*)msg);
ret = 0;
}
@@ -630,7 +604,7 @@ static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
}
}
-static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg)
+static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg, int line)
{
xmlParserCtxtPtr parser;
@@ -638,9 +612,9 @@ static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg)
if (parser != NULL && parser->input != NULL) {
if (parser->input->filename) {
- php_error_docref(NULL, level, "%s in %s, line: %d", msg, parser->input->filename, parser->input->line);
+ php_error_docref(NULL, level, "%s in %s, line: %d", msg, parser->input->filename, line);
} else {
- php_error_docref(NULL, level, "%s in Entity, line: %d", msg, parser->input->line);
+ php_error_docref(NULL, level, "%s in Entity, line: %d", msg, line);
}
} else {
php_error_docref(NULL, E_WARNING, "%s", msg);
@@ -650,13 +624,13 @@ static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg)
void php_libxml_issue_error(int level, const char *msg)
{
if (LIBXML(error_list)) {
- _php_list_set_error_structure(NULL, msg);
+ _php_list_set_error_structure(NULL, msg, 0, 0);
} else {
php_error_docref(NULL, level, "%s", msg);
}
}
-static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap)
+static void php_libxml_internal_error_handler_ex(int error_type, void *ctx, const char **msg, va_list ap, int line, int column)
{
char *buf;
int len, len_iter, output = 0;
@@ -676,15 +650,15 @@ static void php_libxml_internal_error_handler(int error_type, void *ctx, const c
if (output == 1) {
if (LIBXML(error_list)) {
- _php_list_set_error_structure(NULL, ZSTR_VAL(LIBXML(error_buffer).s));
+ _php_list_set_error_structure(NULL, ZSTR_VAL(LIBXML(error_buffer).s), line, column);
} else if (!EG(exception)) {
/* Don't throw additional notices/warnings if an exception has already been thrown. */
switch (error_type) {
case PHP_LIBXML_CTX_ERROR:
- php_libxml_ctx_error_level(E_WARNING, ctx, ZSTR_VAL(LIBXML(error_buffer).s));
+ php_libxml_ctx_error_level(E_WARNING, ctx, ZSTR_VAL(LIBXML(error_buffer).s), line);
break;
case PHP_LIBXML_CTX_WARNING:
- php_libxml_ctx_error_level(E_NOTICE, ctx, ZSTR_VAL(LIBXML(error_buffer).s));
+ php_libxml_ctx_error_level(E_NOTICE, ctx, ZSTR_VAL(LIBXML(error_buffer).s), line);
break;
default:
php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(LIBXML(error_buffer).s));
@@ -694,6 +668,19 @@ static void php_libxml_internal_error_handler(int error_type, void *ctx, const c
}
}
+static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap)
+{
+ int line = 0;
+ int column = 0;
+ xmlParserCtxtPtr parser = (xmlParserCtxtPtr) ctx;
+ /* Context is not valid for PHP_LIBXML_ERROR, don't dereference it in that case */
+ if (error_type != PHP_LIBXML_ERROR && parser != NULL && parser->input != NULL) {
+ line = parser->input->line;
+ column = parser->input->col;
+ }
+ php_libxml_internal_error_handler_ex(error_type, ctx, msg, ap, line, column);
+}
+
static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
const char *ID, xmlParserCtxtPtr context)
{
@@ -823,6 +810,25 @@ static xmlParserInputPtr _php_libxml_pre_ext_ent_loader(const char *URL,
}
}
+PHP_LIBXML_API void php_libxml_pretend_ctx_error_ex(const char *file, int line, int column, const char *msg,...)
+{
+ va_list args;
+ va_start(args, msg);
+ php_libxml_internal_error_handler_ex(PHP_LIBXML_CTX_ERROR, NULL, &msg, args, line, column);
+ va_end(args);
+
+ /* Propagate back into libxml */
+ if (LIBXML(error_list)) {
+ xmlErrorPtr last = zend_llist_get_last(LIBXML(error_list));
+ if (last) {
+ if (!last->file) {
+ last->file = strdup(file);
+ }
+ xmlCopyError(last, &xmlLastError);
+ }
+ }
+}
+
PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
{
va_list args;
@@ -841,7 +847,7 @@ PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...)
static void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error)
{
- _php_list_set_error_structure(error, NULL);
+ _php_list_set_error_structure(error, NULL, 0, 0);
return;
}
@@ -1333,6 +1339,7 @@ PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object,
object->document->refcount = ret_refcount;
object->document->doc_props = NULL;
object->document->cache_tag.modification_nr = 1; /* iterators start at 0, such that they will start in an uninitialised state */
+ object->document->is_modern_api_class = false;
}
return ret_refcount;
diff --git a/ext/libxml/mime_sniff.c b/ext/libxml/mime_sniff.c
new file mode 100644
index 000000000000..5692f70ee251
--- /dev/null
+++ b/ext/libxml/mime_sniff.c
@@ -0,0 +1,323 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Niels Dossche |
+ +----------------------------------------------------------------------+
+*/
+
+/* This file implements the MIME sniff algorithm from https://mimesniff.spec.whatwg.org/#parsing-a-mime-type (Date: 2023-09-27)
+ * It is a strict implementation of the algorithm, i.e. it does not accept malformed headers.
+ * In particular, it exposes php_dom_sniff_charset() to parse the charset from the Content-Type header.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#ifdef HAVE_LIBXML
+
+#include "php_libxml.h"
+
+static bool is_not_slash(char c)
+{
+ return c != '/';
+}
+
+static bool is_not_semicolon(char c)
+{
+ return c != ';';
+}
+
+static bool is_not_semicolon_or_equals(char c)
+{
+ return c != ';' && c != '=';
+}
+
+static bool is_not_quote_or_backslash(char c)
+{
+ return c != '"' && c != '\\';
+}
+
+/* https://fetch.spec.whatwg.org/#http-tab-or-space */
+static bool is_http_tab_or_space(char c)
+{
+ return c == 0x09 || c == 0x20;
+}
+
+/* https://fetch.spec.whatwg.org/#http-whitespace */
+static bool is_http_whitespace(char c)
+{
+ return c == 0x0A || c == 0x0D || is_http_tab_or_space(c);
+}
+
+/* https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point */
+static bool is_http_quoted_string_token(unsigned char c) /* Note: unsigned is important to let the >= 0x20 check work properly! */
+{
+ return c == 0x09 || (c >= 0x20 && c != 0x7F);
+}
+
+/* https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
+ * Implemented by returning the length of the sequence */
+static zend_always_inline size_t collect_a_sequence_of_code_points(const char *position, const char *end, bool (*condition)(char))
+{
+ const char *start = position;
+ while (position < end && condition(*position)) {
+ position++;
+ }
+ return position - start;
+}
+
+/* https://fetch.spec.whatwg.org/#collect-an-http-quoted-string with extract-value always true */
+static zend_string *collect_an_http_quoted_string_with_extract_value(const char *position, const char *end, const char **position_out)
+{
+ /* 1. Saving positionStart is not necessary, as in the extract-value == true variant we don't use it */
+
+ /* 2. Let value be the empty string */
+ zend_string *value = zend_string_alloc(end - position /* can't be longer than this */, false);
+ ZSTR_LEN(value) = 0;
+
+ /* 3. Assert */
+ ZEND_ASSERT(*position == '"');
+
+ /* 4. Advance */
+ position++;
+
+ /* 5. While true */
+ while (true) {
+ /* 5.1. Append the result of collect a sequence of code points that are not '"' or '\\' */
+ size_t length = collect_a_sequence_of_code_points(position, end, is_not_quote_or_backslash);
+ memcpy(ZSTR_VAL(value) + ZSTR_LEN(value), position, length);
+ ZSTR_LEN(value) += length;
+ position += length;
+
+ /* 5.2. Past end check */
+ if (position >= end) {
+ break;
+ }
+
+ /* 5.3. quoteOrBackslash is the code point at position */
+ char quote_or_backslash = *position;
+
+ /* 5.4. Advance */
+ position++;
+
+ /* 5.5. quote_or_backslash is '\\', deal with escaping */
+ if (quote_or_backslash == '\\') {
+ /* 5.5.1. Past end check */
+ if (position >= end) {
+ ZSTR_VAL(value)[ZSTR_LEN(value)] = '\\';
+ ZSTR_LEN(value)++;
+ break;
+ }
+
+ /* 5.5.2. Append code point at position */
+ ZSTR_VAL(value)[ZSTR_LEN(value)] = *position;
+ ZSTR_LEN(value)++;
+
+ /* 5.5.3. Advance */
+ position++;
+ } else {
+ /* 5.6. Otherwise: assert and break */
+ ZEND_ASSERT(quote_or_backslash == '"');
+ break;
+ }
+ }
+
+ ZSTR_VAL(value)[ZSTR_LEN(value)] = '\0';
+
+ *position_out = position;
+
+ /* 6. extract-value is always true, return value */
+ /* Step 7 is not needed because we always return here already */
+ return value;
+}
+
+/* https://infra.spec.whatwg.org/#ascii-alphanumeric */
+static bool is_ascii_alpha_numeric(char c)
+{
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+/* https://mimesniff.spec.whatwg.org/#http-token-code-point */
+static bool is_http_token(char c)
+{
+ return c == 0x21
+ || (c >= 0x23 && c <= 0x27)
+ || c == 0x2A || c == 0x2B || c == 0x2D || c == 0x2E
+ || c == 0x5E || c == 0x5F
+ || c == 0x60
+ || c == 0x7C || c == 0x7E
+ || is_ascii_alpha_numeric(c);
+}
+
+static bool is_empty_string_or_does_not_solely_contain_http_token_code_points(const char *start, size_t len)
+{
+ if (len == 0) {
+ return true;
+ }
+ while (len > 0) {
+ if (!is_http_token(*start)) {
+ return true;
+ }
+ len--;
+ start++;
+ }
+ return false;
+}
+
+static bool solely_contains_http_quoted_string_tokens(const char *start, size_t len)
+{
+ while (len > 0) {
+ if (!is_http_quoted_string_token(*start)) {
+ return false;
+ }
+ len--;
+ start++;
+ }
+ return true;
+}
+
+/* https://mimesniff.spec.whatwg.org/#parsing-a-mime-type
+ * Note: We only care about the charset detection */
+PHP_LIBXML_API zend_string *php_libxml_sniff_charset_from_string(const char *start, const char *end)
+{
+ /* 1. Remove leading & trailing HTTP whitespace */
+ while (start < end && is_http_whitespace(*start)) {
+ start++;
+ }
+ while (start < end && is_http_whitespace(*end)) {
+ end--;
+ }
+
+ /* 2. Position variable: no-op because we move the start pointer instead */
+
+ /* 3. Collect sequence of code points that are not '/' (for type) */
+ size_t type_length = collect_a_sequence_of_code_points(start, end, is_not_slash);
+
+ /* 4. Empty string or not solely http tokens */
+ if (is_empty_string_or_does_not_solely_contain_http_token_code_points(start, type_length)) {
+ return NULL;
+ }
+ start += type_length;
+
+ /* 5. Failure if past end of input (note: end is one past the last char; in practice this is only possible if no '/' was found) */
+ if (start >= end) {
+ return NULL;
+ }
+
+ /* 6. Skip '/' */
+ start++;
+
+ /* 7. Collect sequence of code points that are not ';' (for subtype) */
+ size_t subtype_length = collect_a_sequence_of_code_points(start, end, is_not_semicolon);
+
+ /* 8. Remove trailing HTTP whitespace from subtype, but we don't care about subtype, so no-op */
+
+ /* 9. Empty string or not solely http tokens */
+ if (is_empty_string_or_does_not_solely_contain_http_token_code_points(start, subtype_length)) {
+ return NULL;
+ }
+ start += subtype_length;
+
+ /* 10. Initialise stuff, no-op as well as we don't care about anything other than charset */
+
+ /* 11. Loop with check: position not past end */
+ while (start < end) {
+ /* 11.1. Advance position */
+ start++;
+
+ /* 11.2. Collect sequence that *is* HTTP whitespace */
+ size_t whitespace_length = collect_a_sequence_of_code_points(start, end, is_http_whitespace);
+ start += whitespace_length;
+
+ /* 11.3. Collect a sequence of code points that are not ';' or '=' (for parameterName) */
+ size_t parameter_name_length = collect_a_sequence_of_code_points(start, end, is_not_semicolon_or_equals);
+ const char *parameter_name = start;
+ start += parameter_name_length;
+
+ /* 11.4. Convert parameter_name to ASCII lowercase, no-op because we are only interested in charset which we'll match down below */
+
+ /* 11.5. Position past input check */
+ if (start < end) {
+ if (*start == ';') {
+ continue;
+ }
+ start++;
+ } else {
+ /* 11.6. */
+ break;
+ }
+
+ /* 11.7. Let parameterValue be null */
+ zend_string *parameter_value = NULL;
+
+ /* 11.8. Quoted string check */
+ if (*start == '"') {
+ /* 11.8.1. Set parameterValue to the result of collecting an HTTP quoted string */
+ parameter_value = collect_an_http_quoted_string_with_extract_value(start, end, &start);
+
+ /* 11.8.2. Collect a sequence of code points that are not ';' */
+ start += collect_a_sequence_of_code_points(start, end, is_not_semicolon);
+ } else {
+ /* 11.9. Otherwise */
+ /* 11.9.1. Set parameterValue to the result of collecting a sequence of code points that are not ';' */
+ size_t parameter_value_length = collect_a_sequence_of_code_points(start, end, is_not_semicolon);
+ parameter_value = zend_string_init(start, parameter_value_length, false);
+ start += parameter_name_length;
+
+ /* 11.9.2. Remove trailing HTTP whitespace from parameterValue */
+ while (ZSTR_LEN(parameter_value) > 0 && is_http_whitespace(ZSTR_VAL(parameter_value)[ZSTR_LEN(parameter_value) - 1])) {
+ ZSTR_LEN(parameter_value)--;
+ }
+ ZSTR_VAL(parameter_value)[ZSTR_LEN(parameter_value)] = '\0';
+
+ /* 11.9.3. Continue if parameterValue is empty */
+ if (ZSTR_LEN(parameter_value) == 0) {
+ zend_string_release_ex(parameter_value, false);
+ continue;
+ }
+ }
+
+ /* 11.10. We diverge from the spec here: we're only interested in charset.
+ * Furthermore, as only the first match matters, we can stop immediately with the loop once we set the charset. */
+ if (parameter_name_length == strlen("charset")
+ && strncasecmp(parameter_name, "charset", strlen("charset")) == 0 /* Because of lowercasing in step 11.4 */
+ && solely_contains_http_quoted_string_tokens(ZSTR_VAL(parameter_value), ZSTR_LEN(parameter_value))) {
+ return parameter_value;
+ }
+
+ zend_string_release_ex(parameter_value, false);
+ }
+
+ /* 12. Return mimetype, a no-op / spec divergence */
+ return NULL;
+}
+
+PHP_LIBXML_API zend_string *php_libxml_sniff_charset_from_stream(const php_stream *s)
+{
+ if (Z_TYPE(s->wrapperdata) == IS_ARRAY) {
+ zval *header;
+
+ ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) {
+ const char buf[] = "Content-Type:";
+ if (Z_TYPE_P(header) == IS_STRING &&
+ !zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) {
+ return php_libxml_sniff_charset_from_string(Z_STRVAL_P(header) + sizeof(buf) - 1, Z_STRVAL_P(header) + Z_STRLEN_P(header));
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ return NULL;
+}
+
+#endif /* HAVE_LIBXML */
diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h
index 7ce7def92ae5..2ffbc5b24930 100644
--- a/ext/libxml/php_libxml.h
+++ b/ext/libxml/php_libxml.h
@@ -63,9 +63,10 @@ typedef struct {
typedef struct _php_libxml_ref_obj {
void *ptr;
- int refcount;
libxml_doc_props *doc_props;
php_libxml_cache_tag cache_tag;
+ int refcount;
+ bool is_modern_api_class;
} php_libxml_ref_obj;
typedef struct _php_libxml_node_ptr {
@@ -131,12 +132,17 @@ PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node);
PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object);
PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...);
PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...);
+PHP_LIBXML_API void php_libxml_pretend_ctx_error_ex(const char *file, int line, int column, const char *msg,...);
PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...);
PHP_LIBXML_API int php_libxml_xmlCheckUTF8(const unsigned char *s);
PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext);
PHP_LIBXML_API void php_libxml_issue_error(int level, const char *msg);
PHP_LIBXML_API bool php_libxml_disable_entity_loader(bool disable);
PHP_LIBXML_API void php_libxml_set_old_ns(xmlDocPtr doc, xmlNsPtr ns);
+PHP_LIBXML_API php_stream_context *php_libxml_get_stream_context(void);
+
+PHP_LIBXML_API zend_string *php_libxml_sniff_charset_from_string(const char *start, const char *end);
+PHP_LIBXML_API zend_string *php_libxml_sniff_charset_from_stream(const php_stream *s);
/* Init/shutdown functions*/
PHP_LIBXML_API void php_libxml_initialize(void);
diff --git a/ext/xsl/php_xsl.stub.php b/ext/xsl/php_xsl.stub.php
index 55ad36150bf6..9394562425d7 100644
--- a/ext/xsl/php_xsl.stub.php
+++ b/ext/xsl/php_xsl.stub.php
@@ -76,25 +76,25 @@ class XSLTProcessor
public bool $cloneDocument = false;
/**
- * @param DOMDocument|SimpleXMLElement $stylesheet
+ * @param DOM\Document|SimpleXMLElement $stylesheet
* @tentative-return-type
*/
public function importStylesheet(object $stylesheet): bool {}
/**
- * @param DOMDocument|SimpleXMLElement $document
+ * @param DOM\Document|SimpleXMLElement $document
* @tentative-return-type
*/
public function transformToDoc(object $document, ?string $returnClass = null): object|false {}
/**
- * @param DOMDocument|SimpleXMLElement $document
+ * @param DOM\Document|SimpleXMLElement $document
* @tentative-return-type
*/
public function transformToUri(object $document, string $uri): int {}
/**
- * @param DOMDocument|SimpleXMLElement $document
+ * @param DOM\Document|SimpleXMLElement $document
* @tentative-return-type
*/
public function transformToXml(object $document): string|null|false {}
diff --git a/ext/xsl/php_xsl_arginfo.h b/ext/xsl/php_xsl_arginfo.h
index d1c23d4bfe6f..92a7ab61e781 100644
--- a/ext/xsl/php_xsl_arginfo.h
+++ b/ext/xsl/php_xsl_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5518a63a4adec49c81e650d620ce2dbce41d8d65 */
+ * Stub hash: 87ea452722956b6cfe46458e7fcd97f0bcfb767b */
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_XSLTProcessor_importStylesheet, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, stylesheet, IS_OBJECT, 0)