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, "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, "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-- +", 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" + + + + +--- Modifying the text content: tag --- + + + + +--- Modifying the text content: trick --- + + + + 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 + + + + + + +

+

foo

+
+ + + + + + +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 + + + + + + +
+

foo

+
+ + + + + + +--- XML serialization --- + + + + Test + + + + + + +
+

foo

+
+ + + + + + 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! + + + + + +
+ foo +
+ +
+ + +HTML); +echo $dom->saveHTML(); + +?> +--EXPECT-- + + This is my épic title! + + + + + +
+ foo +
+ +
+ + 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</plaintext> +<noscript>&amp;"&lt;&gt;&nbsp; foobar</noscript> +</body> 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-- +<?php + +$dom = DOM\HTMLDocument::createEmpty(); +$body = $dom->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-- +<body><style>&amp;"&lt;&gt;&nbsp; foobar</style> +<script>&amp;"&lt;&gt;&nbsp; foobar</script> +<xmp>&amp;"&lt;&gt;&nbsp; foobar</xmp> +<iframe>&amp;"&lt;&gt;&nbsp; foobar</iframe> +<noembed>&amp;"&lt;&gt;&nbsp; foobar</noembed> +<noframes>&amp;"&lt;&gt;&nbsp; foobar</noframes> +<plaintext>&amp;"&lt;&gt;&nbsp; foobar</plaintext> +<noscript>&amp;"&lt;&gt;&nbsp; foobar</noscript> +</body> 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-- +<?php + +$dom = DOM\HTMLDocument::createEmpty(); + +$tags = [ + "area", + "base", + "br", + "col", + "embed", + "hr", + "img", + "input", + "link", + "meta", + "source", + "track", + "wbr", + "basefont", + "bgsound", + "frame", + "keygen", + "param", +]; + +foreach ($tags as $tag) { + $element = $dom->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) +<area> +<x:area><inner></inner></x:area> +<base> +<x:base><inner></inner></x:base> +<br> +<x:br><inner></inner></x:br> +<col> +<x:col><inner></inner></x:col> +<embed> +<x:embed><inner></inner></x:embed> +<hr> +<x:hr><inner></inner></x:hr> +<img> +<x:img><inner></inner></x:img> +<input> +<x:input><inner></inner></x:input> +<link> +<x:link><inner></inner></x:link> +<meta> +<x:meta><inner></inner></x:meta> +<source> +<x:source><inner></inner></x:source> +<track> +<x:track><inner></inner></x:track> +<wbr> +<x:wbr><inner></inner></x:wbr> +<basefont> +<x:basefont><inner></inner></x:basefont> +<bgsound> +<x:bgsound><inner></inner></x:bgsound> +<frame> +<x:frame><inner></inner></x:frame> +<keygen> +<x:keygen><inner></inner></x:keygen> +<param> +<x:param><inner></inner></x:param> 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 @@ +<?xml version="1.0"?> +<container xmlns="some:ns" xmlns:bar="another:ns"> + <x> + <subcontainer> + <test xmlns="x:y" /> + <child2 /> + </subcontainer> + <subcontainer2> + <foo xmlns="some:ns" /> + </subcontainer2> + </x> +</container> \ 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-- +<?php + +try { + DOM\XMLDocument::createFromFile(__DIR__ . '/dummy.xml', overrideEncoding: 'nonexistent'); +} catch (ValueError $e) { + echo $e->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-- +<?php + +try { + DOM\XMLDocument::createFromString(file_get_contents(__DIR__ . '/dummy.xml'), overrideEncoding: 'nonexistent'); +} catch (ValueError $e) { + echo $e->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-- +<?php + +$dom = DOM\XMLDocument::createEmpty(); +var_dump($dom); + +?> +--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-- +<?php + +DOM\XMLDocument::createEmpty(encoding: "foo"); + +?> +--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-- +<?php + +$dom = DOM\XMLDocument::createEmpty("1.1"); +var_dump($dom); + +?> +--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-- +<?php + +$dom = DOM\XMLDocument::createEmpty("1.0", "ASCII"); +$dom->append("foo"); +echo $dom->saveXML(); + +?> +--EXPECT-- +<?xml version="1.0" encoding="ASCII"?> +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-- +<?php + +DOM\XMLDocument::createFromString(""); + +?> +--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-- +<?php + +DOM\XMLDocument::createFromFile("\\0"); + +?> +--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-- +<?php + +try { + DOM\XMLDocument::createFromFile("%00"); +} catch (ValueError $e) { + echo $e->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-- +<?php + +$dom = DOM\XMLDocument::createFromFile(__DIR__."/../../book.xml"); +echo $dom->saveXML(); + +?> +--EXPECT-- +<?xml version="1.0" encoding="UTF-8"?> +<books> + <book> + <title>The Grapes of Wrath</title> + <author>John Steinbeck</author> + </book> + <book> + <title>The Pearl</title> + <author>John Steinbeck</author> + </book> +</books> 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-- +<?php + +DOM\XMLDocument::createFromString(""); + +?> +--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-- +<?php + +$dom = DOM\XMLDocument::createFromString('<?xml version="1.0"?><container/>'); +var_dump($dom->saveXMLFile("php://stdout")); + +?> +--EXPECT-- +<?xml version="1.0" encoding="UTF-8"?> +<container/> +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-- +<?php + +$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 +]; + +try { + DOM\XMLDocument::createFromString('<?xml version="1.0"?><container/>', -1); +} catch (ValueError $e) { + echo $e->getMessage(); +} + +foreach ($flags as $flag) { + var_dump(DOM\XMLDocument::createFromString('<?xml version="1.0"?><container/>', $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-- +<?php + +$dom = DOM\XMLDocument::createFromString('<?xml version="1.0" encoding="Windows-1251"?><container/>'); +var_dump($dom->encoding); +echo $dom->saveXML(); + +?> +--EXPECT-- +string(12) "Windows-1251" +<?xml version="1.0" encoding="Windows-1251"?> +<container/> 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-- +<?php + +$dom = DOM\XMLDocument::createFromString('<?xml version="1.0"?><container/>'); + +$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-- +<?php + +$dom = DOM\XMLDocument::createEmpty("1.0", "ASCII"); +$root = $dom->appendChild($dom->createElement("root")); +$child1 = $root->appendChild($dom->createElement("child1")); +$child2 = $root->appendChild($dom->createElement("child2")); +echo $dom->saveXML($child1); + +?> +--EXPECT-- +<child1/> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<container>é</container> 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 <nielsdos@php.net> | + +----------------------------------------------------------------------+ +*/ + +#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, &register_node_ns) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &doc, dom_abstract_base_document_class_entry, &register_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 <libxml/uri.h> #include <libxml/xmlerror.h> #include <libxml/xmlsave.h> +#include <libxml/xmlerror.h> #ifdef LIBXML_SCHEMAS_ENABLED #include <libxml/relaxng.h> #include <libxml/xmlschemas.h> @@ -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 <nielsdos@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* 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)