diff --git a/NEWS b/NEWS index 0e4a16cf6e8a6..0694dc8543309 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,7 @@ PHP NEWS . Handle OOM more consistently. (nielsdos) . Implemented "Improve callbacks in ext/dom and ext/xsl" RFC. (nielsdos) . Added DOMXPath::quote() static method. (divinity76) + . Implemented opt-in ext/dom spec compliance RFC. (nielsdos) - Fileinfo: . Update to libmagic 5.45. (nielsdos) diff --git a/UPGRADING b/UPGRADING index 3b080ac78444d..00ba1c2885bcb 100644 --- a/UPGRADING +++ b/UPGRADING @@ -179,14 +179,6 @@ 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. . It is now possible to pass any callable to registerPhpFunctions(). RFC: https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl @@ -436,6 +428,22 @@ PHP 8.4 UPGRADE NOTES 7. New Classes and Interfaces ======================================== +- DOM: + . Implemented DOM HTML5 parsing and serialization. + RFC: https://wiki.php.net/rfc/domdocument_html5_parser. + This RFC adds the new DOM namespace along with new classes 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. + . Implemented opt-in ext/dom spec compliance RFC. + This adds new classes in the DOM namespace that correspond to modern + equivalents to the old DOM classes in the global namespaces. + The new classes follow the DOM living spec. + RFC: https://wiki.php.net/rfc/opt_in_dom_spec_compliance + ======================================== 8. Removed Extensions and SAPIs ======================================== diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 217d86809ad46..c0fa021120cc3 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -140,6 +140,7 @@ PHP 8.4 INTERNALS UPGRADE NOTES - The macros DOM_NO_ARGS() and DOM_NOT_IMPLEMENTED() have been removed. - New public APIs are available to handle callbacks from XPath, see xpath_callbacks.h. + - Added public APIs to manipulate namespace data, see namespace_compat.h. b. ext/random - The macro RAND_RANGE_BADSCALING() has been removed. The implementation @@ -173,6 +174,8 @@ PHP 8.4 INTERNALS UPGRADE NOTES - Added php_libxml_pretend_ctx_error_ex() to emit errors as if they had come from libxml. - Removed the "properties" HashTable field from php_libxml_node_object. + - Added a way to attached private data to a php_libxml_ref_obj. + - Added a way to fix a class type onto php_libxml_ref_obj. e. ext/date - Added the php_format_date_ex() API to format instances of php_date_obj. diff --git a/ext/dom/attr.c b/ext/dom/attr.c index cb1129c3940f0..3f5783defd148 100644 --- a/ext/dom/attr.c +++ b/ext/dom/attr.c @@ -72,20 +72,24 @@ PHP_METHOD(DOMAttr, __construct) /* {{{ name string readonly=yes URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-1112119403 +Modern spec URL: https://dom.spec.whatwg.org/#dom-attr-name Since: */ zend_result dom_attr_name_read(dom_object *obj, zval *retval) { - xmlAttrPtr attrp; - - attrp = (xmlAttrPtr) dom_object_get_node(obj); + xmlAttrPtr attrp = (xmlAttrPtr) dom_object_get_node(obj); if (attrp == NULL) { php_dom_throw_error(INVALID_STATE_ERR, 1); return FAILURE; } - ZVAL_STRING(retval, (char *) attrp->name); + if (php_dom_follow_spec_intern(obj)) { + zend_string *str = dom_node_get_node_name_attribute_or_element((xmlNodePtr) attrp, false); + ZVAL_NEW_STR(retval, str); + } else { + ZVAL_STRING(retval, (char *) attrp->name); + } return SUCCESS; } @@ -99,7 +103,7 @@ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-86 */ zend_result dom_attr_specified_read(dom_object *obj, zval *retval) { - /* TODO */ + /* From spec: "useless; always returns true" */ ZVAL_TRUE(retval); return SUCCESS; } @@ -147,7 +151,13 @@ zend_result dom_attr_value_write(dom_object *obj, zval *newval) zend_string *str = Z_STR_P(newval); dom_remove_all_children((xmlNodePtr) attrp); - xmlNodeSetContentLen((xmlNodePtr) attrp, (xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str)); + + if (php_dom_follow_spec_intern(obj)) { + xmlNodePtr node = xmlNewDocTextLen(attrp->doc, BAD_CAST ZSTR_VAL(str), ZSTR_LEN(str)); + xmlAddChild((xmlNodePtr) attrp, node); + } else { + xmlNodeSetContentLen((xmlNodePtr) attrp, BAD_CAST ZSTR_VAL(str), ZSTR_LEN(str)); + } return SUCCESS; } diff --git a/ext/dom/characterdata.c b/ext/dom/characterdata.c index 36298f526830f..f54e042aa7d25 100644 --- a/ext/dom/characterdata.c +++ b/ext/dom/characterdata.c @@ -30,6 +30,24 @@ * Since: */ +/* For some peculiar reason, many of these methods operate on unsigned numbers. + * Unfortunately, "old DOM" doesn't, so we have to conditionally convert... + * And the reason we're using "unsigned int" instead of "unsigned zend_long" is because libxml2 internally works with ints. */ +static bool dom_convert_number_unsigned(dom_object *intern, zend_long input, unsigned int *output) +{ + if (input < 0) { + if (php_dom_follow_spec_intern(intern)) { + *output = (unsigned int) input; + } else { + php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document)); + return false; + } + } else { + *output = input; + } + return true; +} + /* {{{ data string readonly=no URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-72AB8359 @@ -96,20 +114,22 @@ zend_result dom_characterdata_length_read(dom_object *obj, zval *retval) /* }}} */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6531BCCF +Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-substringdata Since: */ PHP_METHOD(DOMCharacterData, substringData) { - zval *id; - xmlChar *cur; - xmlChar *substring; - xmlNodePtr node; - zend_long offset, count; - int length; - dom_object *intern; + zval *id; + xmlChar *cur; + xmlChar *substring; + xmlNodePtr node; + zend_long offset_input, count_input; + unsigned int count, offset; + int length; + dom_object *intern; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset, &count) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset_input, &count_input) == FAILURE) { RETURN_THROWS(); } @@ -117,12 +137,21 @@ PHP_METHOD(DOMCharacterData, substringData) cur = node->content; if (cur == NULL) { - RETURN_FALSE; + /* TODO: is this even possible? */ + cur = BAD_CAST ""; } length = xmlUTF8Strlen(cur); + if (ZEND_LONG_INT_OVFL(offset_input) || ZEND_LONG_INT_OVFL(count_input)) { + php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document)); + RETURN_FALSE; + } - if (offset < 0 || count < 0 || ZEND_LONG_INT_OVFL(offset) || ZEND_LONG_INT_OVFL(count) || offset > length) { + if (!dom_convert_number_unsigned(intern, offset_input, &offset) || !dom_convert_number_unsigned(intern, count_input, &count)) { + RETURN_FALSE; + } + + if (offset > length) { php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document)); RETURN_FALSE; } @@ -143,9 +172,10 @@ PHP_METHOD(DOMCharacterData, substringData) /* }}} end dom_characterdata_substring_data */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-32791A2F +Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-appenddata Since: */ -PHP_METHOD(DOMCharacterData, appendData) +static void dom_character_data_append_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true) { zval *id; xmlNode *nodep; @@ -160,26 +190,40 @@ PHP_METHOD(DOMCharacterData, appendData) DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); xmlTextConcat(nodep, (xmlChar *) arg, arg_len); - RETURN_TRUE; + if (return_true) { + RETURN_TRUE; + } +} + +PHP_METHOD(DOMCharacterData, appendData) +{ + dom_character_data_append_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +PHP_METHOD(DOM_CharacterData, appendData) +{ + dom_character_data_append_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } /* }}} end dom_characterdata_append_data */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-3EDB695F +Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-insertdata Since: */ -PHP_METHOD(DOMCharacterData, insertData) +static void dom_character_data_insert_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true) { zval *id; xmlChar *cur, *first, *second; xmlNodePtr node; char *arg; - zend_long offset; + zend_long offset_input; + unsigned int offset; int length; size_t arg_len; dom_object *intern; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &offset, &arg, &arg_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &offset_input, &arg, &arg_len) == FAILURE) { RETURN_THROWS(); } @@ -187,12 +231,22 @@ PHP_METHOD(DOMCharacterData, insertData) cur = node->content; if (cur == NULL) { - RETURN_FALSE; + /* TODO: is this even possible? */ + cur = BAD_CAST ""; } length = xmlUTF8Strlen(cur); - if (offset < 0 || ZEND_LONG_INT_OVFL(offset) || offset > length) { + if (ZEND_LONG_INT_OVFL(offset_input)) { + php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document)); + RETURN_FALSE; + } + + if (!dom_convert_number_unsigned(intern, offset_input, &offset)) { + RETURN_FALSE; + } + + if (offset > length) { php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document)); RETURN_FALSE; } @@ -207,24 +261,38 @@ PHP_METHOD(DOMCharacterData, insertData) xmlFree(first); xmlFree(second); - RETURN_TRUE; + if (return_true) { + RETURN_TRUE; + } +} + +PHP_METHOD(DOMCharacterData, insertData) +{ + dom_character_data_insert_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +PHP_METHOD(DOM_CharacterData, insertData) +{ + dom_character_data_insert_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } /* }}} end dom_characterdata_insert_data */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-7C603781 +Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-deletedata Since: */ -PHP_METHOD(DOMCharacterData, deleteData) +static void dom_character_data_delete_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true) { zval *id; xmlChar *cur, *substring, *second; xmlNodePtr node; - zend_long offset, count; + zend_long offset, count_input; + unsigned int count; int length; dom_object *intern; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset, &count) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset, &count_input) == FAILURE) { RETURN_THROWS(); } @@ -232,16 +300,21 @@ PHP_METHOD(DOMCharacterData, deleteData) cur = node->content; if (cur == NULL) { - RETURN_FALSE; + /* TODO: is this even possible? */ + cur = BAD_CAST ""; } length = xmlUTF8Strlen(cur); - if (offset < 0 || count < 0 || ZEND_LONG_INT_OVFL(offset) || ZEND_LONG_INT_OVFL(count) || offset > length) { + if (offset < 0 || ZEND_LONG_INT_OVFL(offset) || ZEND_LONG_INT_OVFL(count_input) || offset > length) { php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document)); RETURN_FALSE; } + if (!dom_convert_number_unsigned(intern, count_input, &count)) { + RETURN_FALSE; + } + if (offset > 0) { substring = xmlUTF8Strsub(cur, 0, (int)offset); } else { @@ -260,26 +333,40 @@ PHP_METHOD(DOMCharacterData, deleteData) xmlFree(second); xmlFree(substring); - RETURN_TRUE; + if (return_true) { + RETURN_TRUE; + } +} + +PHP_METHOD(DOMCharacterData, deleteData) +{ + dom_character_data_delete_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +PHP_METHOD(DOM_CharacterData, deleteData) +{ + dom_character_data_delete_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } /* }}} end dom_characterdata_delete_data */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-E5CBA7FB +Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-replacedata Since: */ -PHP_METHOD(DOMCharacterData, replaceData) +static void dom_character_data_replace_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true) { zval *id; xmlChar *cur, *substring, *second = NULL; xmlNodePtr node; char *arg; - zend_long offset, count; + zend_long offset, count_input; + unsigned int count; int length; size_t arg_len; dom_object *intern; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "lls", &offset, &count, &arg, &arg_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lls", &offset, &count_input, &arg, &arg_len) == FAILURE) { RETURN_THROWS(); } @@ -287,16 +374,21 @@ PHP_METHOD(DOMCharacterData, replaceData) cur = node->content; if (cur == NULL) { - RETURN_FALSE; + /* TODO: is this even possible? */ + cur = BAD_CAST ""; } length = xmlUTF8Strlen(cur); - if (offset < 0 || count < 0 || ZEND_LONG_INT_OVFL(offset) || ZEND_LONG_INT_OVFL(count) || offset > length) { + if (offset < 0 || ZEND_LONG_INT_OVFL(offset) || ZEND_LONG_INT_OVFL(count_input) || offset > length) { php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document)); RETURN_FALSE; } + if (!dom_convert_number_unsigned(intern, count_input, &count)) { + RETURN_FALSE; + } + if (offset > 0) { substring = xmlUTF8Strsub(cur, 0, (int)offset); } else { @@ -321,7 +413,19 @@ PHP_METHOD(DOMCharacterData, replaceData) } xmlFree(substring); - RETURN_TRUE; + if (return_true) { + RETURN_TRUE; + } +} + +PHP_METHOD(DOMCharacterData, replaceData) +{ + dom_character_data_replace_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +PHP_METHOD(DOM_CharacterData, replaceData) +{ + dom_character_data_replace_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } /* }}} end dom_characterdata_replace_data */ diff --git a/ext/dom/config.m4 b/ext/dom/config.m4 index c0736861460da..b3a92287b3f1c 100644 --- a/ext/dom/config.m4 +++ b/ext/dom/config.m4 @@ -26,7 +26,7 @@ if test "$PHP_DOM" != "no"; then $LEXBOR_DIR/ns/ns.c \ $LEXBOR_DIR/tag/tag.c" PHP_NEW_EXTENSION(dom, [php_dom.c attr.c document.c \ - xml_document.c html_document.c html5_serializer.c html5_parser.c namespace_compat.c \ + xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c \ domexception.c parentnode.c \ processinginstruction.c cdatasection.c \ documentfragment.c domimplementation.c \ @@ -49,7 +49,7 @@ if test "$PHP_DOM" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/ns) PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/tag) PHP_SUBST(DOM_SHARED_LIBADD) - PHP_INSTALL_HEADERS([ext/dom], [xml_common.h xpath_callbacks.h]) + PHP_INSTALL_HEADERS([ext/dom], [xml_common.h xpath_callbacks.h namespace_compat.h]) PHP_ADD_EXTENSION_DEP(dom, libxml) ]) fi diff --git a/ext/dom/config.w32 b/ext/dom/config.w32 index bb0101b960b2f..a70d226d0fa31 100644 --- a/ext/dom/config.w32 +++ b/ext/dom/config.w32 @@ -8,7 +8,7 @@ if (PHP_DOM == "yes") { CHECK_HEADER_ADD_INCLUDE("libxml/parser.h", "CFLAGS_DOM", PHP_PHP_BUILD + "\\include\\libxml2") ) { EXTENSION("dom", "php_dom.c attr.c document.c \ - xml_document.c html_document.c html5_serializer.c html5_parser.c namespace_compat.c \ + xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c \ domexception.c parentnode.c processinginstruction.c \ cdatasection.c documentfragment.c domimplementation.c element.c \ node.c characterdata.c documenttype.c \ @@ -41,7 +41,7 @@ if (PHP_DOM == "yes") { WARNING("dom support can't be enabled, libxml is not found") } } - PHP_INSTALL_HEADERS("ext/dom", "xml_common.h xpath_callbacks.h"); + PHP_INSTALL_HEADERS("ext/dom", "xml_common.h xpath_callbacks.h namespace_compat.h"); } else { WARNING("dom support can't be enabled, libxml is not enabled") PHP_DOM = "no" diff --git a/ext/dom/document.c b/ext/dom/document.c index 6268f9b687594..8dba522eb9d03 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -22,19 +22,15 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "namespace_compat.h" +#include "xml_serializer.h" +#include "internal_helpers.h" #include -#include #ifdef LIBXML_SCHEMAS_ENABLED #include #include #endif -typedef struct _idsIterator idsIterator; -struct _idsIterator { - xmlChar *elementId; - xmlNode *element; -}; - /* * class DOMDocument extends DOMNode * @@ -76,10 +72,9 @@ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core- */ zend_result dom_document_implementation_read(dom_object *obj, zval *retval) { - php_dom_create_implementation(retval); + php_dom_create_implementation(retval, false); return SUCCESS; } - /* }}} */ /* {{{ documentElement DOMElement @@ -253,6 +248,14 @@ zend_result dom_document_version_write(dom_object *obj, zval *newval) return FAILURE; } + if (php_dom_follow_spec_intern(obj)) { + if (!zend_string_equals_literal(str, "1.0") && !zend_string_equals_literal(str, "1.1")) { + zend_value_error("Invalid XML version"); + zend_string_release_ex(str, 0); + return FAILURE; + } + } + if (docp->version != NULL) { xmlFree((xmlChar *) docp->version ); } @@ -434,7 +437,11 @@ zend_result dom_document_document_uri_read(dom_object *obj, zval *retval) if (url != NULL) { ZVAL_STRING(retval, url); } else { - ZVAL_NULL(retval); + if (php_dom_follow_spec_intern(obj)) { + ZVAL_STRING(retval, "about:blank"); + } else { + ZVAL_NULL(retval); + } } return SUCCESS; @@ -481,31 +488,69 @@ 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 +Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createelement Since: */ +PHP_METHOD(DOMDocument, createElement) +{ + xmlDocPtr docp; + dom_object *intern; + int ret; + size_t value_len; + char *value = NULL; + zend_string *name; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(name) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(value, value_len) + ZEND_PARSE_PARAMETERS_END(); + + DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern); + + if (xmlValidateName(BAD_CAST ZSTR_VAL(name), 0) != 0) { + php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document)); + RETURN_FALSE; + } + + xmlNodePtr node = xmlNewDocNode(docp, NULL, BAD_CAST ZSTR_VAL(name), BAD_CAST value); + + if (!node) { + php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); + RETURN_THROWS(); + } + + DOM_RET_OBJ(node, &ret, intern); +} + PHP_METHOD(DOM_Document, createElement) { - zval *id; xmlNode *node; xmlDocPtr docp; dom_object *intern; int ret; - size_t name_len, value_len; - char *name, *value = NULL; + zend_string *name; - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &name, &name_len, &value, &value_len) == FAILURE) { + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(name) + ZEND_PARSE_PARAMETERS_END(); + + DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern); + + if (xmlValidateName(BAD_CAST ZSTR_VAL(name), 0) != 0) { + php_dom_throw_error(INVALID_CHARACTER_ERR, /* strict */ true); RETURN_THROWS(); } - DOM_GET_OBJ(docp, id, xmlDocPtr, intern); - - if (xmlValidateName((xmlChar *) name, 0) != 0) { - php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document)); - RETURN_FALSE; + if (docp->type == XML_HTML_DOCUMENT_NODE) { + php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern); + char *lower = zend_str_tolower_dup_ex(ZSTR_VAL(name), ZSTR_LEN(name)); + node = xmlNewDocRawNode(docp, php_dom_libxml_ns_mapper_ensure_html_ns(ns_mapper), BAD_CAST (lower ? lower : ZSTR_VAL(name)), NULL); + efree(lower); + } else { + node = xmlNewDocNode(docp, NULL, BAD_CAST ZSTR_VAL(name), NULL); } - node = xmlNewDocNode(docp, NULL, (xmlChar *) name, (xmlChar *) value); if (!node) { php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); RETURN_THROWS(); @@ -518,7 +563,7 @@ PHP_METHOD(DOM_Document, createElement) /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-35CB04B5 Since: */ -PHP_METHOD(DOM_Document, createDocumentFragment) +PHP_METHOD(DOMDocument, createDocumentFragment) { zval *id; xmlNode *node; @@ -546,7 +591,7 @@ PHP_METHOD(DOM_Document, createDocumentFragment) /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1975348127 Since: */ -PHP_METHOD(DOM_Document, createTextNode) +PHP_METHOD(DOMDocument, createTextNode) { zval *id; xmlNode *node; @@ -576,7 +621,7 @@ PHP_METHOD(DOM_Document, createTextNode) /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1334481328 Since: */ -PHP_METHOD(DOM_Document, createComment) +PHP_METHOD(DOMDocument, createComment) { zval *id; xmlNode *node; @@ -604,9 +649,10 @@ PHP_METHOD(DOM_Document, createComment) /* }}} end dom_document_create_comment */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D26C0AF8 +Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createcdatasection Since: */ -PHP_METHOD(DOM_Document, createCDATASection) +PHP_METHOD(DOMDocument, createCDATASection) { zval *id; xmlNode *node; @@ -623,6 +669,18 @@ PHP_METHOD(DOM_Document, createCDATASection) DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + if (php_dom_follow_spec_intern(intern)) { + if (docp->type == XML_HTML_DOCUMENT_NODE) { + php_dom_throw_error_with_message(NOT_SUPPORTED_ERR, "This operation is not supported for HTML documents", /* strict */ true); + RETURN_THROWS(); + } + + if (zend_memnstr(value, "]]>", strlen("]]>"), value + value_len) != NULL) { + php_dom_throw_error_with_message(INVALID_CHARACTER_ERR, "Invalid character sequence \"]]>\" in CDATA section", /* strict */ true); + RETURN_THROWS(); + } + } + node = xmlNewCDataBlock(docp, (xmlChar *) value, value_len); if (!node) { php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); @@ -634,11 +692,11 @@ PHP_METHOD(DOM_Document, createCDATASection) /* }}} end dom_document_create_cdatasection */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-135944439 +Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createprocessinginstruction Since: */ -PHP_METHOD(DOM_Document, createProcessingInstruction) +static void dom_document_create_processing_instruction(INTERNAL_FUNCTION_PARAMETERS, bool modern) { - zval *id; xmlNode *node; xmlDocPtr docp; int ret; @@ -646,56 +704,77 @@ PHP_METHOD(DOM_Document, createProcessingInstruction) dom_object *intern; char *name, *value = NULL; - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &name, &name_len, &value, &value_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), modern ? "ss" : "s|s", &name, &name_len, &value, &value_len) != SUCCESS) { RETURN_THROWS(); } - DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern); if (xmlValidateName((xmlChar *) name, 0) != 0) { php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document)); RETURN_FALSE; } - node = xmlNewPI((xmlChar *) name, (xmlChar *) value); + if (modern) { + if (value != NULL && zend_memnstr(value, "?>", strlen("?>"), value + value_len) != NULL) { + php_dom_throw_error_with_message(INVALID_CHARACTER_ERR, "Invalid character sequence \"?>\" in processing instruction", /* strict */ true); + RETURN_THROWS(); + } + } + + node = xmlNewDocPI(docp, (xmlChar *) name, (xmlChar *) value); if (!node) { php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); RETURN_THROWS(); } - node->doc = docp; - DOM_RET_OBJ(node, &ret, intern); } + +PHP_METHOD(DOMDocument, createProcessingInstruction) +{ + dom_document_create_processing_instruction(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Document, createProcessingInstruction) +{ + dom_document_create_processing_instruction(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} /* }}} end dom_document_create_processing_instruction */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1084891198 +Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createattribute Since: */ -PHP_METHOD(DOM_Document, createAttribute) +PHP_METHOD(DOMDocument, createAttribute) { zval *id; xmlAttrPtr node; xmlDocPtr docp; int ret; - size_t name_len; dom_object *intern; - char *name; + zend_string *name; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_PATH_STR(name) + ZEND_PARSE_PARAMETERS_END(); DOM_GET_OBJ(docp, id, xmlDocPtr, intern); - if (xmlValidateName((xmlChar *) name, 0) != 0) { + if (xmlValidateName(BAD_CAST ZSTR_VAL(name), 0) != 0) { php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document)); RETURN_FALSE; } - node = xmlNewDocProp(docp, (xmlChar *) name, NULL); + if (docp->type == XML_HTML_DOCUMENT_NODE && php_dom_follow_spec_intern(intern)) { + char *lower = zend_str_tolower_dup_ex(ZSTR_VAL(name), ZSTR_LEN(name)); + node = xmlNewDocProp(docp, BAD_CAST (lower ? lower : ZSTR_VAL(name)), NULL); + efree(lower); + } else { + node = xmlNewDocProp(docp, BAD_CAST ZSTR_VAL(name), NULL); + } + if (!node) { php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); RETURN_THROWS(); @@ -741,31 +820,11 @@ PHP_METHOD(DOMDocument, createEntityReference) } /* }}} end dom_document_create_entity_reference */ -/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C9094 -Since: -*/ -PHP_METHOD(DOM_Document, getElementsByTagName) -{ - size_t name_len; - dom_object *intern, *namednode; - char *name; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { - RETURN_THROWS(); - } - - DOM_GET_THIS_INTERN(intern); - - php_dom_create_iterator(return_value, DOM_NODELIST); - namednode = Z_DOMOBJ_P(return_value); - dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, NULL, 0); -} -/* }}} end dom_document_get_elements_by_tag_name */ - /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Core-Document-importNode +Modern spec URL: https://dom.spec.whatwg.org/#dom-document-importnode Since: DOM Level 2 */ -PHP_METHOD(DOM_Document, importNode) +PHP_METHOD(DOMDocument, importNode) { zval *node; xmlDocPtr docp; @@ -782,8 +841,7 @@ PHP_METHOD(DOM_Document, importNode) DOM_GET_OBJ(nodep, node, xmlNodePtr, nodeobj); - if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE - || nodep->type == XML_DOCUMENT_TYPE_NODE) { + if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE) { php_error_docref(NULL, E_WARNING, "Cannot import: Node Type Not Supported"); RETURN_FALSE; } @@ -791,12 +849,12 @@ PHP_METHOD(DOM_Document, importNode) if (nodep->doc == docp) { retnodep = nodep; } else { - retnodep = dom_clone_node(nodep, docp, intern, recursive); + retnodep = dom_clone_node(NULL, nodep, docp, recursive); if (!retnodep) { RETURN_FALSE; } - if ((retnodep->type == XML_ATTRIBUTE_NODE) && (nodep->ns != NULL)) { + if (retnodep->type == XML_ATTRIBUTE_NODE && nodep->ns != NULL && retnodep->ns == NULL) { xmlNsPtr nsptr = NULL; xmlNodePtr root = xmlDocGetRootElement(docp); @@ -809,50 +867,93 @@ PHP_METHOD(DOM_Document, importNode) } } - php_libxml_invalidate_node_list_cache(intern->document); + DOM_RET_OBJ(retnodep, &ret, intern); +} - DOM_RET_OBJ((xmlNodePtr) retnodep, &ret, intern); +static void dom_modern_document_import_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce) +{ + zval *node; + xmlDocPtr docp; + xmlNodePtr nodep, retnodep; + dom_object *intern, *nodeobj; + int ret; + bool recursive = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &node, node_ce, &recursive) != SUCCESS) { + RETURN_THROWS(); + } + + DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern); + + DOM_GET_OBJ(nodep, node, xmlNodePtr, nodeobj); + + if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE) { + php_dom_throw_error(NOT_SUPPORTED_ERR, /* strict */ true); + RETURN_THROWS(); + } + + if (nodep->doc == docp) { + retnodep = nodep; + } else { + retnodep = dom_clone_node(php_dom_get_ns_mapper(intern), nodep, docp, recursive); + if (!retnodep) { + php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); + RETURN_THROWS(); + } + } + + DOM_RET_OBJ(retnodep, &ret, intern); +} + +PHP_METHOD(DOM_Document, importNode) +{ + dom_modern_document_import_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry); } /* }}} end dom_document_import_node */ +PHP_METHOD(DOM_Document, importLegacyNode) +{ + dom_modern_document_import_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry); +} + /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS +Modern spec URL: https://dom.spec.whatwg.org/#internal-createelementns-steps Since: DOM Level 2 */ -PHP_METHOD(DOM_Document, createElementNS) +PHP_METHOD(DOMDocument, createElementNS) { - zval *id; xmlDocPtr docp; xmlNodePtr nodep = NULL; int ret; - size_t uri_len = 0, name_len = 0, value_len = 0; - char *uri, *name, *value = NULL; - char *localname = NULL, *prefix = NULL; + size_t value_len = 0; + char *value = NULL; int errorcode; dom_object *intern; + zend_string *name = NULL, *uri; - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s|s", &uri, &uri_len, &name, &name_len, &value, &value_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!S|s", &uri, &name, &value, &value_len) == FAILURE) { RETURN_THROWS(); } - DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern); - errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len); + char *localname = NULL, *prefix = NULL; + errorcode = dom_check_qname(ZSTR_VAL(name), &localname, &prefix, uri ? ZSTR_LEN(uri) : 0, ZSTR_LEN(name)); if (errorcode == 0) { - if (xmlValidateName((xmlChar *) localname, 0) == 0) { - nodep = xmlNewDocNode(docp, NULL, (xmlChar *) localname, (xmlChar *) value); + if (xmlValidateName(BAD_CAST localname, 0) == 0) { + nodep = xmlNewDocNode(docp, NULL, BAD_CAST localname, BAD_CAST value); if (UNEXPECTED(nodep == NULL)) { php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); RETURN_THROWS(); } if (uri != NULL) { - xmlNsPtr nsptr = xmlSearchNsByHref(nodep->doc, nodep, (xmlChar *) uri); + xmlNsPtr nsptr = xmlSearchNsByHref(nodep->doc, nodep, BAD_CAST ZSTR_VAL(uri)); if (nsptr == NULL) { - nsptr = dom_get_ns(nodep, uri, &errorcode, prefix); + nsptr = dom_get_ns(nodep, ZSTR_VAL(uri), &errorcode, prefix); } - xmlSetNs(nodep, nsptr); + nodep->ns = nsptr; } } else { errorcode = INVALID_CHARACTER_ERR; @@ -860,9 +961,7 @@ PHP_METHOD(DOM_Document, createElementNS) } xmlFree(localname); - if (prefix != NULL) { - xmlFree(prefix); - } + xmlFree(prefix); if (errorcode != 0) { xmlFreeNode(nodep); @@ -872,12 +971,46 @@ PHP_METHOD(DOM_Document, createElementNS) DOM_RET_OBJ(nodep, &ret, intern); } + +PHP_METHOD(DOM_Document, createElementNS) +{ + xmlDocPtr docp; + int ret; + dom_object *intern; + zend_string *name = NULL, *uri; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!S", &uri, &name) == FAILURE) { + RETURN_THROWS(); + } + + DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern); + + xmlChar *localname = NULL, *prefix = NULL; + int errorcode = dom_validate_and_extract(uri, name, &localname, &prefix); + + if (errorcode == 0) { + php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern); + xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri); + xmlNodePtr nodep = xmlNewDocNode(docp, ns, localname, NULL); + if (UNEXPECTED(nodep == NULL)) { + php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); + } else { + DOM_RET_OBJ(nodep, &ret, intern); + } + } else { + php_dom_throw_error(errorcode, dom_get_strict_error(intern->document)); + } + + xmlFree(localname); + xmlFree(prefix); +} /* }}} end dom_document_create_element_ns */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNS +Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createattributens Since: DOM Level 2 */ -PHP_METHOD(DOM_Document, createAttributeNS) +PHP_METHOD(DOMDocument, createAttributeNS) { zval *id; xmlDocPtr docp; @@ -885,7 +1018,7 @@ PHP_METHOD(DOM_Document, createAttributeNS) xmlNsPtr nsptr; int ret; zend_string *name, *uri; - char *localname = NULL, *prefix = NULL; + xmlChar *localname = NULL, *prefix = NULL; dom_object *intern; int errorcode; @@ -896,60 +1029,45 @@ PHP_METHOD(DOM_Document, createAttributeNS) DOM_GET_OBJ(docp, id, xmlDocPtr, intern); - if (UNEXPECTED(uri == NULL)) { - uri = zend_empty_string; - } - size_t uri_len = ZSTR_LEN(uri); - root = xmlDocGetRootElement(docp); - if (root != NULL) { - errorcode = dom_check_qname(ZSTR_VAL(name), &localname, &prefix, uri_len, ZSTR_LEN(name)); + if (root != NULL || php_dom_follow_spec_intern(intern)) { + errorcode = dom_validate_and_extract(uri, name, &localname, &prefix); if (UNEXPECTED(errorcode != 0)) { - goto error; - } - if (UNEXPECTED(xmlValidateName((xmlChar *) localname, 0) != 0)) { - errorcode = INVALID_CHARACTER_ERR; - goto error; - } - /* If prefix is "xml" and namespace is not the XML namespace, then throw a "NamespaceError" DOMException. */ - if (UNEXPECTED(!zend_string_equals_literal(uri, "http://www.w3.org/XML/1998/namespace") && xmlStrEqual(BAD_CAST prefix, BAD_CAST "xml"))) { - errorcode = NAMESPACE_ERR; - goto error; - } - /* If either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace, then throw a "NamespaceError" DOMException. */ - if (UNEXPECTED((zend_string_equals_literal(name, "xmlns") || xmlStrEqual(BAD_CAST prefix, BAD_CAST "xmlns")) && !zend_string_equals_literal(uri, "http://www.w3.org/2000/xmlns/"))) { - errorcode = NAMESPACE_ERR; - goto error; - } - /* If namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns", then throw a "NamespaceError" DOMException. */ - if (UNEXPECTED(zend_string_equals_literal(uri, "http://www.w3.org/2000/xmlns/") && !zend_string_equals_literal(name, "xmlns") && !xmlStrEqual(BAD_CAST prefix, BAD_CAST "xmlns"))) { - errorcode = NAMESPACE_ERR; + if (!php_dom_follow_spec_intern(intern)) { + /* legacy behaviour */ + errorcode = NAMESPACE_ERR; + } goto error; } - nodep = (xmlNodePtr) xmlNewDocProp(docp, (xmlChar *) localname, NULL); + nodep = (xmlNodePtr) xmlNewDocProp(docp, localname, NULL); if (UNEXPECTED(nodep == NULL)) { php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); RETURN_THROWS(); } - if (uri_len > 0) { - nsptr = xmlSearchNsByHref(docp, root, BAD_CAST ZSTR_VAL(uri)); - - if (zend_string_equals_literal(name, "xmlns") || xmlStrEqual(BAD_CAST prefix, BAD_CAST "xml")) { - if (nsptr == NULL) { - nsptr = xmlNewNs(NULL, BAD_CAST ZSTR_VAL(uri), BAD_CAST prefix); - php_libxml_set_old_ns(docp, nsptr); - } + if (uri != NULL && ZSTR_LEN(uri) > 0) { + if (php_dom_follow_spec_intern(intern)) { + php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern); + nsptr = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri); } else { - if (nsptr == NULL || nsptr->prefix == NULL) { - nsptr = dom_get_ns_unchecked(root, ZSTR_VAL(uri), prefix ? prefix : "default"); - if (UNEXPECTED(nsptr == NULL)) { - errorcode = NAMESPACE_ERR; + nsptr = xmlSearchNsByHref(docp, root, BAD_CAST ZSTR_VAL(uri)); + + if (zend_string_equals_literal(name, "xmlns") || xmlStrEqual(BAD_CAST prefix, BAD_CAST "xml")) { + if (nsptr == NULL) { + nsptr = xmlNewNs(NULL, BAD_CAST ZSTR_VAL(uri), BAD_CAST prefix); + php_libxml_set_old_ns(docp, nsptr); + } + } else { + if (nsptr == NULL || nsptr->prefix == NULL) { + nsptr = dom_get_ns_unchecked(root, ZSTR_VAL(uri), prefix ? (char *) prefix : "default"); + if (UNEXPECTED(nsptr == NULL)) { + errorcode = NAMESPACE_ERR; + } } } } - xmlSetNs(nodep, nsptr); + nodep->ns = nsptr; } } else { php_error_docref(NULL, E_WARNING, "Document Missing Root Element"); @@ -958,9 +1076,7 @@ PHP_METHOD(DOM_Document, createAttributeNS) error: xmlFree(localname); - if (prefix != NULL) { - xmlFree(prefix); - } + xmlFree(prefix); if (errorcode != 0) { xmlFreeProp((xmlAttrPtr) nodep); @@ -972,31 +1088,10 @@ PHP_METHOD(DOM_Document, createAttributeNS) } /* }}} end dom_document_create_attribute_ns */ -/* {{{ 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(DOM_Document, getElementsByTagNameNS) -{ - size_t uri_len, name_len; - dom_object *intern, *namednode; - char *uri, *name; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) { - RETURN_THROWS(); - } - - DOM_GET_THIS_INTERN(intern); - - php_dom_create_iterator(return_value, DOM_NODELIST); - namednode = Z_DOMOBJ_P(return_value); - dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, uri ? uri : "", uri_len); -} -/* }}} end dom_document_get_elements_by_tag_name_ns */ - /* {{{ 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(DOM_Document, getElementById) +PHP_METHOD(DOMDocument, getElementById) { zval *id; xmlDocPtr docp; @@ -1061,17 +1156,65 @@ static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *n } } +/* Workaround for bug that was fixed in https://github.com/GNOME/libxml2/commit/4bc3ebf3eaba352fbbce2ef70ad00a3c7752478a */ +#if LIBXML_VERSION < 21000 +static xmlChar *libxml_copy_dicted_string(xmlDictPtr src_dict, xmlDictPtr dst_dict, xmlChar *str) +{ + if (str == NULL) { + return NULL; + } + if (xmlDictOwns(src_dict, str) == 1) { + if (dst_dict == NULL) { + return xmlStrdup(str); + } + return BAD_CAST xmlDictLookup(dst_dict, str, -1); + } + return str; +} + +static void libxml_fixup_name_and_content(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node) +{ + if (src_doc != NULL && dst_doc != src_doc && src_doc->dict != NULL) { + node->name = libxml_copy_dicted_string(src_doc->dict, dst_doc->dict, BAD_CAST node->name); + node->content = libxml_copy_dicted_string(src_doc->dict, NULL, node->content); + } +} + +static void libxml_fixup_name_and_content_element(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node) +{ + libxml_fixup_name_and_content(src_doc, dst_doc, node); + for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) { + libxml_fixup_name_and_content(src_doc, dst_doc, (xmlNodePtr) attr); + } + + for (xmlNodePtr child = node->children; child != NULL; child = child->next) { + libxml_fixup_name_and_content_element(src_doc, dst_doc, child); + } +} +#endif + bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document) { - php_libxml_invalidate_node_list_cache_from_doc(nodep->doc); + xmlDocPtr original_document = nodep->doc; + php_libxml_invalidate_node_list_cache_from_doc(original_document); if (nodep->doc != new_document) { php_libxml_invalidate_node_list_cache(dom_object_new_document->document); /* Note for ATTRIBUTE_NODE: specified is always true in ext/dom, * and since this unlink it; the owner element will be unset (i.e. parentNode). */ - int ret = xmlDOMWrapAdoptNode(NULL, nodep->doc, nodep, new_document, NULL, /* options, unused */ 0); - if (UNEXPECTED(ret != 0)) { - return false; + if (php_dom_follow_spec_intern(dom_object_new_document)) { + xmlUnlinkNode(nodep); + xmlSetTreeDoc(nodep, new_document); + php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(dom_object_new_document); + php_dom_libxml_reconcile_modern(ns_mapper, nodep); +#if LIBXML_VERSION < 21000 + libxml_fixup_name_and_content_element(original_document, new_document, nodep); +#endif + } else { + int ret = xmlDOMWrapAdoptNode(NULL, original_document, nodep, new_document, NULL, /* options, unused */ 0); + if (UNEXPECTED(ret != 0)) { + return false; + } } php_dom_transfer_document_ref(nodep, dom_object_new_document->document); @@ -1085,10 +1228,10 @@ 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(DOM_Document, adoptNode) +static void dom_document_adopt_node(INTERNAL_FUNCTION_PARAMETERS, bool modern) { zval *node_zval; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, dom_node_class_entry) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, dom_get_node_ce(modern)) == FAILURE) { RETURN_THROWS(); } @@ -1112,17 +1255,31 @@ PHP_METHOD(DOM_Document, adoptNode) DOM_GET_OBJ(new_document, new_document_zval, xmlDocPtr, dom_object_new_document); if (!php_dom_adopt_node(nodep, dom_object_new_document, new_document)) { + if (modern) { + php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); + RETURN_THROWS(); + } RETURN_FALSE; } RETURN_OBJ_COPY(&dom_object_nodep->std); } + +PHP_METHOD(DOMDocument, adoptNode) +{ + dom_document_adopt_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Document, adoptNode) +{ + dom_document_adopt_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} /* }}} end dom_document_adopt_node */ /* {{{ 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(DOM_Document, normalizeDocument) +PHP_METHOD(DOMDocument, normalizeDocument) { zval *id; xmlDocPtr docp; @@ -1135,9 +1292,7 @@ PHP_METHOD(DOM_Document, normalizeDocument) DOM_GET_OBJ(docp, id, xmlDocPtr, intern); - php_libxml_invalidate_node_list_cache(intern->document); - - dom_normalize((xmlNodePtr) docp); + php_dom_normalize_legacy((xmlNodePtr) docp); } /* }}} end dom_document_normalize_document */ @@ -1274,7 +1429,6 @@ xmlDocPtr dom_document_parser(zval *id, dom_load_mode mode, const char *source, if (file_dest) { ctxt = xmlCreateFileParserCtxt(file_dest); } - } else { ctxt = xmlCreateMemoryParserCtxt(source, source_len); } @@ -1350,7 +1504,7 @@ xmlDocPtr dom_document_parser(zval *id, dom_load_mode mode, const char *source, ret->URL = xmlStrdup((xmlChar *) ctxt->directory); } } else { - ret = NULL; + ret = DOM_DOCUMENT_MALFORMED; xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL; } @@ -1369,13 +1523,13 @@ static void php_dom_finish_loading_document(zval *this, zval *return_value, xmlD dom_object *intern = Z_DOMOBJ_P(this); size_t old_modification_nr = 0; if (intern != NULL) { - bool is_modern_api_class = false; + php_libxml_class_type class_type = PHP_LIBXML_CLASS_LEGACY; 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; + class_type = doc_ptr->class_type; 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; @@ -1390,7 +1544,7 @@ static void php_dom_finish_loading_document(zval *this, zval *return_value, xmlD RETURN_FALSE; } intern->document->doc_props = doc_prop; - intern->document->is_modern_api_class = is_modern_api_class; + intern->document->class_type = class_type; } php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern); @@ -1427,6 +1581,9 @@ static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) } xmlDocPtr newdoc = dom_document_parser(ZEND_THIS, mode, source, source_len, options, NULL); + if (newdoc == DOM_DOCUMENT_MALFORMED) { + newdoc = NULL; + } php_dom_finish_loading_document(ZEND_THIS, return_value, newdoc); } @@ -1493,9 +1650,9 @@ PHP_METHOD(DOMDocument, save) /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML Since: DOM Level 3 */ -PHP_METHOD(DOMDocument, saveXML) +static void dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce) { - zval *id, *nodep = NULL; + zval *nodep = NULL; xmlDoc *docp; xmlNode *node; xmlBufferPtr buf; @@ -1504,16 +1661,16 @@ PHP_METHOD(DOMDocument, saveXML) int size, format, old_xml_save_no_empty_tags; zend_long options = 0; - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!l", &nodep, dom_node_class_entry, &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!l", &nodep, node_ce, &options) != SUCCESS) { RETURN_THROWS(); } - DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern); libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document); format = doc_props->formatoutput; + int status = -1; if (nodep != NULL) { /* Dump contents of Node */ DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj); @@ -1530,7 +1687,22 @@ PHP_METHOD(DOMDocument, saveXML) /* Save libxml2 global, override its vaule, and restore after saving. */ old_xml_save_no_empty_tags = xmlSaveNoEmptyTags; xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0; - xmlNodeDump(buf, docp, node, 0, format); + if (php_dom_follow_spec_intern(intern)) { + xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(buf, (const char *) docp->encoding, XML_SAVE_AS_XML); + if (EXPECTED(ctxt != NULL)) { + xmlCharEncodingHandlerPtr handler = xmlFindCharEncodingHandler((const char *) docp->encoding); + xmlOutputBufferPtr out = xmlOutputBufferCreateBuffer(buf, handler); + if (EXPECTED(out != NULL)) { + status = dom_xml_serialize(ctxt, out, node, format); + status |= xmlOutputBufferFlush(out); + status |= xmlOutputBufferClose(out); + } + (void) xmlSaveClose(ctxt); + xmlCharEncCloseFunc(handler); + } + } else { + status = xmlNodeDump(buf, docp, node, 0, format); + } xmlSaveNoEmptyTags = old_xml_save_no_empty_tags; } else { buf = xmlBufferCreate(); @@ -1557,15 +1729,26 @@ PHP_METHOD(DOMDocument, saveXML) php_error_docref(NULL, E_WARNING, "Could not create save context"); RETURN_FALSE; } - if (UNEXPECTED(xmlSaveDoc(ctxt, docp) < 0)) { - (void) xmlSaveClose(ctxt); - xmlBufferFree(buf); - php_error_docref(NULL, E_WARNING, "Could not save document"); - RETURN_FALSE; + if (php_dom_follow_spec_intern(intern)) { + xmlCharEncodingHandlerPtr handler = xmlFindCharEncodingHandler((const char *) docp->encoding); + xmlOutputBufferPtr out = xmlOutputBufferCreateBuffer(buf, handler); + if (EXPECTED(out != NULL)) { + status = dom_xml_serialize(ctxt, out, (xmlNodePtr) docp, format); + status |= xmlOutputBufferFlush(out); + status |= xmlOutputBufferClose(out); + } else { + xmlCharEncCloseFunc(handler); + } + } else { + status = xmlSaveDoc(ctxt, docp); } - (void) xmlSaveFlush(ctxt); (void) xmlSaveClose(ctxt); } + if (UNEXPECTED(status < 0)) { + xmlBufferFree(buf); + php_error_docref(NULL, E_WARNING, "Could not save document"); + RETURN_FALSE; + } mem = xmlBufferContent(buf); if (!mem) { xmlBufferFree(buf); @@ -1575,6 +1758,16 @@ PHP_METHOD(DOMDocument, saveXML) RETVAL_STRINGL((const char *) mem, size); xmlBufferFree(buf); } + +PHP_METHOD(DOMDocument, saveXML) +{ + dom_document_save_xml(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry); +} + +PHP_METHOD(DOM_XMLDocument, saveXML) +{ + dom_document_save_xml(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry); +} /* }}} end dom_document_savexml */ static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) /* {{{ */ @@ -1800,14 +1993,14 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type /* }}} */ /* {{{ */ -PHP_METHOD(DOM_Document, schemaValidate) +PHP_METHOD(DOMDocument, schemaValidate) { _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); } /* }}} end dom_document_schema_validate_file */ /* {{{ */ -PHP_METHOD(DOM_Document, schemaValidateSource) +PHP_METHOD(DOMDocument, schemaValidateSource) { _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); } @@ -1897,14 +2090,14 @@ static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int typ /* }}} */ /* {{{ */ -PHP_METHOD(DOM_Document, relaxNGValidate) +PHP_METHOD(DOMDocument, relaxNGValidate) { _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); } /* }}} end dom_document_relaxNG_validate_file */ /* {{{ */ -PHP_METHOD(DOM_Document, relaxNGValidateSource) +PHP_METHOD(DOMDocument, relaxNGValidateSource) { _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); } @@ -2110,9 +2303,9 @@ PHP_METHOD(DOMDocument, saveHTML) #endif /* defined(LIBXML_HTML_ENABLED) */ /* {{{ Register extended class used to create base node type */ -PHP_METHOD(DOM_Document, registerNodeClass) +static void dom_document_register_node_class(INTERNAL_FUNCTION_PARAMETERS, bool modern) { - zend_class_entry *basece = dom_node_class_entry, *ce = NULL; + zend_class_entry *basece = dom_get_node_ce(modern), *ce = NULL; dom_object *intern; if (zend_parse_parameters(ZEND_NUM_ARGS(), "CC!", &basece, &ce) == FAILURE) { @@ -2131,17 +2324,31 @@ PHP_METHOD(DOM_Document, registerNodeClass) } DOM_GET_THIS_INTERN(intern); dom_set_doc_classmap(intern->document, basece, ce); - RETURN_TRUE; + if (!modern) { + RETVAL_TRUE; + } + return; } zend_argument_error(NULL, 2, "must be a class name derived from %s or null, %s given", ZSTR_VAL(basece->name), ZSTR_VAL(ce->name)); + RETURN_THROWS(); +} + +PHP_METHOD(DOMDocument, registerNodeClass) +{ + dom_document_register_node_class(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Document, registerNodeClass) +{ + dom_document_register_node_class(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } /* }}} */ /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren Since: */ -PHP_METHOD(DOM_Document, replaceChildren) +PHP_METHOD(DOMDocument, replaceChildren) { uint32_t argc = 0; zval *args; diff --git a/ext/dom/documenttype.c b/ext/dom/documenttype.c index 5e23d57be8ab3..36e168524383c 100644 --- a/ext/dom/documenttype.c +++ b/ext/dom/documenttype.c @@ -60,7 +60,7 @@ zend_result dom_documenttype_entities_read(dom_object *obj, zval *retval) return FAILURE; } - php_dom_create_iterator(retval, DOM_NAMEDNODEMAP); + php_dom_create_iterator(retval, DOM_DTD_NAMEDNODEMAP, php_dom_follow_spec_intern(obj)); entityht = (xmlHashTable *) doctypep->entities; @@ -88,7 +88,7 @@ zend_result dom_documenttype_notations_read(dom_object *obj, zval *retval) return FAILURE; } - php_dom_create_iterator(retval, DOM_NAMEDNODEMAP); + php_dom_create_iterator(retval, DOM_DTD_NAMEDNODEMAP, php_dom_follow_spec_intern(obj)); notationht = (xmlHashTable *) doctypep->notations; @@ -184,7 +184,7 @@ zend_result dom_documenttype_internal_subset_read(dom_object *obj, zval *retval) } if (ret_buf.s) { - ZVAL_STR(retval, smart_str_extract(&ret_buf)); + ZVAL_NEW_STR(retval, smart_str_extract(&ret_buf)); return SUCCESS; } } diff --git a/ext/dom/dom_ce.h b/ext/dom/dom_ce.h index 5b661b2abbb94..847688ca9092f 100644 --- a/ext/dom/dom_ce.h +++ b/ext/dom/dom_ce.h @@ -19,28 +19,51 @@ #define DOM_CE_H extern PHP_DOM_EXPORT zend_class_entry *dom_node_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_node_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_domexception_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_parentnode_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_parentnode_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_childnode_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_childnode_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_domimplementation_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_documentfragment_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_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_modern_nodelist_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_namednodemap_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_namednodemap_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_dtd_namednodemap_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_html_collection_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_characterdata_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_characterdata_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_attr_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_attr_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_element_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_element_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_text_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_text_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_comment_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_comment_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_cdatasection_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_cdatasection_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_documenttype_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_documenttype_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_notation_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_notation_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_entity_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_entity_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_entityreference_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_entityreference_class_entry; extern PHP_DOM_EXPORT zend_class_entry *dom_processinginstruction_class_entry; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_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; +extern PHP_DOM_EXPORT zend_class_entry *dom_modern_xpath_class_entry; #endif extern PHP_DOM_EXPORT zend_class_entry *dom_namespace_node_class_entry; diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c index bb151c198953c..29a567d78474d 100644 --- a/ext/dom/dom_iterators.c +++ b/ext/dom/dom_iterators.c @@ -230,7 +230,7 @@ static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */ curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node; } curnode = dom_get_elements_by_tag_name_ns_raw( - basenode, curnode, (char *) objmap->ns, (char *) objmap->local, &previndex, iter->index); + basenode, curnode, objmap->ns, objmap->local, objmap->local_lower, &previndex, iter->index); } } } else { @@ -316,7 +316,7 @@ zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, i nodep = nodep->children; } curnode = dom_get_elements_by_tag_name_ns_raw( - basep, nodep, (char *) objmap->ns, (char *) objmap->local, &curindex, 0); + basep, nodep, objmap->ns, objmap->local, objmap->local_lower, &curindex, 0); } } } else { diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h index 349b604dddcc7..03525707b9701 100644 --- a/ext/dom/dom_properties.h +++ b/ext/dom/dom_properties.h @@ -34,6 +34,7 @@ zend_result dom_characterdata_length_read(dom_object *obj, zval *retval); /* document properties */ zend_result dom_document_doctype_read(dom_object *obj, zval *retval); zend_result dom_document_implementation_read(dom_object *obj, zval *retval); +zend_result dom_modern_document_implementation_read(dom_object *obj, zval *retval); zend_result dom_document_document_element_read(dom_object *obj, zval *retval); zend_result dom_document_actual_encoding_read(dom_object *obj, zval *retval); zend_result dom_document_actual_encoding_write(dom_object *obj, zval *newval); @@ -115,6 +116,7 @@ zend_result dom_node_is_connected_read(dom_object *obj, zval *retval); zend_result dom_node_owner_document_read(dom_object *obj, zval *retval); zend_result dom_node_namespace_uri_read(dom_object *obj, zval *retval); zend_result dom_node_prefix_read(dom_object *obj, zval *retval); +zend_result dom_modern_node_prefix_read(dom_object *obj, zval *retval); zend_result dom_node_prefix_write(dom_object *obj, zval *newval); zend_result dom_node_local_name_read(dom_object *obj, zval *retval); zend_result dom_node_base_uri_read(dom_object *obj, zval *retval); diff --git a/ext/dom/domimplementation.c b/ext/dom/domimplementation.c index d771eccad8684..5dad65aef8935 100644 --- a/ext/dom/domimplementation.c +++ b/ext/dom/domimplementation.c @@ -22,6 +22,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "namespace_compat.h" /* * class DOMImplementation @@ -46,6 +47,7 @@ PHP_METHOD(DOMImplementation, hasFeature) /* }}} end dom_domimplementation_has_feature */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocType +Modern URL: https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype Since: DOM Level 2 */ PHP_METHOD(DOMImplementation, createDocumentType) @@ -91,10 +93,6 @@ PHP_METHOD(DOMImplementation, createDocumentType) localname = xmlStrdup((xmlChar *) name); } - /* TODO: Test that localname has no invalid chars - php_dom_throw_error(INVALID_CHARACTER_ERR,); - */ - if (uri) { xmlFreeURI(uri); } @@ -109,6 +107,41 @@ PHP_METHOD(DOMImplementation, createDocumentType) DOM_RET_OBJ((xmlNodePtr) doctype, &ret, NULL); } + +PHP_METHOD(DOM_Implementation, createDocumentType) +{ + size_t name_len, publicid_len = 0, systemid_len = 0; + const char *name, *publicid = NULL, *systemid = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppp", &name, &name_len, &publicid, &publicid_len, &systemid, &systemid_len) != SUCCESS) { + RETURN_THROWS(); + } + + /* 1. Validate qualifiedName. */ + if (xmlValidateQName(BAD_CAST name, 0) != 0) { + php_dom_throw_error(NAMESPACE_ERR, true); + RETURN_THROWS(); + } + + /* 2. Return a new doctype, with qualifiedName as its name, publicId as its public ID, and systemId as its system ID ... */ + xmlDtdPtr doctype = xmlCreateIntSubset( + NULL, + BAD_CAST name, + publicid_len ? BAD_CAST publicid : NULL, + systemid_len ? BAD_CAST systemid : NULL + ); + if (UNEXPECTED(doctype == NULL)) { + php_dom_throw_error(INVALID_STATE_ERR, true); + RETURN_THROWS(); + } + + php_dom_instantiate_object_helper( + return_value, + dom_modern_documenttype_class_entry, + (xmlNodePtr) doctype, + NULL + ); +} /* }}} end dom_domimplementation_create_document_type */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocument @@ -216,8 +249,170 @@ PHP_METHOD(DOMImplementation, createDocument) php_libxml_increment_doc_ref((php_libxml_node_object *)doctobj, docp); } } + +PHP_METHOD(DOM_Implementation, createDocument) +{ + zval *dtd = NULL; + xmlDtdPtr doctype = NULL; + zend_string *uri = NULL, *qualified_name = zend_empty_string; + dom_object *doctobj; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P!P|O!", &uri, &qualified_name, &dtd, dom_modern_documenttype_class_entry) != SUCCESS) { + RETURN_THROWS(); + } + + if (dtd != NULL) { + DOM_GET_OBJ(doctype, dtd, xmlDtdPtr, doctobj); + } + + xmlDocPtr document = NULL; + xmlChar *localname = NULL, *prefix = NULL; + php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create(); + + /* 1. Let document be a new XMLDocument. */ + document = xmlNewDoc(BAD_CAST "1.0"); + if (UNEXPECTED(document == NULL)) { + goto oom; + } + document->encoding = xmlStrdup(BAD_CAST "UTF-8"); + + /* 2. Let element be null. */ + xmlNodePtr element = NULL; + + /* 3. If qualifiedName is not the empty string, then set element to the result of running the internal createElementNS steps. */ + if (ZSTR_LEN(qualified_name) != 0) { + int errorcode = dom_validate_and_extract(uri, qualified_name, &localname, &prefix); + + if (EXPECTED(errorcode == 0)) { + xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri); + element = xmlNewDocNode(document, ns, localname, NULL); + if (UNEXPECTED(element == NULL)) { + goto oom; + } + xmlFree(localname); + xmlFree(prefix); + localname = NULL; + prefix = NULL; + } else { + php_dom_throw_error(errorcode, /* strict */ true); + goto error; + } + } + + /* 8. Return document. + * => This is done here already to gain access to the dom_object */ + dom_object *intern = php_dom_instantiate_object_helper( + return_value, + dom_xml_document_class_entry, + (xmlNodePtr) document, + NULL + ); + intern->document->class_type = PHP_LIBXML_CLASS_MODERN; + intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper); + + /* 4. If doctype is non-null, append doctype to document. */ + if (doctype != NULL) { + php_dom_adopt_node((xmlNodePtr) doctype, intern, document); + xmlAddChild((xmlNodePtr) document, (xmlNodePtr) doctype); + doctype->doc = document; + document->intSubset = (xmlDtdPtr) doctype; + ZEND_ASSERT(doctype->parent == document); + } + + /* 5. If element is non-null, append element to document. */ + if (element != NULL) { + xmlAddChild((xmlNodePtr) document, element); + } + + /* 6. document’s origin is this’s associated document’s origin. + * => We don't store the origin in ext/dom. */ + + /* 7. document’s content type is determined by namespace: + * => We don't store the content type in ext/dom. */ + + return; + +oom: + php_dom_throw_error(INVALID_STATE_ERR, true); +error: + xmlFree(localname); + xmlFree(prefix); + xmlFreeDoc(document); + php_dom_libxml_ns_mapper_destroy(ns_mapper); + RETURN_THROWS(); +} /* }}} end dom_domimplementation_create_document */ +/* {{{ URL: https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument */ +PHP_METHOD(DOM_Implementation, createHTMLDocument) +{ + const char *title = NULL; + size_t title_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p!", &title, &title_len) != SUCCESS) { + RETURN_THROWS(); + } + + /* 1. Let doc be a new document that is an HTML document. */ + xmlDocPtr doc = php_dom_create_html_doc(); + if (UNEXPECTED(doc == NULL)) { + php_dom_throw_error(INVALID_STATE_ERR, true); + RETURN_THROWS(); + } + doc->encoding = xmlStrdup(BAD_CAST "UTF-8"); + + /* 2. Set doc’s content type to "text/html". + * => We don't store the content type in ext/dom. */ + + /* 3. Append a new doctype, with "html" as its name and with its node document set to doc, to doc. */ + xmlDtdPtr dtd = xmlCreateIntSubset(doc, BAD_CAST "html", NULL, NULL); + + php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create(); + xmlNsPtr html_ns = php_dom_libxml_ns_mapper_ensure_html_ns(ns_mapper); + + /* 4. Append the result of creating an element given doc, html, and the HTML namespace, to doc. */ + xmlNodePtr html_element = xmlNewDocRawNode(doc, html_ns, BAD_CAST "html", NULL); + xmlAddChild((xmlNodePtr) doc, html_element); + + /* 5. Append the result of creating an element given doc, head, and the HTML namespace, to the html element created earlier. */ + xmlNodePtr head_element = xmlNewDocRawNode(doc, html_ns, BAD_CAST "head", NULL); + xmlAddChild(html_element, head_element); + + /* 6. If title is given: */ + xmlNodePtr title_element = NULL; + if (title != NULL) { + /* 6.1. Append the result of creating an element given doc, title, and the HTML namespace, to the head element created earlier. */ + /* 6.2. Append the result of creating a text node given doc and title, to the title element created earlier. */ + title_element = xmlNewDocRawNode(doc, html_ns, BAD_CAST "title", BAD_CAST title); + xmlAddChild(head_element, title_element); + } + + /* 7. Append the result of creating an element given doc, body, and the HTML namespace, to the html element created earlier. */ + xmlNodePtr body_element = xmlNewDocRawNode(doc, html_ns, BAD_CAST "body", NULL); + xmlAddChild(html_element, body_element); + + /* 8. doc’s origin is this’s associated document’s origin. + * => We don't store the origin in ext/dom. */ + + if (UNEXPECTED(dtd == NULL || html_element == NULL || head_element == NULL || (title != NULL && title_element == NULL) || body_element == NULL)) { + php_dom_throw_error(INVALID_STATE_ERR, true); + xmlFreeDoc(doc); + php_dom_libxml_ns_mapper_destroy(ns_mapper); + RETURN_THROWS(); + } + + /* 9. Return doc. */ + dom_object *intern = php_dom_instantiate_object_helper( + return_value, + dom_html_document_class_entry, + (xmlNodePtr) doc, + NULL + ); + intern->document->class_type = PHP_LIBXML_CLASS_MODERN; + intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper); +} +/* }}} */ + /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMImplementation3-getFeature Since: DOM Level 3 */ diff --git a/ext/dom/element.c b/ext/dom/element.c index 5061af68e272a..3b81d8141138e 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -22,6 +22,8 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "namespace_compat.h" +#include "internal_helpers.h" /* * class DOMElement extends DOMNode @@ -106,31 +108,22 @@ PHP_METHOD(DOMElement, __construct) /* {{{ tagName string readonly=yes URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-104682815 +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-tagname Since: */ zend_result dom_element_tag_name_read(dom_object *obj, zval *retval) { - xmlNodePtr nodep; - xmlNsPtr ns; - xmlChar *qname; - - nodep = dom_object_get_node(obj); + xmlNodePtr nodep = dom_object_get_node(obj); if (nodep == NULL) { php_dom_throw_error(INVALID_STATE_ERR, 1); return FAILURE; } - ns = nodep->ns; - if (ns != NULL && ns->prefix) { - qname = xmlStrdup(ns->prefix); - qname = xmlStrcat(qname, (xmlChar *)":"); - qname = xmlStrcat(qname, nodep->name); - ZVAL_STRING(retval, (char *)qname); - xmlFree(qname); - } else { - ZVAL_STRING(retval, (char *) nodep->name); - } + bool uppercase = php_dom_follow_spec_intern(obj) && php_dom_ns_is_html_and_document_is_html(nodep); + + zend_string *result = dom_node_get_node_name_attribute_or_element((const xmlNode *) nodep, uppercase); + ZVAL_NEW_STR(retval, result); return SUCCESS; } @@ -226,49 +219,53 @@ zend_result dom_element_schema_type_info_read(dom_object *obj, zval *retval) /* }}} */ /* Note: the object returned is not necessarily a node, but can be an attribute or a namespace declaration. */ -static xmlNodePtr dom_get_dom1_attribute(xmlNodePtr elem, xmlChar *name) /* {{{ */ +static xmlNodePtr dom_get_attribute_or_nsdecl(dom_object *intern, xmlNodePtr elem, const xmlChar *name, size_t name_len) /* {{{ */ { - int len; - const xmlChar *nqname; - - nqname = xmlSplitQName3(name, &len); - if (nqname != NULL) { - xmlNsPtr ns; - if (strncmp((const char *) name, "xmlns:", len + 1) == 0) { - ns = elem->nsDef; - while (ns) { - if (xmlStrEqual(ns->prefix, nqname)) { - break; + if (!php_dom_follow_spec_intern(intern)) { + int len; + const xmlChar *nqname = xmlSplitQName3(name, &len); + + if (nqname != NULL) { + xmlNsPtr ns; + if (strncmp((const char *) name, "xmlns:", len + 1) == 0) { + ns = elem->nsDef; + while (ns) { + if (xmlStrEqual(ns->prefix, nqname)) { + break; + } + ns = ns->next; } - ns = ns->next; + return (xmlNodePtr)ns; } - return (xmlNodePtr)ns; - } - xmlChar *prefix = xmlStrndup(name, len); - ns = xmlSearchNs(elem->doc, elem, prefix); - if (prefix != NULL) { - xmlFree(prefix); - } - if (ns != NULL) { - return (xmlNodePtr)xmlHasNsProp(elem, nqname, ns->href); - } - } else { - if (xmlStrEqual(name, (xmlChar *)"xmlns")) { - xmlNsPtr nsPtr = elem->nsDef; - while (nsPtr) { - if (nsPtr->prefix == NULL) { - return (xmlNodePtr)nsPtr; + xmlChar *prefix = xmlStrndup(name, len); + ns = xmlSearchNs(elem->doc, elem, prefix); + if (prefix != NULL) { + xmlFree(prefix); + } + if (ns != NULL) { + return (xmlNodePtr)xmlHasNsProp(elem, nqname, ns->href); + } + } else { + if (xmlStrEqual(name, (xmlChar *)"xmlns")) { + xmlNsPtr nsPtr = elem->nsDef; + while (nsPtr) { + if (nsPtr->prefix == NULL) { + return (xmlNodePtr)nsPtr; + } + nsPtr = nsPtr->next; } - nsPtr = nsPtr->next; + return NULL; } - return NULL; } + return (xmlNodePtr) xmlHasNsProp(elem, name, NULL); + } else { + return (xmlNodePtr) php_dom_get_attribute_node(elem, name, name_len); } - return (xmlNodePtr)xmlHasNsProp(elem, name, NULL); } /* }}} */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-666EE0F9 +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattribute Since: */ PHP_METHOD(DOMElement, getAttribute) @@ -280,7 +277,7 @@ PHP_METHOD(DOMElement, getAttribute) dom_object *intern; xmlNodePtr attr; size_t name_len; - bool should_free; + bool should_free = false; id = ZEND_THIS; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { @@ -289,7 +286,7 @@ PHP_METHOD(DOMElement, getAttribute) DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - attr = dom_get_dom1_attribute(nodep, (xmlChar *)name); + attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len); if (attr) { switch (attr->type) { case XML_ATTRIBUTE_NODE: @@ -307,6 +304,9 @@ PHP_METHOD(DOMElement, getAttribute) } if (value == NULL) { + if (php_dom_follow_spec_intern(intern)) { + RETURN_NULL(); + } RETURN_EMPTY_STRING(); } else { RETVAL_STRING((char *)value); @@ -324,27 +324,29 @@ PHP_METHOD(DOMElement, getAttributeNames) { zval *id; xmlNode *nodep; - dom_object *unused_intern; + dom_object *intern; zval tmp; if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); } - DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, unused_intern); + DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern); array_init(return_value); HashTable *ht = Z_ARRVAL_P(return_value); zend_hash_real_init_packed(ht); - for (xmlNsPtr nsptr = nodep->nsDef; nsptr; nsptr = nsptr->next) { - const char *prefix = (const char *) nsptr->prefix; - ZVAL_STR(&tmp, dom_node_concatenated_name_helper(strlen(prefix), prefix, strlen("xmlns"), (const char *) "xmlns")); - zend_hash_next_index_insert(ht, &tmp); + if (!php_dom_follow_spec_intern(intern)) { + for (xmlNsPtr nsptr = nodep->nsDef; nsptr; nsptr = nsptr->next) { + const char *prefix = (const char *) nsptr->prefix; + ZVAL_NEW_STR(&tmp, dom_node_concatenated_name_helper(strlen(prefix), prefix, strlen("xmlns"), (const char *) "xmlns")); + zend_hash_next_index_insert(ht, &tmp); + } } for (xmlAttrPtr attr = nodep->properties; attr; attr = attr->next) { - ZVAL_STR(&tmp, dom_node_get_node_name_attribute_or_element((const xmlNode *) attr)); + ZVAL_NEW_STR(&tmp, dom_node_get_node_name_attribute_or_element((const xmlNode *) attr, false)); zend_hash_next_index_insert(ht, &tmp); } } @@ -360,6 +362,7 @@ static xmlNodePtr dom_create_attribute(xmlNodePtr nodep, const char *name, const } /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68F082 +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattribute Since: */ PHP_METHOD(DOMElement, setAttribute) @@ -390,31 +393,52 @@ PHP_METHOD(DOMElement, setAttribute) DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - attr = dom_get_dom1_attribute(nodep, (xmlChar *)name); - if (attr != NULL) { - switch (attr->type) { - case XML_ATTRIBUTE_NODE: - node_list_unlink(attr->children); - break; - case XML_NAMESPACE_DECL: - RETURN_FALSE; - default: - break; + if (php_dom_follow_spec_intern(intern)) { + xmlChar *name_processed = BAD_CAST name; + if (php_dom_ns_is_html_and_document_is_html(nodep)) { + char *lowercase_copy = zend_str_tolower_dup_ex(name, name_len); + if (lowercase_copy != NULL) { + name_processed = BAD_CAST lowercase_copy; + } } - } + /* Can't use xmlSetNsProp unconditionally here because that doesn't take into account the qualified name matching... */ + attr = (xmlNodePtr) php_dom_get_attribute_node(nodep, BAD_CAST name, name_len); + if (attr != NULL) { + dom_remove_all_children(attr); + xmlNodePtr node = xmlNewDocText(attr->doc, BAD_CAST value); + xmlAddChild(attr, node); + } else { + attr = (xmlNodePtr) xmlSetNsProp(nodep, NULL, name_processed, BAD_CAST value); + } - attr = dom_create_attribute(nodep, name, value); - if (!attr) { - zend_argument_value_error(1, "must be a valid XML attribute"); - RETURN_THROWS(); - } - if (attr->type == XML_NAMESPACE_DECL) { - RETURN_TRUE; - } + if (name_processed != BAD_CAST name) { + efree(name_processed); + } + } else { + attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len); + if (attr != NULL) { + switch (attr->type) { + case XML_ATTRIBUTE_NODE: + node_list_unlink(attr->children); + break; + case XML_NAMESPACE_DECL: + RETURN_FALSE; + EMPTY_SWITCH_DEFAULT_CASE(); + } + } - DOM_RET_OBJ(attr, &ret, intern); + attr = dom_create_attribute(nodep, name, value); + if (!attr) { + zend_argument_value_error(1, "must be a valid XML attribute"); + RETURN_THROWS(); + } + if (attr->type == XML_NAMESPACE_DECL) { + RETURN_TRUE; + } + DOM_RET_OBJ(attr, &ret, intern); + } } /* }}} end dom_element_set_attribute */ @@ -533,33 +557,52 @@ static bool dom_remove_attribute(xmlNodePtr thisp, xmlNodePtr attrp) } /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6D6AC0F9 +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-removeattribute Since: */ PHP_METHOD(DOMElement, removeAttribute) { - zval *id; xmlNodePtr nodep, attrp; dom_object *intern; size_t name_len; char *name; - id = ZEND_THIS; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { RETURN_THROWS(); } - DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); + DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern); - attrp = dom_get_dom1_attribute(nodep, (xmlChar *)name); + attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len); if (attrp == NULL) { RETURN_FALSE; } RETURN_BOOL(dom_remove_attribute(nodep, attrp)); } + +PHP_METHOD(DOM_Element, removeAttribute) +{ + xmlNodePtr nodep, attrp; + dom_object *intern; + size_t name_len; + char *name; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { + RETURN_THROWS(); + } + + DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern); + + attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len); + if (attrp != NULL) { + dom_remove_attribute(nodep, attrp); + } +} /* }}} end dom_element_remove_attribute */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-217A91B8 +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributenode Since: */ PHP_METHOD(DOMElement, getAttributeNode) @@ -578,8 +621,11 @@ PHP_METHOD(DOMElement, getAttributeNode) DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - attrp = dom_get_dom1_attribute(nodep, (xmlChar *)name); + attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len); if (attrp == NULL) { + if (php_dom_follow_spec_intern(intern)) { + RETURN_NULL(); + } RETURN_FALSE; } @@ -594,34 +640,50 @@ PHP_METHOD(DOMElement, getAttributeNode) } /* }}} end dom_element_get_attribute_node */ -/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-887236154 -Since: -*/ -PHP_METHOD(DOMElement, setAttributeNode) +static void dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAMETERS, bool use_ns, bool modern) { zval *id, *node; xmlNode *nodep; + xmlNs *nsp; xmlAttr *attrp, *existattrp = NULL; dom_object *intern, *attrobj, *oldobj; int ret; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_attr_class_entry) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_get_node_ce(modern)) == FAILURE) { RETURN_THROWS(); } DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj); + /* ZPP Guarantees that a DOMAttr class is given, as it is converted to a xmlAttr + * to pass to libxml (see http://www.xmlsoft.org/html/libxml-tree.html#xmlAttr) + * if it is not of type XML_ATTRIBUTE_NODE it indicates a bug somewhere */ ZEND_ASSERT(attrp->type == XML_ATTRIBUTE_NODE); - if (!(attrp->doc == NULL || attrp->doc == nodep->doc)) { - php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document)); - RETURN_FALSE; + if (modern) { + if (attrp->parent != NULL && attrp->parent != nodep) { + php_dom_throw_error(INUSE_ATTRIBUTE_ERR, /* strict */ true); + RETURN_THROWS(); + } + if (attrp->doc != NULL && attrp->doc != nodep->doc) { + php_dom_adopt_node((xmlNodePtr) attrp, intern, nodep->doc); + } + } else { + if (!(attrp->doc == NULL || attrp->doc == nodep->doc)) { + php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document)); + RETURN_FALSE; + } + } + + nsp = attrp->ns; + if (use_ns && nsp != NULL) { + existattrp = xmlHasNsProp(nodep, attrp->name, nsp->href); + } else { + existattrp = xmlHasProp(nodep, attrp->name); } - existattrp = xmlHasProp(nodep, attrp->name); if (existattrp != NULL && existattrp->type != XML_ATTRIBUTE_DECL) { if ((oldobj = php_dom_object_get_data((xmlNodePtr) existattrp)) != NULL && ((php_libxml_node_ptr *)oldobj->ptr)->node == (xmlNodePtr) attrp) @@ -641,35 +703,44 @@ PHP_METHOD(DOMElement, setAttributeNode) } xmlAddChild(nodep, (xmlNodePtr) attrp); - php_dom_reconcile_attribute_namespace_after_insertion(attrp); + if (!modern) { + php_dom_reconcile_attribute_namespace_after_insertion(attrp); + } /* Returns old property if removed otherwise NULL */ if (existattrp != NULL) { DOM_RET_OBJ((xmlNodePtr) existattrp, &ret, intern); } else { - RETVAL_NULL(); + RETURN_NULL(); } +} +/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-887236154 +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributenode +Since: +*/ +PHP_METHOD(DOMElement, setAttributeNode) +{ + dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ false, /* modern */ false); } /* }}} end dom_element_set_attribute_node */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D589198 Since: */ -PHP_METHOD(DOMElement, removeAttributeNode) +static void dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce) { - zval *id, *node; + zval *node; xmlNode *nodep; xmlAttr *attrp; dom_object *intern, *attrobj; int ret; - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_attr_class_entry) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, node_ce) == FAILURE) { RETURN_THROWS(); } - DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); + DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern); DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj); @@ -683,14 +754,24 @@ PHP_METHOD(DOMElement, removeAttributeNode) xmlUnlinkNode((xmlNodePtr) attrp); DOM_RET_OBJ((xmlNodePtr) attrp, &ret, intern); +} +PHP_METHOD(DOMElement, removeAttributeNode) +{ + dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry); +} + +PHP_METHOD(DOM_Element, removeAttributeNode) +{ + dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry); } /* }}} end dom_element_remove_attribute_node */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1938918D +Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagname Since: */ -PHP_METHOD(DOMElement, getElementsByTagName) +static void dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS, bool modern) { size_t name_len; dom_object *intern, *namednode; @@ -702,24 +783,64 @@ PHP_METHOD(DOMElement, getElementsByTagName) DOM_GET_THIS_INTERN(intern); - php_dom_create_iterator(return_value, DOM_NODELIST); + if (modern) { + php_dom_create_iterator(return_value, DOM_HTMLCOLLECTION, true); + } else { + php_dom_create_iterator(return_value, DOM_NODELIST, false); + } namednode = Z_DOMOBJ_P(return_value); dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, NULL, 0); } + +PHP_METHOD(DOMElement, getElementsByTagName) +{ + dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Element, getElementsByTagName) +{ + dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} /* }}} end dom_element_get_elements_by_tag_name */ +/* should_free_result must be initialized to false */ +static const xmlChar *dom_get_attribute_ns(dom_object *intern, xmlNodePtr elemp, const char *uri, size_t uri_len, const char *name, bool *should_free_result) +{ + bool follow_spec = php_dom_follow_spec_intern(intern); + if (follow_spec && uri_len == 0) { + uri = NULL; + } + + xmlChar *strattr = xmlGetNsProp(elemp, (xmlChar *) name, (xmlChar *) uri); + + if (strattr != NULL) { + *should_free_result = true; + return strattr; + } else { + if (!follow_spec && xmlStrEqual((xmlChar *) uri, (xmlChar *)DOM_XMLNS_NAMESPACE)) { + xmlNsPtr nsptr = dom_get_nsdecl(elemp, (xmlChar *)name); + if (nsptr != NULL) { + return nsptr->href; + } else { + return NULL; + } + } else { + return NULL; + } + } +} + /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAttrNS +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributens Since: DOM Level 2 */ PHP_METHOD(DOMElement, getAttributeNS) { zval *id; xmlNodePtr elemp; - xmlNsPtr nsptr; dom_object *intern; size_t uri_len = 0, name_len = 0; char *uri, *name; - xmlChar *strattr; id = ZEND_THIS; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) { @@ -728,57 +849,37 @@ PHP_METHOD(DOMElement, getAttributeNS) DOM_GET_OBJ(elemp, id, xmlNodePtr, intern); - strattr = xmlGetNsProp(elemp, (xmlChar *) name, (xmlChar *) uri); - - if (strattr != NULL) { - RETVAL_STRING((char *)strattr); - xmlFree(strattr); + bool should_free_result = false; + const xmlChar *result = dom_get_attribute_ns(intern, elemp, uri, uri_len, name, &should_free_result); + if (result == NULL) { + if (php_dom_follow_spec_intern(intern)) { + RETURN_NULL(); + } + RETURN_EMPTY_STRING(); } else { - if (xmlStrEqual((xmlChar *) uri, (xmlChar *)DOM_XMLNS_NAMESPACE)) { - nsptr = dom_get_nsdecl(elemp, (xmlChar *)name); - if (nsptr != NULL) { - RETVAL_STRING((char *) nsptr->href); - } else { - RETVAL_EMPTY_STRING(); - } - } else { - RETVAL_EMPTY_STRING(); + RETVAL_STRING((const char *) result); + if (should_free_result) { + xmlFree(BAD_CAST result); } } - } /* }}} end dom_element_get_attribute_ns */ -/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAttrNS -Since: DOM Level 2 -*/ -PHP_METHOD(DOMElement, setAttributeNS) +static void dom_set_attribute_ns_legacy(dom_object *intern, xmlNodePtr elemp, char *uri, size_t uri_len, char *name, size_t name_len, const char *value) { - zval *id; - xmlNodePtr elemp, nodep = NULL; - xmlNsPtr nsptr; - xmlAttr *attr; - size_t uri_len = 0, name_len = 0, value_len = 0; - char *uri, *name, *value; - char *localname = NULL, *prefix = NULL; - dom_object *intern; - int errorcode = 0, stricterror, is_xmlns = 0, name_valid; - - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!ss", &uri, &uri_len, &name, &name_len, &value, &value_len) == FAILURE) { - RETURN_THROWS(); - } - if (name_len == 0) { zend_argument_value_error(2, "cannot be empty"); - RETURN_THROWS(); + return; } - DOM_GET_OBJ(elemp, id, xmlNodePtr, intern); - - stricterror = dom_get_strict_error(intern->document); + xmlNodePtr nodep = NULL; + xmlNsPtr nsptr; + xmlAttr *attr; + char *localname = NULL, *prefix = NULL; + int is_xmlns = 0, name_valid; + int stricterror = dom_get_strict_error(intern->document); - errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len); + int errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len); if (errorcode == 0) { if (uri_len > 0) { @@ -858,8 +959,54 @@ PHP_METHOD(DOMElement, setAttributeNS) if (errorcode != 0) { php_dom_throw_error(errorcode, stricterror); } +} - RETURN_NULL(); +/* https://dom.spec.whatwg.org/#dom-element-setattributens */ +static void dom_set_attribute_ns_modern(dom_object *intern, xmlNodePtr elemp, zend_string *uri, const zend_string *name, const char *value) +{ + xmlChar *localname = NULL, *prefix = NULL; + int errorcode = dom_validate_and_extract(uri, name, &localname, &prefix); + + if (errorcode == 0) { + php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern); + xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri); + if (UNEXPECTED(xmlSetNsProp(elemp, ns, localname, BAD_CAST value) == NULL)) { + php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); + } + } else { + php_dom_throw_error(errorcode, /* strict */ true); + } + + xmlFree(localname); + xmlFree(prefix); +} + +/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAttrNS +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributens +Since: DOM Level 2 +*/ +PHP_METHOD(DOMElement, setAttributeNS) +{ + zval *id; + xmlNodePtr elemp; + size_t value_len = 0; + char *value; + zend_string *uri; + zend_string *name = NULL; + dom_object *intern; + + id = ZEND_THIS; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!Ss", &uri, &name, &value, &value_len) == FAILURE) { + RETURN_THROWS(); + } + + DOM_GET_OBJ(elemp, id, xmlNodePtr, intern); + + if (php_dom_follow_spec_intern(intern)) { + dom_set_attribute_ns_modern(intern, elemp, uri, name, value); + } else { + dom_set_attribute_ns_legacy(intern, elemp, uri ? ZSTR_VAL(uri) : NULL, uri ? ZSTR_LEN(uri) : 0, ZSTR_VAL(name), ZSTR_LEN(name), value); + } } /* }}} end dom_element_set_attribute_ns */ @@ -895,18 +1042,7 @@ static void dom_remove_eliminated_ns(xmlNodePtr node, xmlNsPtr eliminatedNs) } } - 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 == base) { - return; - } - } while (node->next == NULL); - node = node->next; - } + node = php_dom_next_in_tree_order(node, base); } } @@ -941,6 +1077,7 @@ static void dom_eliminate_ns(xmlNodePtr nodep, xmlNsPtr nsptr) } /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-removeattributens Since: DOM Level 2 */ PHP_METHOD(DOMElement, removeAttributeNS) @@ -960,14 +1097,21 @@ PHP_METHOD(DOMElement, removeAttributeNS) DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - attrp = xmlHasNsProp(nodep, (xmlChar *)name, (xmlChar *)uri); + bool follow_spec = php_dom_follow_spec_intern(intern); + if (follow_spec && uri_len == 0) { + uri = NULL; + } - nsptr = dom_get_nsdecl(nodep, (xmlChar *)name); - if (nsptr != NULL) { - if (xmlStrEqual((xmlChar *)uri, nsptr->href)) { - dom_eliminate_ns(nodep, nsptr); - } else { - RETURN_NULL(); + attrp = xmlHasNsProp(nodep, BAD_CAST name, BAD_CAST uri); + + if (!follow_spec) { + nsptr = dom_get_nsdecl(nodep, BAD_CAST name); + if (nsptr != NULL) { + if (xmlStrEqual(BAD_CAST uri, nsptr->href)) { + dom_eliminate_ns(nodep, nsptr); + } else { + return; + } } } @@ -980,12 +1124,11 @@ PHP_METHOD(DOMElement, removeAttributeNS) xmlUnlinkNode((xmlNodePtr) attrp); } } - - RETURN_NULL(); } /* }}} end dom_element_remove_attribute_ns */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAtNodeNS +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributenodens Since: DOM Level 2 */ PHP_METHOD(DOMElement, getAttributeNodeNS) @@ -1005,12 +1148,17 @@ PHP_METHOD(DOMElement, getAttributeNodeNS) DOM_GET_OBJ(elemp, id, xmlNodePtr, intern); - attrp = xmlHasNsProp(elemp, (xmlChar *)name, (xmlChar *)uri); + bool follow_spec = php_dom_follow_spec_intern(intern); + if (follow_spec && uri_len == 0) { + uri = NULL; + } + + attrp = xmlHasNsProp(elemp, BAD_CAST name, BAD_CAST uri); if (attrp == NULL) { - if (xmlStrEqual((xmlChar *) uri, (xmlChar *)DOM_XMLNS_NAMESPACE)) { + if (!follow_spec && xmlStrEqual(BAD_CAST uri, DOM_XMLNS_NAMESPACE)) { xmlNsPtr nsptr; - nsptr = dom_get_nsdecl(elemp, (xmlChar *)name); + nsptr = dom_get_nsdecl(elemp, BAD_CAST name); if (nsptr != NULL) { /* Keep parent alive, because we're a fake child. */ GC_ADDREF(&intern->std); @@ -1019,7 +1167,7 @@ PHP_METHOD(DOMElement, getAttributeNodeNS) RETURN_NULL(); } } else { - RETURN_NULL(); + RETURN_NULL(); } } else { DOM_RET_OBJ((xmlNodePtr) attrp, &ret, intern); @@ -1029,77 +1177,25 @@ PHP_METHOD(DOMElement, getAttributeNodeNS) /* }}} end dom_element_get_attribute_node_ns */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAtNodeNS +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributenodens Since: DOM Level 2 */ PHP_METHOD(DOMElement, setAttributeNodeNS) { - zval *id, *node; - xmlNode *nodep; - xmlNs *nsp; - xmlAttr *attrp, *existattrp = NULL; - dom_object *intern, *attrobj, *oldobj; - int ret; - - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_attr_class_entry) == FAILURE) { - RETURN_THROWS(); - } - - DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj); - - /* ZPP Guarantees that a DOMAttr class is given, as it is converted to a xmlAttr - * to pass to libxml (see http://www.xmlsoft.org/html/libxml-tree.html#xmlAttr) - * if it is not of type XML_ATTRIBUTE_NODE it indicates a bug somewhere */ - ZEND_ASSERT(attrp->type == XML_ATTRIBUTE_NODE); - - if (!(attrp->doc == NULL || attrp->doc == nodep->doc)) { - php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document)); - RETURN_FALSE; - } - - nsp = attrp->ns; - if (nsp != NULL) { - existattrp = xmlHasNsProp(nodep, attrp->name, nsp->href); - } else { - existattrp = xmlHasProp(nodep, attrp->name); - } - - if (existattrp != NULL && existattrp->type != XML_ATTRIBUTE_DECL) { - if ((oldobj = php_dom_object_get_data((xmlNodePtr) existattrp)) != NULL && - ((php_libxml_node_ptr *)oldobj->ptr)->node == (xmlNodePtr) attrp) - { - RETURN_NULL(); - } - xmlUnlinkNode((xmlNodePtr) existattrp); - } - - if (attrp->parent != NULL) { - xmlUnlinkNode((xmlNodePtr) attrp); - } - - if (attrp->doc == NULL && nodep->doc != NULL) { - attrobj->document = intern->document; - php_libxml_increment_doc_ref((php_libxml_node_object *)attrobj, NULL); - } - - xmlAddChild(nodep, (xmlNodePtr) attrp); - php_dom_reconcile_attribute_namespace_after_insertion(attrp); - - /* Returns old property if removed otherwise NULL */ - if (existattrp != NULL) { - DOM_RET_OBJ((xmlNodePtr) existattrp, &ret, intern); - } else { - RETVAL_NULL(); - } + dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ true, /* modern */ false); +} +PHP_METHOD(DOM_Element, setAttributeNodeNS) +{ + dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ true, /* modern */ true); } /* }}} end dom_element_set_attribute_node_ns */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C90942 +Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagnamens Since: DOM Level 2 */ -PHP_METHOD(DOMElement, getElementsByTagNameNS) +static void dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS, bool modern) { size_t uri_len, name_len; dom_object *intern, *namednode; @@ -1111,14 +1207,28 @@ PHP_METHOD(DOMElement, getElementsByTagNameNS) DOM_GET_THIS_INTERN(intern); - php_dom_create_iterator(return_value, DOM_NODELIST); + if (modern) { + php_dom_create_iterator(return_value, DOM_HTMLCOLLECTION, true); + } else { + php_dom_create_iterator(return_value, DOM_NODELIST, false); + } namednode = Z_DOMOBJ_P(return_value); dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, uri ? uri : "", uri_len); +} +PHP_METHOD(DOMElement, getElementsByTagNameNS) +{ + dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Element, getElementsByTagNameNS) +{ + dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } /* }}} end dom_element_get_elements_by_tag_name_ns */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttr +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-hasattribute Since: DOM Level 2 */ PHP_METHOD(DOMElement, hasAttribute) @@ -1137,7 +1247,7 @@ PHP_METHOD(DOMElement, hasAttribute) DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - attr = dom_get_dom1_attribute(nodep, (xmlChar *)name); + attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len); if (attr == NULL) { RETURN_FALSE; } else { @@ -1147,17 +1257,16 @@ PHP_METHOD(DOMElement, hasAttribute) /* }}} end dom_element_has_attribute */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttrNS +Modern spec URL: https://dom.spec.whatwg.org/#dom-element-hasattributens Since: DOM Level 2 */ PHP_METHOD(DOMElement, hasAttributeNS) { zval *id; xmlNodePtr elemp; - xmlNs *nsp; dom_object *intern; size_t uri_len, name_len; char *uri, *name; - xmlChar *value; id = ZEND_THIS; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) { @@ -1166,21 +1275,16 @@ PHP_METHOD(DOMElement, hasAttributeNS) DOM_GET_OBJ(elemp, id, xmlNodePtr, intern); - value = xmlGetNsProp(elemp, (xmlChar *)name, (xmlChar *)uri); - - if (value != NULL) { - xmlFree(value); - RETURN_TRUE; + bool should_free_result = false; + const xmlChar *result = dom_get_attribute_ns(intern, elemp, uri, uri_len, name, &should_free_result); + if (result == NULL) { + RETURN_FALSE; } else { - if (xmlStrEqual((xmlChar *)uri, (xmlChar *)DOM_XMLNS_NAMESPACE)) { - nsp = dom_get_nsdecl(elemp, (xmlChar *)name); - if (nsp != NULL) { - RETURN_TRUE; - } + if (should_free_result) { + xmlFree(BAD_CAST result); } + RETURN_TRUE; } - - RETURN_FALSE; } /* }}} end dom_element_has_attribute_ns */ @@ -1266,7 +1370,7 @@ PHP_METHOD(DOMElement, setIdAttributeNS) /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNode Since: DOM Level 3 */ -PHP_METHOD(DOMElement, setIdAttributeNode) +static void dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *attr_ce) { zval *id, *node; xmlNode *nodep; @@ -1275,7 +1379,7 @@ PHP_METHOD(DOMElement, setIdAttributeNode) bool is_id; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ob", &node, dom_attr_class_entry, &is_id) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ob", &node, attr_ce, &is_id) != SUCCESS) { RETURN_THROWS(); } @@ -1290,6 +1394,16 @@ PHP_METHOD(DOMElement, setIdAttributeNode) RETURN_NULL(); } + +PHP_METHOD(DOMElement, setIdAttributeNode) +{ + dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_attr_class_entry); +} + +PHP_METHOD(DOM_Element, setIdAttributeNode) +{ + dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_attr_class_entry); +} /* }}} end dom_element_set_id_attribute_node */ /* {{{ URL: @@ -1415,7 +1529,9 @@ PHP_METHOD(DOMElement, replaceChildren) } /* }}} */ -#define INSERT_ADJACENT_RES_FAILED ((void*) -1) +#define INSERT_ADJACENT_RES_ADOPT_FAILED ((void*) -1) +#define INSERT_ADJACENT_RES_SYNTAX_FAILED INSERT_ADJACENT_RES_ADOPT_FAILED +#define INSERT_ADJACENT_RES_PRE_INSERT_FAILED ((void*) -2) static xmlNodePtr dom_insert_adjacent(const zend_string *where, xmlNodePtr thisp, dom_object *this_intern, xmlNodePtr otherp) { @@ -1423,60 +1539,47 @@ static xmlNodePtr dom_insert_adjacent(const zend_string *where, xmlNodePtr thisp if (thisp->parent == NULL) { return NULL; } - if (dom_hierarchy(thisp->parent, otherp) == FAILURE) { - php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document)); - return INSERT_ADJACENT_RES_FAILED; - } if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) { - return INSERT_ADJACENT_RES_FAILED; + return INSERT_ADJACENT_RES_ADOPT_FAILED; } - otherp = xmlAddPrevSibling(thisp, otherp); - } else if (zend_string_equals_literal_ci(where, "afterbegin")) { - if (dom_hierarchy(thisp, otherp) == FAILURE) { - php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document)); - return INSERT_ADJACENT_RES_FAILED; + if (!php_dom_pre_insert(this_intern->document, otherp, thisp->parent, thisp)) { + return INSERT_ADJACENT_RES_PRE_INSERT_FAILED; } + } else if (zend_string_equals_literal_ci(where, "afterbegin")) { if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) { - return INSERT_ADJACENT_RES_FAILED; + return INSERT_ADJACENT_RES_ADOPT_FAILED; } - if (thisp->children == NULL) { - otherp = xmlAddChild(thisp, otherp); - } else { - otherp = xmlAddPrevSibling(thisp->children, otherp); + if (!php_dom_pre_insert(this_intern->document, otherp, thisp, thisp->children)) { + return INSERT_ADJACENT_RES_PRE_INSERT_FAILED; } } else if (zend_string_equals_literal_ci(where, "beforeend")) { - if (dom_hierarchy(thisp, otherp) == FAILURE) { - php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document)); - return INSERT_ADJACENT_RES_FAILED; - } if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) { - return INSERT_ADJACENT_RES_FAILED; + return INSERT_ADJACENT_RES_ADOPT_FAILED; + } + if (!php_dom_pre_insert(this_intern->document, otherp, thisp, NULL)) { + return INSERT_ADJACENT_RES_PRE_INSERT_FAILED; } - otherp = xmlAddChild(thisp, otherp); } else if (zend_string_equals_literal_ci(where, "afterend")) { if (thisp->parent == NULL) { return NULL; } - if (dom_hierarchy(thisp->parent, otherp) == FAILURE) { - php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document)); - return INSERT_ADJACENT_RES_FAILED; - } if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) { - return INSERT_ADJACENT_RES_FAILED; + return INSERT_ADJACENT_RES_ADOPT_FAILED; + } + if (!php_dom_pre_insert(this_intern->document, otherp, thisp->parent, thisp->next)) { + return INSERT_ADJACENT_RES_PRE_INSERT_FAILED; } - otherp = xmlAddNextSibling(thisp, otherp); } else { php_dom_throw_error(SYNTAX_ERR, dom_get_strict_error(this_intern->document)); - return INSERT_ADJACENT_RES_FAILED; + return INSERT_ADJACENT_RES_SYNTAX_FAILED; } - dom_reconcile_ns(thisp->doc, otherp); return otherp; } /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacentelement Since: */ -PHP_METHOD(DOMElement, insertAdjacentElement) +static void dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *element_ce) { zend_string *where; zval *element_zval, *id; @@ -1484,7 +1587,7 @@ PHP_METHOD(DOMElement, insertAdjacentElement) dom_object *this_intern, *other_intern; int ret; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "SO", &where, &element_zval, dom_element_class_entry) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SO", &where, &element_zval, element_ce) != SUCCESS) { RETURN_THROWS(); } @@ -1494,12 +1597,22 @@ PHP_METHOD(DOMElement, insertAdjacentElement) xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp); if (result == NULL) { RETURN_NULL(); - } else if (result != INSERT_ADJACENT_RES_FAILED) { + } else if (result != INSERT_ADJACENT_RES_ADOPT_FAILED && result != INSERT_ADJACENT_RES_PRE_INSERT_FAILED) { DOM_RET_OBJ(otherp, &ret, other_intern); } else { RETURN_THROWS(); } } + +PHP_METHOD(DOMElement, insertAdjacentElement) +{ + dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_element_class_entry); +} + +PHP_METHOD(DOM_Element, insertAdjacentElement) +{ + dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_element_class_entry); +} /* }}} end DOMElement::insertAdjacentElement */ /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacenttext @@ -1525,11 +1638,12 @@ PHP_METHOD(DOMElement, insertAdjacentText) xmlNodePtr otherp = xmlNewDocTextLen(thisp->doc, (const xmlChar *) ZSTR_VAL(data), ZSTR_LEN(data)); xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp); - if (result == NULL || result == INSERT_ADJACENT_RES_FAILED) { + if (result == NULL || result == INSERT_ADJACENT_RES_ADOPT_FAILED) { xmlFreeNode(otherp); } } /* }}} end DOMElement::insertAdjacentText */ + /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-toggleattribute Since: */ @@ -1555,8 +1669,11 @@ PHP_METHOD(DOMElement, toggleAttribute) RETURN_THROWS(); } + bool follow_spec = php_dom_follow_spec_intern(intern); + /* Step 2 */ - if (thisp->doc != NULL && thisp->doc->type == XML_HTML_DOCUMENT_NODE && (thisp->ns == NULL || xmlStrEqual(thisp->ns->href, (const xmlChar *) "http://www.w3.org/1999/xhtml"))) { + if (thisp->doc != NULL && thisp->doc->type == XML_HTML_DOCUMENT_NODE + && ((!follow_spec && thisp->ns == NULL) || (thisp->ns != NULL && xmlStrEqual(thisp->ns->href, BAD_CAST DOM_XHTML_NS_URI)))) { qname_tmp = zend_str_tolower_dup_ex(qname, qname_length); if (qname_tmp != NULL) { qname = qname_tmp; @@ -1564,22 +1681,26 @@ PHP_METHOD(DOMElement, toggleAttribute) } /* Step 3 */ - xmlNodePtr attribute = dom_get_dom1_attribute(thisp, (xmlChar *) qname); + xmlNodePtr attribute = dom_get_attribute_or_nsdecl(intern, thisp, BAD_CAST qname, qname_length); /* Step 4 */ if (attribute == NULL) { /* Step 4.1 */ if (force_is_null || force) { - /* The behaviour for namespaces isn't defined by spec, but this is based on observing browers behaviour. - * It follows the same rules when you'd manually add an attribute using the other APIs. */ - int len; - const xmlChar *split = xmlSplitQName3((const xmlChar *) qname, &len); - if (split == NULL || strncmp(qname, "xmlns:", len + 1) != 0) { - /* unqualified name, or qualified name with no xml namespace declaration */ - dom_create_attribute(thisp, qname, ""); + if (follow_spec) { + xmlSetNsProp(thisp, NULL, BAD_CAST qname, NULL); } else { - /* qualified name with xml namespace declaration */ - xmlNewNs(thisp, (const xmlChar *) "", (const xmlChar *) (qname + len + 1)); + /* The behaviour for namespaces isn't defined by spec, but this is based on observing browers behaviour. + * It follows the same rules when you'd manually add an attribute using the other APIs. */ + int len; + const xmlChar *split = xmlSplitQName3((const xmlChar *) qname, &len); + if (split == NULL || strncmp(qname, "xmlns:", len + 1 /* +1 for matching ':' too */) != 0) { + /* unqualified name, or qualified name with no xml namespace declaration */ + dom_create_attribute(thisp, qname, ""); + } else { + /* qualified name with xml namespace declaration */ + xmlNewNs(thisp, (const xmlChar *) "", (const xmlChar *) (qname + len + 1)); + } } retval = true; goto out; diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c index 6a262318a89eb..c30d8ee86a082 100644 --- a/ext/dom/html5_parser.c +++ b/ext/dom/html5_parser.c @@ -20,11 +20,10 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) +#include "php_dom.h" #include "html5_parser.h" -#include "namespace_compat.h" #include #include -#include #include #include #include @@ -63,14 +62,14 @@ static unsigned short sanitize_line_nr(size_t line) return (unsigned short) line; } -static const xmlChar *get_libxml_namespace_href(uintptr_t lexbor_namespace) +static const php_dom_ns_magic_token *get_libxml_namespace_href(uintptr_t lexbor_namespace) { if (lexbor_namespace == LXB_NS_SVG) { - return (const xmlChar *) DOM_SVG_NS_URI; + return php_dom_ns_is_svg_magic_token; } else if (lexbor_namespace == LXB_NS_MATH) { - return (const xmlChar *) DOM_MATHML_NS_URI; + return php_dom_ns_is_mathml_magic_token; } else { - return (const xmlChar *) DOM_XHTML_NS_URI; + return php_dom_ns_is_html_magic_token; } } @@ -98,11 +97,16 @@ 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 + bool create_default_ns, + php_dom_libxml_ns_mapper *ns_mapper ) { lexbor_libxml2_bridge_status retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OK; + xmlNsPtr html_ns = php_dom_libxml_ns_mapper_ensure_html_ns(ns_mapper); + xmlNsPtr xlink_ns = NULL; + xmlNsPtr prefixed_xmlns_ns = NULL; + lexbor_array_obj_t work_list; lexbor_array_obj_init(&work_list, WORK_LIST_INIT_SIZE, sizeof(work_list_item)); @@ -135,7 +139,17 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( 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); + if (entering_namespace == LXB_NS_HTML) { + current_lxml_ns = html_ns; + } else { + const php_dom_ns_magic_token *magic_token = get_libxml_namespace_href(entering_namespace); + zend_string *uri = zend_string_init((char *) magic_token, strlen((char *) magic_token), false); + current_lxml_ns = php_dom_libxml_ns_mapper_get_ns(ns_mapper, NULL, uri); + zend_string_release_ex(uri, false); + if (EXPECTED(current_lxml_ns != NULL)) { + current_lxml_ns->_private = (void *) magic_token; + } + } } /* Instead of xmlSetNs() because we know the arguments are valid. Prevents overhead. */ lxml_element->ns = current_lxml_ns; @@ -181,6 +195,25 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( } lxml_attr->children = lxml_attr->last = lxml_text; + lxml_text->parent = (xmlNodePtr) lxml_attr; + + if (attr->node.ns == LXB_NS_XMLNS) { + if (strcmp((const char *) local_name, "xmlns") != 0) { + if (prefixed_xmlns_ns == NULL) { + prefixed_xmlns_ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ns_mapper, "xmlns", DOM_XMLNS_NS_URI); + } + lxml_attr->ns = prefixed_xmlns_ns; + } else { + lxml_attr->ns = php_dom_libxml_ns_mapper_ensure_prefixless_xmlns_ns(ns_mapper); + } + lxml_attr->ns->_private = (void *) php_dom_ns_is_xmlns_magic_token; + } else if (attr->node.ns == LXB_NS_XLINK) { + if (xlink_ns == NULL) { + xlink_ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ns_mapper, "xlink", DOM_XLINK_NS_URI); + xlink_ns->_private = (void *) php_dom_ns_is_xlink_magic_token; + } + lxml_attr->ns = xlink_ns; + } if (last_added_attr == NULL) { lxml_element->properties = lxml_attr; @@ -270,29 +303,20 @@ 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 + bool create_default_ns, + php_dom_libxml_ns_mapper *ns_mapper ) { -#ifdef LIBXML_HTML_ENABLED - xmlDocPtr lxml_doc = htmlNewDocNoDtD(NULL, NULL); + xmlDocPtr lxml_doc = php_dom_create_html_doc(); if (UNEXPECTED(!lxml_doc)) { return LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; } -#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"); - if (UNEXPECTED(!lxml_doc)) { - return LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; - } - lxml_doc->type = XML_HTML_DOCUMENT_NODE; -#endif - lxml_doc->dict = xmlDictCreate(); lexbor_libxml2_bridge_status status = lexbor_libxml2_bridge_convert( lxb_dom_interface_node(document)->last_child, lxml_doc, compact_text_nodes, - create_default_ns + create_default_ns, + ns_mapper ); if (status != LEXBOR_LIBXML2_BRIDGE_STATUS_OK) { xmlFreeDoc(lxml_doc); diff --git a/ext/dom/html5_parser.h b/ext/dom/html5_parser.h index 4ed6dc2ed03db..bffa6b0c8aaab 100644 --- a/ext/dom/html5_parser.h +++ b/ext/dom/html5_parser.h @@ -17,6 +17,7 @@ #ifndef HTML5_PARSER_H #define HTML5_PARSER_H +#include "namespace_compat.h" #include #include #include @@ -68,7 +69,8 @@ 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 + bool create_default_ns, + php_dom_libxml_ns_mapper *ns_mapper ); void lexbor_libxml2_bridge_report_errors( const lexbor_libxml2_bridge_parse_context *ctx, diff --git a/ext/dom/html5_serializer.c b/ext/dom/html5_serializer.c index f61254257abb5..3503bb3afc3f5 100644 --- a/ext/dom/html5_serializer.c +++ b/ext/dom/html5_serializer.c @@ -23,24 +23,14 @@ #include "php_dom.h" #include "html5_serializer.h" #include "namespace_compat.h" +#include "serialize_common.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; -} +/* This file implements the HTML 5 serialization algorithm. + * https://html.spec.whatwg.org/multipage/parsing.html#serialising-html-fragments (Date 2023-12-14) + */ -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; -} +#define TRY(x) do { if (UNEXPECTED((x) != SUCCESS)) { return FAILURE; } } while (0) static zend_result dom_html5_serialize_doctype(dom_html5_serialize_context *ctx, const xmlDtd *dtd) { @@ -61,10 +51,19 @@ static zend_result dom_html5_serialize_processing_instruction(dom_html5_serializ 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)); + if (node->content) { + TRY(ctx->write_string(ctx->application_data, (const char *) node->content)); + } return ctx->write_string_len(ctx->application_data, ">", strlen(">")); } +static zend_result dom_html5_serialize_entity_ref(dom_html5_serialize_context *ctx, const xmlNode *node) +{ + TRY(ctx->write_string_len(ctx->application_data, "&", strlen("&"))); + TRY(ctx->write_string(ctx->application_data, (const char *) node->name)); + 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) { @@ -132,7 +131,7 @@ static zend_result dom_html5_escape_string(dom_html5_serialize_context *ctx, con 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)) { + if (node->parent->type == XML_ELEMENT_NODE && php_dom_ns_is_fast(node->parent, php_dom_ns_is_html_magic_token)) { 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. @@ -156,7 +155,7 @@ static zend_result dom_html5_serialize_element_tag_name(dom_html5_serialize_cont { /* 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))) { + && !(php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) || php_dom_ns_is_fast(node, php_dom_ns_is_mathml_magic_token) || php_dom_ns_is_fast(node, php_dom_ns_is_svg_magic_token))) { TRY(ctx->write_string(ctx->application_data, (const char *) node->ns->prefix)); TRY(ctx->write_string_len(ctx->application_data, ":", strlen(":"))); } @@ -170,32 +169,15 @@ static zend_result dom_html5_serialize_element_start(dom_html5_serialize_context /* 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)) { + if (php_dom_ns_is_fast((const xmlNode *) attr, php_dom_ns_is_xml_magic_token)) { 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)) { + } else if (php_dom_ns_is_fast((const xmlNode *) attr, php_dom_ns_is_xmlns_magic_token)) { /* Compatibility for real attributes */ if (strcmp((const char *) attr->name, "xmlns") == 0) { TRY(ctx->write_string_len(ctx->application_data, "xmlns", strlen("xmlns"))); @@ -203,7 +185,7 @@ static zend_result dom_html5_serialize_element_start(dom_html5_serialize_context 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)) { + } else if (php_dom_ns_is_fast((const xmlNode *) attr, php_dom_ns_is_xlink_magic_token)) { 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) { @@ -233,7 +215,7 @@ static zend_result dom_html5_serialize_element_start(dom_html5_serialize_context * 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)) { + if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token)) { 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) @@ -311,6 +293,12 @@ static zend_result dom_html5_serialize_node(dom_html5_serialize_context *ctx, co break; } + /* Only exists for compatibility with XML and old DOM. */ + case XML_ENTITY_REF_NODE: { + TRY(dom_html5_serialize_entity_ref(ctx, node)); + break; + } + default: break; } @@ -335,8 +323,7 @@ static zend_result dom_html5_serialize_node(dom_html5_serialize_context *ctx, co 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! */ +/* 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. */ @@ -357,4 +344,22 @@ zend_result dom_html5_serialize(dom_html5_serialize_context *ctx, const xmlNode return dom_html5_serialize_node(ctx, node->children, node); } +/* Variant on the above that is equivalent to the "outer HTML". */ +zend_result dom_html5_serialize_outer(dom_html5_serialize_context *ctx, const xmlNode *node) +{ + if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE || node->type == XML_DOCUMENT_FRAG_NODE) { + node = node->children; + if (!node) { + return SUCCESS; + } + return dom_html5_serialize_node(ctx, node, node->parent); + } else { + xmlNodePtr old_next = node->next; + ((xmlNodePtr) node)->next = NULL; + zend_result result = dom_html5_serialize_node(ctx, node, node->parent); + ((xmlNodePtr) node)->next = old_next; + return result; + } +} + #endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/ext/dom/html5_serializer.h b/ext/dom/html5_serializer.h index a7eb4ee9be0c1..8ddedd48495ab 100644 --- a/ext/dom/html5_serializer.h +++ b/ext/dom/html5_serializer.h @@ -27,5 +27,6 @@ typedef struct { } dom_html5_serialize_context; zend_result dom_html5_serialize(dom_html5_serialize_context *ctx, const xmlNode *node); +zend_result dom_html5_serialize_outer(dom_html5_serialize_context *ctx, const xmlNode *node); #endif diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c index f684f449f6f65..c14ada84aefc4 100644 --- a/ext/dom/html_document.c +++ b/ext/dom/html_document.c @@ -78,6 +78,28 @@ typedef struct _dom_decoding_encoding_ctx { lxb_codepoint_t codepoints[4096]; } dom_decoding_encoding_ctx; +/* https://dom.spec.whatwg.org/#dom-document-implementation */ +zend_result dom_modern_document_implementation_read(dom_object *obj, zval *retval) +{ + const uint32_t PROP_INDEX = 14; + +#if ZEND_DEBUG + zend_string *implementation_str = ZSTR_INIT_LITERAL("implementation", false); + const zend_property_info *prop_info = zend_get_property_info(dom_abstract_base_document_class_entry, implementation_str, 0); + zend_string_release_ex(implementation_str, false); + ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX); +#endif + + zval *cached_implementation = OBJ_PROP_NUM(&obj->std, PROP_INDEX); + if (Z_ISUNDEF_P(cached_implementation)) { + php_dom_create_implementation(cached_implementation, true); + } + + ZVAL_OBJ_COPY(retval, Z_OBJ_P(cached_implementation)); + + return SUCCESS; +} + static void dom_decoding_encoding_ctx_init(dom_decoding_encoding_ctx *ctx) { ctx->encode_data = lxb_encoding_data(LXB_ENCODING_UTF_8); @@ -279,7 +301,7 @@ static void dom_lexbor_libxml2_bridge_tree_error_reporter( return; } - if (UNEXPECTED(len <= 1)) { + if (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, @@ -350,23 +372,7 @@ static void dom_post_process_html5_loading( 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; - } - } } } } @@ -719,19 +725,11 @@ PHP_METHOD(DOM_HTMLDocument, createEmpty) 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"); + + xmlDocPtr lxml_doc = php_dom_create_html_doc(); if (UNEXPECTED(lxml_doc == NULL)) { goto oom; } - lxml_doc->type = XML_HTML_DOCUMENT_NODE; -#endif lxml_doc->encoding = xmlStrdup((const xmlChar *) encoding); @@ -741,7 +739,8 @@ PHP_METHOD(DOM_HTMLDocument, createEmpty) (xmlNodePtr) lxml_doc, NULL ); - intern->document->is_modern_api_class = true; + intern->document->class_type = PHP_LIBXML_CLASS_MODERN; + intern->document->private_data = php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper_create()); return; oom: @@ -852,15 +851,19 @@ PHP_METHOD(DOM_HTMLDocument, createFromString) goto fail_oom; } + php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create(); + 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) + !(options & DOM_HTML_NO_DEFAULT_NS), + ns_mapper ); lexbor_libxml2_bridge_copy_observations(parser->tree, &ctx.observations); if (UNEXPECTED(bridge_status != LEXBOR_LIBXML2_BRIDGE_STATUS_OK)) { + php_dom_libxml_ns_mapper_destroy(ns_mapper); php_libxml_ctx_error( NULL, "%s in %s", @@ -886,7 +889,8 @@ PHP_METHOD(DOM_HTMLDocument, createFromString) (xmlNodePtr) lxml_doc, NULL ); - intern->document->is_modern_api_class = true; + intern->document->class_type = PHP_LIBXML_CLASS_MODERN; + intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper); return; fail_oom: @@ -898,6 +902,7 @@ PHP_METHOD(DOM_HTMLDocument, createFromString) PHP_METHOD(DOM_HTMLDocument, createFromFile) { const char *filename, *override_encoding = NULL; + php_dom_libxml_ns_mapper *ns_mapper = NULL; size_t filename_len, override_encoding_len; zend_long options = 0; php_stream *stream = NULL; @@ -958,7 +963,8 @@ PHP_METHOD(DOM_HTMLDocument, createFromFile) 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()); + zend_string *opened_path = NULL; + stream = php_stream_open_wrapper_ex(filename, "rb", REPORT_ERRORS, &opened_path, php_libxml_get_stream_context()); if (!stream) { if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Cannot open file '%s'", filename); @@ -1035,19 +1041,21 @@ PHP_METHOD(DOM_HTMLDocument, createFromFile) goto fail_oom; } + ns_mapper = php_dom_libxml_ns_mapper_create(); + 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) + !(options & DOM_HTML_NO_DEFAULT_NS), + ns_mapper ); 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; + RETVAL_FALSE; + goto fail_general; } lxb_html_document_destroy(document); @@ -1059,8 +1067,8 @@ PHP_METHOD(DOM_HTMLDocument, createFromFile) lxml_doc->encoding = xmlStrdup((const xmlChar *) "UTF-8"); } - if (stream->wrapper == &php_plain_files_wrapper) { - xmlChar *converted = xmlPathToURI((const xmlChar *) filename); + if (stream->wrapper == &php_plain_files_wrapper && opened_path != NULL) { + xmlChar *converted = xmlPathToURI((const xmlChar *) ZSTR_VAL(opened_path)); if (UNEXPECTED(!converted)) { goto fail_oom; } @@ -1080,12 +1088,18 @@ PHP_METHOD(DOM_HTMLDocument, createFromFile) xmlFree(converted); lxml_doc->URL = new_buffer; } else { +#if PHP_WIN32 + converted = php_dom_libxml_fix_file_path(converted); +#endif lxml_doc->URL = converted; } } else { lxml_doc->URL = xmlStrdup((const xmlChar *) filename); } + if (opened_path != NULL) { + zend_string_release_ex(opened_path, false); + } php_stream_close(stream); stream = NULL; @@ -1095,16 +1109,21 @@ PHP_METHOD(DOM_HTMLDocument, createFromFile) (xmlNodePtr) lxml_doc, NULL ); - intern->document->is_modern_api_class = true; + intern->document->class_type = PHP_LIBXML_CLASS_MODERN; + intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper); return; fail_oom: php_dom_throw_error(INVALID_STATE_ERR, 1); +fail_general: + if (ns_mapper != NULL) { + php_dom_libxml_ns_mapper_destroy(ns_mapper); + } lxb_html_document_destroy(document); - if (stream) { - php_stream_close(stream); + php_stream_close(stream); + if (opened_path != NULL) { + zend_string_release_ex(opened_path, false); } - RETURN_THROWS(); } static zend_result dom_write_output_smart_str(void *ctx, const char *buf, size_t size) @@ -1189,7 +1208,7 @@ static zend_result dom_common_save(dom_output_ctx *output_ctx, const xmlDoc *doc 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)) { + if (UNEXPECTED(dom_html5_serialize_outer(&ctx, node) != SUCCESS)) { return FAILURE; } @@ -1265,7 +1284,7 @@ PHP_METHOD(DOM_HTMLDocument, saveHTML) const xmlNode *node; dom_object *intern, *nodeobj; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &nodep, dom_node_class_entry) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &nodep, dom_modern_node_class_entry) == FAILURE) { RETURN_THROWS(); } @@ -1292,12 +1311,6 @@ PHP_METHOD(DOM_HTMLDocument, saveHTML) 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); diff --git a/ext/dom/internal_helpers.h b/ext/dom/internal_helpers.h new file mode 100644 index 0000000000000..0f67550898ab4 --- /dev/null +++ b/ext/dom/internal_helpers.h @@ -0,0 +1,66 @@ +/* + +----------------------------------------------------------------------+ + | 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 DOM_INTERNAL_HELPERS +#define DOM_INTERNAL_HELPERS + +/* We're using the type flags of the zval to store an extra flag. */ +#define DOM_Z_OWNED(z, v) ZVAL_PTR(z, (void *) v) +#define DOM_Z_UNOWNED(z, v) ZVAL_INDIRECT(z, (void *) v) +#define DOM_Z_IS_OWNED(z) (Z_TYPE_P(z) == IS_PTR) + +#ifdef DOM_CE_H + +#define DOM_DEF_GET_CE_FUNC(name) \ +static zend_always_inline zend_class_entry *dom_get_##name##_ce(bool modern) \ +{ \ + return modern ? dom_modern_##name##_class_entry : dom_##name##_class_entry; \ +} + +static zend_always_inline zend_class_entry *dom_get_html_document_ce(bool modern) +{ + return modern ? dom_html_document_class_entry : dom_document_class_entry; +} + +static zend_always_inline zend_class_entry *dom_get_xml_document_ce(bool modern) +{ + return modern ? dom_xml_document_class_entry : dom_document_class_entry; +} + +static zend_always_inline zend_class_entry *dom_get_dtd_namednodemap_ce(bool modern) +{ + return modern ? dom_modern_dtd_namednodemap_class_entry : dom_namednodemap_class_entry; +} + +DOM_DEF_GET_CE_FUNC(node) +DOM_DEF_GET_CE_FUNC(documenttype) +DOM_DEF_GET_CE_FUNC(element) +DOM_DEF_GET_CE_FUNC(attr) +DOM_DEF_GET_CE_FUNC(entity) +DOM_DEF_GET_CE_FUNC(entityreference) +DOM_DEF_GET_CE_FUNC(processinginstruction) +DOM_DEF_GET_CE_FUNC(comment) +DOM_DEF_GET_CE_FUNC(text) +DOM_DEF_GET_CE_FUNC(cdatasection) +DOM_DEF_GET_CE_FUNC(notation) +DOM_DEF_GET_CE_FUNC(documentfragment) +DOM_DEF_GET_CE_FUNC(namednodemap) +DOM_DEF_GET_CE_FUNC(nodelist) +DOM_DEF_GET_CE_FUNC(domimplementation) + +#endif + +#endif diff --git a/ext/dom/namednodemap.c b/ext/dom/namednodemap.c index 32d10a1bc0dee..02966036b93bf 100644 --- a/ext/dom/namednodemap.c +++ b/ext/dom/namednodemap.c @@ -71,7 +71,7 @@ zend_result dom_namednodemap_length_read(dom_object *obj, zval *retval) /* }}} */ -xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const char *named, bool may_transform) +xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const zend_string *named, bool may_transform) { xmlNodePtr itemnode = NULL; if (objmap != NULL) { @@ -79,9 +79,9 @@ xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, co objmap->nodetype == XML_ENTITY_NODE) { if (objmap->ht) { if (objmap->nodetype == XML_ENTITY_NODE) { - itemnode = (xmlNodePtr)xmlHashLookup(objmap->ht, (const xmlChar *) named); + itemnode = (xmlNodePtr)xmlHashLookup(objmap->ht, BAD_CAST ZSTR_VAL(named)); } else { - xmlNotationPtr notep = xmlHashLookup(objmap->ht, (const xmlChar *) named); + xmlNotationPtr notep = xmlHashLookup(objmap->ht, BAD_CAST ZSTR_VAL(named)); if (notep) { if (may_transform) { itemnode = create_notation(notep->name, notep->PublicID, notep->SystemID); @@ -94,14 +94,18 @@ xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, co } else { xmlNodePtr nodep = dom_object_get_node(objmap->baseobj); if (nodep) { - itemnode = (xmlNodePtr)xmlHasProp(nodep, (const xmlChar *) named); + if (php_dom_follow_spec_intern(objmap->baseobj)) { + itemnode = (xmlNodePtr) php_dom_get_attribute_node(nodep, BAD_CAST ZSTR_VAL(named), ZSTR_LEN(named)); + } else { + itemnode = (xmlNodePtr) xmlHasProp(nodep, BAD_CAST ZSTR_VAL(named)); + } } } } return itemnode; } -void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const char *named, zval *return_value) +void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, zval *return_value) { int ret; xmlNodePtr itemnode = php_dom_named_node_map_get_named_item(objmap, named, true); @@ -117,10 +121,9 @@ void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap */ PHP_METHOD(DOMNamedNodeMap, getNamedItem) { - size_t namedlen; - char *named; + zend_string *named; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &named, &namedlen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &named) == FAILURE) { RETURN_THROWS(); } @@ -221,9 +224,9 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS) objmap->nodetype == XML_ENTITY_NODE) { if (objmap->ht) { if (objmap->nodetype == XML_ENTITY_NODE) { - itemnode = (xmlNodePtr)xmlHashLookup(objmap->ht, (xmlChar *) named); + itemnode = (xmlNodePtr)xmlHashLookup(objmap->ht, BAD_CAST named); } else { - notep = (xmlNotation *)xmlHashLookup(objmap->ht, (xmlChar *) named); + notep = (xmlNotation *)xmlHashLookup(objmap->ht, BAD_CAST named); if (notep) { itemnode = create_notation(notep->name, notep->PublicID, notep->SystemID); } @@ -232,7 +235,7 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS) } else { nodep = dom_object_get_node(objmap->baseobj); if (nodep) { - itemnode = (xmlNodePtr)xmlHasNsProp(nodep, (xmlChar *) named, (xmlChar *) uri); + itemnode = (xmlNodePtr)xmlHasNsProp(nodep, BAD_CAST named, BAD_CAST uri); } } } diff --git a/ext/dom/namespace_compat.c b/ext/dom/namespace_compat.c index efd51cd6545d6..29378b583372e 100644 --- a/ext/dom/namespace_compat.c +++ b/ext/dom/namespace_compat.c @@ -22,33 +22,515 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" #include "namespace_compat.h" +#include "internal_helpers.h" -bool dom_ns_is_also_an_attribute(const xmlNs *ns) { - return ns->_private != NULL; +PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_html_magic_token = (const php_dom_ns_magic_token *) DOM_XHTML_NS_URI; +PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_mathml_magic_token = (const php_dom_ns_magic_token *) DOM_MATHML_NS_URI; +PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_svg_magic_token = (const php_dom_ns_magic_token *) DOM_SVG_NS_URI; +PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_xlink_magic_token = (const php_dom_ns_magic_token *) DOM_XLINK_NS_URI; +PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_xml_magic_token = (const php_dom_ns_magic_token *) DOM_XML_NS_URI; +PHP_DOM_EXPORT const php_dom_ns_magic_token *php_dom_ns_is_xmlns_magic_token = (const php_dom_ns_magic_token *) DOM_XMLNS_NS_URI; + +struct _php_dom_libxml_ns_mapper { + php_libxml_private_data_header header; + /* This is used almost all the time for HTML documents, so it makes sense to cache this. */ + xmlNsPtr html_ns; + /* Used for every prefixless namespace declaration in XML, so also very common. */ + xmlNsPtr prefixless_xmlns_ns; + HashTable uri_to_prefix_map; +}; + +static void php_dom_libxml_ns_mapper_prefix_map_element_dtor(zval *zv) +{ + if (DOM_Z_IS_OWNED(zv)) { + efree(Z_PTR_P(zv)); + } +} + +static HashTable *php_dom_libxml_ns_mapper_ensure_prefix_map(php_dom_libxml_ns_mapper *mapper, zend_string **uri) +{ + zval *zv = zend_hash_find(&mapper->uri_to_prefix_map, *uri); + HashTable *prefix_map; + if (zv == NULL) { + prefix_map = emalloc(sizeof(HashTable)); + zend_hash_init(prefix_map, 0, NULL, php_dom_libxml_ns_mapper_prefix_map_element_dtor, false); + zval zv; + ZVAL_ARR(&zv, prefix_map); + zend_hash_add_new(&mapper->uri_to_prefix_map, *uri, &zv); + } else { + /* cast to Bucket* only works if this holds, I would prefer a static assert but we're stuck at C99. */ + ZEND_ASSERT(XtOffsetOf(Bucket, val) == 0); + ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); + Bucket *bucket = (Bucket *) zv; + /* Make sure we take the value from the key string that lives long enough. */ + *uri = bucket->key; + prefix_map = Z_ARRVAL_P(zv); + } + return prefix_map; +} + +static void php_dom_libxml_ns_mapper_header_destroy(php_libxml_private_data_header *header) +{ + php_dom_libxml_ns_mapper_destroy((php_dom_libxml_ns_mapper *) header); +} + +PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_libxml_ns_mapper_create(void) +{ + php_dom_libxml_ns_mapper *mapper = emalloc(sizeof(*mapper)); + mapper->header.dtor = php_dom_libxml_ns_mapper_header_destroy; + mapper->html_ns = NULL; + mapper->prefixless_xmlns_ns = NULL; + zend_hash_init(&mapper->uri_to_prefix_map, 0, NULL, ZVAL_PTR_DTOR, false); + return mapper; +} + +void php_dom_libxml_ns_mapper_destroy(php_dom_libxml_ns_mapper *mapper) +{ + zend_hash_destroy(&mapper->uri_to_prefix_map); + efree(mapper); +} + +static xmlNsPtr php_dom_libxml_ns_mapper_ensure_cached_ns(php_dom_libxml_ns_mapper *mapper, xmlNsPtr *ptr, const char *uri, size_t length, const php_dom_ns_magic_token *token) +{ + if (EXPECTED(*ptr != NULL)) { + return *ptr; + } + + zend_string *uri_str = zend_string_init(uri, length, false); + *ptr = php_dom_libxml_ns_mapper_get_ns(mapper, NULL, uri_str); + (*ptr)->_private = (void *) token; + zend_string_release_ex(uri_str, false); + return *ptr; } -void dom_ns_compat_mark_attribute(xmlNsPtr ns) { - ns->_private = (void *) 1; +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_ensure_html_ns(php_dom_libxml_ns_mapper *mapper) +{ + return php_dom_libxml_ns_mapper_ensure_cached_ns(mapper, &mapper->html_ns, DOM_XHTML_NS_URI, sizeof(DOM_XHTML_NS_URI) - 1, php_dom_ns_is_html_magic_token); } -void dom_ns_compat_mark_attribute_list(xmlNsPtr ns) { - while (ns != NULL) { - dom_ns_compat_mark_attribute(ns); - ns = ns->next; - } +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_ensure_prefixless_xmlns_ns(php_dom_libxml_ns_mapper *mapper) +{ + return php_dom_libxml_ns_mapper_ensure_cached_ns(mapper, &mapper->prefixless_xmlns_ns, DOM_XMLNS_NS_URI, sizeof(DOM_XMLNS_NS_URI) - 1, php_dom_ns_is_xmlns_magic_token); +} + +static xmlNsPtr dom_create_owned_ns(zend_string *prefix, zend_string *uri) +{ + ZEND_ASSERT(prefix != NULL); + ZEND_ASSERT(uri != NULL); + + xmlNsPtr ns = emalloc(sizeof(*ns)); + memset(ns, 0, sizeof(*ns)); + ns->type = XML_LOCAL_NAMESPACE; + /* These two strings are kept alive because they're the hash table keys that lead to this entry. */ + ns->prefix = ZSTR_LEN(prefix) == 0 ? NULL : BAD_CAST ZSTR_VAL(prefix); + ns->href = BAD_CAST ZSTR_VAL(uri); + /* Note ns->context is unused in libxml2 at the moment, and if it were used it would be for + * LIBXML_NAMESPACE_DICT which is opt-in anyway. */ + + return ns; +} + +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns(php_dom_libxml_ns_mapper *mapper, zend_string *prefix, zend_string *uri) +{ + if (uri == NULL) { + uri = zend_empty_string; + } + + if (prefix == NULL) { + prefix = zend_empty_string; + } + + if (ZSTR_LEN(prefix) == 0 && ZSTR_LEN(uri) == 0) { + return NULL; + } + + HashTable *prefix_map = php_dom_libxml_ns_mapper_ensure_prefix_map(mapper, &uri); + xmlNsPtr found = zend_hash_find_ptr(prefix_map, prefix); + if (found != NULL) { + return found; + } + + xmlNsPtr ns = dom_create_owned_ns(prefix, uri); + + zval new_zv; + DOM_Z_OWNED(&new_zv, ns); + zend_hash_add_new(prefix_map, prefix, &new_zv); + + return ns; +} + +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(php_dom_libxml_ns_mapper *mapper, const xmlChar *prefix, size_t prefix_len, zend_string *uri) +{ + xmlNsPtr ns; + if (prefix_len == 0) { + /* Fast path */ + ns = php_dom_libxml_ns_mapper_get_ns(mapper, zend_empty_string, uri); + } else { + zend_string *prefix_str = zend_string_init((const char *) prefix, prefix_len, false); + ns = php_dom_libxml_ns_mapper_get_ns(mapper, prefix_str, uri); + zend_string_release_ex(prefix_str, false); + } + return ns; +} + +static xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_strings_ex(php_dom_libxml_ns_mapper *mapper, const char *prefix, size_t prefix_len, const char *uri, size_t uri_len) +{ + zend_string *prefix_str = zend_string_init(prefix, prefix_len, false); + zend_string *uri_str = zend_string_init(uri, uri_len, false); + xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns(mapper, prefix_str, uri_str); + zend_string_release_ex(prefix_str, false); + zend_string_release_ex(uri_str, false); + return ns; +} + +static zend_always_inline xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_strings(php_dom_libxml_ns_mapper *mapper, const char *prefix, const char *uri) +{ + return php_dom_libxml_ns_mapper_get_ns_raw_strings_ex(mapper, prefix, strlen(prefix), uri, strlen(uri)); +} + +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(php_dom_libxml_ns_mapper *mapper, const char *prefix, const char *uri) +{ + if (prefix == NULL) { + prefix = ""; + } + if (uri == NULL) { + uri = ""; + } + return php_dom_libxml_ns_mapper_get_ns_raw_strings(mapper, prefix, uri); +} + +static xmlNsPtr php_dom_libxml_ns_mapper_store_and_normalize_parsed_ns(php_dom_libxml_ns_mapper *mapper, xmlNsPtr ns) +{ + ZEND_ASSERT(ns != NULL); + + zend_string *href_str = zend_string_init((const char *) ns->href, xmlStrlen(ns->href), false); + zend_string *href_str_orig = href_str; + HashTable *prefix_map = php_dom_libxml_ns_mapper_ensure_prefix_map(mapper, &href_str); + zend_string_release_ex(href_str_orig, false); + + const char *prefix = (const char *) ns->prefix; + size_t prefix_len; + if (prefix == NULL) { + prefix = ""; + prefix_len = 0; + } else { + prefix_len = xmlStrlen(ns->prefix); + } + + zval *zv = zend_hash_str_find_ptr(prefix_map, prefix, prefix_len); + if (zv != NULL) { + return Z_PTR_P(zv); + } + + zval new_zv; + DOM_Z_UNOWNED(&new_zv, ns); + zend_hash_str_add_new(prefix_map, prefix, prefix_len, &new_zv); + + return ns; +} + +PHP_DOM_EXPORT php_libxml_private_data_header *php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper *mapper) +{ + return mapper == NULL ? NULL : &mapper->header; +} + +typedef struct { + /* Fast lookup for created mappings. */ + HashTable old_ns_to_new_ns_ptr; + /* It is common that the last created mapping will be used for a while, + * cache it too to bypass the hash table. */ + xmlNsPtr last_mapped_src, last_mapped_dst; + php_dom_libxml_ns_mapper *ns_mapper; +} dom_libxml_reconcile_ctx; + +PHP_DOM_EXPORT xmlAttrPtr php_dom_ns_compat_mark_attribute(php_dom_libxml_ns_mapper *mapper, xmlNodePtr node, xmlNsPtr ns) +{ + xmlNsPtr xmlns_ns; + const xmlChar *name; + if (ns->prefix != NULL) { + xmlns_ns = php_dom_libxml_ns_mapper_get_ns_raw_strings(mapper, "xmlns", DOM_XMLNS_NS_URI); + name = ns->prefix; + } else { + xmlns_ns = php_dom_libxml_ns_mapper_ensure_prefixless_xmlns_ns(mapper); + name = BAD_CAST "xmlns"; + } + + ZEND_ASSERT(xmlns_ns != NULL); + + return xmlSetNsProp(node, xmlns_ns, name, ns->href); +} + +PHP_DOM_EXPORT void php_dom_ns_compat_mark_attribute_list(php_dom_libxml_ns_mapper *mapper, xmlNodePtr node) +{ + if (node->nsDef == NULL) { + return; + } + + /* We want to prepend at the front, but in order of the namespace definitions. + * So temporarily unlink the existing properties and add them again at the end. */ + xmlAttrPtr attr = node->properties; + node->properties = NULL; + + xmlNsPtr ns = node->nsDef; + xmlAttrPtr last_added = NULL; + do { + last_added = php_dom_ns_compat_mark_attribute(mapper, node, ns); + php_dom_libxml_ns_mapper_store_and_normalize_parsed_ns(mapper, ns); + xmlNsPtr next = ns->next; + ns->next = NULL; + php_libxml_set_old_ns(node->doc, ns); + ns = next; + } while (ns != NULL); + + if (last_added != NULL) { + /* node->properties now points to the first namespace declaration attribute. */ + if (attr != NULL) { + last_added->next = attr; + attr->prev = last_added; + } + } else { + /* Nothing added, so nothing changed. Only really possible on OOM. */ + node->properties = attr; + } + + node->nsDef = NULL; +} + +PHP_DOM_EXPORT bool php_dom_ns_is_fast_ex(xmlNsPtr ns, const php_dom_ns_magic_token *magic_token) +{ + ZEND_ASSERT(ns != NULL); + /* cached for fast checking */ + if (ns->_private == magic_token) { + return true; + } else if (ns->_private != NULL) { + /* Other token stored */ + return false; + } + /* Slow path */ + if (xmlStrEqual(ns->href, BAD_CAST magic_token)) { + ns->_private = (void *) magic_token; + return true; + } + return false; +} + +PHP_DOM_EXPORT bool php_dom_ns_is_fast(const xmlNode *nodep, const php_dom_ns_magic_token *magic_token) +{ + ZEND_ASSERT(nodep != NULL); + xmlNsPtr ns = nodep->ns; + if (ns != NULL) { + return php_dom_ns_is_fast_ex(ns, magic_token); + } + return false; +} + +PHP_DOM_EXPORT bool php_dom_ns_is_html_and_document_is_html(const xmlNode *nodep) +{ + ZEND_ASSERT(nodep != NULL); + return nodep->doc && nodep->doc->type == XML_HTML_DOCUMENT_NODE && php_dom_ns_is_fast(nodep, php_dom_ns_is_html_magic_token); +} + +/* will rename prefixes if there is a declaration with the same prefix but different uri. */ +PHP_DOM_EXPORT void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp) +{ + ZEND_ASSERT(attrp != NULL); + + if (attrp->ns != NULL) { + /* Try to link to an existing namespace. If that won't work, reconcile. */ + xmlNodePtr nodep = attrp->parent; + xmlNsPtr matching_ns = xmlSearchNs(nodep->doc, nodep, attrp->ns->prefix); + if (matching_ns && xmlStrEqual(matching_ns->href, attrp->ns->href)) { + /* Doesn't leak because this doesn't define the declaration. */ + attrp->ns = matching_ns; + } else { + if (attrp->ns->prefix != NULL) { + /* Note: explicitly use the legacy reconciliation as it mostly (i.e. as good as it gets for legacy DOM) + * does the right thing for attributes. */ + xmlReconciliateNs(nodep->doc, nodep); + } + } + } +} + +static zend_always_inline zend_long dom_mangle_pointer_for_key(void *ptr) +{ + zend_ulong value = (zend_ulong) (uintptr_t) ptr; + /* Shift 3/4 for better hash distribution because the low 3/4 bits are always 0. */ +#if SIZEOF_ZEND_LONG == 8 + return value >> 4; +#else + return value >> 3; +#endif +} + +static zend_always_inline void php_dom_libxml_reconcile_modern_single_node(dom_libxml_reconcile_ctx *ctx, xmlNodePtr ns_holder, xmlNodePtr node) +{ + ZEND_ASSERT(node->ns != NULL); + + if (node->ns == ctx->last_mapped_src) { + node->ns = ctx->last_mapped_dst; + return; + } + + /* If the namespace is the same as in the map, we're good. */ + xmlNsPtr new_ns = zend_hash_index_find_ptr(&ctx->old_ns_to_new_ns_ptr, dom_mangle_pointer_for_key(node->ns)); + if (new_ns == NULL) { + /* We have to create an alternative declaration, and we'll add it to the map. */ + const char *prefix = (const char *) node->ns->prefix; + const char *href = (const char *) node->ns->href; + new_ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ctx->ns_mapper, prefix, href); + zend_hash_index_add_new_ptr(&ctx->old_ns_to_new_ns_ptr, dom_mangle_pointer_for_key(node->ns), new_ns); + ctx->last_mapped_src = node->ns; + ctx->last_mapped_dst = new_ns; + node->ns = new_ns; + } else if (node->ns != new_ns) { + /* The namespace is different, so we have to replace it. */ + node->ns = new_ns; + } +} + +static zend_always_inline bool dom_libxml_reconcile_fast_element_skip(xmlNodePtr node) +{ + /* Fast path: this is a lone element and the namespace is defined by the node (or the namespace is NULL). */ + ZEND_ASSERT(node->type == XML_ELEMENT_NODE); + return node->children == NULL && node->properties == NULL && node->ns == node->nsDef; +} + +static zend_always_inline void php_dom_libxml_reconcile_modern_single_element_node(dom_libxml_reconcile_ctx *ctx, xmlNodePtr node) +{ + ZEND_ASSERT(node->type == XML_ELEMENT_NODE); + + /* Since this is modern DOM, the declarations are not on the node and thus there's nothing to add from nsDef. */ + ZEND_ASSERT(node->nsDef == NULL); + + if (node->ns != NULL) { + php_dom_libxml_reconcile_modern_single_node(ctx, node, node); + } + + for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) { + if (attr->ns != NULL) { + php_dom_libxml_reconcile_modern_single_node(ctx, node, (xmlNodePtr) attr); + } + } +} + +PHP_DOM_EXPORT void php_dom_libxml_reconcile_modern(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node) +{ + if (node->type == XML_ATTRIBUTE_NODE) { + if (node->ns != NULL) { + node->ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ns_mapper, (const char *) node->ns->prefix, (const char *) node->ns->href); + } + return; + } + + if (node->type != XML_ELEMENT_NODE || dom_libxml_reconcile_fast_element_skip(node)) { + return; + } + + dom_libxml_reconcile_ctx ctx; + zend_hash_init(&ctx.old_ns_to_new_ns_ptr, 0, NULL, NULL, 0); + ctx.last_mapped_src = NULL; + ctx.last_mapped_dst = NULL; + ctx.ns_mapper = ns_mapper; + + php_dom_libxml_reconcile_modern_single_element_node(&ctx, node); + + xmlNodePtr base = node; + node = node->children; + while (node != NULL) { + ZEND_ASSERT(node != base); + + if (node->type == XML_ELEMENT_NODE) { + php_dom_libxml_reconcile_modern_single_element_node(&ctx, node); + + if (node->children) { + node = node->children; + continue; + } + } + + node = php_dom_next_in_tree_order(node, base); + } + + zend_hash_destroy(&ctx.old_ns_to_new_ns_ptr); +} + +PHP_DOM_EXPORT php_dom_in_scope_ns php_dom_get_in_scope_ns(php_dom_libxml_ns_mapper *ns_mapper, const xmlNode *node) +{ + ZEND_ASSERT(node != NULL); + + php_dom_in_scope_ns in_scope_ns; + in_scope_ns.origin_is_ns_compat = true; + + /* libxml fetches all nsDef items from bottom to top - left to right, ignoring prefixes already in the list. + * We don't have nsDef, but we can use the ns pointer (as that is necessarily in scope), + * and check the xmlns attributes. */ + HashTable tmp_prefix_to_ns_table; + zend_hash_init(&tmp_prefix_to_ns_table, 0, NULL, NULL, false); + zend_hash_real_init_mixed(&tmp_prefix_to_ns_table); + + for (const xmlNode *cur = node; cur != NULL; cur = cur->parent) { + if (cur->type == XML_ELEMENT_NODE) { + /* Register namespace of element */ + if (cur->ns != NULL && cur->ns->prefix != NULL) { + const char *prefix = (const char *) cur->ns->prefix; + zend_hash_str_add_ptr(&tmp_prefix_to_ns_table, prefix, strlen(prefix), cur->ns); + } + + /* Register xmlns attributes */ + for (const xmlAttr *attr = cur->properties; attr != NULL; attr = attr->next) { + if (attr->ns != NULL && attr->ns->prefix != NULL && php_dom_ns_is_fast_ex(attr->ns, php_dom_ns_is_xmlns_magic_token) + && attr->children != NULL && attr->children->content != NULL) { + /* This attribute declares a namespace, get the relevant instance. + * The declared namespace is not the same as the namespace of this attribute (which is xmlns). */ + const char *prefix = (const char *) attr->name; + xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_strings(ns_mapper, prefix, (const char *) attr->children->content); + zend_hash_str_add_ptr(&tmp_prefix_to_ns_table, prefix, strlen(prefix), ns); + } + } + } + } + + in_scope_ns.count = zend_hash_num_elements(&tmp_prefix_to_ns_table); + in_scope_ns.list = safe_emalloc(in_scope_ns.count, sizeof(xmlNsPtr), 0); + + size_t index = 0; + xmlNsPtr ns; + ZEND_HASH_MAP_FOREACH_PTR(&tmp_prefix_to_ns_table, ns) { + in_scope_ns.list[index++] = ns; + } ZEND_HASH_FOREACH_END(); + + zend_hash_destroy(&tmp_prefix_to_ns_table); + + return in_scope_ns; +} + +PHP_DOM_EXPORT php_dom_in_scope_ns php_dom_get_in_scope_ns_legacy(const xmlNode *node) +{ + ZEND_ASSERT(node != NULL); + + php_dom_in_scope_ns in_scope_ns; + in_scope_ns.origin_is_ns_compat = false; + in_scope_ns.list = xmlGetNsList(node->doc, node); + in_scope_ns.count = 0; + + if (in_scope_ns.list != NULL) { + while (in_scope_ns.list[in_scope_ns.count] != NULL) { + in_scope_ns.count++; + } + } + + return in_scope_ns; } -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; - } +PHP_DOM_EXPORT void php_dom_in_scope_ns_destroy(php_dom_in_scope_ns *in_scope_ns) +{ + ZEND_ASSERT(in_scope_ns != NULL); + if (in_scope_ns->origin_is_ns_compat) { + efree(in_scope_ns->list); + } else { + xmlFree(in_scope_ns->list); + } } #endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/ext/dom/namespace_compat.h b/ext/dom/namespace_compat.h index ab514a08bbada..1147c62892292 100644 --- a/ext/dom/namespace_compat.h +++ b/ext/dom/namespace_compat.h @@ -17,7 +17,7 @@ #ifndef NAMESPACE_COMPAT_H #define NAMESPACE_COMPAT_H -#include +#include "xml_common.h" /* https://infra.spec.whatwg.org/#namespaces */ #define DOM_XHTML_NS_URI "http://www.w3.org/1999/xhtml" @@ -27,13 +27,51 @@ #define DOM_XML_NS_URI "http://www.w3.org/XML/1998/namespace" #define DOM_XMLNS_NS_URI "http://www.w3.org/2000/xmlns/" +struct php_dom_ns_magic_token; +typedef struct php_dom_ns_magic_token php_dom_ns_magic_token; + +struct _php_dom_libxml_ns_mapper; +typedef struct _php_dom_libxml_ns_mapper php_dom_libxml_ns_mapper; + +PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_html_magic_token; +PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_mathml_magic_token; +PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_svg_magic_token; +PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_xlink_magic_token; +PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_xml_magic_token; +PHP_DOM_EXPORT extern const php_dom_ns_magic_token *php_dom_ns_is_xmlns_magic_token; + +typedef struct _php_libxml_private_data_header php_libxml_private_data_header; +struct _php_libxml_private_data_header; + /* These functions make it possible to make a namespace declaration also visible as an attribute by - * setting a flag that can be checked with dom_ns_is_also_an_attribute(). - * This is used in the serializer for example. */ + * creating an equivalent attribute node. */ + +PHP_DOM_EXPORT php_dom_libxml_ns_mapper *php_dom_libxml_ns_mapper_create(void); +PHP_DOM_EXPORT void php_dom_libxml_ns_mapper_destroy(php_dom_libxml_ns_mapper *mapper); +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_ensure_html_ns(php_dom_libxml_ns_mapper *mapper); +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_ensure_prefixless_xmlns_ns(php_dom_libxml_ns_mapper *mapper); +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns(php_dom_libxml_ns_mapper *mapper, zend_string *prefix, zend_string *uri); +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(php_dom_libxml_ns_mapper *mapper, const xmlChar *prefix, size_t prefix_len, zend_string *uri); +PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(php_dom_libxml_ns_mapper *mapper, const char *prefix, const char *uri); + +PHP_DOM_EXPORT php_libxml_private_data_header *php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper *mapper); +PHP_DOM_EXPORT void php_dom_ns_compat_mark_attribute_list(php_dom_libxml_ns_mapper *mapper, xmlNodePtr node); +PHP_DOM_EXPORT void php_dom_libxml_reconcile_modern(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node); +PHP_DOM_EXPORT void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp); +PHP_DOM_EXPORT xmlAttrPtr php_dom_ns_compat_mark_attribute(php_dom_libxml_ns_mapper *mapper, xmlNodePtr node, xmlNsPtr ns); + +PHP_DOM_EXPORT bool php_dom_ns_is_fast(const xmlNode *nodep, const php_dom_ns_magic_token *magic_token); +PHP_DOM_EXPORT bool php_dom_ns_is_fast_ex(xmlNsPtr ns, const php_dom_ns_magic_token *magic_token); +PHP_DOM_EXPORT bool php_dom_ns_is_html_and_document_is_html(const xmlNode *nodep); + +typedef struct _php_dom_in_scope_ns { + xmlNsPtr *list; + size_t count; + bool origin_is_ns_compat; +} php_dom_in_scope_ns; -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); +PHP_DOM_EXPORT php_dom_in_scope_ns php_dom_get_in_scope_ns(php_dom_libxml_ns_mapper *ns_mapper, const xmlNode *node); +PHP_DOM_EXPORT php_dom_in_scope_ns php_dom_get_in_scope_ns_legacy(const xmlNode *node); +PHP_DOM_EXPORT void php_dom_in_scope_ns_destroy(php_dom_in_scope_ns *in_scope_ns); #endif diff --git a/ext/dom/node.c b/ext/dom/node.c index dfa5b825983c0..b51376ad24cf8 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -22,6 +22,8 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "namespace_compat.h" +#include "internal_helpers.h" /* * class DOMNode @@ -42,14 +44,19 @@ zend_string *dom_node_concatenated_name_helper(size_t name_len, const char *name return str; } -zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep) +zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep, bool uppercase) { + zend_string *ret; size_t name_len = strlen((const char *) nodep->name); if (nodep->ns != NULL && nodep->ns->prefix != NULL) { - return dom_node_concatenated_name_helper(name_len, (const char *) nodep->name, strlen((const char *) nodep->ns->prefix), (const char *) nodep->ns->prefix); + ret = dom_node_concatenated_name_helper(name_len, (const char *) nodep->name, strlen((const char *) nodep->ns->prefix), (const char *) nodep->ns->prefix); } else { - return zend_string_init((const char *) nodep->name, name_len, false); + ret = zend_string_init((const char *) nodep->name, name_len, false); } + if (uppercase) { + zend_str_toupper(ZSTR_VAL(ret), ZSTR_LEN(ret)); + } + return ret; } bool php_dom_is_node_connected(const xmlNode *node) @@ -67,6 +74,7 @@ bool php_dom_is_node_connected(const xmlNode *node) /* {{{ nodeName string readonly=yes URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68D095 +Modern spec URL: https://dom.spec.whatwg.org/#dom-node-nodename Since: */ zend_result dom_node_node_name_read(dom_object *obj, zval *retval) @@ -78,19 +86,20 @@ zend_result dom_node_node_name_read(dom_object *obj, zval *retval) return FAILURE; } + bool uppercase = false; + switch (nodep->type) { - case XML_ATTRIBUTE_NODE: case XML_ELEMENT_NODE: - ZVAL_STR(retval, dom_node_get_node_name_attribute_or_element(nodep)); + uppercase = php_dom_follow_spec_intern(obj) && php_dom_ns_is_html_and_document_is_html(nodep); + ZEND_FALLTHROUGH; + case XML_ATTRIBUTE_NODE: + ZVAL_NEW_STR(retval, dom_node_get_node_name_attribute_or_element(nodep, uppercase)); break; case XML_NAMESPACE_DECL: { xmlNsPtr ns = nodep->ns; if (ns != NULL && ns->prefix) { - xmlChar *qname = xmlStrdup((xmlChar *) "xmlns"); - qname = xmlStrcat(qname, (xmlChar *) ":"); - qname = xmlStrcat(qname, nodep->name); - ZVAL_STRING(retval, (const char *) qname); - xmlFree(qname); + zend_string *str = dom_node_concatenated_name_helper(strlen((const char *) ns->prefix), (const char *) ns->prefix, strlen("xmlns"), "xmlns"); + ZVAL_NEW_STR(retval, str); } else { ZVAL_STRING(retval, (const char *) nodep->name); } @@ -131,6 +140,7 @@ zend_result dom_node_node_name_read(dom_object *obj, zval *retval) /* {{{ nodeValue string readonly=no URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68D080 +Modern spec URL: https://dom.spec.whatwg.org/#dom-node-nodevalue Since: */ zend_result dom_node_node_value_read(dom_object *obj, zval *retval) @@ -144,9 +154,15 @@ zend_result dom_node_node_value_read(dom_object *obj, zval *retval) /* Access to Element node is implemented as a convenience method */ switch (nodep->type) { + case XML_ELEMENT_NODE: { + if (php_dom_follow_spec_intern(obj)) { + ZVAL_NULL(retval); + break; + } + ZEND_FALLTHROUGH; + } case XML_ATTRIBUTE_NODE: case XML_TEXT_NODE: - case XML_ELEMENT_NODE: case XML_COMMENT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: @@ -188,6 +204,12 @@ zend_result dom_node_node_value_write(dom_object *obj, zval *newval) /* Access to Element node is implemented as a convenience method */ switch (nodep->type) { case XML_ATTRIBUTE_NODE: + if (php_dom_follow_spec_intern(obj)) { + dom_remove_all_children(nodep); + xmlAddChild(nodep, xmlNewTextLen((xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str))); + break; + } + ZEND_FALLTHROUGH; case XML_ELEMENT_NODE: dom_remove_all_children(nodep); ZEND_FALLTHROUGH; @@ -295,7 +317,7 @@ zend_result dom_node_child_nodes_read(dom_object *obj, zval *retval) return FAILURE; } - php_dom_create_iterator(retval, DOM_NODELIST); + php_dom_create_iterator(retval, DOM_NODELIST, php_dom_follow_spec_intern(obj)); intern = Z_DOMOBJ_P(retval); dom_namednode_iter(obj, XML_ELEMENT_NODE, intern, NULL, NULL, 0, NULL, 0); @@ -319,7 +341,7 @@ zend_result dom_node_first_child_read(dom_object *obj, zval *retval) return FAILURE; } - if (dom_node_children_valid(nodep) == SUCCESS) { + if (dom_node_children_valid(nodep)) { first = nodep->children; } @@ -350,7 +372,7 @@ zend_result dom_node_last_child_read(dom_object *obj, zval *retval) return FAILURE; } - if (dom_node_children_valid(nodep) == SUCCESS) { + if (dom_node_children_valid(nodep)) { last = nodep->last; } @@ -503,7 +525,7 @@ zend_result dom_node_attributes_read(dom_object *obj, zval *retval) } if (nodep->type == XML_ELEMENT_NODE) { - php_dom_create_iterator(retval, DOM_NAMEDNODEMAP); + php_dom_create_iterator(retval, DOM_NAMEDNODEMAP, php_dom_follow_spec_intern(obj)); intern = Z_DOMOBJ_P(retval); dom_namednode_iter(obj, XML_ATTRIBUTE_NODE, intern, NULL, NULL, 0, NULL, 0); } else { @@ -607,6 +629,7 @@ zend_result dom_node_namespace_uri_read(dom_object *obj, zval *retval) /* {{{ prefix string readonly=no URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-NodeNSPrefix +Modern spec URL: https://dom.spec.whatwg.org/#concept-element-namespace-prefix Since: DOM Level 2 */ zend_result dom_node_prefix_read(dom_object *obj, zval *retval) @@ -640,7 +663,24 @@ zend_result dom_node_prefix_read(dom_object *obj, zval *retval) ZVAL_STRING(retval, str); } return SUCCESS; +} +zend_result dom_modern_node_prefix_read(dom_object *obj, zval *retval) +{ + xmlNode *nodep = dom_object_get_node(obj); + + if (nodep == NULL) { + php_dom_throw_error(INVALID_STATE_ERR, true); + return FAILURE; + } + + xmlNsPtr ns = nodep->ns; + if (ns != NULL && ns->prefix != NULL) { + ZVAL_STRING(retval, (const char *) ns->prefix); + } else { + ZVAL_NULL(retval); + } + return SUCCESS; } zend_result dom_node_prefix_write(dom_object *obj, zval *newval) @@ -763,10 +803,18 @@ zend_result dom_node_base_uri_read(dom_object *obj, zval *retval) baseuri = xmlNodeGetBase(nodep->doc, nodep); if (baseuri) { - ZVAL_STRING(retval, (char *) (baseuri)); + ZVAL_STRING(retval, (const char *) baseuri); xmlFree(baseuri); } else { - ZVAL_NULL(retval); + if (php_dom_follow_spec_intern(obj)) { + if (nodep->doc->URL) { + ZVAL_STRING(retval, (const char *) nodep->doc->URL); + } else { + ZVAL_STRING(retval, "about:blank"); + } + } else { + ZVAL_NULL(retval); + } } return SUCCESS; @@ -777,8 +825,23 @@ zend_result dom_node_base_uri_read(dom_object *obj, zval *retval) /* {{{ textContent string readonly=no URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-textContent +Modern spec URL: https://dom.spec.whatwg.org/#dom-node-textcontent Since: DOM Level 3 */ +/* Determines when the operation is a no-op. */ +static bool dom_skip_text_content(dom_object *obj, xmlNodePtr nodep) +{ + if (php_dom_follow_spec_intern(obj)) { + int type = nodep->type; + if (type != XML_DOCUMENT_FRAG_NODE && type != XML_ELEMENT_NODE && type != XML_ATTRIBUTE_NODE + && type != XML_TEXT_NODE && type != XML_CDATA_SECTION_NODE && type != XML_COMMENT_NODE && type != XML_PI_NODE) { + /* Yes, success... It's a no-op for these cases. */ + return true; + } + } + return false; +} + zend_result dom_node_text_content_read(dom_object *obj, zval *retval) { xmlNode *nodep = dom_object_get_node(obj); @@ -788,7 +851,11 @@ zend_result dom_node_text_content_read(dom_object *obj, zval *retval) return FAILURE; } - php_dom_get_content_into_zval(nodep, retval, false); + if (dom_skip_text_content(obj, nodep)) { + ZVAL_NULL(retval); + } else { + php_dom_get_content_into_zval(nodep, retval, false); + } return SUCCESS; } @@ -805,8 +872,17 @@ zend_result dom_node_text_content_write(dom_object *obj, zval *newval) php_libxml_invalidate_node_list_cache(obj->document); /* Typed property, this is already a string */ - ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING); - const xmlChar *xmlChars = (const xmlChar *) Z_STRVAL_P(newval); + ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING || Z_TYPE_P(newval) == IS_NULL); + const xmlChar *xmlChars; + size_t len; + if (Z_TYPE_P(newval) == IS_NULL) { + xmlChars = (const xmlChar *) ""; + len = 0; + } else { + xmlChars = (const xmlChar *) Z_STRVAL_P(newval); + len = Z_STRLEN_P(newval); + } + int type = nodep->type; /* We can't directly call xmlNodeSetContent, because it might encode the string through @@ -817,7 +893,7 @@ zend_result dom_node_text_content_write(dom_object *obj, zval *newval) * the content without encoding. */ if (type == XML_DOCUMENT_FRAG_NODE || type == XML_ELEMENT_NODE || type == XML_ATTRIBUTE_NODE) { dom_remove_all_children(nodep); - xmlNode *textNode = xmlNewText(xmlChars); + xmlNode *textNode = xmlNewDocTextLen(nodep->doc, xmlChars, len); xmlAddChild(nodep, textNode); } else { xmlNodeSetContent(nodep, xmlChars); @@ -876,29 +952,15 @@ static xmlNodePtr _php_dom_insert_fragment(xmlNodePtr nodep, xmlNodePtr prevsib, /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-952280727 Since: */ -PHP_METHOD(DOMNode, insertBefore) +static void dom_node_insert_before_legacy(zval *return_value, zval *ref, dom_object *intern, dom_object *childobj, xmlNodePtr parentp, xmlNodePtr child) { - zval *id, *node, *ref = NULL; - xmlNodePtr child, new_child, parentp, refp; - dom_object *intern, *childobj, *refpobj; - int ret, stricterror; - - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|O!", &node, dom_node_class_entry, &ref, dom_node_class_entry) == FAILURE) { - RETURN_THROWS(); - } - - DOM_GET_OBJ(parentp, id, xmlNodePtr, intern); - - if (dom_node_children_valid(parentp) == FAILURE) { + if (!dom_node_children_valid(parentp)) { RETURN_FALSE; } - DOM_GET_OBJ(child, node, xmlNodePtr, childobj); - - new_child = NULL; - - stricterror = dom_get_strict_error(intern->document); + xmlNodePtr new_child = NULL; + bool stricterror = dom_get_strict_error(intern->document); + int ret; if (dom_node_is_read_only(parentp) == SUCCESS || (child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) { @@ -930,6 +992,8 @@ PHP_METHOD(DOMNode, insertBefore) php_libxml_invalidate_node_list_cache(intern->document); if (ref != NULL) { + xmlNodePtr refp; + dom_object *refpobj; DOM_GET_OBJ(refp, ref, xmlNodePtr, refpobj); if (refp->parent != parentp) { php_dom_throw_error(NOT_FOUND_ERR, stricterror); @@ -1050,58 +1114,198 @@ PHP_METHOD(DOMNode, insertBefore) } /* }}} end dom_node_insert_before */ +/* https://dom.spec.whatwg.org/#dom-node-insertbefore */ +static void dom_node_insert_before_modern(zval *return_value, zval *ref, dom_object *intern, dom_object *childobj, xmlNodePtr parentp, xmlNodePtr child) +{ + int ret; + xmlNodePtr refp = NULL; + dom_object *refobjp; + if (php_dom_pre_insert_is_parent_invalid(parentp)) { + php_dom_throw_error(HIERARCHY_REQUEST_ERR, /* strict */ true); + RETURN_THROWS(); + } + if (ref != NULL) { + DOM_GET_OBJ(refp, ref, xmlNodePtr, refobjp); + } + php_libxml_invalidate_node_list_cache(intern->document); + php_dom_pre_insert(intern->document, child, parentp, refp); + DOM_RET_OBJ(child, &ret, intern); +} + +static void dom_node_insert_before(INTERNAL_FUNCTION_PARAMETERS, bool modern) +{ + zval *id, *node, *ref = NULL; + xmlNodePtr child, parentp; + dom_object *intern, *childobj; + + id = ZEND_THIS; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|O!", &node, dom_get_node_ce(modern), &ref, dom_get_node_ce(modern)) == FAILURE) { + RETURN_THROWS(); + } + + DOM_GET_OBJ(parentp, id, xmlNodePtr, intern); + + DOM_GET_OBJ(child, node, xmlNodePtr, childobj); + + if (modern) { + dom_node_insert_before_modern(return_value, ref, intern, childobj, parentp, child); + } else { + dom_node_insert_before_legacy(return_value, ref, intern, childobj, parentp, child); + } +} + +PHP_METHOD(DOMNode, insertBefore) +{ + dom_node_insert_before(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Node, insertBefore) +{ + dom_node_insert_before(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +/* https://dom.spec.whatwg.org/#concept-node-replace */ +static zend_result dom_replace_node_validity_checks(xmlNodePtr parent, xmlNodePtr node, xmlNodePtr child) +{ + /* 1. If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException. */ + if (php_dom_pre_insert_is_parent_invalid(parent)) { + php_dom_throw_error(HIERARCHY_REQUEST_ERR, /* strict */ true); + return FAILURE; + } + + /* 2. If node is a host-including inclusive ancestor of parent, then throw a "HierarchyRequestError" DOMException. */ + if (dom_hierarchy(parent, node) != SUCCESS) { + php_dom_throw_error(HIERARCHY_REQUEST_ERR, /* strict */ true); + return FAILURE; + } + + /* 3. If child’s parent is not parent, then throw a "NotFoundError" DOMException. */ + if (child->parent != parent) { + php_dom_throw_error(NOT_FOUND_ERR, /* strict */ true); + return FAILURE; + } + + /* 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException. */ + if (node->type != XML_DOCUMENT_FRAG_NODE + && node->type != XML_DTD_NODE + && node->type != XML_ELEMENT_NODE + && node->type != XML_TEXT_NODE + && node->type != XML_CDATA_SECTION_NODE + && node->type != XML_COMMENT_NODE + && node->type != XML_PI_NODE) { + php_dom_throw_error(HIERARCHY_REQUEST_ERR, /* strict */ true); + return FAILURE; + } + + /* 5. If either node is a Text node and parent is a document, or node is a doctype and parent is not a document, + * then throw a "HierarchyRequestError" DOMException. */ + bool parent_is_document = parent->type == XML_DOCUMENT_NODE || parent->type == XML_HTML_DOCUMENT_NODE; + if (parent_is_document && (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE)) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot insert text as a child of a document", /* strict */ true); + return FAILURE; + } + if (!parent_is_document && node->type == XML_DTD_NODE) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot insert a document type into anything other than a document", /* strict */ true); + return FAILURE; + } + + /* 6. If parent is a document, and any of the statements below, switched on the interface node implements, are true, + * then throw a "HierarchyRequestError" DOMException. + * Spec note: These statements _slightly_ differ from the pre-insert algorithm. */ + if (parent_is_document) { + /* DocumentFragment */ + if (node->type == XML_DOCUMENT_FRAG_NODE) { + if (!php_dom_fragment_insertion_hierarchy_check_replace(parent, node, child)) { + return FAILURE; + } + } + /* Element */ + else if (node->type == XML_ELEMENT_NODE) { + /* parent has an element child that is not child ... */ + if (xmlDocGetRootElement((xmlDocPtr) parent) != child) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot have more than one element child in a document", /* strict */ true); + return FAILURE; + } + /* ... or a doctype is following child. */ + if (php_dom_has_sibling_following_node(child, XML_DTD_NODE)) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Document types must be the first child in a document", /* strict */ true); + return FAILURE; + } + } + /* DocumentType */ + else if (node->type == XML_DTD_NODE) { + /* parent has a doctype child that is not child, or an element is preceding child. */ + xmlDocPtr doc = (xmlDocPtr) parent; + if (doc->intSubset != (xmlDtdPtr) child || php_dom_has_sibling_preceding_node(child, XML_ELEMENT_NODE)) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Document types must be the first child in a document", /* strict */ true); + return FAILURE; + } + } + } + + /* Steps 7 and onward perform the removal and insertion, and also track changes for mutation records. + * We don't implement mutation records so we can just skip straight to the replace part. */ + + return SUCCESS; +} + /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-785887307 +Modern spec URL: https://dom.spec.whatwg.org/#dom-node-replacechild Since: */ -PHP_METHOD(DOMNode, replaceChild) +static void dom_node_replace_child(INTERNAL_FUNCTION_PARAMETERS, bool modern) { zval *id, *newnode, *oldnode; xmlNodePtr newchild, oldchild, nodep; dom_object *intern, *newchildobj, *oldchildobj; - int stricterror; - bool replacedoctype = false; int ret; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "OO", &newnode, dom_node_class_entry, &oldnode, dom_node_class_entry) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "OO", &newnode, dom_get_node_ce(modern), &oldnode, dom_get_node_ce(modern)) == FAILURE) { RETURN_THROWS(); } DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - if (dom_node_children_valid(nodep) == FAILURE) { - RETURN_FALSE; - } - DOM_GET_OBJ(newchild, newnode, xmlNodePtr, newchildobj); DOM_GET_OBJ(oldchild, oldnode, xmlNodePtr, oldchildobj); - if (!nodep->children) { - RETURN_FALSE; - } - - stricterror = dom_get_strict_error(intern->document); - - if (dom_node_is_read_only(nodep) == SUCCESS || - (newchild->parent != NULL && dom_node_is_read_only(newchild->parent) == SUCCESS)) { - php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror); - RETURN_FALSE; - } + bool stricterror = dom_get_strict_error(intern->document); if (newchild->doc != nodep->doc && newchild->doc != NULL) { php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror); RETURN_FALSE; } - if (dom_hierarchy(nodep, newchild) == FAILURE) { - php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror); - RETURN_FALSE; - } + if (modern) { + if (dom_replace_node_validity_checks(nodep, newchild, oldchild) != SUCCESS) { + RETURN_THROWS(); + } + } else { + if (!dom_node_children_valid(nodep)) { + RETURN_FALSE; + } - if (oldchild->parent != nodep) { - php_dom_throw_error(NOT_FOUND_ERR, stricterror); - RETURN_FALSE; + if (!nodep->children) { + RETURN_FALSE; + } + + if (dom_node_is_read_only(nodep) == SUCCESS || + (newchild->parent != NULL && dom_node_is_read_only(newchild->parent) == SUCCESS)) { + php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror); + RETURN_FALSE; + } + + if (dom_hierarchy(nodep, newchild) == FAILURE) { + php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror); + RETURN_FALSE; + } + + if (oldchild->parent != nodep) { + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_FALSE; + } } if (newchild->type == XML_DOCUMENT_FRAG_NODE) { @@ -1113,12 +1317,12 @@ PHP_METHOD(DOMNode, replaceChild) xmlNodePtr last = newchild->last; newchild = _php_dom_insert_fragment(nodep, prevsib, nextsib, newchild, intern); - if (newchild) { + if (newchild && !modern) { dom_reconcile_ns_list(nodep->doc, newchild, last); } } else if (oldchild != newchild) { xmlDtdPtr intSubset = xmlGetIntSubset(nodep->doc); - replacedoctype = (intSubset == (xmlDtd *) oldchild); + bool replacedoctype = (intSubset == (xmlDtd *) oldchild); if (newchild->doc == NULL && nodep->doc != NULL) { xmlSetTreeDoc(newchild, nodep->doc); @@ -1126,7 +1330,9 @@ PHP_METHOD(DOMNode, replaceChild) php_libxml_increment_doc_ref((php_libxml_node_object *)newchildobj, NULL); } xmlReplaceNode(oldchild, newchild); - dom_reconcile_ns(nodep->doc, newchild); + if (!modern) { + dom_reconcile_ns(nodep->doc, newchild); + } if (replacedoctype) { nodep->doc->intSubset = (xmlDtd *) newchild; @@ -1135,12 +1341,22 @@ PHP_METHOD(DOMNode, replaceChild) php_libxml_invalidate_node_list_cache(intern->document); DOM_RET_OBJ(oldchild, &ret, intern); } + +PHP_METHOD(DOMNode, replaceChild) +{ + dom_node_replace_child(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Node, replaceChild) +{ + dom_node_replace_child(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} /* }}} end dom_node_replace_child */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1734834066 Since: */ -PHP_METHOD(DOMNode, removeChild) +static void dom_node_remove_child(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce) { zval *id, *node; xmlNodePtr child, nodep; @@ -1148,13 +1364,13 @@ PHP_METHOD(DOMNode, removeChild) int ret, stricterror; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, node_ce) == FAILURE) { RETURN_THROWS(); } DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - if (dom_node_children_valid(nodep) == FAILURE) { + if (!dom_node_children_valid(nodep)) { RETURN_FALSE; } @@ -1177,31 +1393,31 @@ PHP_METHOD(DOMNode, removeChild) php_libxml_invalidate_node_list_cache(intern->document); DOM_RET_OBJ(child, &ret, intern); } + +PHP_METHOD(DOMNode, removeChild) +{ + dom_node_remove_child(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry); +} + +PHP_METHOD(DOM_Node, removeChild) +{ + dom_node_remove_child(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry); +} /* }}} end dom_node_remove_child */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-184E7107 +Modern spec URL: https://dom.spec.whatwg.org/#dom-node-appendchild Since: */ -PHP_METHOD(DOMNode, appendChild) +static void dom_node_append_child_legacy(zval *return_value, dom_object *intern, dom_object *childobj, xmlNodePtr nodep, xmlNodePtr child) { - zval *id, *node; - xmlNodePtr child, nodep, new_child = NULL; - dom_object *intern, *childobj; + xmlNodePtr new_child = NULL; int ret, stricterror; - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) { - RETURN_THROWS(); - } - - DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - - if (dom_node_children_valid(nodep) == FAILURE) { + if (!dom_node_children_valid(nodep)) { RETURN_FALSE; } - DOM_GET_OBJ(child, node, xmlNodePtr, childobj); - stricterror = dom_get_strict_error(intern->document); if (dom_node_is_read_only(nodep) == SUCCESS || @@ -1272,6 +1488,16 @@ PHP_METHOD(DOMNode, appendChild) xmlNodePtr last = child->last; new_child = _php_dom_insert_fragment(nodep, nodep->last, NULL, child, intern); dom_reconcile_ns_list(nodep->doc, new_child, last); + } else if (child->type == XML_DTD_NODE) { + if (nodep->doc->intSubset != NULL) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "A document may only contain one document type", stricterror); + RETURN_FALSE; + } + new_child = xmlAddChild(nodep, child); + if (UNEXPECTED(new_child == NULL)) { + goto cannot_add; + } + nodep->doc->intSubset = (xmlDtdPtr) new_child; } else { new_child = xmlAddChild(nodep, child); if (UNEXPECTED(new_child == NULL)) { @@ -1290,6 +1516,48 @@ PHP_METHOD(DOMNode, appendChild) } /* }}} end dom_node_append_child */ +PHP_METHOD(DOMNode, appendChild) +{ + zval *node; + xmlNodePtr nodep, child; + dom_object *intern, *childobj; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(node, dom_node_class_entry) + ZEND_PARSE_PARAMETERS_END(); + + DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern); + DOM_GET_OBJ(child, node, xmlNodePtr, childobj); + + dom_node_append_child_legacy(return_value, intern, childobj, nodep, child); +} + +PHP_METHOD(DOM_Node, appendChild) +{ + zval *node; + xmlNodePtr nodep, child; + dom_object *intern, *childobj; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(node, dom_modern_node_class_entry) + ZEND_PARSE_PARAMETERS_END(); + + DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern); + DOM_GET_OBJ(child, node, xmlNodePtr, childobj); + + /* Parent check from pre-insertion validation done here: + * If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException. */ + if (php_dom_pre_insert_is_parent_invalid(nodep)) { + php_dom_throw_error(HIERARCHY_REQUEST_ERR, /* strict */ true); + RETURN_THROWS(); + } + /* Append, this doesn't do the parent check so we do it here. */ + php_libxml_invalidate_node_list_cache(intern->document); + php_dom_node_append(intern->document, child, nodep); + int ret; + DOM_RET_OBJ(child, &ret, intern); +} + /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-810594187 Since: */ @@ -1306,7 +1574,7 @@ PHP_METHOD(DOMNode, hasChildNodes) DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - if (dom_node_children_valid(nodep) == FAILURE) { + if (!dom_node_children_valid(nodep)) { RETURN_FALSE; } @@ -1336,25 +1604,51 @@ PHP_METHOD(DOMNode, cloneNode) DOM_GET_OBJ(n, id, xmlNodePtr, intern); - node = dom_clone_node(n, n->doc, intern, recursive); + php_dom_libxml_ns_mapper *ns_mapper = NULL; + bool clone_document = n->type == XML_DOCUMENT_NODE || n->type == XML_HTML_DOCUMENT_NODE; + if (php_dom_follow_spec_intern(intern)) { + if (clone_document) { + ns_mapper = php_dom_libxml_ns_mapper_create(); + } else { + ns_mapper = php_dom_get_ns_mapper(intern); + } + } + + node = dom_clone_node(ns_mapper, n, n->doc, recursive); if (!node) { + if (clone_document && ns_mapper != NULL) { + php_dom_libxml_ns_mapper_destroy(ns_mapper); + } RETURN_FALSE; } - 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. - * libxml2 doesn't set the ns because it can't know that this is safe. */ - node->ns = n->ns; - } - /* If document cloned we want a new document proxy */ - if (node->doc != n->doc) { - intern = NULL; - } + if (clone_document) { + dom_object *new_intern; + if (ns_mapper) { + /* We have the issue here that we can't create a modern node without an intern. + * Fortunately, it's impossible to have a custom document class for the modern DOM (final base class), + * so we can solve this by invoking the instantiation helper directly. */ + zend_class_entry *ce = n->type == XML_DOCUMENT_NODE ? dom_xml_document_class_entry : dom_html_document_class_entry; + new_intern = php_dom_instantiate_object_helper(return_value, ce, node, NULL); + } else { + DOM_RET_OBJ(node, &ret, NULL); + new_intern = Z_DOMOBJ_P(return_value); + } + php_dom_update_document_after_clone(intern, n, new_intern, node); + ZEND_ASSERT(new_intern->document->private_data == NULL); + new_intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper); + } else { + if (node->type == XML_ATTRIBUTE_NODE && n->ns != NULL && node->ns == NULL) { + /* Let reconciliation deal with this. The lifetime of the namespace poses no problem + * because we're increasing the refcount of the document proxy at the return. + * libxml2 doesn't set the ns because it can't know that this is safe. */ + node->ns = n->ns; + } - DOM_RET_OBJ(node, &ret, intern); + DOM_RET_OBJ(node, &ret, intern); + } } /* }}} end dom_node_clone_node */ @@ -1374,10 +1668,11 @@ PHP_METHOD(DOMNode, normalize) DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - php_libxml_invalidate_node_list_cache(intern->document); - - dom_normalize(nodep); - + if (php_dom_follow_spec_intern(intern)) { + php_dom_normalize_modern(nodep); + } else { + php_dom_normalize_legacy(nodep); + } } /* }}} end dom_node_normalize */ @@ -1426,18 +1721,13 @@ PHP_METHOD(DOMNode, hasAttributes) /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-isSameNode Since: DOM Level 3 */ -PHP_METHOD(DOMNode, isSameNode) +static void dom_node_is_same_node(INTERNAL_FUNCTION_PARAMETERS, zval *node) { - zval *id, *node; + zval *id; xmlNodePtr nodeotherp, nodep; dom_object *intern, *nodeotherobj; - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) { - RETURN_THROWS(); - } - - DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); + DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern); DOM_GET_OBJ(nodeotherp, node, xmlNodePtr, nodeotherobj); @@ -1447,6 +1737,30 @@ PHP_METHOD(DOMNode, isSameNode) RETURN_FALSE; } } + +PHP_METHOD(DOMNode, isSameNode) +{ + zval *node; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) != SUCCESS) { + RETURN_THROWS(); + } + + dom_node_is_same_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, node); +} + +PHP_METHOD(DOM_Node, isSameNode) +{ + zval *node; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O!", &node, dom_modern_node_class_entry) != SUCCESS) { + RETURN_THROWS(); + } + + if (node == NULL) { + RETURN_FALSE; + } + + dom_node_is_same_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, node); +} /* }}} end dom_node_is_same_node */ static bool php_dom_node_is_content_equal(const xmlNode *this, const xmlNode *other) @@ -1473,55 +1787,55 @@ static bool php_dom_node_is_ns_prefix_equal(const xmlNode *this, const xmlNode * return xmlStrEqual(this_ns, other_ns); } -static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other); +static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other, bool spec_compliant); #define PHP_DOM_FUNC_CAT(prefix, suffix) prefix##_##suffix /* xmlNode and xmlNs have incompatible struct layouts, i.e. the next field is in a different offset */ -#define PHP_DOM_DEFINE_LIST_COUNTER_HELPER(type) \ - static size_t PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(const type *node) \ - { \ - size_t counter = 0; \ - while (node) { \ - counter++; \ - node = node->next; \ - } \ - return counter; \ - } -#define PHP_DOM_DEFINE_LIST_EQUALITY_ORDERED_HELPER(type) \ - static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check_ordered, type)(const type *list1, const type *list2) \ - { \ - size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1); \ - if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) { \ - return false; \ - } \ - for (size_t i = 0; i < count; i++) { \ - if (!php_dom_node_is_equal_node((const xmlNode *) list1, (const xmlNode *) list2)) { \ - return false; \ - } \ - list1 = list1->next; \ - list2 = list2->next; \ - } \ - return true; \ - } -#define PHP_DOM_DEFINE_LIST_EQUALITY_UNORDERED_HELPER(type) \ - static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check_unordered, type)(const type *list1, const type *list2)\ - { \ - size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1); \ - if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) { \ - return false; \ - } \ - for (const type *n1 = list1; n1 != NULL; n1 = n1->next) { \ - bool found = false; \ - for (const type *n2 = list2; n2 != NULL && !found; n2 = n2->next) { \ - if (php_dom_node_is_equal_node((const xmlNode *) n1, (const xmlNode *) n2)) { \ - found = true; \ - } \ - } \ - if (!found) { \ - return false; \ - } \ - } \ - return true; \ +#define PHP_DOM_DEFINE_LIST_COUNTER_HELPER(type) \ + static size_t PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(const type *node) \ + { \ + size_t counter = 0; \ + while (node) { \ + counter++; \ + node = node->next; \ + } \ + return counter; \ + } +#define PHP_DOM_DEFINE_LIST_EQUALITY_ORDERED_HELPER(type) \ + static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check_ordered, type)(const type *list1, const type *list2, bool spec_compliant) \ + { \ + size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1); \ + if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) { \ + return false; \ + } \ + for (size_t i = 0; i < count; i++) { \ + if (!php_dom_node_is_equal_node((const xmlNode *) list1, (const xmlNode *) list2, spec_compliant)) { \ + return false; \ + } \ + list1 = list1->next; \ + list2 = list2->next; \ + } \ + return true; \ + } +#define PHP_DOM_DEFINE_LIST_EQUALITY_UNORDERED_HELPER(type) \ + static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check_unordered, type)(const type *list1, const type *list2, bool spec_compliant)\ + { \ + size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1); \ + if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) { \ + return false; \ + } \ + for (const type *n1 = list1; n1 != NULL; n1 = n1->next) { \ + bool found = false; \ + for (const type *n2 = list2; n2 != NULL && !found; n2 = n2->next) { \ + if (php_dom_node_is_equal_node((const xmlNode *) n1, (const xmlNode *) n2, spec_compliant)) { \ + found = true; \ + } \ + } \ + if (!found) { \ + return false; \ + } \ + } \ + return true; \ } PHP_DOM_DEFINE_LIST_COUNTER_HELPER(xmlNode) @@ -1539,7 +1853,7 @@ static bool php_dom_is_equal_attr(const xmlAttr *this_attr, const xmlAttr *other && php_dom_node_is_content_equal((const xmlNode *) this_attr, (const xmlNode *) other_attr); } -static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other) +static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other, bool spec_compliant) { ZEND_ASSERT(this != NULL); ZEND_ASSERT(other != NULL); @@ -1556,9 +1870,9 @@ static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other && php_dom_node_is_ns_prefix_equal(this, other) && php_dom_node_is_ns_uri_equal(this, other) /* Check attributes first, then namespace declarations, then children */ - && php_dom_node_list_equality_check_unordered_xmlNode((const xmlNode *) this->properties, (const xmlNode *) other->properties) - && php_dom_node_list_equality_check_unordered_xmlNs(this->nsDef, other->nsDef) - && php_dom_node_list_equality_check_ordered_xmlNode(this->children, other->children); + && php_dom_node_list_equality_check_unordered_xmlNode((const xmlNode *) this->properties, (const xmlNode *) other->properties, spec_compliant) + && (spec_compliant || php_dom_node_list_equality_check_unordered_xmlNs(this->nsDef, other->nsDef, false)) + && php_dom_node_list_equality_check_ordered_xmlNode(this->children, other->children, spec_compliant); } else if (this->type == XML_DTD_NODE) { /* Note: in the living spec entity declarations and notations are no longer compared because they're considered obsolete. */ const xmlDtd *this_dtd = (const xmlDtd *) this; @@ -1589,7 +1903,7 @@ static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other const xmlNs *other_ns = (const xmlNs *) other; return xmlStrEqual(this_ns->prefix, other_ns->prefix) && xmlStrEqual(this_ns->href, other_ns->href); } else if (this->type == XML_DOCUMENT_FRAG_NODE || this->type == XML_HTML_DOCUMENT_NODE || this->type == XML_DOCUMENT_NODE) { - return php_dom_node_list_equality_check_ordered_xmlNode(this->children, other->children); + return php_dom_node_list_equality_check_ordered_xmlNode(this->children, other->children, spec_compliant); } return false; @@ -1599,14 +1913,14 @@ static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other * URL: https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/DOM3-Core.html#core-Node3-isEqualNode (for old nodes removed from the living spec) Since: DOM Level 3 */ -PHP_METHOD(DOMNode, isEqualNode) +static void dom_node_is_equal_node_common(INTERNAL_FUNCTION_PARAMETERS, bool modern) { zval *id, *node; xmlNodePtr otherp, nodep; - dom_object *unused_intern; + dom_object *intern; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O!", &node, dom_node_class_entry) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O!", &node, dom_get_node_ce(modern)) == FAILURE) { RETURN_THROWS(); } @@ -1614,26 +1928,63 @@ PHP_METHOD(DOMNode, isEqualNode) RETURN_FALSE; } - DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, unused_intern); - DOM_GET_OBJ(otherp, node, xmlNodePtr, unused_intern); + DOM_GET_OBJ(otherp, node, xmlNodePtr, intern); + DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern); if (nodep == otherp) { RETURN_TRUE; } /* Empty fragments/documents only match if they're both empty */ - if (UNEXPECTED(nodep == NULL || otherp == NULL)) { + if (nodep == NULL || otherp == NULL) { RETURN_BOOL(nodep == NULL && otherp == NULL); } - RETURN_BOOL(php_dom_node_is_equal_node(nodep, otherp)); + RETURN_BOOL(php_dom_node_is_equal_node(nodep, otherp, modern)); +} + +PHP_METHOD(DOMNode, isEqualNode) +{ + dom_node_is_equal_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Node, isEqualNode) +{ + dom_node_is_equal_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } /* }}} end DOMNode::isEqualNode */ +/* https://dom.spec.whatwg.org/#locate-a-namespace-prefix */ +static const xmlChar *dom_locate_a_namespace_prefix(xmlNodePtr elem, const char *uri) +{ + do { + /* 1. If element’s namespace is namespace and its namespace prefix is non-null, then return its namespace prefix. */ + if (elem->ns != NULL && elem->ns->prefix != NULL && xmlStrEqual(elem->ns->href, BAD_CAST uri)) { + return elem->ns->prefix; + } + + /* 2. If element has an attribute whose namespace prefix is "xmlns" and value is namespace, + * then return element’s first such attribute’s local name. */ + for (xmlAttrPtr attr = elem->properties; attr != NULL; attr = attr->next) { + if (attr->ns != NULL && attr->children != NULL + && xmlStrEqual(attr->ns->prefix, BAD_CAST "xmlns") && xmlStrEqual(attr->children->content, BAD_CAST uri)) { + return attr->name; + } + } + + /* 3. If element’s parent element is not null, then return the result of running locate a namespace prefix on that element using namespace. */ + elem = elem->parent; + } while (elem != NULL && elem->type == XML_ELEMENT_NODE); + + /* 4. Return null. */ + return NULL; +} + /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-lookupNamespacePrefix +Modern spec URL: https://dom.spec.whatwg.org/#dom-node-lookupprefix Since: DOM Level 3 */ -PHP_METHOD(DOMNode, lookupPrefix) +static void dom_node_lookup_prefix(INTERNAL_FUNCTION_PARAMETERS, bool modern) { zval *id; xmlNodePtr nodep, lookupp = NULL; @@ -1643,13 +1994,15 @@ PHP_METHOD(DOMNode, lookupPrefix) char *uri; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &uri, &uri_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), modern ? "s!" : "s", &uri, &uri_len) == FAILURE) { RETURN_THROWS(); } DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); + /* 1. If namespace is null or the empty string, then return null. */ if (uri_len > 0) { + /* 2. Switch on the interface this implements: */ switch (nodep->type) { case XML_ELEMENT_NODE: lookupp = nodep; @@ -1666,22 +2019,110 @@ PHP_METHOD(DOMNode, lookupPrefix) RETURN_NULL(); break; default: - lookupp = nodep->parent; + lookupp = nodep->parent; } if (lookupp != NULL) { - nsptr = xmlSearchNsByHref(lookupp->doc, lookupp, (xmlChar *) uri); - if (nsptr && nsptr->prefix != NULL) { - RETURN_STRING((char *) nsptr->prefix); + if (modern) { + const char * result = (const char *) dom_locate_a_namespace_prefix(lookupp, uri); + if (result != NULL) { + RETURN_STRING(result); + } + } else { + nsptr = xmlSearchNsByHref(lookupp->doc, lookupp, (xmlChar *) uri); + if (nsptr && nsptr->prefix != NULL) { + RETURN_STRING((const char *) nsptr->prefix); + } } } } RETURN_NULL(); } + +PHP_METHOD(DOMNode, lookupPrefix) +{ + dom_node_lookup_prefix(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Node, lookupPrefix) +{ + dom_node_lookup_prefix(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} /* }}} end dom_node_lookup_prefix */ +/* https://dom.spec.whatwg.org/#locate-a-namespace */ +static const char *dom_locate_a_namespace(xmlNodePtr node, const zend_string *prefix) +{ + /* switch on the interface node implements: */ + if (node->type == XML_ELEMENT_NODE) { + if (prefix != NULL) { + /* 1. If prefix is "xml", then return the XML namespace. */ + if (zend_string_equals_literal_ci(prefix, "xml")) { + return DOM_XML_NS_URI; + } + + /* 2. If prefix is "xmlns", then return the XMLNS namespace. */ + if (zend_string_equals_literal_ci(prefix, "xmlns")) { + return DOM_XMLNS_NS_URI; + } + } + + do { + /* 3. If its namespace is non-null and its namespace prefix is prefix, then return namespace. */ + if (node->ns != NULL && xmlStrEqual(node->ns->prefix, BAD_CAST (prefix ? ZSTR_VAL(prefix) : NULL))) { + return (const char *) node->ns->href; + } + + /* 4. If it has an attribute whose namespace is the XMLNS namespace, namespace prefix is "xmlns", and local name is prefix, + * or if prefix is null and it has an attribute whose namespace is the XMLNS namespace, namespace prefix is null, and local name is "xmlns", + * then return its value if it is not the empty string, and null otherwise. */ + for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) { + if (attr->ns == NULL || !php_dom_ns_is_fast_ex(attr->ns, php_dom_ns_is_xmlns_magic_token)) { + continue; + } + if ((prefix != NULL && xmlStrEqual(attr->ns->prefix, BAD_CAST "xmlns") && xmlStrEqual(attr->name, BAD_CAST ZSTR_VAL(prefix))) + || (prefix == NULL && attr->ns->prefix == NULL && xmlStrEqual(attr->name, BAD_CAST "xmlns"))) { + if (attr->children != NULL && attr->children->content[0] != '\0') { + return (const char *) attr->children->content; + } else { + return NULL; + } + } + } + + /* 5. If its parent element is null, then return null. */ + if (node->parent == NULL || node->parent->type != XML_ELEMENT_NODE) { + return NULL; + } + + /* 6. Return the result of running locate a namespace on its parent element using prefix. */ + node = node->parent; + } while (true); + } else if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) { + /* 1. If its document element is null, then return null. */ + node = xmlDocGetRootElement((xmlDocPtr) node); + if (UNEXPECTED(node == NULL)) { + return NULL; + } + + /* 2. Return the result of running locate a namespace on its document element using prefix. */ + return dom_locate_a_namespace(node, prefix); + } else if (node->type == XML_DTD_NODE || node->type == XML_DOCUMENT_FRAG_NODE) { + return NULL; + } else { + /* 1. If its element is null, then return null / If its parent element is null, then return null. */ + if (node->parent == NULL || node->parent->type != XML_ELEMENT_NODE) { + return NULL; + } + + /* 2. Return the result of running locate a namespace on its element using prefix. */ + return dom_locate_a_namespace(node->parent, prefix); + } +} + /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace +Modern spec URL: https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace Since: DOM Level 3 */ PHP_METHOD(DOMNode, isDefaultNamespace) @@ -1693,17 +2134,20 @@ PHP_METHOD(DOMNode, isDefaultNamespace) size_t uri_len = 0; char *uri; - id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &uri, &uri_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &uri, &uri_len) != SUCCESS) { RETURN_THROWS(); } - DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) { - nodep = xmlDocGetRootElement((xmlDocPtr) nodep); - } + DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern); + + if (uri_len > 0) { + if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) { + nodep = xmlDocGetRootElement((xmlDocPtr) nodep); + if (nodep == NULL) { + RETURN_FALSE; + } + } - if (nodep && uri_len > 0) { nsptr = xmlSearchNs(nodep->doc, nodep, NULL); if (nsptr && xmlStrEqual(nsptr->href, (xmlChar *) uri)) { RETURN_TRUE; @@ -1712,9 +2156,31 @@ PHP_METHOD(DOMNode, isDefaultNamespace) RETURN_FALSE; } + +PHP_METHOD(DOM_Node, isDefaultNamespace) +{ + zval *id; + xmlNodePtr nodep; + dom_object *intern; + size_t uri_len = 0; + char *uri; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &uri, &uri_len) != SUCCESS) { + RETURN_THROWS(); + } + + DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern); + + if (uri_len == 0) { + uri = NULL; + } + const char *ns_uri = dom_locate_a_namespace(nodep, NULL); + RETURN_BOOL(xmlStrEqual(BAD_CAST uri, BAD_CAST ns_uri)); +} /* }}} end dom_node_is_default_namespace */ /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI +Modern spec URL: https://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri Since: DOM Level 3 */ PHP_METHOD(DOMNode, lookupNamespaceURI) @@ -1723,25 +2189,37 @@ PHP_METHOD(DOMNode, lookupNamespaceURI) xmlNodePtr nodep; dom_object *intern; xmlNsPtr nsptr; - size_t prefix_len; - char *prefix; + zend_string *prefix; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &prefix, &prefix_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!", &prefix) == FAILURE) { RETURN_THROWS(); } DOM_GET_OBJ(nodep, id, xmlNodePtr, intern); - if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) { - nodep = xmlDocGetRootElement((xmlDocPtr) nodep); - if (nodep == NULL) { + + if (php_dom_follow_spec_intern(intern)) { + if (prefix != NULL && ZSTR_LEN(prefix) == 0) { + prefix = NULL; + } + const char *ns_uri = dom_locate_a_namespace(nodep, prefix); + if (ns_uri == NULL) { RETURN_NULL(); + } else { + RETURN_STRING(ns_uri); + } + } else { + if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) { + nodep = xmlDocGetRootElement((xmlDocPtr) nodep); + if (nodep == NULL) { + RETURN_NULL(); + } } - } - nsptr = xmlSearchNs(nodep->doc, nodep, (xmlChar *) prefix); - if (nsptr && nsptr->href != NULL) { - RETURN_STRING((char *) nsptr->href); + nsptr = xmlSearchNs(nodep->doc, nodep, BAD_CAST (prefix ? ZSTR_VAL(prefix) : NULL)); + if (nsptr && nsptr->href != NULL) { + RETURN_STRING((char *) nsptr->href); + } } RETURN_NULL(); @@ -1947,7 +2425,7 @@ PHP_METHOD(DOMNode, C14NFile) /* }}} */ /* {{{ Gets an xpath for a node */ -PHP_METHOD(DOMNode, getNodePath) +static void dom_node_get_node_path(INTERNAL_FUNCTION_PARAMETERS, bool throw) { zval *id; xmlNode *nodep; @@ -1962,13 +2440,28 @@ PHP_METHOD(DOMNode, getNodePath) value = (char *) xmlGetNodePath(nodep); if (value == NULL) { - /* TODO Research if can return empty string */ + /* This is only possible when an invalid argument is passed (e.g. namespace declaration, but that's not the case for this call site), + * or on allocation failure. So in other words, this only happens on allocation failure. */ + if (throw) { + php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); + RETURN_THROWS(); + } RETURN_NULL(); } else { RETVAL_STRING(value); xmlFree(value); } } + +PHP_METHOD(DOMNode, getNodePath) +{ + dom_node_get_node_path(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Node, getNodePath) +{ + dom_node_get_node_path(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} /* }}} */ /* {{{ Gets line number for a node */ @@ -1991,6 +2484,18 @@ PHP_METHOD(DOMNode, getLineNo) /* {{{ URL: https://dom.spec.whatwg.org/#dom-node-contains Since: */ +static bool dom_node_contains(xmlNodePtr thisp, xmlNodePtr otherp) +{ + do { + if (otherp == thisp) { + return true; + } + otherp = otherp->parent; + } while (otherp); + + return false; +} + PHP_METHOD(DOMNode, contains) { zval *other, *id; @@ -2013,14 +2518,27 @@ PHP_METHOD(DOMNode, contains) DOM_GET_OBJ(otherp, other, xmlNodePtr, unused_intern); DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, unused_intern); - do { - if (otherp == thisp) { - RETURN_TRUE; - } - otherp = otherp->parent; - } while (otherp); + RETURN_BOOL(dom_node_contains(thisp, otherp)); +} - RETURN_FALSE; +PHP_METHOD(DOM_Node, contains) +{ + zval *other, *id; + xmlNodePtr otherp, thisp; + dom_object *unused_intern; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(other, dom_modern_node_class_entry) + ZEND_PARSE_PARAMETERS_END(); + + if (other == NULL) { + RETURN_FALSE; + } + + DOM_GET_OBJ(otherp, other, xmlNodePtr, unused_intern); + DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, unused_intern); + + RETURN_BOOL(dom_node_contains(thisp, otherp)); } /* }}} */ @@ -2033,9 +2551,9 @@ PHP_METHOD(DOMNode, getRootNode) xmlNodePtr thisp; dom_object *intern; /* Unused now because we don't support the shadow DOM nodes. Options only influence shadow DOM nodes. */ - zval *options = NULL; + zval *options; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &options) != SUCCESS) { RETURN_THROWS(); } @@ -2061,13 +2579,13 @@ PHP_METHOD(DOMNode, getRootNode) #define DOCUMENT_POSITION_CONTAINED_BY 0x10 #define DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC 0x20 -PHP_METHOD(DOMNode, compareDocumentPosition) +static void dom_node_compare_document_position(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce) { zval *id, *node_zval; xmlNodePtr other, this; dom_object *this_intern, *other_intern; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, dom_node_class_entry) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, node_ce) != SUCCESS) { RETURN_THROWS(); } @@ -2205,7 +2723,7 @@ PHP_METHOD(DOMNode, compareDocumentPosition) disconnected:; zend_long ordering; - if (UNEXPECTED(node1 == node2)) { + if (node1 == node2) { /* Degenerate case, they're both NULL, but the ordering must be consistent... */ ZEND_ASSERT(node1 == NULL); ordering = other_intern < this_intern ? DOCUMENT_POSITION_PRECEDING : DOCUMENT_POSITION_FOLLOWING; @@ -2214,6 +2732,16 @@ disconnected:; } RETURN_LONG(DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | ordering); } + +PHP_METHOD(DOMNode, compareDocumentPosition) +{ + dom_node_compare_document_position(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry); +} + +PHP_METHOD(DOM_Node, compareDocumentPosition) +{ + dom_node_compare_document_position(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry); +} /* }}} */ /** @@ -2225,6 +2753,11 @@ disconnected:; * - If the user implements __sleep / __wakeup, then it's also not a problem because they will not enter the throwing methods. */ +PHP_METHOD(DOM_Node, __construct) +{ + ZEND_UNREACHABLE(); +} + PHP_METHOD(DOMNode, __sleep) { zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed, unless serialization methods are implemented in a subclass", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name)); diff --git a/ext/dom/nodelist.c b/ext/dom/nodelist.c index b74a23ee7228b..74c5918c6d330 100644 --- a/ext/dom/nodelist.c +++ b/ext/dom/nodelist.c @@ -99,7 +99,7 @@ int php_dom_get_nodelist_length(dom_object *obj) nodep = nodep->children; } dom_get_elements_by_tag_name_ns_raw( - basep, nodep, (char *) objmap->ns, (char *) objmap->local, &count, INT_MAX - 1 /* because of <= */); + basep, nodep, objmap->ns, objmap->local, objmap->local_lower, &count, INT_MAX - 1 /* because of <= */); } objmap->cached_length = count; @@ -200,7 +200,7 @@ void php_dom_nodelist_get_item_into_zval(dom_nnodemap_object *objmap, zend_long nodep = basep->children; } } - itemnode = dom_get_elements_by_tag_name_ns_raw(basep, nodep, (char *) objmap->ns, (char *) objmap->local, &count, relative_index); + itemnode = dom_get_elements_by_tag_name_ns_raw(basep, nodep, objmap->ns, objmap->local, objmap->local_lower, &count, relative_index); } cache_itemnode = true; } diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index 9298c57553827..181cd6bc3b459 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Benjamin Eberlei | + | Niels Dossche | +----------------------------------------------------------------------+ */ @@ -23,6 +24,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "internal_helpers.h" /* {{{ firstElementChild DomParentNode readonly=yes @@ -39,7 +41,7 @@ zend_result dom_parent_node_first_element_child_read(dom_object *obj, zval *retv return FAILURE; } - if (dom_node_children_valid(nodep) == SUCCESS) { + if (dom_node_children_valid(nodep)) { first = nodep->children; while (first && first->type != XML_ELEMENT_NODE) { @@ -72,7 +74,7 @@ zend_result dom_parent_node_last_element_child_read(dom_object *obj, zval *retva return FAILURE; } - if (dom_node_children_valid(nodep) == SUCCESS) { + if (dom_node_children_valid(nodep)) { last = nodep->last; while (last && last->type != XML_ELEMENT_NODE) { @@ -106,7 +108,7 @@ zend_result dom_parent_node_child_element_count(dom_object *obj, zval *retval) return FAILURE; } - if (dom_node_children_valid(nodep) == SUCCESS) { + if (dom_node_children_valid(nodep)) { first = nodep->children; while (first != NULL) { @@ -146,26 +148,269 @@ static xmlDocPtr dom_doc_from_context_node(xmlNodePtr contextNode) } } -xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNode, zval *nodes, int nodesc) +/* Citing from the docs (https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/libxml2-tree.html#xmlAddChild): + * "Add a new node to @parent, at the end of the child (or property) list merging adjacent TEXT nodes (in which case @cur is freed)". + * So we must use a custom way of adding that does not merge. */ +static void dom_add_child_without_merging(xmlNodePtr parent, xmlNodePtr child) +{ + if (parent->children == NULL) { + parent->children = child; + } else { + xmlNodePtr last = parent->last; + last->next = child; + child->prev = last; + } + parent->last = child; + child->parent = parent; +} + +static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fragment) +{ + xmlNodePtr node = fragment->children; + + while (node != NULL) { + node->parent = parentNode; + + if (node == fragment->last) { + break; + } + node = node->next; + } +} + +/* This part is common logic between the pre-insertion validity and replaceChild code. */ +static bool dom_fragment_common_hierarchy_check_part(xmlNodePtr node, bool *seen_element) +{ + /* If node has more than one element child or has a Text node child. */ + xmlNodePtr iter = node->children; + *seen_element = false; + while (iter != NULL) { + if (iter->type == XML_ELEMENT_NODE) { + if (*seen_element) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot have more than one element child in a document", /* strict */ true); + return false; + } + *seen_element = true; + } else if (iter->type == XML_TEXT_NODE || iter->type == XML_CDATA_SECTION_NODE) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot insert text as a child of a document", /* strict */ true); + return false; + } + iter = iter->next; + } + return true; +} + +/* https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity + * DocumentFragment validation part. */ +bool php_dom_fragment_insertion_hierarchy_check_pre_insertion(xmlNodePtr parent, xmlNodePtr node, xmlNodePtr child) +{ + bool seen_element; + if (!dom_fragment_common_hierarchy_check_part(node, &seen_element)) { + return false; + } + + /* Otherwise, if node has one element child + * and either parent has an element child, child is a doctype, or child is non-null and a doctype is following child. */ + if (seen_element) { + if (php_dom_has_child_of_type(parent, XML_ELEMENT_NODE)) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot have more than one element child in a document", /* strict */ true); + return false; + } + + if (child != NULL && (child->type == XML_DTD_NODE || php_dom_has_sibling_following_node(child, XML_DTD_NODE))) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Document types must be the first child in a document", /* strict */ true); + return false; + } + } + + return true; +} + +/* https://dom.spec.whatwg.org/#concept-node-replace + * DocumentFragment validation part. */ +bool php_dom_fragment_insertion_hierarchy_check_replace(xmlNodePtr parent, xmlNodePtr node, xmlNodePtr child) +{ + bool seen_element; + if (!dom_fragment_common_hierarchy_check_part(node, &seen_element)) { + return false; + } + + /* Otherwise, if node has one element child + * and either parent has an element child that is not child or a doctype is following child. */ + if (seen_element) { + xmlNodePtr iter = parent->children; + while (iter != NULL) { + if (iter->type == XML_ELEMENT_NODE && iter != child) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot have more than one element child in a document", /* strict */ true); + return false; + } + iter = iter->next; + } + + ZEND_ASSERT(child != NULL); + if (php_dom_has_sibling_following_node(child, XML_DTD_NODE)) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Document types must be the first child in a document", /* strict */ true); + return false; + } + } + + return true; +} + +/* https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity */ +bool php_dom_pre_insert_is_parent_invalid(xmlNodePtr parent) +{ + return parent->type != XML_DOCUMENT_NODE + && parent->type != XML_HTML_DOCUMENT_NODE + && parent->type != XML_ELEMENT_NODE + && parent->type != XML_DOCUMENT_FRAG_NODE; +} + +/* https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity */ +static bool dom_is_pre_insert_valid_without_step_1(php_libxml_ref_obj *document, xmlNodePtr parentNode, xmlNodePtr node, xmlNodePtr child, xmlDocPtr documentNode) +{ + ZEND_ASSERT(parentNode != NULL); + + /* 1. If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException. + * => Impossible */ + ZEND_ASSERT(!php_dom_pre_insert_is_parent_invalid(parentNode)); + + if (node->doc != documentNode) { + php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(document)); + return false; + } + + /* 3. If child is non-null and its parent is not parent, then throw a "NotFoundError" DOMException. */ + if (child != NULL && child->parent != parentNode) { + php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(document)); + return false; + } + + bool parent_is_document = parentNode->type == XML_DOCUMENT_NODE || parentNode->type == XML_HTML_DOCUMENT_NODE; + + if (/* 2. If node is a host-including inclusive ancestor of parent, then throw a "HierarchyRequestError" DOMException. */ + dom_hierarchy(parentNode, node) != SUCCESS + /* 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException. */ + || node->type == XML_ATTRIBUTE_NODE + || (php_dom_follow_spec_doc_ref(document) && ( + node->type == XML_ENTITY_REF_NODE + || node->type == XML_ENTITY_NODE + || node->type == XML_NOTATION_NODE + || node->type == XML_DOCUMENT_NODE + || node->type == XML_HTML_DOCUMENT_NODE + || node->type >= XML_ELEMENT_DECL))) { + php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(document)); + return false; + } + + if (php_dom_follow_spec_doc_ref(document)) { + /* 5. If either node is a Text node and parent is a document... */ + if (parent_is_document && (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE)) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot insert text as a child of a document", /* strict */ true); + return false; + } + + /* 5. ..., or node is a doctype and parent is not a document, then throw a "HierarchyRequestError" DOMException. */ + if (!parent_is_document && node->type == XML_DTD_NODE) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot insert a document type into anything other than a document", /* strict */ true); + return false; + } + + /* 6. If parent is a document, and any of the statements below, switched on the interface node implements, + * are true, then throw a "HierarchyRequestError" DOMException. */ + if (parent_is_document) { + /* DocumentFragment */ + if (node->type == XML_DOCUMENT_FRAG_NODE) { + if (!php_dom_fragment_insertion_hierarchy_check_pre_insertion(parentNode, node, child)) { + return false; + } + } + /* Element */ + else if (node->type == XML_ELEMENT_NODE) { + /* parent has an element child, child is a doctype, or child is non-null and a doctype is following child. */ + if (php_dom_has_child_of_type(parentNode, XML_ELEMENT_NODE)) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot have more than one element child in a document", /* strict */ true); + return false; + } + if (child != NULL && (child->type == XML_DTD_NODE || php_dom_has_sibling_following_node(child, XML_DTD_NODE))) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Document types must be the first child in a document", /* strict */ true); + return false; + } + } + /* DocumentType */ + else if (node->type == XML_DTD_NODE) { + /* parent has a doctype child, child is non-null and an element is preceding child, or child is null and parent has an element child. */ + if (php_dom_has_child_of_type(parentNode, XML_DTD_NODE)) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Cannot have more than one document type", /* strict */ true); + return false; + } + if ((child != NULL && php_dom_has_sibling_preceding_node(child, XML_ELEMENT_NODE)) + || (child == NULL && php_dom_has_child_of_type(parentNode, XML_ELEMENT_NODE))) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Document types must be the first child in a document", /* strict */ true); + return false; + } + } + } + } + + return true; +} + +static void dom_free_node_after_zval_single_node_creation(xmlNodePtr node) +{ + /* For the object cases, the user did provide them, so they don't have to be freed as there are still references. + * For the newly created text nodes, we do have to free them. */ + xmlNodePtr next; + for (xmlNodePtr child = node->children; child != NULL; child = next) { + next = child->next; + xmlUnlinkNode(child); + if (child->_private == NULL) { + xmlFreeNode(child); + } + } +} + +/* https://dom.spec.whatwg.org/#converting-nodes-into-a-node */ +xmlNode* dom_zvals_to_single_node(php_libxml_ref_obj *document, xmlNode *contextNode, zval *nodes, uint32_t nodesc) { xmlDoc *documentNode; - xmlNode *fragment; xmlNode *newNode; dom_object *newNodeObj; documentNode = dom_doc_from_context_node(contextNode); - fragment = xmlNewDocFragment(documentNode); + /* 1. Let node be null. */ + xmlNodePtr node = NULL; - if (!fragment) { + /* 2. => handled in the loop. */ + + /* 3. If nodes contains one node, then set node to nodes[0]. */ + if (nodesc == 1) { + /* ... and return */ + if (Z_TYPE_P(nodes) == IS_OBJECT) { + return dom_object_get_node(Z_DOMOBJ_P(nodes)); + } else { + ZEND_ASSERT(Z_TYPE_P(nodes) == IS_STRING); + return xmlNewDocTextLen(documentNode, BAD_CAST Z_STRVAL_P(nodes), Z_STRLEN_P(nodes)); + } + } + + node = xmlNewDocFragment(documentNode); + if (UNEXPECTED(!node)) { return NULL; } + /* 4. Otherwise, set node to a new DocumentFragment node whose node document is document, + * and then append each node in nodes, if any, to it. */ for (uint32_t i = 0; i < nodesc; i++) { if (Z_TYPE(nodes[i]) == IS_OBJECT) { newNodeObj = Z_DOMOBJ_P(&nodes[i]); newNode = dom_object_get_node(newNodeObj); + if (!dom_is_pre_insert_valid_without_step_1(document, node, newNode, NULL, documentNode)) { + goto err; + } + if (newNode->parent != NULL) { xmlUnlinkNode(newNode); } @@ -173,99 +418,52 @@ xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNod newNodeObj->document = document; xmlSetTreeDoc(newNode, documentNode); - /* Citing from the docs (https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/libxml2-tree.html#xmlAddChild): - * "Add a new node to @parent, at the end of the child (or property) list merging adjacent TEXT nodes (in which case @cur is freed)". - * So we must take a copy if this situation arises to prevent a use-after-free. */ - bool will_free = newNode->type == XML_TEXT_NODE && fragment->last && fragment->last->type == XML_TEXT_NODE; - if (will_free) { - newNode = xmlCopyNode(newNode, 1); - } - if (newNode->type == XML_DOCUMENT_FRAG_NODE) { /* Unpack document fragment nodes, the behaviour differs for different libxml2 versions. */ newNode = newNode->children; while (newNode) { xmlNodePtr next = newNode->next; xmlUnlinkNode(newNode); - if (!xmlAddChild(fragment, newNode)) { - goto err; - } + dom_add_child_without_merging(node, newNode); newNode = next; } - } else if (!xmlAddChild(fragment, newNode)) { - if (will_free) { - xmlFreeNode(newNode); - } - goto err; + } else { + dom_add_child_without_merging(node, newNode); } } else { + /* 2. Replace each string in nodes with a new Text node whose data is the string and node document is document. */ ZEND_ASSERT(Z_TYPE(nodes[i]) == IS_STRING); - newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[i])); - - if (!xmlAddChild(fragment, newNode)) { - xmlFreeNode(newNode); - goto err; - } + /* Text nodes can't violate the hierarchy at this point. */ + newNode = xmlNewDocTextLen(documentNode, BAD_CAST Z_STRVAL(nodes[i]), Z_STRLEN(nodes[i])); + dom_add_child_without_merging(node, newNode); } } - return fragment; + /* 5. Return node. */ + return node; err: - xmlFreeNode(fragment); + /* For the object cases, the user did provide them, so they don't have to be freed as there are still references. + * For the newly created text nodes, we do have to free them. */ + dom_free_node_after_zval_single_node_creation(node); + xmlFree(node); return NULL; } -static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fragment) -{ - xmlNodePtr node = fragment->children; - - while (node != NULL) { - node->parent = parentNode; - - if (node == fragment->last) { - break; - } - node = node->next; - } - - fragment->children = NULL; - fragment->last = NULL; -} - -static zend_result dom_sanity_check_node_list_for_insertion(php_libxml_ref_obj *document, xmlNodePtr parentNode, zval *nodes, int nodesc) +static zend_result dom_sanity_check_node_list_types(zval *nodes, int nodesc, zend_class_entry *node_ce) { - if (UNEXPECTED(parentNode == NULL)) { - /* No error required, this must be a no-op per spec */ - return FAILURE; - } - - xmlDocPtr documentNode = dom_doc_from_context_node(parentNode); - for (uint32_t i = 0; i < nodesc; i++) { zend_uchar type = Z_TYPE(nodes[i]); if (type == IS_OBJECT) { const zend_class_entry *ce = Z_OBJCE(nodes[i]); - if (instanceof_function(ce, dom_node_class_entry)) { - xmlNodePtr node = dom_object_get_node(Z_DOMOBJ_P(nodes + i)); - - if (node->doc != documentNode) { - php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(document)); - return FAILURE; - } - - if (node->type == XML_ATTRIBUTE_NODE || dom_hierarchy(parentNode, node) != SUCCESS) { - php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(document)); - return FAILURE; - } - } else { - zend_argument_type_error(i + 1, "must be of type DOMNode|string, %s given", zend_zval_type_name(&nodes[i])); + if (!instanceof_function(ce, node_ce)) { + zend_argument_type_error(i + 1, "must be of type %s|string, %s given", ZSTR_VAL(node_ce->name), zend_zval_type_name(&nodes[i])); return FAILURE; } } else if (type != IS_STRING) { - zend_argument_type_error(i + 1, "must be of type DOMNode|string, %s given", zend_zval_type_name(&nodes[i])); + zend_argument_type_error(i + 1, "must be of type %s|string, %s given", ZSTR_VAL(node_ce->name), zend_zval_type_name(&nodes[i])); return FAILURE; } } @@ -273,7 +471,7 @@ static zend_result dom_sanity_check_node_list_for_insertion(php_libxml_ref_obj * return SUCCESS; } -static void dom_pre_insert(xmlNodePtr insertion_point, xmlNodePtr parentNode, xmlNodePtr newchild, xmlNodePtr fragment) +static void php_dom_pre_insert_helper(xmlNodePtr insertion_point, xmlNodePtr parentNode, xmlNodePtr newchild, xmlNodePtr last) { if (!insertion_point) { /* Place it as last node */ @@ -285,61 +483,136 @@ static void dom_pre_insert(xmlNodePtr insertion_point, xmlNodePtr parentNode, xm /* No children, because they moved out when they became a fragment */ parentNode->children = newchild; } - parentNode->last = fragment->last; + parentNode->last = last; } else { /* Insert fragment before insertion_point */ - fragment->last->next = insertion_point; + last->next = insertion_point; if (insertion_point->prev) { insertion_point->prev->next = newchild; newchild->prev = insertion_point->prev; } - insertion_point->prev = fragment->last; + insertion_point->prev = last; if (parentNode->children == insertion_point) { parentNode->children = newchild; } } } -void dom_parent_node_append(dom_object *context, zval *nodes, uint32_t nodesc) +static void dom_insert_node_list_cleanup(xmlNodePtr node) { - xmlNode *parentNode = dom_object_get_node(context); - xmlNodePtr newchild, prevsib; - - if (UNEXPECTED(dom_sanity_check_node_list_for_insertion(context->document, parentNode, nodes, nodesc) != SUCCESS)) { + if (node->_private != NULL) { + /* Not a temporary node. */ return; } + if (node->type == XML_DOCUMENT_FRAG_NODE) { + dom_free_node_after_zval_single_node_creation(node); + xmlFree(node); /* Don't free the children, now-empty fragment! */ + } else if (node->type == XML_TEXT_NODE) { + ZEND_ASSERT(node->parent == NULL); + xmlFreeNode(node); + } else { + /* Must have been a directly-passed node. */ + ZEND_ASSERT(node->_private != NULL); + } +} - php_libxml_invalidate_node_list_cache(context->document); +/* https://dom.spec.whatwg.org/#concept-node-pre-insert */ +static void dom_insert_node_list_unchecked(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent, xmlNodePtr insertion_point) +{ + /* Step 1 should be checked by the caller. */ - xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); + if (node->type == XML_DOCUMENT_FRAG_NODE) { + /* Steps 2-3 are not applicable here, the condition is impossible. */ - if (fragment == NULL) { - return; - } + xmlNodePtr newchild = node->children; - newchild = fragment->children; - prevsib = parentNode->last; + /* 4. Insert node into parent before referenceChild (i.e. insertion_point here because of the impossible condition). */ + if (newchild) { + xmlNodePtr last = node->last; + php_dom_pre_insert_helper(insertion_point, parent, newchild, last); + dom_fragment_assign_parent_node(parent, node); + if (!php_dom_follow_spec_doc_ref(document)) { + dom_reconcile_ns_list(parent->doc, newchild, last); + } + if (parent->doc && newchild->type == XML_DTD_NODE) { + parent->doc->intSubset = (xmlDtdPtr) newchild; + newchild->parent = (xmlNodePtr) parent->doc; + } + } - if (newchild) { - if (prevsib != NULL) { - prevsib->next = newchild; + if (node->_private == NULL) { + xmlFree(node); } else { - parentNode->children = newchild; + node->children = NULL; + node->last = NULL; + } + } else { + /* 2. Let referenceChild be child. + * 3. If referenceChild is node, then set referenceChild to node’s next sibling. */ + if (insertion_point == node) { + insertion_point = node->next; } - xmlNodePtr last = fragment->last; - parentNode->last = last; + /* 4. Insert node into parent before referenceChild. */ + xmlUnlinkNode(node); + php_dom_pre_insert_helper(insertion_point, parent, node, node); + node->parent = parent; + if (parent->doc && node->type == XML_DTD_NODE) { + parent->doc->intSubset = (xmlDtdPtr) node; + node->parent = (xmlNodePtr) parent->doc; + } else { + if (!php_dom_follow_spec_doc_ref(document)) { + dom_reconcile_ns(parent->doc, node); + } + } + } +} - newchild->prev = prevsib; +/* https://dom.spec.whatwg.org/#concept-node-pre-insert */ +bool php_dom_pre_insert(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent, xmlNodePtr insertion_point) +{ + if (UNEXPECTED(node == NULL)) { + return false; + } + + /* Step 1 checked here, other steps delegated to other function. */ + if (dom_is_pre_insert_valid_without_step_1(document, parent, node, insertion_point, parent->doc)) { + dom_insert_node_list_unchecked(document, node, parent, insertion_point); + return true; + } else { + dom_insert_node_list_cleanup(node); + return false; + } +} + +/* https://dom.spec.whatwg.org/#concept-node-append */ +void php_dom_node_append(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent) +{ + php_dom_pre_insert(document, node, parent, NULL); +} + +/* https://dom.spec.whatwg.org/#dom-parentnode-append */ +void dom_parent_node_append(dom_object *context, zval *nodes, uint32_t nodesc) +{ + if (UNEXPECTED(dom_sanity_check_node_list_types(nodes, nodesc, dom_get_node_ce(php_dom_follow_spec_doc_ref(context->document))) != SUCCESS)) { + return; + } - dom_fragment_assign_parent_node(parentNode, fragment); + xmlNode *parentNode = dom_object_get_node(context); + + php_libxml_invalidate_node_list_cache(context->document); - dom_reconcile_ns_list(parentNode->doc, newchild, last); + /* 1. Let node be the result of converting nodes into a node given nodes and this’s node document. */ + xmlNodePtr node = dom_zvals_to_single_node(context->document, parentNode, nodes, nodesc); + if (UNEXPECTED(node == NULL)) { + return; } - xmlFree(fragment); + /* 2. Append node to this. */ + php_dom_node_append(context->document, node, parentNode); } +/* https://dom.spec.whatwg.org/#dom-parentnode-prepend */ void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc) { xmlNode *parentNode = dom_object_get_node(context); @@ -349,141 +622,91 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc) return; } - if (UNEXPECTED(dom_sanity_check_node_list_for_insertion(context->document, parentNode, nodes, nodesc) != SUCCESS)) { + if (UNEXPECTED(dom_sanity_check_node_list_types(nodes, nodesc, dom_get_node_ce(php_dom_follow_spec_doc_ref(context->document))) != SUCCESS)) { return; } php_libxml_invalidate_node_list_cache(context->document); - xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); - - if (fragment == NULL) { + /* 1. Let node be the result of converting nodes into a node given nodes and this’s node document. */ + xmlNodePtr node = dom_zvals_to_single_node(context->document, parentNode, nodes, nodesc); + if (UNEXPECTED(node == NULL)) { return; } - xmlNode *newchild = fragment->children; - - if (newchild) { - xmlNodePtr last = fragment->last; - - dom_pre_insert(parentNode->children, parentNode, newchild, fragment); - - dom_fragment_assign_parent_node(parentNode, fragment); - - dom_reconcile_ns_list(parentNode->doc, newchild, last); - } - - xmlFree(fragment); + /* 2. Pre-insert node into this before this’s first child. */ + php_dom_pre_insert(context->document, node, parentNode, parentNode->children); } +/* https://dom.spec.whatwg.org/#dom-childnode-after */ void dom_parent_node_after(dom_object *context, zval *nodes, uint32_t nodesc) { - /* Spec link: https://dom.spec.whatwg.org/#dom-childnode-after */ + if (UNEXPECTED(dom_sanity_check_node_list_types(nodes, nodesc, dom_get_node_ce(php_dom_follow_spec_doc_ref(context->document))) != SUCCESS)) { + return; + } - xmlNode *prevsib = dom_object_get_node(context); - xmlNodePtr newchild, parentNode; - xmlNode *fragment; - xmlDoc *doc; + xmlNode *thisp = dom_object_get_node(context); - /* Spec step 1 */ - parentNode = prevsib->parent; + /* 1. Let parent be this’s parent. */ + xmlNodePtr parentNode = thisp->parent; - /* Sanity check for fragment, includes spec step 2 */ - if (UNEXPECTED(dom_sanity_check_node_list_for_insertion(context->document, parentNode, nodes, nodesc) != SUCCESS)) { + /* 2. If parent is null, then return. */ + if (UNEXPECTED(parentNode == NULL)) { return; } - /* Spec step 3: find first following child not in nodes; otherwise null */ - xmlNodePtr viable_next_sibling = prevsib->next; - while (viable_next_sibling) { - if (!dom_is_node_in_list(nodes, nodesc, viable_next_sibling)) { - break; - } + /* 3. Let viableNextSibling be this’s first following sibling not in nodes; otherwise null. */ + xmlNodePtr viable_next_sibling = thisp->next; + while (viable_next_sibling && dom_is_node_in_list(nodes, nodesc, viable_next_sibling)) { viable_next_sibling = viable_next_sibling->next; } - doc = prevsib->doc; - php_libxml_invalidate_node_list_cache(context->document); - /* Spec step 4: convert nodes into fragment */ - fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); - - if (fragment == NULL) { - return; - } - - newchild = fragment->children; - - if (newchild) { - xmlNodePtr last = fragment->last; + /* 4. Let node be the result of converting nodes into a node, given nodes and this’s node document. */ + xmlNodePtr fragment = dom_zvals_to_single_node(context->document, parentNode, nodes, nodesc); - /* Step 5: place fragment into the parent before viable_next_sibling */ - dom_pre_insert(viable_next_sibling, parentNode, newchild, fragment); - - dom_fragment_assign_parent_node(parentNode, fragment); - dom_reconcile_ns_list(doc, newchild, last); - } - - xmlFree(fragment); + /* 5. Pre-insert node into parent before viableNextSibling. */ + php_dom_pre_insert(context->document, fragment, parentNode, viable_next_sibling); } +/* https://dom.spec.whatwg.org/#dom-childnode-before */ void dom_parent_node_before(dom_object *context, zval *nodes, uint32_t nodesc) { - /* Spec link: https://dom.spec.whatwg.org/#dom-childnode-before */ + if (UNEXPECTED(dom_sanity_check_node_list_types(nodes, nodesc, dom_get_node_ce(php_dom_follow_spec_doc_ref(context->document))) != SUCCESS)) { + return; + } - xmlNode *nextsib = dom_object_get_node(context); - xmlNodePtr newchild, parentNode; - xmlNode *fragment; - xmlDoc *doc; + xmlNode *thisp = dom_object_get_node(context); - /* Spec step 1 */ - parentNode = nextsib->parent; + /* 1. Let parent be this’s parent. */ + xmlNodePtr parentNode = thisp->parent; - /* Sanity check for fragment, includes spec step 2 */ - if (UNEXPECTED(dom_sanity_check_node_list_for_insertion(context->document, parentNode, nodes, nodesc) != SUCCESS)) { + /* 2. If parent is null, then return. */ + if (UNEXPECTED(parentNode == NULL)) { return; } - /* Spec step 3: find first following child not in nodes; otherwise null */ - xmlNodePtr viable_previous_sibling = nextsib->prev; - while (viable_previous_sibling) { - if (!dom_is_node_in_list(nodes, nodesc, viable_previous_sibling)) { - break; - } + /* 3. Let viablePreviousSibling be this’s first preceding sibling not in nodes; otherwise null. */ + xmlNodePtr viable_previous_sibling = thisp->prev; + while (viable_previous_sibling && dom_is_node_in_list(nodes, nodesc, viable_previous_sibling)) { viable_previous_sibling = viable_previous_sibling->prev; } - doc = nextsib->doc; - php_libxml_invalidate_node_list_cache(context->document); - /* Spec step 4: convert nodes into fragment */ - fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); - - if (fragment == NULL) { - return; - } - - newchild = fragment->children; - - if (newchild) { - xmlNodePtr last = fragment->last; + /* 4. Let node be the result of converting nodes into a node, given nodes and this’s node document. */ + xmlNodePtr fragment = dom_zvals_to_single_node(context->document, parentNode, nodes, nodesc); - /* Step 5: if viable_previous_sibling is null, set it to the parent's first child, otherwise viable_previous_sibling's next sibling */ - if (!viable_previous_sibling) { - viable_previous_sibling = parentNode->children; - } else { - viable_previous_sibling = viable_previous_sibling->next; - } - /* Step 6: place fragment into the parent after viable_previous_sibling */ - dom_pre_insert(viable_previous_sibling, parentNode, newchild, fragment); - - dom_fragment_assign_parent_node(parentNode, fragment); - dom_reconcile_ns_list(doc, newchild, last); + /* 5. If viable_previous_sibling is null, set it to the parent's first child, otherwise viable_previous_sibling's next sibling. */ + if (!viable_previous_sibling) { + viable_previous_sibling = parentNode->children; + } else { + viable_previous_sibling = viable_previous_sibling->next; } - xmlFree(fragment); + /* 6. Pre-insert node into parent before viablePreviousSibling. */ + php_dom_pre_insert(context->document, fragment, parentNode, viable_previous_sibling); } static zend_result dom_child_removal_preconditions(const xmlNodePtr child, int stricterror) @@ -499,7 +722,7 @@ static zend_result dom_child_removal_preconditions(const xmlNodePtr child, int s return FAILURE; } - if (dom_node_children_valid(child->parent) == FAILURE) { + if (!dom_node_children_valid(child->parent)) { return FAILURE; } @@ -528,95 +751,80 @@ void dom_child_node_remove(dom_object *context) xmlUnlinkNode(child); } +/* https://dom.spec.whatwg.org/#dom-childnode-replacewith */ void dom_child_replace_with(dom_object *context, zval *nodes, uint32_t nodesc) { - /* Spec link: https://dom.spec.whatwg.org/#dom-childnode-replacewith */ + if (UNEXPECTED(dom_sanity_check_node_list_types(nodes, nodesc, dom_get_node_ce(php_dom_follow_spec_doc_ref(context->document))) != SUCCESS)) { + return; + } xmlNodePtr child = dom_object_get_node(context); - /* Spec step 1 */ + /* 1. Let parent be this’s parent. */ xmlNodePtr parentNode = child->parent; - /* Sanity check for fragment, includes spec step 2 */ - if (UNEXPECTED(dom_sanity_check_node_list_for_insertion(context->document, parentNode, nodes, nodesc) != SUCCESS)) { - return; - } - - int stricterror = dom_get_strict_error(context->document); - if (UNEXPECTED(dom_child_removal_preconditions(child, stricterror) != SUCCESS)) { + /* 2. If parent is null, then return. */ + if (UNEXPECTED(parentNode == NULL)) { return; } - /* Spec step 3: find first following child not in nodes; otherwise null */ + /* 3. Let viableNextSibling be this’s first following sibling not in nodes; otherwise null. */ xmlNodePtr viable_next_sibling = child->next; - while (viable_next_sibling) { - if (!dom_is_node_in_list(nodes, nodesc, viable_next_sibling)) { - break; - } + while (viable_next_sibling && dom_is_node_in_list(nodes, nodesc, viable_next_sibling)) { viable_next_sibling = viable_next_sibling->next; } - xmlDocPtr doc = parentNode->doc; - php_libxml_invalidate_node_list_cache(context->document); - - /* Spec step 4: convert nodes into fragment */ - xmlNodePtr fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); - if (UNEXPECTED(fragment == NULL)) { + bool stricterror = dom_get_strict_error(context->document); + if (UNEXPECTED(dom_child_removal_preconditions(child, stricterror) != SUCCESS)) { return; } - /* Spec step 5: perform the replacement */ - - xmlNodePtr newchild = fragment->children; + php_libxml_invalidate_node_list_cache(context->document); - /* Unlink it unless it became a part of the fragment. - * Freeing will be taken care of by the lifetime of the returned dom object. */ - if (child->parent != fragment) { - xmlUnlinkNode(child); + /* 4. Let node be the result of converting nodes into a node, given nodes and this’s node document. */ + xmlNodePtr node = dom_zvals_to_single_node(context->document, parentNode, nodes, nodesc); + if (UNEXPECTED(node == NULL)) { + return; } - if (newchild) { - xmlNodePtr last = fragment->last; - - dom_pre_insert(viable_next_sibling, parentNode, newchild, fragment); + /* Spec step 5-6: perform the replacement */ + if (dom_is_pre_insert_valid_without_step_1(context->document, parentNode, node, viable_next_sibling, parentNode->doc)) { + /* Unlink it unless it became a part of the fragment. + * Freeing will be taken care of by the lifetime of the returned dom object. */ + if (child->parent != node) { + xmlUnlinkNode(child); + } - dom_fragment_assign_parent_node(parentNode, fragment); - dom_reconcile_ns_list(doc, newchild, last); + dom_insert_node_list_unchecked(context->document, node, parentNode, viable_next_sibling); + } else { + dom_insert_node_list_cleanup(node); } - - xmlFree(fragment); } +/* https://dom.spec.whatwg.org/#dom-parentnode-replacechildren */ void dom_parent_node_replace_children(dom_object *context, zval *nodes, uint32_t nodesc) { - /* Spec link: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren */ - - xmlNodePtr thisp = dom_object_get_node(context); - /* Note: Only rule 2 of pre-insertion validity can be broken */ - if (UNEXPECTED(dom_sanity_check_node_list_for_insertion(context->document, thisp, nodes, nodesc) != SUCCESS)) { + if (UNEXPECTED(dom_sanity_check_node_list_types(nodes, nodesc, dom_get_node_ce(php_dom_follow_spec_doc_ref(context->document))) != SUCCESS)) { return; } - xmlNodePtr fragment = dom_zvals_to_fragment(context->document, thisp, nodes, nodesc); - if (UNEXPECTED(fragment == NULL)) { - return; - } + xmlNodePtr thisp = dom_object_get_node(context); php_libxml_invalidate_node_list_cache(context->document); - dom_remove_all_children(thisp); - - xmlNodePtr newchild = fragment->children; - if (newchild) { - xmlNodePtr last = fragment->last; - - dom_pre_insert(NULL, thisp, newchild, fragment); - - dom_fragment_assign_parent_node(thisp, fragment); - dom_reconcile_ns_list(thisp->doc, newchild, last); + /* 1. Let node be the result of converting nodes into a node given nodes and this’s node document. */ + xmlNodePtr node = dom_zvals_to_single_node(context->document, thisp, nodes, nodesc); + if (UNEXPECTED(node == NULL)) { + return; } - xmlFree(fragment); + /* Spec steps 2-3: replace all */ + if (dom_is_pre_insert_valid_without_step_1(context->document, thisp, node, NULL, thisp->doc)) { + dom_remove_all_children(thisp); + php_dom_pre_insert(context->document, node, thisp, NULL); + } else { + dom_insert_node_list_cleanup(node); + } } #endif diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 1c7e2691742fd..be3ef067876fe 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -23,6 +23,8 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "namespace_compat.h" +#include "internal_helpers.h" #include "php_dom_arginfo.h" #include "dom_properties.h" #include "zend_interfaces.h" @@ -33,38 +35,62 @@ /* {{{ class entries */ PHP_DOM_EXPORT zend_class_entry *dom_node_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_node_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domexception_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_parentnode_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_parentnode_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_childnode_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_childnode_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_domimplementation_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_documentfragment_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_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_modern_nodelist_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_namednodemap_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_namednodemap_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_dtd_namednodemap_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_html_collection_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_characterdata_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_characterdata_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_attr_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_attr_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_element_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_element_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_text_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_text_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_comment_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_comment_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_cdatasection_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_cdatasection_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_documenttype_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_documenttype_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_notation_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_notation_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_entity_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_entity_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_entityreference_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_entityreference_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_processinginstruction_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_modern_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; +PHP_DOM_EXPORT zend_class_entry *dom_modern_xpath_class_entry; #endif PHP_DOM_EXPORT zend_class_entry *dom_namespace_node_class_entry; /* }}} */ -zend_object_handlers dom_object_handlers; -zend_object_handlers dom_nnodemap_object_handlers; -zend_object_handlers dom_nodelist_object_handlers; -zend_object_handlers dom_object_namespace_node_handlers; +static zend_object_handlers dom_object_handlers; +static zend_object_handlers dom_nnodemap_object_handlers; +static zend_object_handlers dom_nodelist_object_handlers; +static zend_object_handlers dom_modern_nnodemap_object_handlers; +static zend_object_handlers dom_modern_nodelist_object_handlers; +static zend_object_handlers dom_object_namespace_node_handlers; +static zend_object_handlers dom_modern_domimplementation_object_handlers; #ifdef LIBXML_XPATH_ENABLED zend_object_handlers dom_xpath_object_handlers; #endif @@ -73,19 +99,30 @@ 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_abstract_base_document_prop_handlers; static HashTable dom_documentfragment_prop_handlers; +static HashTable dom_modern_documentfragment_prop_handlers; static HashTable dom_node_prop_handlers; +static HashTable dom_modern_node_prop_handlers; static HashTable dom_nodelist_prop_handlers; static HashTable dom_namednodemap_prop_handlers; static HashTable dom_characterdata_prop_handlers; +static HashTable dom_modern_characterdata_prop_handlers; static HashTable dom_attr_prop_handlers; +static HashTable dom_modern_attr_prop_handlers; static HashTable dom_element_prop_handlers; +static HashTable dom_modern_element_prop_handlers; +static HashTable dom_modern_element_prop_handlers; static HashTable dom_text_prop_handlers; +static HashTable dom_modern_text_prop_handlers; static HashTable dom_documenttype_prop_handlers; +static HashTable dom_modern_documenttype_prop_handlers; static HashTable dom_notation_prop_handlers; +static HashTable dom_modern_notation_prop_handlers; static HashTable dom_entity_prop_handlers; +static HashTable dom_modern_entity_prop_handlers; static HashTable dom_processinginstruction_prop_handlers; +static HashTable dom_modern_processinginstruction_prop_handlers; static HashTable dom_namespace_node_prop_handlers; #ifdef LIBXML_XPATH_ENABLED static HashTable dom_xpath_prop_handlers; @@ -128,8 +165,7 @@ int dom_node_is_read_only(xmlNodePtr node) { } /* }}} end dom_node_is_read_only */ -/* {{{ int dom_node_children_valid(xmlNodePtr node) */ -int dom_node_children_valid(xmlNodePtr node) { +bool dom_node_children_valid(xmlNodePtr node) { switch (node->type) { case XML_DOCUMENT_TYPE_NODE: case XML_DTD_NODE: @@ -138,13 +174,11 @@ int dom_node_children_valid(xmlNodePtr node) { case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_NOTATION_NODE: - return FAILURE; - break; + return false; default: - return SUCCESS; + return true; } } -/* }}} end dom_node_children_valid */ static const libxml_doc_props default_doc_props = { .formatoutput = false, @@ -184,7 +218,7 @@ libxml_doc_props const* dom_get_doc_props_read_only(const php_libxml_ref_obj *do } } -static void dom_copy_doc_props(php_libxml_ref_obj *source_doc, php_libxml_ref_obj *dest_doc) +static void dom_copy_document_ref(php_libxml_ref_obj *source_doc, php_libxml_ref_obj *dest_doc) { dom_doc_propsptr dest; @@ -206,7 +240,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; + dest_doc->class_type = source_doc->class_type; } } @@ -282,11 +316,21 @@ static void dom_register_prop_handler(HashTable *prop_handler, const char *name, zend_string_release_ex(str, true); } +static void dom_overwrite_prop_handler(HashTable *prop_handler, const char *name, size_t name_len, const dom_prop_handler *hnd) +{ + zend_hash_str_update_ptr(prop_handler, name, name_len, (void *) hnd); +} + #define DOM_REGISTER_PROP_HANDLER(prop_handler, name, prop_read_func, prop_write_func) do { \ static const dom_prop_handler hnd = {.read_func = prop_read_func, .write_func = prop_write_func}; \ dom_register_prop_handler(prop_handler, "" name, sizeof("" name) - 1, &hnd); \ } while (0) +#define DOM_OVERWRITE_PROP_HANDLER(prop_handler, name, prop_read_func, prop_write_func) do { \ + static const dom_prop_handler hnd = {.read_func = prop_read_func, .write_func = prop_write_func}; \ + dom_overwrite_prop_handler(prop_handler, "" name, sizeof("" name) - 1, &hnd); \ + } while (0) + static zval *dom_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot) { dom_object *obj = php_dom_obj_from_obj(object); @@ -335,23 +379,20 @@ zval *dom_write_property(zend_object *object, zend_string *name, zval *value, vo if (hnd) { if (!hnd->write_func) { - zend_throw_error(NULL, "Cannot write read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); + zend_throw_error(NULL, "Cannot modify readonly property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); return &EG(error_zval); } zend_property_info *prop = zend_get_property_info(object->ce, name, /* silent */ true); - if (prop && ZEND_TYPE_IS_SET(prop->type)) { - zval tmp; - ZVAL_COPY(&tmp, value); - if (!zend_verify_property_type(prop, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) { - zval_ptr_dtor(&tmp); - return &EG(error_zval); - } - hnd->write_func(obj, &tmp); + ZEND_ASSERT(prop && ZEND_TYPE_IS_SET(prop->type)); + zval tmp; + ZVAL_COPY(&tmp, value); + if (!zend_verify_property_type(prop, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) { zval_ptr_dtor(&tmp); - } else { - hnd->write_func(obj, value); + return &EG(error_zval); } + hnd->write_func(obj, &tmp); + zval_ptr_dtor(&tmp); return value; } @@ -456,7 +497,7 @@ void *php_dom_export_node(zval *object) /* {{{ */ /* }}} */ /* {{{ Get a simplexml_element object from dom to allow for processing */ -PHP_FUNCTION(dom_import_simplexml) +static void dom_import_simplexml_common(INTERNAL_FUNCTION_PARAMETERS, php_libxml_class_type new_class) { zval *node; xmlNodePtr nodep = NULL; @@ -470,17 +511,55 @@ PHP_FUNCTION(dom_import_simplexml) nodeobj = (php_libxml_node_object *) ((char *) Z_OBJ_P(node) - Z_OBJ_HT_P(node)->offset); nodep = php_libxml_import_node(node); - if (nodep && nodeobj && (nodep->type == XML_ELEMENT_NODE || nodep->type == XML_ATTRIBUTE_NODE)) { + if (nodep && nodeobj && nodeobj->document && (nodep->type == XML_ELEMENT_NODE || nodep->type == XML_ATTRIBUTE_NODE)) { + php_libxml_class_type old_class_type = nodeobj->document->class_type; + if (old_class_type != PHP_LIBXML_CLASS_UNSET && old_class_type != new_class) { + if (new_class == PHP_LIBXML_CLASS_MODERN) { + zend_argument_type_error(1, "must not be already imported as a DOMNode"); + } else { + zend_argument_type_error(1, "must not be already imported as a DOM\\Node"); + } + RETURN_THROWS(); + } + + /* Lock the node class type to prevent creating multiple representations of the same node. */ + nodeobj->document->class_type = new_class; + + if (old_class_type != PHP_LIBXML_CLASS_MODERN && new_class == PHP_LIBXML_CLASS_MODERN && nodep->doc != NULL) { + dom_document_convert_to_modern(nodeobj->document, nodep->doc); + } + DOM_RET_OBJ((xmlNodePtr) nodep, &ret, (dom_object *)nodeobj); } else { - zend_argument_value_error(1, "is not a valid node type"); + zend_argument_type_error(1, "is not a valid node type"); RETURN_THROWS(); } } + +PHP_FUNCTION(dom_import_simplexml) +{ + dom_import_simplexml_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LIBXML_CLASS_LEGACY); +} + +PHP_FUNCTION(DOM_import_simplexml) +{ + dom_import_simplexml_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LIBXML_CLASS_MODERN); +} /* }}} */ static dom_object* dom_objects_set_class(zend_class_entry *class_type); +void php_dom_update_document_after_clone(dom_object *original, xmlNodePtr original_node, dom_object *clone, xmlNodePtr cloned_node) +{ + dom_copy_document_ref(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 +} + static void dom_update_refcount_after_clone(dom_object *original, xmlNodePtr original_node, dom_object *clone, xmlNodePtr cloned_node) { /* If we cloned a document then we must create new doc proxy */ @@ -490,13 +569,7 @@ static void dom_update_refcount_after_clone(dom_object *original, xmlNodePtr ori php_libxml_increment_doc_ref((php_libxml_node_object *)clone, cloned_node->doc); 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 + php_dom_update_document_after_clone(original, original_node, clone, cloned_node); } } @@ -505,16 +578,23 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ dom_object *intern = php_dom_obj_from_obj(zobject); dom_object *clone = dom_objects_set_class(intern->std.ce); - clone->std.handlers = &dom_object_handlers; - - if (instanceof_function(intern->std.ce, dom_node_class_entry)) { + if (instanceof_function(intern->std.ce, dom_node_class_entry) || instanceof_function(intern->std.ce, dom_modern_node_class_entry)) { xmlNodePtr node = (xmlNodePtr)dom_object_get_node(intern); if (node != NULL) { - xmlNodePtr cloned_node = xmlDocCopyNode(node, node->doc, 1); + php_dom_libxml_ns_mapper *ns_mapper = NULL; + if (php_dom_follow_spec_intern(intern)) { + if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) { + ns_mapper = php_dom_libxml_ns_mapper_create(); + } else { + ns_mapper = php_dom_get_ns_mapper(intern); + } + } + + xmlNodePtr cloned_node = dom_clone_node(ns_mapper, node, node->doc, true); if (cloned_node != NULL) { dom_update_refcount_after_clone(intern, node, clone, cloned_node); } - + clone->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper); } } @@ -575,6 +655,10 @@ static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int static int dom_nodelist_has_dimension(zend_object *object, zval *member, int check_empty); static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv); static int dom_nodemap_has_dimension(zend_object *object, zval *member, int check_empty); +static zval *dom_modern_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv); +static int dom_modern_nodemap_has_dimension(zend_object *object, zval *member, int check_empty); +static zval *dom_modern_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv); +static int dom_modern_nodelist_has_dimension(zend_object *object, zval *member, int check_empty); static zend_object *dom_objects_store_clone_obj(zend_object *zobject); #ifdef LIBXML_XPATH_ENABLED @@ -598,22 +682,6 @@ static void dom_free(void *ptr) { efree(ptr); } -static void register_nondeprecated_xml_props(HashTable *table) -{ - DOM_REGISTER_PROP_HANDLER(table, "encoding", dom_document_encoding_read, dom_document_encoding_write); - DOM_REGISTER_PROP_HANDLER(table, "xmlEncoding", dom_document_encoding_read, NULL); - DOM_REGISTER_PROP_HANDLER(table, "standalone", dom_document_standalone_read, dom_document_standalone_write); - DOM_REGISTER_PROP_HANDLER(table, "xmlStandalone", dom_document_standalone_read, dom_document_standalone_write); - DOM_REGISTER_PROP_HANDLER(table, "version", dom_document_version_read, dom_document_version_write); - DOM_REGISTER_PROP_HANDLER(table, "xmlVersion", dom_document_version_read, dom_document_version_write); - DOM_REGISTER_PROP_HANDLER(table, "formatOutput", dom_document_format_output_read, dom_document_format_output_write); - DOM_REGISTER_PROP_HANDLER(table, "validateOnParse", dom_document_validate_on_parse_read, dom_document_validate_on_parse_write); - DOM_REGISTER_PROP_HANDLER(table, "resolveExternals", dom_document_resolve_externals_read, dom_document_resolve_externals_write); - DOM_REGISTER_PROP_HANDLER(table, "preserveWhiteSpace", dom_document_preserve_whitespace_read, dom_document_preserve_whitespace_write); - DOM_REGISTER_PROP_HANDLER(table, "recover", dom_document_recover_read, dom_document_recover_write); - DOM_REGISTER_PROP_HANDLER(table, "substituteEntities", dom_document_substitue_entities_read, dom_document_substitue_entities_write); -} - /* {{{ PHP_MINIT_FUNCTION(dom) */ PHP_MINIT_FUNCTION(dom) { @@ -627,6 +695,11 @@ PHP_MINIT_FUNCTION(dom) dom_object_handlers.has_property = dom_property_exists; dom_object_handlers.get_debug_info = dom_get_debug_info; + memcpy(&dom_modern_domimplementation_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); + /* The IDL has the [SameObject] constraint, which is incompatible with cloning because it imposes that there is only + * one instance per parent object. */ + dom_modern_domimplementation_object_handlers.clone_obj = NULL; + memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage; dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension; @@ -636,6 +709,14 @@ PHP_MINIT_FUNCTION(dom) dom_nodelist_object_handlers.read_dimension = dom_nodelist_read_dimension; dom_nodelist_object_handlers.has_dimension = dom_nodelist_has_dimension; + memcpy(&dom_modern_nnodemap_object_handlers, &dom_nnodemap_object_handlers, sizeof(zend_object_handlers)); + dom_modern_nnodemap_object_handlers.read_dimension = dom_modern_nodemap_read_dimension; + dom_modern_nnodemap_object_handlers.has_dimension = dom_modern_nodemap_has_dimension; + + memcpy(&dom_modern_nodelist_object_handlers, &dom_nodelist_object_handlers, sizeof(zend_object_handlers)); + dom_modern_nodelist_object_handlers.read_dimension = dom_modern_nodelist_read_dimension; + dom_modern_nodelist_object_handlers.has_dimension = dom_modern_nodelist_has_dimension; + memcpy(&dom_object_namespace_node_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_object_namespace_node_handlers.offset = XtOffsetOf(dom_object_namespace_node, dom.std); dom_object_namespace_node_handlers.free_obj = dom_object_namespace_node_free_storage; @@ -646,14 +727,21 @@ PHP_MINIT_FUNCTION(dom) dom_domexception_class_entry = register_class_DOMException(zend_ce_exception); dom_parentnode_class_entry = register_class_DOMParentNode(); - + dom_modern_parentnode_class_entry = register_class_DOM_ParentNode(); dom_childnode_class_entry = register_class_DOMChildNode(); + dom_modern_childnode_class_entry = register_class_DOM_ChildNode(); dom_domimplementation_class_entry = register_class_DOMImplementation(); dom_domimplementation_class_entry->create_object = dom_objects_new; + dom_domimplementation_class_entry->default_object_handlers = &dom_object_handlers; + + dom_modern_domimplementation_class_entry = register_class_DOM_Implementation(); + dom_modern_domimplementation_class_entry->create_object = dom_objects_new; + dom_modern_domimplementation_class_entry->default_object_handlers = &dom_modern_domimplementation_object_handlers; dom_node_class_entry = register_class_DOMNode(); dom_node_class_entry->create_object = dom_objects_new; + dom_node_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_node_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL); @@ -676,8 +764,31 @@ PHP_MINIT_FUNCTION(dom) DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write); zend_hash_add_new_ptr(&classes, dom_node_class_entry->name, &dom_node_prop_handlers); + dom_modern_node_class_entry = register_class_DOM_Node(); + dom_modern_node_class_entry->create_object = dom_objects_new; + dom_modern_node_class_entry->default_object_handlers = &dom_object_handlers; + + zend_hash_init(&dom_modern_node_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "baseURI", dom_node_base_uri_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "childNodes", dom_node_child_nodes_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "firstChild", dom_node_first_child_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "lastChild", dom_node_last_child_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "previousSibling", dom_node_previous_sibling_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nextSibling", dom_node_next_sibling_read, NULL); + /* We will set-up the setter for the derived classes afterwards on a class-by-class basis. */ + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeValue", dom_node_node_value_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "textContent", dom_node_text_content_read, NULL); + zend_hash_add_new_ptr(&classes, dom_modern_node_class_entry->name, &dom_modern_node_prop_handlers); + dom_namespace_node_class_entry = register_class_DOMNameSpaceNode(); dom_namespace_node_class_entry->create_object = dom_objects_namespace_node_new; + dom_namespace_node_class_entry->default_object_handlers = &dom_object_namespace_node_handlers; zend_hash_init(&dom_namespace_node_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL); @@ -694,58 +805,89 @@ PHP_MINIT_FUNCTION(dom) dom_documentfragment_class_entry = register_class_DOMDocumentFragment(dom_node_class_entry, dom_parentnode_class_entry); dom_documentfragment_class_entry->create_object = dom_objects_new; + dom_documentfragment_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_documentfragment_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); - zend_hash_merge(&dom_documentfragment_prop_handlers, &dom_node_prop_handlers, NULL, false); zend_hash_add_new_ptr(&classes, dom_documentfragment_class_entry->name, &dom_documentfragment_prop_handlers); - 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; + dom_modern_documentfragment_class_entry = register_class_DOM_DocumentFragment(dom_modern_node_class_entry, dom_modern_parentnode_class_entry); + dom_modern_documentfragment_class_entry->create_object = dom_objects_new; + dom_modern_documentfragment_class_entry->default_object_handlers = &dom_object_handlers; + zend_hash_init(&dom_modern_documentfragment_prop_handlers, 0, NULL, NULL, true); + + DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); + zend_hash_merge(&dom_modern_documentfragment_prop_handlers, &dom_modern_node_prop_handlers, NULL, false); + DOM_OVERWRITE_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write); + zend_hash_add_new_ptr(&classes, dom_modern_documentfragment_class_entry->name, &dom_modern_documentfragment_prop_handlers); + + dom_abstract_base_document_class_entry = register_class_DOM_Document(dom_modern_node_class_entry, dom_modern_parentnode_class_entry); + dom_abstract_base_document_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_abstract_base_document_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "implementation", dom_modern_document_implementation_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "URL", dom_document_document_uri_read, dom_document_document_uri_write); + DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentURI", dom_document_document_uri_read, dom_document_document_uri_write); + DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "characterSet", dom_document_encoding_read, dom_html_document_encoding_write); + DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "charset", dom_document_encoding_read, dom_html_document_encoding_write); + DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "inputEncoding", dom_document_encoding_read, dom_html_document_encoding_write); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "doctype", dom_document_doctype_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentElement", dom_document_document_element_read, NULL); - DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "strictErrorChecking", dom_document_strict_error_checking_read, dom_document_strict_error_checking_write); - DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentURI", dom_document_document_uri_read, dom_document_document_uri_write); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); - zend_hash_merge(&dom_abstract_base_document_prop_handlers, &dom_node_prop_handlers, NULL, false); - /* No need to register in &classes, because this is only used for merging. This is destroyed down below. */ + zend_hash_merge(&dom_abstract_base_document_prop_handlers, &dom_modern_node_prop_handlers, NULL, false); + /* No need to register in &classes because this is an abstract class handler. */ - dom_document_class_entry = register_class_DOMDocument(dom_abstract_base_document_class_entry); + dom_document_class_entry = register_class_DOMDocument(dom_node_class_entry, dom_parentnode_class_entry); dom_document_class_entry->create_object = dom_objects_new; + dom_document_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_document_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "doctype", dom_document_doctype_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "implementation", dom_document_implementation_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "documentElement", dom_document_document_element_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "actualEncoding", dom_document_encoding_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "encoding", dom_document_encoding_read, dom_document_encoding_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "xmlEncoding", dom_document_encoding_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "standalone", dom_document_standalone_read, dom_document_standalone_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "xmlStandalone", dom_document_standalone_read, dom_document_standalone_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "version", dom_document_version_read, dom_document_version_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "xmlVersion", dom_document_version_read, dom_document_version_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "strictErrorChecking", dom_document_strict_error_checking_read, dom_document_strict_error_checking_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "documentURI", dom_document_document_uri_read, dom_document_document_uri_write); DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "config", dom_document_config_read, NULL); - register_nondeprecated_xml_props(&dom_document_prop_handlers); - - zend_hash_merge(&dom_document_prop_handlers, &dom_abstract_base_document_prop_handlers, NULL, false); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "formatOutput", dom_document_format_output_read, dom_document_format_output_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "validateOnParse", dom_document_validate_on_parse_read, dom_document_validate_on_parse_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "resolveExternals", dom_document_resolve_externals_read, dom_document_resolve_externals_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "preserveWhiteSpace", dom_document_preserve_whitespace_read, dom_document_preserve_whitespace_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "recover", dom_document_recover_read, dom_document_recover_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "substituteEntities", dom_document_substitue_entities_read, dom_document_substitue_entities_write); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); + + zend_hash_merge(&dom_document_prop_handlers, &dom_node_prop_handlers, NULL, false); zend_hash_add_new_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, NULL, true); - DOM_REGISTER_PROP_HANDLER(&dom_html_document_prop_handlers, "encoding", dom_document_encoding_read, dom_html_document_encoding_write); - - zend_hash_merge(&dom_html_document_prop_handlers, &dom_abstract_base_document_prop_handlers, NULL, false); - zend_hash_add_new_ptr(&classes, dom_html_document_class_entry->name, &dom_html_document_prop_handlers); + dom_html_document_class_entry->default_object_handlers = &dom_object_handlers; + zend_hash_add_new_ptr(&classes, dom_html_document_class_entry->name, &dom_abstract_base_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; + dom_xml_document_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_xml_document_prop_handlers, 0, NULL, NULL, true); - register_nondeprecated_xml_props(&dom_xml_document_prop_handlers); + DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "xmlEncoding", dom_document_encoding_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "xmlStandalone", dom_document_standalone_read, dom_document_standalone_write); + DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "xmlVersion", dom_document_version_read, dom_document_version_write); + DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "formatOutput", dom_document_format_output_read, dom_document_format_output_write); zend_hash_merge(&dom_xml_document_prop_handlers, &dom_abstract_base_document_prop_handlers, NULL, false); zend_hash_add_new_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; @@ -755,6 +897,13 @@ PHP_MINIT_FUNCTION(dom) DOM_REGISTER_PROP_HANDLER(&dom_nodelist_prop_handlers, "length", dom_nodelist_length_read, NULL); zend_hash_add_new_ptr(&classes, dom_nodelist_class_entry->name, &dom_nodelist_prop_handlers); + dom_modern_nodelist_class_entry = register_class_DOM_NodeList(zend_ce_aggregate, zend_ce_countable); + dom_modern_nodelist_class_entry->create_object = dom_nnodemap_objects_new; + dom_modern_nodelist_class_entry->default_object_handlers = &dom_modern_nodelist_object_handlers; + dom_modern_nodelist_class_entry->get_iterator = php_dom_get_iterator; + + zend_hash_add_new_ptr(&classes, dom_modern_nodelist_class_entry->name, &dom_nodelist_prop_handlers); + dom_namednodemap_class_entry = register_class_DOMNamedNodeMap(zend_ce_aggregate, zend_ce_countable); dom_namednodemap_class_entry->create_object = dom_nnodemap_objects_new; dom_namednodemap_class_entry->default_object_handlers = &dom_nnodemap_object_handlers; @@ -764,8 +913,30 @@ PHP_MINIT_FUNCTION(dom) DOM_REGISTER_PROP_HANDLER(&dom_namednodemap_prop_handlers, "length", dom_namednodemap_length_read, NULL); zend_hash_add_new_ptr(&classes, dom_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers); + dom_modern_namednodemap_class_entry = register_class_DOM_NamedNodeMap(zend_ce_aggregate, zend_ce_countable); + dom_modern_namednodemap_class_entry->create_object = dom_nnodemap_objects_new; + dom_modern_namednodemap_class_entry->default_object_handlers = &dom_modern_nnodemap_object_handlers; + dom_modern_namednodemap_class_entry->get_iterator = php_dom_get_iterator; + + zend_hash_add_new_ptr(&classes, dom_modern_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers); + + dom_modern_dtd_namednodemap_class_entry = register_class_DOM_DTDNamedNodeMap(zend_ce_aggregate, zend_ce_countable); + dom_modern_dtd_namednodemap_class_entry->create_object = dom_nnodemap_objects_new; + dom_modern_dtd_namednodemap_class_entry->default_object_handlers = &dom_modern_nnodemap_object_handlers; + dom_modern_dtd_namednodemap_class_entry->get_iterator = php_dom_get_iterator; + + zend_hash_add_new_ptr(&classes, dom_modern_dtd_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers); + + dom_html_collection_class_entry = register_class_DOM_HTMLCollection(zend_ce_aggregate, zend_ce_countable); + dom_html_collection_class_entry->create_object = dom_nnodemap_objects_new; + dom_html_collection_class_entry->default_object_handlers = &dom_modern_nodelist_object_handlers; + dom_html_collection_class_entry->get_iterator = php_dom_get_iterator; + + zend_hash_add_new_ptr(&classes, dom_html_collection_class_entry->name, &dom_nodelist_prop_handlers); + dom_characterdata_class_entry = register_class_DOMCharacterData(dom_node_class_entry, dom_childnode_class_entry); dom_characterdata_class_entry->create_object = dom_objects_new; + dom_characterdata_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_characterdata_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "data", dom_characterdata_data_read, dom_characterdata_data_write); @@ -775,8 +946,23 @@ PHP_MINIT_FUNCTION(dom) zend_hash_merge(&dom_characterdata_prop_handlers, &dom_node_prop_handlers, NULL, false); zend_hash_add_new_ptr(&classes, dom_characterdata_class_entry->name, &dom_characterdata_prop_handlers); + dom_modern_characterdata_class_entry = register_class_DOM_CharacterData(dom_modern_node_class_entry, dom_modern_childnode_class_entry); + dom_modern_characterdata_class_entry->create_object = dom_objects_new; + dom_modern_characterdata_class_entry->default_object_handlers = &dom_object_handlers; + + zend_hash_init(&dom_modern_characterdata_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "data", dom_characterdata_data_read, dom_characterdata_data_write); + DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "length", dom_characterdata_length_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL); + zend_hash_merge(&dom_modern_characterdata_prop_handlers, &dom_modern_node_prop_handlers, NULL, false); + DOM_OVERWRITE_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "nodeValue", dom_node_node_value_read, dom_node_node_value_write); + DOM_OVERWRITE_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write); + zend_hash_add_new_ptr(&classes, dom_modern_characterdata_class_entry->name, &dom_modern_characterdata_prop_handlers); + dom_attr_class_entry = register_class_DOMAttr(dom_node_class_entry); dom_attr_class_entry->create_object = dom_objects_new; + dom_attr_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_attr_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "name", dom_attr_name_read, NULL); @@ -787,8 +973,26 @@ PHP_MINIT_FUNCTION(dom) zend_hash_merge(&dom_attr_prop_handlers, &dom_node_prop_handlers, NULL, false); zend_hash_add_new_ptr(&classes, dom_attr_class_entry->name, &dom_attr_prop_handlers); + dom_modern_attr_class_entry = register_class_DOM_Attr(dom_modern_node_class_entry); + dom_modern_attr_class_entry->create_object = dom_objects_new; + dom_modern_attr_class_entry->default_object_handlers = &dom_object_handlers; + + zend_hash_init(&dom_modern_attr_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "prefix", dom_modern_node_prefix_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "localName", dom_node_local_name_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "name", dom_attr_name_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "value", dom_attr_value_read, dom_attr_value_write); + DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "ownerElement", dom_attr_owner_element_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "specified", dom_attr_specified_read, NULL); + zend_hash_merge(&dom_modern_attr_prop_handlers, &dom_node_prop_handlers, NULL, false); + DOM_OVERWRITE_PROP_HANDLER(&dom_modern_attr_prop_handlers, "nodeValue", dom_node_node_value_read, dom_node_node_value_write); + DOM_OVERWRITE_PROP_HANDLER(&dom_modern_attr_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write); + zend_hash_add_new_ptr(&classes, dom_modern_attr_class_entry->name, &dom_modern_attr_prop_handlers); + dom_element_class_entry = register_class_DOMElement(dom_node_class_entry, dom_parentnode_class_entry, dom_childnode_class_entry); dom_element_class_entry->create_object = dom_objects_new; + dom_element_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_element_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "tagName", dom_element_tag_name_read, NULL); @@ -803,24 +1007,68 @@ PHP_MINIT_FUNCTION(dom) zend_hash_merge(&dom_element_prop_handlers, &dom_node_prop_handlers, NULL, false); zend_hash_add_new_ptr(&classes, dom_element_class_entry->name, &dom_element_prop_handlers); + dom_modern_element_class_entry = register_class_DOM_Element(dom_modern_node_class_entry, dom_modern_parentnode_class_entry, dom_modern_childnode_class_entry); + dom_modern_element_class_entry->create_object = dom_objects_new; + dom_modern_element_class_entry->default_object_handlers = &dom_object_handlers; + + zend_hash_init(&dom_modern_element_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "prefix", dom_modern_node_prefix_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "localName", dom_node_local_name_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "tagName", dom_element_tag_name_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "id", dom_element_id_read, dom_element_id_write); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "className", dom_element_class_name_read, dom_element_class_name_write); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "attributes", dom_node_attributes_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL); + zend_hash_merge(&dom_modern_element_prop_handlers, &dom_modern_node_prop_handlers, NULL, false); + DOM_OVERWRITE_PROP_HANDLER(&dom_modern_element_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write); + zend_hash_add_new_ptr(&classes, dom_modern_element_class_entry->name, &dom_modern_element_prop_handlers); + dom_text_class_entry = register_class_DOMText(dom_characterdata_class_entry); dom_text_class_entry->create_object = dom_objects_new; + dom_text_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_text_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_text_prop_handlers, "wholeText", dom_text_whole_text_read, NULL); zend_hash_merge(&dom_text_prop_handlers, &dom_characterdata_prop_handlers, NULL, false); zend_hash_add_new_ptr(&classes, dom_text_class_entry->name, &dom_text_prop_handlers); + dom_modern_text_class_entry = register_class_DOM_Text(dom_modern_characterdata_class_entry); + dom_modern_text_class_entry->create_object = dom_objects_new; + dom_modern_text_class_entry->default_object_handlers = &dom_object_handlers; + + zend_hash_init(&dom_modern_text_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_text_prop_handlers, "wholeText", dom_text_whole_text_read, NULL); + zend_hash_merge(&dom_modern_text_prop_handlers, &dom_modern_characterdata_prop_handlers, NULL, false); + zend_hash_add_new_ptr(&classes, dom_modern_text_class_entry->name, &dom_modern_text_prop_handlers); + dom_comment_class_entry = register_class_DOMComment(dom_characterdata_class_entry); dom_comment_class_entry->create_object = dom_objects_new; + dom_comment_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_add_new_ptr(&classes, dom_comment_class_entry->name, &dom_characterdata_prop_handlers); + dom_modern_comment_class_entry = register_class_DOM_Comment(dom_modern_characterdata_class_entry); + dom_modern_comment_class_entry->create_object = dom_objects_new; + dom_modern_comment_class_entry->default_object_handlers = &dom_object_handlers; + zend_hash_add_new_ptr(&classes, dom_modern_comment_class_entry->name, &dom_modern_characterdata_prop_handlers); + dom_cdatasection_class_entry = register_class_DOMCdataSection(dom_text_class_entry); dom_cdatasection_class_entry->create_object = dom_objects_new; + dom_cdatasection_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_add_new_ptr(&classes, dom_cdatasection_class_entry->name, &dom_text_prop_handlers); + dom_modern_cdatasection_class_entry = register_class_DOM_CDATASection(dom_modern_text_class_entry); + dom_modern_cdatasection_class_entry->create_object = dom_objects_new; + dom_modern_cdatasection_class_entry->default_object_handlers = &dom_object_handlers; + zend_hash_add_new_ptr(&classes, dom_modern_cdatasection_class_entry->name, &dom_modern_text_prop_handlers); + dom_documenttype_class_entry = register_class_DOMDocumentType(dom_node_class_entry); dom_documenttype_class_entry->create_object = dom_objects_new; + dom_documenttype_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_documenttype_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "name", dom_documenttype_name_read, NULL); @@ -832,8 +1080,23 @@ PHP_MINIT_FUNCTION(dom) zend_hash_merge(&dom_documenttype_prop_handlers, &dom_node_prop_handlers, NULL, false); zend_hash_add_new_ptr(&classes, dom_documenttype_class_entry->name, &dom_documenttype_prop_handlers); + dom_modern_documenttype_class_entry = register_class_DOM_DocumentType(dom_modern_node_class_entry, dom_modern_childnode_class_entry); + dom_modern_documenttype_class_entry->create_object = dom_objects_new; + dom_modern_documenttype_class_entry->default_object_handlers = &dom_object_handlers; + + zend_hash_init(&dom_modern_documenttype_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "name", dom_documenttype_name_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "entities", dom_documenttype_entities_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "notations", dom_documenttype_notations_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "publicId", dom_documenttype_public_id_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "systemId", dom_documenttype_system_id_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "internalSubset", dom_documenttype_internal_subset_read, NULL); + zend_hash_merge(&dom_modern_documenttype_prop_handlers, &dom_node_prop_handlers, NULL, false); + zend_hash_add_new_ptr(&classes, dom_modern_documenttype_class_entry->name, &dom_modern_documenttype_prop_handlers); + dom_notation_class_entry = register_class_DOMNotation(dom_node_class_entry); dom_notation_class_entry->create_object = dom_objects_new; + dom_notation_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_notation_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_notation_prop_handlers, "publicId", dom_notation_public_id_read, NULL); @@ -841,8 +1104,19 @@ PHP_MINIT_FUNCTION(dom) zend_hash_merge(&dom_notation_prop_handlers, &dom_node_prop_handlers, NULL, false); zend_hash_add_new_ptr(&classes, dom_notation_class_entry->name, &dom_notation_prop_handlers); + dom_modern_notation_class_entry = register_class_DOM_Notation(dom_modern_node_class_entry); + dom_modern_notation_class_entry->create_object = dom_objects_new; + dom_modern_notation_class_entry->default_object_handlers = &dom_object_handlers; + + zend_hash_init(&dom_modern_notation_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_notation_prop_handlers, "publicId", dom_notation_public_id_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_notation_prop_handlers, "systemId", dom_notation_system_id_read, NULL); + zend_hash_merge(&dom_modern_notation_prop_handlers, &dom_modern_node_prop_handlers, NULL, false); + zend_hash_add_new_ptr(&classes, dom_modern_notation_class_entry->name, &dom_modern_node_prop_handlers); + dom_entity_class_entry = register_class_DOMEntity(dom_node_class_entry); dom_entity_class_entry->create_object = dom_objects_new; + dom_entity_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_entity_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "publicId", dom_entity_public_id_read, NULL); @@ -854,12 +1128,30 @@ PHP_MINIT_FUNCTION(dom) zend_hash_merge(&dom_entity_prop_handlers, &dom_node_prop_handlers, NULL, false); zend_hash_add_new_ptr(&classes, dom_entity_class_entry->name, &dom_entity_prop_handlers); + dom_modern_entity_class_entry = register_class_DOM_Entity(dom_modern_node_class_entry); + dom_modern_entity_class_entry->create_object = dom_objects_new; + dom_modern_entity_class_entry->default_object_handlers = &dom_object_handlers; + + zend_hash_init(&dom_modern_entity_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_entity_prop_handlers, "publicId", dom_entity_public_id_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_entity_prop_handlers, "systemId", dom_entity_system_id_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_entity_prop_handlers, "notationName", dom_entity_notation_name_read, NULL); + zend_hash_merge(&dom_modern_entity_prop_handlers, &dom_modern_node_prop_handlers, NULL, false); + zend_hash_add_new_ptr(&classes, dom_modern_entity_class_entry->name, &dom_modern_entity_prop_handlers); + dom_entityreference_class_entry = register_class_DOMEntityReference(dom_node_class_entry); dom_entityreference_class_entry->create_object = dom_objects_new; + dom_entityreference_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_add_new_ptr(&classes, dom_entityreference_class_entry->name, &dom_node_prop_handlers); + dom_modern_entityreference_class_entry = register_class_DOM_EntityReference(dom_modern_node_class_entry); + dom_modern_entityreference_class_entry->create_object = dom_objects_new; + dom_modern_entityreference_class_entry->default_object_handlers = &dom_object_handlers; + zend_hash_add_new_ptr(&classes, dom_modern_entityreference_class_entry->name, &dom_modern_node_prop_handlers); + dom_processinginstruction_class_entry = register_class_DOMProcessingInstruction(dom_node_class_entry); dom_processinginstruction_class_entry->create_object = dom_objects_new; + dom_processinginstruction_class_entry->default_object_handlers = &dom_object_handlers; zend_hash_init(&dom_processinginstruction_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_processinginstruction_prop_handlers, "target", dom_processinginstruction_target_read, NULL); @@ -867,6 +1159,16 @@ PHP_MINIT_FUNCTION(dom) zend_hash_merge(&dom_processinginstruction_prop_handlers, &dom_node_prop_handlers, NULL, false); zend_hash_add_new_ptr(&classes, dom_processinginstruction_class_entry->name, &dom_processinginstruction_prop_handlers); + dom_modern_processinginstruction_class_entry = register_class_DOM_ProcessingInstruction(dom_modern_characterdata_class_entry); + dom_modern_processinginstruction_class_entry->create_object = dom_objects_new; + dom_modern_processinginstruction_class_entry->default_object_handlers = &dom_object_handlers; + + zend_hash_init(&dom_modern_processinginstruction_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_processinginstruction_prop_handlers, "target", dom_processinginstruction_target_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_processinginstruction_prop_handlers, "data", dom_processinginstruction_data_read, dom_processinginstruction_data_write); + zend_hash_merge(&dom_modern_processinginstruction_prop_handlers, &dom_modern_characterdata_prop_handlers, NULL, false); + zend_hash_add_new_ptr(&classes, dom_modern_processinginstruction_class_entry->name, &dom_modern_processinginstruction_prop_handlers); + #ifdef LIBXML_XPATH_ENABLED memcpy(&dom_xpath_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_xpath_object_handlers.offset = XtOffsetOf(dom_xpath_object, dom) + XtOffsetOf(dom_object, std); @@ -882,11 +1184,18 @@ PHP_MINIT_FUNCTION(dom) DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "document", dom_xpath_document_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "registerNodeNamespaces", dom_xpath_register_node_ns_read, dom_xpath_register_node_ns_write); zend_hash_add_new_ptr(&classes, dom_xpath_class_entry->name, &dom_xpath_prop_handlers); + + dom_modern_xpath_class_entry = register_class_DOM_XPath(); + dom_modern_xpath_class_entry->create_object = dom_xpath_objects_new; + dom_modern_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers; + + zend_hash_add_new_ptr(&classes, dom_modern_xpath_class_entry->name, &dom_xpath_prop_handlers); #endif register_php_dom_symbols(module_number); php_libxml_register_export(dom_node_class_entry, php_dom_export_node); + php_libxml_register_export(dom_modern_node_class_entry, php_dom_export_node); lexbor_memory_setup(dom_malloc, dom_realloc, dom_calloc, dom_free); @@ -921,21 +1230,31 @@ 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_abstract_base_document_prop_handlers); zend_hash_destroy(&dom_xml_document_prop_handlers); zend_hash_destroy(&dom_documentfragment_prop_handlers); + zend_hash_destroy(&dom_modern_documentfragment_prop_handlers); zend_hash_destroy(&dom_node_prop_handlers); + zend_hash_destroy(&dom_modern_node_prop_handlers); zend_hash_destroy(&dom_namespace_node_prop_handlers); zend_hash_destroy(&dom_nodelist_prop_handlers); zend_hash_destroy(&dom_namednodemap_prop_handlers); zend_hash_destroy(&dom_characterdata_prop_handlers); + zend_hash_destroy(&dom_modern_characterdata_prop_handlers); zend_hash_destroy(&dom_attr_prop_handlers); + zend_hash_destroy(&dom_modern_attr_prop_handlers); zend_hash_destroy(&dom_element_prop_handlers); + zend_hash_destroy(&dom_modern_element_prop_handlers); zend_hash_destroy(&dom_text_prop_handlers); + zend_hash_destroy(&dom_modern_text_prop_handlers); zend_hash_destroy(&dom_documenttype_prop_handlers); + zend_hash_destroy(&dom_modern_documenttype_prop_handlers); zend_hash_destroy(&dom_notation_prop_handlers); + zend_hash_destroy(&dom_modern_notation_prop_handlers); zend_hash_destroy(&dom_entity_prop_handlers); + zend_hash_destroy(&dom_modern_entity_prop_handlers); zend_hash_destroy(&dom_processinginstruction_prop_handlers); + zend_hash_destroy(&dom_modern_processinginstruction_prop_handlers); #ifdef LIBXML_XPATH_ENABLED zend_hash_destroy(&dom_xpath_prop_handlers); #endif @@ -1028,6 +1347,12 @@ void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xml mapptr->local = xmlCharStrndup(local, len); mapptr->free_local = true; } + mapptr->local_lower = BAD_CAST estrdup(local); + if (len < 0) { + zend_str_tolower((char *) mapptr->local_lower, strlen((const char *) mapptr->local_lower)); + } else { + zend_str_tolower((char *) mapptr->local_lower, len); + } } if (ns) { @@ -1066,7 +1391,6 @@ static dom_object* dom_objects_set_class(zend_class_entry *class_type) zend_object *dom_objects_new(zend_class_entry *class_type) { dom_object *intern = dom_objects_set_class(class_type); - intern->std.handlers = &dom_object_handlers; return &intern->std; } /* }}} */ @@ -1075,7 +1399,6 @@ static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type) { dom_object_namespace_node *intern = zend_object_alloc(sizeof(dom_object_namespace_node), class_type); dom_objects_set_class_ex(class_type, &intern->dom); - intern->dom.std.handlers = &dom_object_namespace_node_handlers; return &intern->dom.std; } @@ -1126,6 +1449,9 @@ void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */ if (objmap->free_ns) { xmlFree(objmap->ns); } + if (objmap->local_lower) { + efree(objmap->local_lower); + } if (!Z_ISUNDEF(objmap->baseobj_zv)) { zval_ptr_dtor(&objmap->baseobj_zv); } @@ -1152,6 +1478,7 @@ zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type) objmap->nodetype = 0; objmap->ht = NULL; objmap->local = NULL; + objmap->local_lower = NULL; objmap->free_local = false; objmap->ns = NULL; objmap->free_ns = false; @@ -1163,14 +1490,21 @@ zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type) return &intern->std; } -void php_dom_create_iterator(zval *return_value, int ce_type) /* {{{ */ +void php_dom_create_iterator(zval *return_value, dom_iterator_type iterator_type, bool modern) /* {{{ */ { zend_class_entry *ce; - if (ce_type == DOM_NAMEDNODEMAP) { - ce = dom_namednodemap_class_entry; + if (iterator_type == DOM_NAMEDNODEMAP) { + ce = dom_get_namednodemap_ce(modern); + } else if (iterator_type == DOM_HTMLCOLLECTION) { + /* This only exists in modern DOM. */ + ZEND_ASSERT(modern); + ce = dom_html_collection_class_entry; + } else if (iterator_type == DOM_DTD_NAMEDNODEMAP) { + ce = dom_get_dtd_namednodemap_ce(modern); } else { - ce = dom_nodelist_class_entry; + ZEND_ASSERT(iterator_type == DOM_NODELIST); + ce = dom_get_nodelist_ce(modern); } object_init_ex(return_value, ce); @@ -1193,89 +1527,83 @@ PHP_DOM_EXPORT bool php_dom_create_object(xmlNodePtr obj, zval *return_value, do return 1; } + bool modern = domobj && php_dom_follow_spec_intern(domobj); 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; - } + ce = dom_get_xml_document_ce(modern); break; } case XML_HTML_DOCUMENT_NODE: { - if (domobj && domobj->document->is_modern_api_class) { - ce = dom_html_document_class_entry; - } else { - ce = dom_document_class_entry; - } + ce = dom_get_html_document_ce(modern); break; } case XML_DTD_NODE: case XML_DOCUMENT_TYPE_NODE: { - ce = dom_documenttype_class_entry; + ce = dom_get_documenttype_ce(modern); break; } case XML_ELEMENT_NODE: { - ce = dom_element_class_entry; + ce = dom_get_element_ce(modern); break; } case XML_ATTRIBUTE_NODE: { - ce = dom_attr_class_entry; + ce = dom_get_attr_ce(modern); break; } case XML_TEXT_NODE: { - ce = dom_text_class_entry; + ce = dom_get_text_ce(modern); break; } case XML_COMMENT_NODE: { - ce = dom_comment_class_entry; + ce = dom_get_comment_ce(modern); break; } case XML_PI_NODE: { - ce = dom_processinginstruction_class_entry; + ce = dom_get_processinginstruction_ce(modern); break; } case XML_ENTITY_REF_NODE: { - ce = dom_entityreference_class_entry; + ce = dom_get_entityreference_ce(modern); break; } case XML_ENTITY_DECL: case XML_ELEMENT_DECL: { - ce = dom_entity_class_entry; + ce = dom_get_entity_ce(modern); break; } case XML_CDATA_SECTION_NODE: { - ce = dom_cdatasection_class_entry; + ce = dom_get_cdatasection_ce(modern); break; } case XML_DOCUMENT_FRAG_NODE: { - ce = dom_documentfragment_class_entry; + ce = dom_get_documentfragment_ce(modern); break; } case XML_NOTATION_NODE: { - ce = dom_notation_class_entry; + ce = dom_get_notation_ce(modern); break; } case XML_NAMESPACE_DECL: { + /* This has no modern equivalent */ ce = dom_namespace_node_class_entry; break; } default: - /* TODO Convert to a ZEND assertion? */ + /* TODO you can actually hit this with fixed attributes in the DTD for example... */ zend_throw_error(NULL, "Unsupported node type: %d", obj->type); ZVAL_NULL(return_value); return 0; @@ -1306,8 +1634,8 @@ dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_ent return intern; } -void php_dom_create_implementation(zval *retval) { - object_init_ex(retval, dom_domimplementation_class_entry); +void php_dom_create_implementation(zval *retval, bool modern) { + object_init_ex(retval, dom_get_domimplementation_ce(modern)); } /* {{{ int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child) */ @@ -1354,7 +1682,43 @@ bool dom_has_feature(zend_string *feature, zend_string *version) } /* }}} end dom_has_feature */ -xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, char *ns, char *local, int *cur, int index) /* {{{ */ +bool dom_match_qualified_name_according_to_spec(const xmlChar *qname, const xmlNode *nodep) +{ + const xmlChar *node_local = nodep->name; + + /* The qualified name must be matched, which means either: + * - The local parts are equal and there is no ns prefix for this element (i.e. the fqn is in the local name). + * - There is a prefix, the prefixes are equal and the local parts are equal. */ + if (nodep->ns != NULL && nodep->ns->prefix != NULL) { + const char *prefix = (const char *) nodep->ns->prefix; + /* 1. match prefix up to |prefix| characters. + * This won't overflow as it'll stop at the '\0' if the lengths don't match. */ + size_t prefix_len = strlen(prefix); + if (strncmp((const char *) qname, prefix, prefix_len) != 0) { + return false; + } + /* 2. match ':' */ + if (qname[prefix_len] != ':') { + return false; + } + /* 3. match local name */ + return xmlStrEqual(qname + prefix_len + 1, node_local); + } else { + return xmlStrEqual(node_local, qname); + } +} + +static bool dom_match_qualified_name_for_tag_name_equality(const xmlChar *local, const xmlChar *local_lower, const xmlNode *nodep, bool match_qname) +{ + if (!match_qname) { + return xmlStrEqual(nodep->name, local); + } + + const xmlChar *local_to_use = nodep->doc->type == XML_HTML_DOCUMENT_NODE && php_dom_ns_is_fast(nodep, php_dom_ns_is_html_magic_token) ? local_lower : local; + return dom_match_qualified_name_according_to_spec(local_to_use, nodep); +} + +xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, xmlChar *ns, xmlChar *local, xmlChar *local_lower, int *cur, int index) /* {{{ */ { /* Can happen with detached document */ if (UNEXPECTED(nodep == NULL)) { @@ -1369,10 +1733,12 @@ xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, * This is because for PHP ns == NULL has another meaning: "match every namespace" instead of "match the empty namespace". */ bool ns_match_any = ns == NULL || (ns[0] == '*' && ns[1] == '\0'); + bool match_qname = ns == NULL && php_dom_follow_spec_node(basep); + while (*cur <= index) { if (nodep->type == XML_ELEMENT_NODE) { - if (local_match_any || xmlStrEqual(nodep->name, (xmlChar *)local)) { - if (ns_match_any || (ns[0] == '\0' && nodep->ns == NULL) || (nodep->ns != NULL && xmlStrEqual(nodep->ns->href, (xmlChar *)ns))) { + if (local_match_any || dom_match_qualified_name_for_tag_name_equality(local, local_lower, nodep, match_qname)) { + if (ns_match_any || (ns[0] == '\0' && nodep->ns == NULL) || (nodep->ns != NULL && xmlStrEqual(nodep->ns->href, ns))) { if (*cur == index) { ret = nodep; break; @@ -1387,22 +1753,9 @@ xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, } } - if (nodep->next) { - nodep = nodep->next; - } else { - /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */ - do { - nodep = nodep->parent; - if (nodep == basep) { - return NULL; - } - /* This shouldn't happen, unless there's an invalidation bug somewhere. */ - if (UNEXPECTED(nodep == NULL)) { - zend_throw_error(NULL, "Current node in traversal is not in the document. Please report this as a bug in php-src."); - return NULL; - } - } while (nodep->next == NULL); - nodep = nodep->next; + nodep = php_dom_next_in_tree_order(nodep, basep); + if (!nodep) { + return NULL; } } return ret; @@ -1411,63 +1764,96 @@ xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, static inline bool is_empty_node(xmlNodePtr nodep) { - xmlChar *strContent = xmlNodeGetContent(nodep); - bool ret = strContent == NULL || *strContent == '\0'; - xmlFree(strContent); - return ret; + return nodep->content == NULL || *nodep->content == '\0'; +} + +static inline void free_node(xmlNodePtr node) +{ + if (node->_private == NULL) { + xmlFreeNode(node); + } } -/* {{{ void dom_normalize (xmlNodePtr nodep) */ -void dom_normalize (xmlNodePtr nodep) +static void dom_merge_adjacent_exclusive_text_nodes(xmlNodePtr node) { - xmlNodePtr child, nextp, newnextp; - xmlAttrPtr attr; - xmlChar *strContent; + xmlNodePtr nextp = node->next; + while (nextp != NULL && nextp->type == XML_TEXT_NODE) { + xmlNodePtr newnextp = nextp->next; + xmlChar *strContent = nextp->content; + if (strContent != NULL) { + xmlNodeAddContent(node, strContent); + } + xmlUnlinkNode(nextp); + free_node(nextp); + nextp = newnextp; + } +} - child = nodep->children; +/* {{{ void php_dom_normalize_legacy(xmlNodePtr nodep) */ +void php_dom_normalize_legacy(xmlNodePtr nodep) +{ + xmlNodePtr child = nodep->children; while(child != NULL) { switch (child->type) { case XML_TEXT_NODE: - nextp = child->next; - while (nextp != NULL) { - if (nextp->type == XML_TEXT_NODE) { - newnextp = nextp->next; - strContent = xmlNodeGetContent(nextp); - xmlNodeAddContent(child, strContent); - xmlFree(strContent); - xmlUnlinkNode(nextp); - php_libxml_node_free_resource(nextp); - nextp = newnextp; - } else { - break; - } - } + dom_merge_adjacent_exclusive_text_nodes(child); if (is_empty_node(child)) { - nextp = child->next; + xmlNodePtr nextp = child->next; xmlUnlinkNode(child); - php_libxml_node_free_resource(child); + free_node(child); child = nextp; continue; } break; case XML_ELEMENT_NODE: - dom_normalize (child); - attr = child->properties; + php_dom_normalize_legacy(child); + xmlAttrPtr attr = child->properties; while (attr != NULL) { - dom_normalize((xmlNodePtr) attr); + php_dom_normalize_legacy((xmlNodePtr) attr); attr = attr->next; } break; - case XML_ATTRIBUTE_NODE: - dom_normalize (child); - break; default: break; } child = child->next; } } -/* }}} end dom_normalize */ +/* }}} end php_dom_normalize_legacy */ + +/* https://dom.spec.whatwg.org/#dom-node-normalize */ +void php_dom_normalize_modern(xmlNodePtr this) +{ + /* for each descendant exclusive Text node node of this: */ + xmlNodePtr node = this->children; + while (node != NULL) { + if (node->type == XML_TEXT_NODE) { + /* 1. Let length be node’s length. + * We'll deviate a bit here: we'll just check if it's empty or not as we don't want to compute the length. */ + bool is_empty = is_empty_node(node); + + /* 2. If length is zero, then remove node and continue with the next exclusive Text node, if any. */ + if (is_empty) { + xmlNodePtr next = node->next; + xmlUnlinkNode(node); + free_node(node); + node = next; + continue; + } + + /* 3. Let data be the concatenation of the data of node’s contiguous exclusive Text nodes (excluding itself), in tree order. + * 4. Replace data with node node, offset length, count 0, and data data. + * 7. Remove node’s contiguous exclusive Text nodes (excluding itself), in tree order. + * => In other words: Concat every contiguous text node into node and delete the merged nodes. */ + dom_merge_adjacent_exclusive_text_nodes(node); + + /* Steps 5-6 deal with mutation records, we don't do that here. */ + } else if (node->type == XML_ELEMENT_NODE) { + php_dom_normalize_modern(node); + } + node = node->next; + } +} static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr search_parent) { @@ -1500,38 +1886,6 @@ static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePt } } -void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp) -{ - ZEND_ASSERT(attrp != NULL); - - if (attrp->ns != NULL) { - /* Try to link to an existing namespace. If that won't work, reconcile. */ - xmlNodePtr nodep = attrp->parent; - xmlNsPtr matching_ns = xmlSearchNs(nodep->doc, nodep, attrp->ns->prefix); - if (matching_ns && xmlStrEqual(matching_ns->href, attrp->ns->href)) { - attrp->ns = matching_ns; - } else { - if (attrp->ns->prefix != NULL) { - xmlReconciliateNs(nodep->doc, nodep); - } - } - } -} - -static void dom_libxml_reconcile_ensure_namespaces_are_declared(xmlNodePtr nodep) -{ - /* Ideally we'd use the DOM-wrapped version, but we cannot: https://github.com/php/php-src/pull/12308. */ -#if 0 - /* Put on stack to avoid allocation. - * Although libxml2 currently does not use this for the reconciliation, it still - * makes sense to do this just in case libxml2's internal change in the future. */ - xmlDOMWrapCtxt dummy_ctxt = {0}; - xmlDOMWrapReconcileNamespaces(&dummy_ctxt, nodep, /* options */ 0); -#else - xmlReconciliateNs(nodep->doc, nodep); -#endif -} - void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */ { ZEND_ASSERT(nodep->type != XML_ATTRIBUTE_NODE); @@ -1540,7 +1894,7 @@ void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */ * we still want to do the internal reconciliation conditionally. */ if (nodep->type == XML_ELEMENT_NODE) { dom_reconcile_ns_internal(doc, nodep, nodep->parent); - dom_libxml_reconcile_ensure_namespaces_are_declared(nodep); + xmlReconciliateNs(doc, nodep); } } /* }}} */ @@ -1569,7 +1923,7 @@ void dom_reconcile_ns_list(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last) * dom_libxml_reconcile_ensure_namespaces_are_declared() performs its own recursion. */ while (true) { /* The internal libxml2 call will already check the node type, no need for us to do it here. */ - dom_libxml_reconcile_ensure_namespaces_are_declared(nodep); + xmlReconciliateNs(doc, nodep); if (nodep == last) { break; } @@ -1577,6 +1931,52 @@ void dom_reconcile_ns_list(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last) } } +/* https://dom.spec.whatwg.org/#validate-and-extract */ +int dom_validate_and_extract(const zend_string *namespace, const zend_string *qname, xmlChar **localName, xmlChar **prefix) +{ + /* 1. If namespace is the empty string, then set it to null. + * However, we're going to cheat and do the opposite to make + * implementation of the below steps with existing zend_ helpers easier. */ + if (namespace == NULL) { + namespace = zend_empty_string; + } + + /* 2. Validate qualifiedName. */ + if (xmlValidateQName(BAD_CAST ZSTR_VAL(qname), /* allow spaces */ 0) != 0) { + return INVALID_CHARACTER_ERR; + } + + /* Steps 3-5 */ + *localName = xmlSplitQName2(BAD_CAST ZSTR_VAL(qname), prefix); + + /* 6. If prefix is non-null and namespace is null, then throw a "NamespaceError" DOMException. + * Note that null namespace means empty string here becaue of step 1. */ + if (*prefix != NULL && ZSTR_VAL(namespace)[0] == '\0') { + return NAMESPACE_ERR; + } + + /* 7. If prefix is "xml" and namespace is not the XML namespace, then throw a "NamespaceError" DOMException. */ + if (UNEXPECTED(!zend_string_equals_literal(namespace, "http://www.w3.org/XML/1998/namespace") && xmlStrEqual(*prefix, BAD_CAST "xml"))) { + return NAMESPACE_ERR; + } + + /* 8. If either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace, then throw a "NamespaceError" DOMException. */ + if (UNEXPECTED((zend_string_equals_literal(qname, "xmlns") || xmlStrEqual(*prefix, BAD_CAST "xmlns")) && !zend_string_equals_literal(namespace, "http://www.w3.org/2000/xmlns/"))) { + return NAMESPACE_ERR; + } + + /* 9. If namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns", then throw a "NamespaceError" DOMException. */ + if (UNEXPECTED(zend_string_equals_literal(namespace, "http://www.w3.org/2000/xmlns/") && !zend_string_equals_literal(qname, "xmlns") && !xmlStrEqual(*prefix, BAD_CAST "xmlns"))) { + return NAMESPACE_ERR; + } + + if (*localName == NULL) { + *localName = xmlStrdup(BAD_CAST ZSTR_VAL(qname)); + } + + return 0; +} + /* http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS @@ -1763,10 +2163,10 @@ static bool dom_nodemap_or_nodelist_process_offset_as_named(zval *offset, zend_l return false; } -static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv) /* {{{ */ +static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv) { if (UNEXPECTED(!offset)) { - zend_throw_error(NULL, "Cannot access DOMNodeList without offset"); + zend_throw_error(NULL, "Cannot access %s without offset", ZSTR_VAL(object->ce->name)); return NULL; } @@ -1781,12 +2181,15 @@ static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int php_dom_nodelist_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv); return rv; -} /* }}} end dom_nodelist_read_dimension */ +} static int dom_nodelist_has_dimension(zend_object *object, zval *member, int check_empty) { ZVAL_DEREF(member); + /* If it exists, it cannot be empty because nodes aren't empty. */ + ZEND_IGNORE_VALUE(check_empty); + zend_long offset; if (dom_nodemap_or_nodelist_process_offset_as_named(member, &offset)) { /* does not support named lookup */ @@ -1794,7 +2197,59 @@ static int dom_nodelist_has_dimension(zend_object *object, zval *member, int che } return offset >= 0 && offset < php_dom_get_nodelist_length(php_dom_obj_from_obj(object)); -} /* }}} end dom_nodelist_has_dimension */ +} + +static zend_long dom_modern_nodelist_get_index(zval *offset, bool *failed) +{ + zend_ulong lval; + ZVAL_DEREF(offset); + if (Z_TYPE_P(offset) == IS_LONG) { + *failed = false; + return Z_LVAL_P(offset); + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + *failed = false; + return zend_dval_to_lval_safe(Z_DVAL_P(offset)); + } else if (Z_TYPE_P(offset) == IS_STRING && ZEND_HANDLE_NUMERIC(Z_STR_P(offset), lval)) { + *failed = false; + return (zend_long) lval; + } else { + *failed = true; + return 0; + } +} + +static zval *dom_modern_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv) +{ + if (UNEXPECTED(!offset)) { + zend_throw_error(NULL, "Cannot append to %s", ZSTR_VAL(object->ce->name)); + return NULL; + } + + bool failed; + zend_long lval = dom_modern_nodelist_get_index(offset, &failed); + if (UNEXPECTED(failed)) { + zend_illegal_container_offset(object->ce->name, offset, type); + return NULL; + } + + php_dom_nodelist_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv); + return rv; +} + +static int dom_modern_nodelist_has_dimension(zend_object *object, zval *member, int check_empty) +{ + /* If it exists, it cannot be empty because nodes aren't empty. */ + ZEND_IGNORE_VALUE(check_empty); + + bool failed; + zend_long lval = dom_modern_nodelist_get_index(member, &failed); + if (UNEXPECTED(failed)) { + zend_illegal_container_offset(object->ce->name, member, BP_VAR_IS); + return 0; + } + + return lval >= 0 && lval < php_dom_get_nodelist_length(php_dom_obj_from_obj(object)); +} void dom_remove_all_children(xmlNodePtr nodep) { @@ -1839,10 +2294,10 @@ void php_dom_get_content_into_zval(const xmlNode *nodep, zval *retval, bool defa } } -static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv) /* {{{ */ +static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv) { if (UNEXPECTED(!offset)) { - zend_throw_error(NULL, "Cannot access DOMNamedNodeMap without offset"); + zend_throw_error(NULL, "Cannot access %s without offset", ZSTR_VAL(object->ce->name)); return NULL; } @@ -1851,7 +2306,7 @@ static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int t zend_long lval; if (dom_nodemap_or_nodelist_process_offset_as_named(offset, &lval)) { /* exceptional case, switch to named lookup */ - php_dom_named_node_map_get_named_item_into_zval(php_dom_obj_from_obj(object)->ptr, Z_STRVAL_P(offset), rv); + php_dom_named_node_map_get_named_item_into_zval(php_dom_obj_from_obj(object)->ptr, Z_STR_P(offset), rv); return rv; } @@ -1863,38 +2318,338 @@ static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int t php_dom_named_node_map_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv); return rv; -} /* }}} end dom_nodemap_read_dimension */ +} static int dom_nodemap_has_dimension(zend_object *object, zval *member, int check_empty) { ZVAL_DEREF(member); + /* If it exists, it cannot be empty because nodes aren't empty. */ + ZEND_IGNORE_VALUE(check_empty); + zend_long offset; if (dom_nodemap_or_nodelist_process_offset_as_named(member, &offset)) { /* exceptional case, switch to named lookup */ - return php_dom_named_node_map_get_named_item(php_dom_obj_from_obj(object)->ptr, Z_STRVAL_P(member), false) != NULL; + return php_dom_named_node_map_get_named_item(php_dom_obj_from_obj(object)->ptr, Z_STR_P(member), false) != NULL; } 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) +static zval *dom_modern_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv) { - /* 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; + if (UNEXPECTED(!offset)) { + zend_throw_error(NULL, "Cannot append to %s", ZSTR_VAL(object->ce->name)); + return NULL; } - xmlNodePtr copy = xmlDocCopyNode(node, doc, extended_recursive); - if (UNEXPECTED(!copy)) { + + dom_nnodemap_object *map = php_dom_obj_from_obj(object)->ptr; + + ZVAL_DEREF(offset); + if (Z_TYPE_P(offset) == IS_STRING) { + zend_ulong lval; + if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), lval)) { + php_dom_named_node_map_get_item_into_zval(map, (zend_long) lval, rv); + } else { + php_dom_named_node_map_get_named_item_into_zval(map, Z_STR_P(offset), rv); + } + } else if (Z_TYPE_P(offset) == IS_LONG) { + php_dom_named_node_map_get_item_into_zval(map, Z_LVAL_P(offset), rv); + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + php_dom_named_node_map_get_item_into_zval(map, zend_dval_to_lval_safe(Z_DVAL_P(offset)), rv); + } else { + zend_illegal_container_offset(object->ce->name, offset, type); return NULL; } - if (intern->document && intern->document->is_modern_api_class) { - dom_mark_namespaces_for_copy_based_on_copy(copy, node); + return rv; +} + +static int dom_modern_nodemap_has_dimension(zend_object *object, zval *member, int check_empty) +{ + /* If it exists, it cannot be empty because nodes aren't empty. */ + ZEND_IGNORE_VALUE(check_empty); + + dom_object *obj = php_dom_obj_from_obj(object); + dom_nnodemap_object *map = obj->ptr; + + ZVAL_DEREF(member); + if (Z_TYPE_P(member) == IS_STRING) { + zend_ulong lval; + if (ZEND_HANDLE_NUMERIC(Z_STR_P(member), lval)) { + return (zend_long) lval >= 0 && (zend_long) lval < php_dom_get_namednodemap_length(obj); + } else { + return php_dom_named_node_map_get_named_item(map, Z_STR_P(member), false) != NULL; + } + } else if (Z_TYPE_P(member) == IS_LONG) { + zend_long offset = Z_LVAL_P(member); + return offset >= 0 && offset < php_dom_get_namednodemap_length(obj); + } else if (Z_TYPE_P(member) == IS_DOUBLE) { + zend_long offset = zend_dval_to_lval_safe(Z_DVAL_P(member)); + return offset >= 0 && offset < php_dom_get_namednodemap_length(obj); + } else { + zend_illegal_container_offset(object->ce->name, member, BP_VAR_IS); + return 0; + } +} + +static xmlNodePtr dom_clone_container_helper(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr src_node, xmlDocPtr dst_doc) +{ + xmlNodePtr clone = xmlDocCopyNode(src_node, dst_doc, 0); + if (EXPECTED(clone != NULL)) { + /* Set namespace to the original, reconciliation will fix this up. */ + clone->ns = src_node->ns; + + if (src_node->type == XML_ELEMENT_NODE) { + xmlAttrPtr last_added_attr = NULL; + + if (src_node->nsDef != NULL) { + xmlNsPtr current_ns = src_node->nsDef; + do { + php_dom_ns_compat_mark_attribute(ns_mapper, clone, current_ns); + } while ((current_ns = current_ns->next) != NULL); + + last_added_attr = clone->properties; + while (last_added_attr->next != NULL) { + last_added_attr = last_added_attr->next; + } + } + + /* Attribute cloning logic. */ + for (xmlAttrPtr attr = src_node->properties; attr != NULL; attr = attr->next) { + xmlAttrPtr new_attr = (xmlAttrPtr) xmlDocCopyNode((xmlNodePtr) attr, dst_doc, 0); + if (UNEXPECTED(new_attr == NULL)) { + xmlFreeNode(clone); + return NULL; + } + if (last_added_attr == NULL) { + clone->properties = new_attr; + } else { + new_attr->prev = last_added_attr; + last_added_attr->next = new_attr; + } + new_attr->parent = clone; + last_added_attr = new_attr; + + /* Set namespace to the original, reconciliation will fix this up. */ + new_attr->ns = attr->ns; + } + } } + return clone; +} + +static xmlNodePtr dom_clone_helper(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr src_node, xmlDocPtr dst_doc, bool recursive) +{ + xmlNodePtr outer_clone = dom_clone_container_helper(ns_mapper, src_node, dst_doc); + + if (!recursive || (src_node->type != XML_ELEMENT_NODE && src_node->type != XML_DOCUMENT_FRAG_NODE && src_node->type != XML_DOCUMENT_NODE && src_node->type != XML_HTML_DOCUMENT_NODE)) { + return outer_clone; + } + + /* Handle dtd separately, because it is linked twice and requires a special copy function. */ + if (src_node->type == XML_DOCUMENT_NODE || src_node->type == XML_HTML_DOCUMENT_NODE) { + dst_doc = (xmlDocPtr) outer_clone; - return copy; + xmlDtdPtr original_subset = ((xmlDocPtr) src_node)->intSubset; + if (original_subset != NULL) { + dst_doc->intSubset = xmlCopyDtd(((xmlDocPtr) src_node)->intSubset); + if (UNEXPECTED(dst_doc->intSubset == NULL)) { + xmlFreeNode(outer_clone); + return NULL; + } + dst_doc->intSubset->parent = dst_doc; + xmlSetTreeDoc((xmlNodePtr) dst_doc->intSubset, dst_doc); + dst_doc->children = dst_doc->last = (xmlNodePtr) dst_doc->intSubset; + } + } + + xmlNodePtr cloned_parent = outer_clone; + xmlNodePtr base = src_node; + src_node = src_node->children; + while (src_node != NULL) { + ZEND_ASSERT(src_node != base); + + xmlNodePtr cloned; + if (src_node->type == XML_ELEMENT_NODE) { + cloned = dom_clone_container_helper(ns_mapper, src_node, dst_doc); + } else if (src_node->type == XML_DTD_NODE) { + /* Already handled. */ + cloned = NULL; + } else { + cloned = xmlDocCopyNode(src_node, dst_doc, 1); + } + + if (EXPECTED(cloned != NULL)) { + if (cloned_parent->children == NULL) { + cloned_parent->children = cloned; + } else { + cloned->prev = cloned_parent->last; + cloned_parent->last->next = cloned; + } + cloned->parent = cloned_parent; + cloned_parent->last = cloned; + } + + if (src_node->type == XML_ELEMENT_NODE && src_node->children) { + cloned_parent = cloned; + src_node = src_node->children; + } else if (src_node->next) { + src_node = src_node->next; + } else { + /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */ + do { + src_node = src_node->parent; + if (src_node == base) { + return outer_clone; + } + cloned_parent = cloned_parent->parent; + } while (src_node->next == NULL); + src_node = src_node->next; + } + } + + return outer_clone; +} + +xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive) +{ + if (node->type == XML_DTD_NODE) { + /* The behaviour w.r.t. the internal subset is implementation-defined according to DOM 3. + * This follows what e.g. Java and C# do: copy it regardless of the recursiveness. + * Makes sense as the subset is not exactly a child in the normal sense. */ + xmlDtdPtr dtd = xmlCopyDtd((xmlDtdPtr) node); + xmlSetTreeDoc((xmlNodePtr) dtd, doc); + return (xmlNodePtr) dtd; + } + + if (ns_mapper != NULL) { + xmlNodePtr clone = dom_clone_helper(ns_mapper, node, doc, recursive); + if (EXPECTED(clone != NULL)) { + if (clone->type == XML_DOCUMENT_NODE || clone->type == XML_HTML_DOCUMENT_NODE || clone->type == XML_DOCUMENT_FRAG_NODE) { + for (xmlNodePtr child = clone->children; child != NULL; child = child->next) { + php_dom_libxml_reconcile_modern(ns_mapper, child); + } + } else { + php_dom_libxml_reconcile_modern(ns_mapper, clone); + } + } + return clone; + } else { + /* 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; + } + return xmlDocCopyNode(node, doc, extended_recursive); + } +} + +bool php_dom_has_child_of_type(xmlNodePtr node, xmlElementType type) +{ + xmlNodePtr child = node->children; + + while (child != NULL) { + if (child->type == type) { + return true; + } + + child = child->next; + } + + return false; +} + +bool php_dom_has_sibling_following_node(xmlNodePtr node, xmlElementType type) +{ + xmlNodePtr next = node->next; + + while (next != NULL) { + if (next->type == type) { + return true; + } + + next = next->next; + } + + return false; +} + +bool php_dom_has_sibling_preceding_node(xmlNodePtr node, xmlElementType type) +{ + xmlNodePtr prev = node->prev; + + while (prev != NULL) { + if (prev->type == type) { + return true; + } + + prev = prev->prev; + } + + return false; +} + +xmlAttrPtr php_dom_get_attribute_node(xmlNodePtr elem, const xmlChar *name, size_t name_len) +{ + xmlChar *name_processed = BAD_CAST name; + if (php_dom_ns_is_html_and_document_is_html(elem)) { + char *lowercase_copy = zend_str_tolower_dup_ex((char *) name, name_len); + if (lowercase_copy != NULL) { + name_processed = BAD_CAST lowercase_copy; + } + } + + xmlAttrPtr ret = NULL; + for (xmlAttrPtr attr = elem->properties; attr != NULL; attr = attr->next) { + if (dom_match_qualified_name_according_to_spec(name_processed, (xmlNodePtr) attr)) { + ret = attr; + break; + } + } + + if (name_processed != name) { + efree(name_processed); + } + + return ret; +} + +/* Workaround for a libxml2 bug on Windows: https://gitlab.gnome.org/GNOME/libxml2/-/issues/611. */ +xmlChar *php_dom_libxml_fix_file_path(xmlChar *path) +{ + if (strncmp((char *) path, "file:/", sizeof("file:/") - 1) == 0) { + if (path[6] != '/' && path[6] != '\0' && path[7] != '/' && path[7] != '\0') { + /* The path is file:/xx... where xx != "//", which is broken */ + xmlChar *new_path = xmlStrdup(BAD_CAST "file:///"); + if (UNEXPECTED(new_path == NULL)) { + return path; + } + new_path = xmlStrcat(new_path, path + 6); + xmlFree(path); + return new_path; + } + } + return path; +} + +xmlDocPtr php_dom_create_html_doc(void) +{ +#ifdef LIBXML_HTML_ENABLED + xmlDocPtr lxml_doc = htmlNewDocNoDtD(NULL, NULL); + if (EXPECTED(lxml_doc)) { + lxml_doc->dict = xmlDictCreate(); + } +#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"); + if (EXPECTED(lxml_doc)) { + lxml_doc->type = XML_HTML_DOCUMENT_NODE; + lxml_doc->dict = xmlDictCreate(); + } +#endif + return lxml_doc; } #endif /* HAVE_DOM */ diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 860e0bd697afc..fc94ed00f44cd 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -83,7 +83,7 @@ typedef struct _dom_nnodemap_object { int nodetype; int cached_length; xmlHashTable *ht; - xmlChar *local; + xmlChar *local, *local_lower; xmlChar *ns; php_libxml_cache_tag cache_tag; dom_object *cached_obj; @@ -107,6 +107,16 @@ typedef struct { dom_object dom; } dom_object_namespace_node; +typedef enum _dom_iterator_type { + DOM_NODELIST, + DOM_NAMEDNODEMAP, + DOM_DTD_NAMEDNODEMAP, + DOM_HTMLCOLLECTION, +} dom_iterator_type; + +struct _php_dom_libxml_ns_mapper; +typedef struct _php_dom_libxml_ns_mapper php_dom_libxml_ns_mapper; + static inline dom_object_namespace_node *php_dom_namespace_node_obj_from_obj(zend_object *obj) { return (dom_object_namespace_node*)((char*)(obj) - XtOffsetOf(dom_object_namespace_node, dom.std)); } @@ -133,14 +143,15 @@ xmlNsPtr dom_get_ns_unchecked(xmlNodePtr nodep, char *uri, char *prefix); void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep); void dom_reconcile_ns_list(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last); xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName); -void dom_normalize (xmlNodePtr nodep); -xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, char *ns, char *local, int *cur, int index); -void php_dom_create_implementation(zval *retval); +void php_dom_normalize_legacy(xmlNodePtr nodep); +void php_dom_normalize_modern(xmlNodePtr nodep); +xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, xmlChar *ns, xmlChar *local, xmlChar *local_lower, int *cur, int index); +void php_dom_create_implementation(zval *retval, bool modern); int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child); bool dom_has_feature(zend_string *feature, zend_string *version); int dom_node_is_read_only(xmlNodePtr node); -int dom_node_children_valid(xmlNodePtr node); -void php_dom_create_iterator(zval *return_value, int ce_type); +bool dom_node_children_valid(xmlNodePtr node); +void php_dom_create_iterator(zval *return_value, dom_iterator_type iterator_type, bool modern); void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xmlHashTablePtr ht, const char *local, size_t local_len, const char *ns, size_t ns_len); xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID); xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index); @@ -150,21 +161,51 @@ void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern); void php_dom_get_content_into_zval(const xmlNode *nodep, zval *target, bool default_is_null); zend_string *dom_node_concatenated_name_helper(size_t name_len, const char *name, size_t prefix_len, const char *prefix); -zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep); +zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep, bool uppercase); bool php_dom_is_node_connected(const xmlNode *node); bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document); xmlNsPtr dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree, const char *uri); -void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp); - +int dom_validate_and_extract(const zend_string *namespace, const zend_string *qname, xmlChar **localName, xmlChar **prefix); +bool dom_match_qualified_name_according_to_spec(const xmlChar *qname, const xmlNode *nodep); +bool php_dom_has_sibling_following_node(xmlNodePtr node, xmlElementType type); +bool php_dom_has_sibling_preceding_node(xmlNodePtr node, xmlElementType type); +bool php_dom_has_child_of_type(xmlNodePtr node, xmlElementType type); +void php_dom_update_document_after_clone(dom_object *original, xmlNodePtr original_node, dom_object *clone, xmlNodePtr cloned_node); void php_dom_document_constructor(INTERNAL_FUNCTION_PARAMETERS); - +xmlAttrPtr php_dom_get_attribute_node(xmlNodePtr elem, const xmlChar *name, size_t name_len); +xmlChar *php_dom_libxml_fix_file_path(xmlChar *path); +void dom_document_convert_to_modern(php_libxml_ref_obj *document, xmlDocPtr lxml_doc); dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_entry *ce, xmlNodePtr obj, dom_object *parent); +xmlDocPtr php_dom_create_html_doc(void); + +static zend_always_inline xmlNodePtr php_dom_next_in_tree_order(const xmlNode *nodep, const xmlNode *basep) +{ + if (nodep->next) { + return nodep->next; + } else { + /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */ + do { + nodep = nodep->parent; + if (nodep == basep) { + return NULL; + } + /* This shouldn't happen, unless there's an invalidation bug somewhere. */ + if (UNEXPECTED(nodep == NULL)) { + zend_throw_error(NULL, "Current node in traversal is not in the document. Please report this as a bug in php-src."); + return NULL; + } + } while (nodep->next == NULL); + return nodep->next; + } +} typedef enum { DOM_LOAD_STRING = 0, DOM_LOAD_FILE = 1, } dom_load_mode; +#define DOM_DOCUMENT_MALFORMED ((xmlDocPtr) -1) + xmlDocPtr dom_document_parser(zval *id, dom_load_mode mode, const char *source, size_t source_len, size_t options, xmlCharEncodingHandlerPtr encoding); /* parentnode */ @@ -175,20 +216,23 @@ void dom_parent_node_before(dom_object *context, zval *nodes, uint32_t nodesc); void dom_parent_node_replace_children(dom_object *context, zval *nodes, uint32_t nodesc); void dom_child_node_remove(dom_object *context); void dom_child_replace_with(dom_object *context, zval *nodes, uint32_t nodesc); - void dom_remove_all_children(xmlNodePtr nodep); +bool php_dom_fragment_insertion_hierarchy_check_pre_insertion(xmlNodePtr parent, xmlNodePtr node, xmlNodePtr child); +bool php_dom_fragment_insertion_hierarchy_check_replace(xmlNodePtr parent, xmlNodePtr node, xmlNodePtr child); +void php_dom_node_append(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent); +bool php_dom_pre_insert(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent, xmlNodePtr insertion_point); +bool php_dom_pre_insert_is_parent_invalid(xmlNodePtr parent); /* nodemap and nodelist APIs */ -xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const char *named, bool may_transform); -void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const char *named, zval *return_value); +xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const zend_string *named, bool may_transform); +void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, zval *return_value); xmlNodePtr php_dom_named_node_map_get_item(dom_nnodemap_object *objmap, zend_long index); void php_dom_named_node_map_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value); void php_dom_nodelist_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value); 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); +xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive); #define DOM_GET_INTERN(__id, __intern) { \ __intern = Z_DOMOBJ_P(__id); \ @@ -205,9 +249,6 @@ void dom_mark_namespaces_for_copy_based_on_copy(xmlNodePtr copy, const xmlNode * __ptr = (__prtype)((php_libxml_node_ptr *)__intern->ptr)->node; \ } -#define DOM_NODELIST 0 -#define DOM_NAMEDNODEMAP 1 - static zend_always_inline bool php_dom_is_cache_tag_stale_from_doc_ptr(const php_libxml_cache_tag *cache_tag, const php_libxml_ref_obj *doc_ptr) { ZEND_ASSERT(cache_tag != NULL); @@ -223,11 +264,11 @@ static zend_always_inline bool php_dom_is_cache_tag_stale_from_doc_ptr(const php static zend_always_inline bool php_dom_is_cache_tag_stale_from_node(const php_libxml_cache_tag *cache_tag, const xmlNodePtr node) { ZEND_ASSERT(node != NULL); - php_libxml_node_ptr *private = node->_private; - if (!private) { + php_libxml_node_ptr *_private = node->_private; + if (!_private) { return true; } - php_libxml_node_object *object_private = private->_private; + php_libxml_node_object *object_private = _private->_private; if (!object_private || !object_private->document) { return true; } @@ -237,15 +278,45 @@ static zend_always_inline bool php_dom_is_cache_tag_stale_from_node(const php_li static zend_always_inline void php_dom_mark_cache_tag_up_to_date_from_node(php_libxml_cache_tag *cache_tag, const xmlNodePtr node) { ZEND_ASSERT(cache_tag != NULL); - php_libxml_node_ptr *private = node->_private; - if (private) { - php_libxml_node_object *object_private = private->_private; + php_libxml_node_ptr *_private = node->_private; + if (_private) { + php_libxml_node_object *object_private = _private->_private; if (object_private->document) { cache_tag->modification_nr = object_private->document->cache_tag.modification_nr; } } } +static zend_always_inline bool php_dom_follow_spec_doc_ref(const php_libxml_ref_obj *document) +{ + return document != NULL && document->class_type == PHP_LIBXML_CLASS_MODERN; +} + +static zend_always_inline bool php_dom_follow_spec_intern(const dom_object *intern) +{ + ZEND_ASSERT(intern != NULL); + return php_dom_follow_spec_doc_ref(intern->document); +} + +static zend_always_inline bool php_dom_follow_spec_node(const xmlNode *node) +{ + ZEND_ASSERT(node != NULL); + php_libxml_node_ptr *_private = node->_private; + if (_private) { + php_libxml_node_object *object_private = _private->_private; + if (object_private->document) { + return php_dom_follow_spec_doc_ref(object_private->document); + } + } + return false; +} + +static zend_always_inline php_dom_libxml_ns_mapper *php_dom_get_ns_mapper(dom_object *intern) +{ + ZEND_ASSERT(intern->document != NULL); + return (php_dom_libxml_ns_mapper *) intern->document->private_data; +} + PHP_MINIT_FUNCTION(dom); PHP_MSHUTDOWN_FUNCTION(dom); PHP_MINFO_FUNCTION(dom); diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php index 8a6e87676f2eb..7f3a928a093a4 100644 --- a/ext/dom/php_dom.stub.php +++ b/ext/dom/php_dom.stub.php @@ -239,7 +239,6 @@ */ const DOM_VALIDATION_ERR = UNKNOWN; - /** @alias DOM\DocumentType */ class DOMDocumentType extends DOMNode { /** @readonly */ @@ -261,19 +260,16 @@ class DOMDocumentType extends DOMNode public ?string $internalSubset; } - /** @alias DOM\CDATASection */ class DOMCdataSection extends DOMText { public function __construct(string $data) {} } - /** @alias DOM\Comment */ class DOMComment extends DOMCharacterData { public function __construct(string $data = "") {} } - /** @alias DOM\ParentNode */ interface DOMParentNode { /** @param DOMNode|string $nodes */ @@ -286,7 +282,6 @@ public function prepend(...$nodes): void; public function replaceChildren(...$nodes): void; } - /** @alias DOM\ChildNode */ interface DOMChildNode { public function remove(): void; @@ -301,7 +296,6 @@ public function after(...$nodes): void; public function replaceWith(...$nodes): void; } - /** @alias DOM\Node */ class DOMNode { public const int DOCUMENT_POSITION_DISCONNECTED = 0x01; @@ -347,7 +341,7 @@ class DOMNode public bool $isConnected; /** @readonly */ - public ?DOM\Document $ownerDocument; + public ?DOMDocument $ownerDocument; /** @readonly */ public ?string $namespaceURI; @@ -426,7 +420,6 @@ public function __sleep(): array {} public function __wakeup(): void {} } - /** @alias DOM\NameSpaceNode */ class DOMNameSpaceNode { /** @readonly */ @@ -481,7 +474,6 @@ public function createDocumentType(string $qualifiedName, string $publicId = "", public function createDocument(?string $namespace = null, string $qualifiedName = "", ?DOMDocumentType $doctype = null): DOMDocument {} } - /** @alias DOM\DocumentFragment */ class DOMDocumentFragment extends DOMNode implements DOMParentNode { /** @readonly */ @@ -512,12 +504,11 @@ public function prepend(...$nodes): void {} /** * @param DOMNode|string $nodes - * @implementation-alias DOM\Document::replaceChildren + * @implementation-alias DOMDocument::replaceChildren */ public function replaceChildren(...$nodes): void {} } - /** @alias DOM\NodeList */ class DOMNodeList implements IteratorAggregate, Countable { /** @readonly */ @@ -532,7 +523,6 @@ public function getIterator(): Iterator {} public function item(int $index) {} } - /** @alias DOM\CharacterData */ class DOMCharacterData extends DOMNode implements DOMChildNode { public string $data; @@ -583,7 +573,6 @@ public function before(... $nodes): void {} public function after(...$nodes): void {} } - /** @alias DOM\Attr */ class DOMAttr extends DOMNode { /** @readonly */ @@ -606,7 +595,6 @@ public function __construct(string $name, string $value = "") {} public function isId(): bool {} } - /** @alias DOM\Element */ class DOMElement extends DOMNode implements \DOMParentNode, \DOMChildNode { /** @readonly */ @@ -672,7 +660,7 @@ public function removeAttributeNS(?string $namespace, string $localName): void { 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 + public function setAttribute(string $qualifiedName, string $value) {} /** @tentative-return-type */ public function setAttributeNS(?string $namespace, string $qualifiedName, string $value): void {} @@ -719,17 +707,25 @@ public function insertAdjacentElement(string $where, DOMElement $element): ?DOME public function insertAdjacentText(string $where, string $data): void {} } - class DOMDocument extends DOM\Document + class DOMDocument extends DOMNode implements DOMParentNode { + /** @readonly */ + public ?DOMDocumentType $doctype; + /** @readonly */ public DOMImplementation $implementation; + /** @readonly */ + public ?DOMElement $documentElement; + /** * @readonly * @deprecated */ public ?string $actualEncoding; + public ?string $encoding; + /** @readonly */ public ?string $xmlEncoding; @@ -741,6 +737,10 @@ class DOMDocument extends DOM\Document public ?string $xmlVersion; + public bool $strictErrorChecking; + + public ?string $documentURI; + /** * @readonly * @deprecated @@ -759,17 +759,77 @@ class DOMDocument extends DOM\Document public bool $substituteEntities; + /** @readonly */ + public ?DOMElement $firstElementChild; + + /** @readonly */ + public ?DOMElement $lastElementChild; + + /** @readonly */ + public int $childElementCount; + public function __construct(string $version = "1.0", string $encoding = "") {} + /** @return DOMAttr|false */ + public function createAttribute(string $localName) {} + + /** @return DOMAttr|false */ + public function createAttributeNS(?string $namespace, string $qualifiedName) {} + + /** @return DOMCdataSection|false */ + public function createCDATASection(string $data) {} + + /** @tentative-return-type */ + public function createComment(string $data): DOMComment {} + + /** @tentative-return-type */ + public function createDocumentFragment(): DOMDocumentFragment {} + + /** @return DOMElement|false */ + public function createElement(string $localName, string $value = "") {} + + /** @return DOMElement|false */ + public function createElementNS(?string $namespace, string $qualifiedName, string $value = "") {} + /** @return DOMEntityReference|false */ public function createEntityReference(string $name) {} + /** @return DOMProcessingInstruction|false */ + public function createProcessingInstruction(string $target, string $data = "") {} + + /** @tentative-return-type */ + public function createTextNode(string $data): DOMText {} + + /** @tentative-return-type */ + public function getElementById(string $elementId): ?DOMElement {} + + /** + * @tentative-return-type + * @implementation-alias DOMElement::getElementsByTagName + */ + public function getElementsByTagName(string $qualifiedName): DOMNodeList {} + + /** + * @tentative-return-type + * @implementation-alias DOMElement::getElementsByTagNameNS + */ + public function getElementsByTagNameNS(?string $namespace, string $localName): DOMNodeList {} + + /** @return DOMNode|false */ + public function importNode(DOMNode $node, bool $deep = false) {} + /** @tentative-return-type */ public function load(string $filename, int $options = 0): bool {} /** @tentative-return-type */ public function loadXML(string $source, int $options = 0): bool {} + /** @tentative-return-type */ + public function normalizeDocument(): void {} + + /** @tentative-return-type */ + public function registerNodeClass(string $baseClass, ?string $extendedClass): true {} + /** @tentative-return-type */ public function save(string $filename, int $options = 0): int|false {} @@ -790,11 +850,43 @@ public function saveHTMLFile(string $filename): int|false {} /** @tentative-return-type */ public function saveXML(?DOMNode $node = null, int $options = 0): string|false {} +#ifdef LIBXML_SCHEMAS_ENABLED + /** @tentative-return-type */ + public function schemaValidate(string $filename, int $flags = 0): bool {} + + /** @tentative-return-type */ + public function schemaValidateSource(string $source, int $flags = 0): bool {} + + /** @tentative-return-type */ + public function relaxNGValidate(string $filename): bool {} + + /** @tentative-return-type */ + public function relaxNGValidateSource(string $source): bool {} +#endif + /** @tentative-return-type */ public function validate(): bool {} /** @tentative-return-type */ public function xinclude(int $options = 0): int|false {} + + /** @tentative-return-type */ + public function adoptNode(DOMNode $node): DOMNode|false {} + + /** + * @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 */ + public function replaceChildren(...$nodes): void {} } /** @alias DOM\DOMException */ @@ -807,7 +899,6 @@ final class DOMException extends Exception public $code = 0; // TODO add proper type (i.e. int|string) } - /** @alias DOM\Text */ class DOMText extends DOMCharacterData { /** @readonly */ @@ -828,20 +919,19 @@ public function isElementContentWhitespace(): bool {} 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 + public function getNamedItem(string $qualifiedName): ?DOMNode {} /** @tentative-return-type */ - public function getNamedItemNS(?string $namespace, string $localName): ?DOMNode {} // TODO DOM spec returns DOMAttr + public function getNamedItemNS(?string $namespace, string $localName): ?DOMNode {} /** @tentative-return-type */ - public function item(int $index): ?DOMNode {} // TODO DOM spec returns DOMAttr + public function item(int $index): ?DOMNode {} /** @tentative-return-type */ public function count(): int {} @@ -849,7 +939,6 @@ public function count(): int {} public function getIterator(): Iterator {} } - /** @alias DOM\Entity */ class DOMEntity extends DOMNode { /** @readonly */ @@ -880,13 +969,11 @@ class DOMEntity extends DOMNode public ?string $version = null; } - /** @alias DOM\EntityReference */ class DOMEntityReference extends DOMNode { public function __construct(string $name) {} } - /** @alias DOM\Notation */ class DOMNotation extends DOMNode { /** @readonly */ @@ -896,7 +983,6 @@ class DOMNotation extends DOMNode public string $systemId; } - /** @alias DOM\ProcessingInstruction */ class DOMProcessingInstruction extends DOMNode { /** @readonly */ @@ -908,18 +994,15 @@ public function __construct(string $name, string $value = "") {} } #ifdef LIBXML_XPATH_ENABLED - /** - * @not-serializable - * @alias DOM\XPath - */ + /** @not-serializable */ class DOMXPath { /** @readonly */ - public DOM\Document $document; + public DOMDocument $document; public bool $registerNodeNamespaces; - public function __construct(DOM\Document $document, bool $registerNodeNS = true) {} + public function __construct(DOMDocument $document, bool $registerNodeNS = true) {} /** @tentative-return-type */ public function evaluate(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed {} @@ -944,11 +1027,6 @@ function dom_import_simplexml(object $node): DOMElement {} namespace DOM { - /** - * @var int - * @cvalue PHP_ERR - */ - const PHP_ERR = UNKNOWN; /** * @var int * @cvalue INDEX_SIZE_ERR @@ -1019,11 +1097,6 @@ function dom_import_simplexml(object $node): DOMElement {} * @cvalue NAMESPACE_ERR */ const NAMESPACE_ERR = UNKNOWN; - /** - * @var int - * @cvalue INVALID_ACCESS_ERR - */ - const INVALID_ACCESS_ERR = UNKNOWN; /** * @var int * @cvalue VALIDATION_ERR @@ -1036,119 +1109,491 @@ function dom_import_simplexml(object $node): DOMElement {} */ const HTML_NO_DEFAULT_NS = UNKNOWN; - abstract class Document extends \DOMNode implements \DOMParentNode + interface ParentNode + { + public function append(Node|string ...$nodes): void; + public function prepend(Node|string ...$nodes): void; + public function replaceChildren(Node|string ...$nodes): void; + } + + interface ChildNode { + public function remove(): void; + public function before(Node|string ...$nodes): void; + public function after(Node|string ...$nodes): void; + public function replaceWith(Node|string ...$nodes): void; + } + + /** + * @strict-properties + * @not-serializable + */ + class Implementation + { + public function createDocumentType(string $qualifiedName, string $publicId, string $systemId): DocumentType {} + + public function createDocument(?string $namespace, string $qualifiedName, ?DocumentType $doctype = null): XMLDocument {} + + public function createHTMLDocument(?string $title = null): HTMLDocument {} + } + + /** @strict-properties */ + class Node + { + private final function __construct() {} + /** @readonly */ - public ?DocumentType $doctype; + public int $nodeType; + /** @readonly */ + public string $nodeName; /** @readonly */ - public ?Element $documentElement; + public string $baseURI; - public ?string $encoding; + /** @readonly */ + public bool $isConnected; + /** @readonly */ + public ?Document $ownerDocument; - public bool $strictErrorChecking; + /** @implementation-alias DOMNode::getRootNode */ + public function getRootNode(array $options = []): Node {} + /** @readonly */ + public ?Node $parentNode; + /** @readonly */ + public ?Element $parentElement; + /** @implementation-alias DOMNode::hasChildNodes */ + public function hasChildNodes(): bool {} + /** @readonly */ + public NodeList $childNodes; + /** @readonly */ + public ?Node $firstChild; + /** @readonly */ + public ?Node $lastChild; + /** @readonly */ + public ?Node $previousSibling; + /** @readonly */ + public ?Node $nextSibling; - public ?string $documentURI; + public ?string $nodeValue; + public ?string $textContent; + /** @implementation-alias DOMNode::normalize */ + public function normalize(): void {} + + /** @implementation-alias DOMNode::cloneNode */ + public function cloneNode(bool $deep = false): Node {} + public function isEqualNode(?Node $otherNode): bool {} + public function isSameNode(?Node $otherNode): bool {} + + 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; + public function compareDocumentPosition(Node $other): int {} + public function contains(?Node $other): bool {} + + public function lookupPrefix(?string $namespace): ?string {} + /** @implementation-alias DOMNode::lookupNamespaceURI */ + public function lookupNamespaceURI(?string $prefix): ?string {} + public function isDefaultNamespace(?string $namespace): bool {} + + public function insertBefore(Node $node, ?Node $child): Node {} + public function appendChild(Node $node): Node {} + public function replaceChild(Node $node, Node $child): Node {} + public function removeChild(Node $child): Node {} + + /** @implementation-alias DOMNode::getLineNo */ + public function getLineNo(): int {} + public function getNodePath(): string {} + /** @implementation-alias DOMNode::C14N */ + public function C14N(bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): string|false {} + /** @implementation-alias DOMNode::C14NFile */ + public function C14NFile(string $uri, bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): int|false {} + + /** @implementation-alias DOMNode::__sleep */ + public function __sleep(): array {} + /** @implementation-alias DOMNode::__wakeup */ + public function __wakeup(): void {} + } + + class NodeList implements IteratorAggregate, Countable + { /** @readonly */ - public ?Element $firstElementChild; + public int $length; + /** @implementation-alias DOMNodeList::count */ + public function count(): int {} + + /** @implementation-alias DOMNodeList::getIterator */ + public function getIterator(): \Iterator {} + + /** @implementation-alias DOMNodeList::item */ + public function item(int $index): ?Node {} + } + + class NamedNodeMap implements IteratorAggregate, Countable + { /** @readonly */ - public ?Element $lastElementChild; + public int $length; + + /** @implementation-alias DOMNamedNodeMap::item */ + public function item(int $index): ?Attr {} + /** @implementation-alias DOMNamedNodeMap::getNamedItem */ + public function getNamedItem(string $qualifiedName): ?Attr {} + /** @implementation-alias DOMNamedNodeMap::getNamedItemNS */ + public function getNamedItemNS(?string $namespace, string $localName): ?Attr {} + + /** @implementation-alias DOMNamedNodeMap::count */ + public function count(): int {} + + /** @implementation-alias DOMNamedNodeMap::getIterator */ + public function getIterator(): \Iterator {} + } + + class DTDNamedNodeMap implements IteratorAggregate, Countable + { + /** @readonly */ + public int $length; + + /** @implementation-alias DOMNamedNodeMap::item */ + public function item(int $index): Entity|Notation|null {} + /** @implementation-alias DOMNamedNodeMap::getNamedItem */ + public function getNamedItem(string $qualifiedName): Entity|Notation|null {} + /** @implementation-alias DOMNamedNodeMap::getNamedItemNS */ + public function getNamedItemNS(?string $namespace, string $localName): Entity|Notation|null {} + + /** @implementation-alias DOMNamedNodeMap::count */ + public function count(): int {} + + /** @implementation-alias DOMNamedNodeMap::getIterator */ + public function getIterator(): \Iterator {} + } + + class HTMLCollection implements IteratorAggregate, Countable + { + /** @readonly */ + public int $length; + + /** @implementation-alias DOMNodeList::item */ + public function item(int $index): ?Element {} + + /* TODO: implement namedItem */ + + /** @implementation-alias DOMNodeList::count */ + public function count(): int {} + + /** @implementation-alias DOMNodeList::getIterator */ + public function getIterator(): \Iterator {} + } + + class Element extends Node implements ParentNode, ChildNode + { + /** @readonly */ + public ?string $namespaceURI; + /** @readonly */ + public ?string $prefix; + /** @readonly */ + public string $localName; + /** @readonly */ + public string $tagName; + + public string $id; + public string $className; + + /** @implementation-alias DOMNode::hasAttributes */ + public function hasAttributes(): bool {} + /** @readonly */ + public NamedNodeMap $attributes; + /** @implementation-alias DOMElement::getAttributeNames */ + public function getAttributeNames(): array {} + /** @implementation-alias DOMElement::getAttribute */ + public function getAttribute(string $qualifiedName): ?string {} + /** @implementation-alias DOMElement::getAttributeNS */ + public function getAttributeNS(?string $namespace, string $localName): ?string {} + /** @implementation-alias DOMElement::setAttribute */ + public function setAttribute(string $qualifiedName, string $value): void {} + /** @implementation-alias DOMElement::setAttributeNS */ + public function setAttributeNS(?string $namespace, string $qualifiedName, string $value): void {} + public function removeAttribute(string $qualifiedName): void {} + /** @implementation-alias DOMElement::removeAttributeNS */ + public function removeAttributeNS(?string $namespace, string $localName): void {} + /** @implementation-alias DOMElement::toggleAttribute */ + public function toggleAttribute(string $qualifiedName, ?bool $force = null): bool {} + /** @implementation-alias DOMElement::hasAttribute */ + public function hasAttribute(string $qualifiedName): bool {} + /** @implementation-alias DOMElement::hasAttributeNS */ + public function hasAttributeNS(?string $namespace, string $localName): bool {} + + /** @implementation-alias DOMElement::getAttributeNode */ + public function getAttributeNode(string $qualifiedName): ?Attr {} + /** @implementation-alias DOMElement::getAttributeNodeNS */ + public function getAttributeNodeNS(?string $namespace, string $localName): ?Attr {} + /** @implementation-alias DOM\Element::setAttributeNodeNS */ + public function setAttributeNode(Attr $attr) : ?Attr {} + public function setAttributeNodeNS(Attr $attr) : ?Attr {} + public function removeAttributeNode(Attr $attr) : Attr {} + public function getElementsByTagName(string $qualifiedName): HTMLCollection {} + public function getElementsByTagNameNS(?string $namespace, string $localName): HTMLCollection {} + + public function insertAdjacentElement(string $where, Element $element): ?Element {} + /** @implementation-alias DOMElement::insertAdjacentText */ + public function insertAdjacentText(string $where, string $data): void {} + + /** @readonly */ + public ?Element $firstElementChild; + /** @readonly */ + public ?Element $lastElementChild; /** @readonly */ public int $childElementCount; + /** @readonly */ + public ?Element $previousElementSibling; + /** @readonly */ + public ?Element $nextElementSibling; - /** @return Attr|false */ - public function createAttribute(string $localName) {} + /** @implementation-alias DOMElement::setIdAttribute */ + public function setIdAttribute(string $qualifiedName, bool $isId): void {} + /** @implementation-alias DOMElement::setIdAttributeNS */ + public function setIdAttributeNS(?string $namespace, string $qualifiedName, bool $isId): void {} + public function setIdAttributeNode(Attr $attr, bool $isId): void {} - /** @return Attr|false */ - public function createAttributeNS(?string $namespace, string $qualifiedName) {} + /** @implementation-alias DOMElement::remove */ + public function remove(): void {} + /** @implementation-alias DOMElement::before */ + public function before(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::after */ + public function after(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::replaceWith */ + public function replaceWith(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::append */ + public function append(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::prepend */ + public function prepend(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::replaceChildren */ + public function replaceChildren(Node|string ...$nodes): void {} + } - /** @return CDATASection|false */ - public function createCDATASection(string $data) {} + class Attr extends Node + { + /** @readonly */ + public ?string $namespaceURI; + /** @readonly */ + public ?string $prefix; + /** @readonly */ + public string $localName; + /** @readonly */ + public string $name; + public string $value; - /** @tentative-return-type */ - public function createComment(string $data): Comment {} + /** @readonly */ + public ?Element $ownerElement; - /** @tentative-return-type */ - public function createDocumentFragment(): DocumentFragment {} + /** @readonly */ + public bool $specified = true; - /** @return Element|false */ - public function createElement(string $localName, string $value = "") {} + /** @implementation-alias DOMAttr::isId */ + public function isId(): bool {} + } - /** @return Element|false */ - public function createElementNS(?string $namespace, string $qualifiedName, string $value = "") {} + class CharacterData extends Node implements ChildNode + { + /** @readonly */ + public ?Element $previousElementSibling; + /** @readonly */ + public ?Element $nextElementSibling; - /** @return ProcessingInstruction|false */ - public function createProcessingInstruction(string $target, string $data = "") {} + public string $data; + /** @readonly */ + public int $length; + /** @implementation-alias DOMCharacterData::substringData */ + public function substringData(int $offset, int $count): string {} + public function appendData(string $data): void {} + public function insertData(int $offset, string $data): void {} + public function deleteData(int $offset, int $count): void {} + public function replaceData(int $offset, int $count, string $data): void {} - /** @tentative-return-type */ - public function createTextNode(string $data): Text {} + /** @implementation-alias DOMElement::remove */ + public function remove(): void {} + /** @implementation-alias DOMElement::before */ + public function before(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::after */ + public function after(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::replaceWith */ + public function replaceWith(Node|string ...$nodes): void {} + } - /** @tentative-return-type */ - public function getElementById(string $elementId): ?Element {} + class Text extends CharacterData + { + /* No constructor because Node has a final private constructor, so PHP does not allow overriding that. */ - /** @tentative-return-type */ - public function getElementsByTagName(string $qualifiedName): NodeList {} + /** @implementation-alias DOMText::splitText */ + public function splitText(int $offset): Text {} + /** @readonly */ + public string $wholeText; + } - /** @tentative-return-type */ - public function getElementsByTagNameNS(?string $namespace, string $localName): NodeList {} + class CDATASection extends Text {} - /** @return Node|false */ - public function importNode(Node $node, bool $deep = false) {} + class ProcessingInstruction extends CharacterData + { + /** @readonly */ + public string $target; + } - /** @tentative-return-type */ - public function normalizeDocument(): void {} + class Comment extends CharacterData + { + /* No constructor because Node has a final private constructor, so PHP does not allow overriding that. */ + } - /** @tentative-return-type */ - public function registerNodeClass(string $baseClass, ?string $extendedClass): true {} + class DocumentType extends Node implements ChildNode + { + /** @readonly */ + public string $name; + /** @readonly */ + public DTDNamedNodeMap $entities; + /** @readonly */ + public DTDNamedNodeMap $notations; + /** @readonly */ + public string $publicId; + /** @readonly */ + public string $systemId; + /** @readonly */ + public ?string $internalSubset; + + /** @implementation-alias DOMElement::remove */ + public function remove(): void {} + /** @implementation-alias DOMElement::before */ + public function before(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::after */ + public function after(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::replaceWith */ + public function replaceWith(Node|string ...$nodes): void {} + } + + class DocumentFragment extends Node implements ParentNode + { + /** @readonly */ + public ?Element $firstElementChild; + /** @readonly */ + public ?Element $lastElementChild; + /** @readonly */ + public int $childElementCount; + + /** @implementation-alias DOMDocumentFragment::appendXML */ + public function appendXML(string $data): bool {} + /** @implementation-alias DOMElement::append */ + public function append(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::prepend */ + public function prepend(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::replaceChildren */ + public function replaceChildren(Node|string ...$nodes): void {} + } + + class Entity extends Node + { + /** @readonly */ + public ?string $publicId; + /** @readonly */ + public ?string $systemId; + /** @readonly */ + public ?string $notationName; + } + + class EntityReference extends Node {} + + class Notation extends Node + { + /** @readonly */ + public string $publicId; + /** @readonly */ + public string $systemId; + } + + abstract class Document extends Node implements ParentNode + { + /** @readonly */ + public Implementation $implementation; + /** @readonly */ + public string $URL; + /** @readonly */ + public string $documentURI; + public string $characterSet; + public string $charset; + public string $inputEncoding; + + /** @readonly */ + public ?DocumentType $doctype; + /** @readonly */ + public ?Element $documentElement; + /** @implementation-alias DOM\Element::getElementsByTagName */ + public function getElementsByTagName(string $qualifiedName): HTMLCollection {} + /** @implementation-alias DOM\Element::getElementsByTagNameNS */ + public function getElementsByTagNameNS(?string $namespace, string $localName): HTMLCollection {} + + public function createElement(string $localName): Element {} + public function createElementNS(?string $namespace, string $qualifiedName): Element {} + /** @implementation-alias DOMDocument::createDocumentFragment */ + public function createDocumentFragment(): DocumentFragment {} + /** @implementation-alias DOMDocument::createTextNode */ + public function createTextNode(string $data): Text {} + /** @implementation-alias DOMDocument::createCDATASection */ + public function createCDATASection(string $data): CDATASection {} + /** @implementation-alias DOMDocument::createComment */ + public function createComment(string $data): Comment {} + public function createProcessingInstruction(string $target, string $data): ProcessingInstruction {} + + public function importNode(?Node $node, bool $deep = false): Node {} + public function adoptNode(Node $node): Node {} + + /** @implementation-alias DOMDocument::createAttribute */ + public function createAttribute(string $localName): Attr {} + /** @implementation-alias DOMDocument::createAttributeNS */ + public function createAttributeNS(?string $namespace, string $qualifiedName): Attr {} + + /** @readonly */ + public ?Element $firstElementChild; + /** @readonly */ + public ?Element $lastElementChild; + /** @readonly */ + public int $childElementCount; + + /** @implementation-alias DOMDocument::getElementById */ + public function getElementById(string $elementId): ?Element {} + + public function registerNodeClass(string $baseClass, ?string $extendedClass): void {} #ifdef LIBXML_SCHEMAS_ENABLED - /** @tentative-return-type */ + /** @implementation-alias DOMDocument::schemaValidate */ public function schemaValidate(string $filename, int $flags = 0): bool {} - - /** @tentative-return-type */ + /** @implementation-alias DOMDocument::schemaValidateSource */ public function schemaValidateSource(string $source, int $flags = 0): bool {} - - /** @tentative-return-type */ + /** @implementation-alias DOMDocument::relaxNGValidate */ public function relaxNGValidate(string $filename): bool {} - - /** @tentative-return-type */ + /** @implementation-alias DOMDocument::relaxNGValidateSource */ public function relaxNGValidateSource(string $source): bool {} #endif - /** @tentative-return-type */ - public function adoptNode(Node $node): Node|false {} - - /** - * @param Node|string $nodes - * @implementation-alias DOMElement::append - */ - public function append(...$nodes): void {} + /** @implementation-alias DOMElement::append */ + public function append(Node|string ...$nodes): void {} + /** @implementation-alias DOMElement::prepend */ + public function prepend(Node|string ...$nodes): void {} + /** @implementation-alias DOMDocument::replaceChildren */ + public function replaceChildren(Node|string ...$nodes): void {} - /** - * @param Node|string $nodes - * @implementation-alias DOMElement::prepend - */ - public function prepend(...$nodes): void {} - - /** @param Node|string $nodes */ - public function replaceChildren(...$nodes): void {} + public function importLegacyNode(\DOMNode $node, bool $deep = false): Node {} } - /** @strict-properties */ final class HTMLDocument extends Document { - private function __construct() {} - public static function createEmpty(string $encoding = "UTF-8"): HTMLDocument {} public static function createFromFile(string $path, int $options = 0, ?string $overrideEncoding = null): HTMLDocument {} public static function createFromString(string $source, int $options = 0, ?string $overrideEncoding = null): HTMLDocument {} - /** @implementation-alias DOMDocument::saveXML */ + /** @implementation-alias DOM\XMLDocument::saveXML */ public function saveXML(?Node $node = null, int $options = 0): string|false {} /** @implementation-alias DOMDocument::save */ @@ -1159,12 +1604,8 @@ public function saveHTML(?Node $node = null): string|false {} public function saveHTMLFile(string $filename): int|false {} } - /** @strict-properties */ final class XMLDocument extends Document { - /** @implementation-alias DOM\HTMLDocument::__construct */ - private function __construct() {} - public static function createEmpty(string $version = "1.0", string $encoding = "UTF-8"): XMLDocument {} public static function createFromFile(string $path, int $options = 0, ?string $overrideEncoding = null): XMLDocument {} @@ -1172,58 +1613,57 @@ public static function createFromFile(string $path, int $options = 0, ?string $o public static function createFromString(string $source, int $options = 0, ?string $overrideEncoding = null): XMLDocument {} /** @readonly */ - public ?string $xmlEncoding; - - public bool $standalone; + public string $xmlEncoding; public bool $xmlStandalone; - public ?string $version; - - public ?string $xmlVersion; + public string $xmlVersion; public bool $formatOutput; - public bool $validateOnParse; + /** @implementation-alias DOMDocument::createEntityReference */ + public function createEntityReference(string $name): EntityReference {} - public bool $resolveExternals; + /** @implementation-alias DOMDocument::validate */ + public function validate(): bool {} - public bool $preserveWhiteSpace; + /** @implementation-alias DOMDocument::xinclude */ + public function xinclude(int $options = 0): int|false {} - public bool $recover; + public function saveXML(?Node $node = null, int $options = 0): string|false {} - public bool $substituteEntities; + /** @implementation-alias DOMDocument::save */ + public function saveXMLFile(string $filename, int $options = 0): int|false {} + } - /** - * @implementation-alias DOMDocument::createEntityReference - * @return EntityReference|false - */ - public function createEntityReference(string $name) {} +#ifdef LIBXML_XPATH_ENABLED + /** @not-serializable */ + final class XPath + { + /** @readonly */ + public Document $document; - /** - * @implementation-alias DOMDocument::validate - */ - public function validate(): bool {} + public bool $registerNodeNamespaces; - /** - * @tentative-return-type - * @implementation-alias DOMDocument::xinclude - */ - public function xinclude(int $options = 0): int|false {} + public function __construct(Document $document, bool $registerNodeNS = true) {} - /** - * @tentative-return-type - * @implementation-alias DOMDocument::saveXML - */ - public function saveXML(?Node $node = null, int $options = 0): string|false {} + public function evaluate(string $expression, ?Node $contextNode = null, bool $registerNodeNS = true): null|bool|float|string|NodeList {} - /** - * @tentative-return-type - * @implementation-alias DOMDocument::save - */ - public function saveXMLFile(string $filename, int $options = 0): int|false {} + public function query(string $expression, ?Node $contextNode = null, bool $registerNodeNS = true): NodeList {} + + /** @implementation-alias DOMXPath::registerNamespace */ + public function registerNamespace(string $prefix, string $namespace): bool {} + + /** @implementation-alias DOMXPath::registerPhpFunctions */ + public function registerPhpFunctions(string|array|null $restrict = null): void {} + + /** @implementation-alias DOMXPath::registerPhpFunctionNS */ + public function registerPhpFunctionNS(string $namespaceURI, string $name, callable $callable): void {} + + /** @implementation-alias DOMXPath::quote */ + public static function quote(string $str): string {} } +#endif - /** @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 0c86ac8d73f5b..280b32e6152fb 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 498e4aa2e670454b78808215e8efaedb2ce7d251 */ + * Stub hash: bca273cb7255e24536575922505d3cce7488b32d */ 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) @@ -332,10 +332,61 @@ 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") @@ -346,6 +397,13 @@ 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_TRUE, 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") @@ -382,12 +440,48 @@ 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 @@ -421,7 +515,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, DOM\\Document, 0) + ZEND_ARG_OBJ_INFO(0, document, DOMDocument, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, registerNodeNS, _IS_BOOL, 0, "true") ZEND_END_ARG_INFO() #endif @@ -465,178 +559,542 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOMXPath_quote, 0, 1, IS_S 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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_ParentNode_append, 0, 0, IS_VOID, 0) + ZEND_ARG_VARIADIC_OBJ_TYPE_MASK(0, nodes, DOM\\\116ode, MAY_BE_STRING) +ZEND_END_ARG_INFO() + +#define arginfo_class_DOM_ParentNode_prepend arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_ParentNode_replaceChildren arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_ChildNode_remove arginfo_class_DOMChildNode_remove + +#define arginfo_class_DOM_ChildNode_before arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_ChildNode_after arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_ChildNode_replaceWith arginfo_class_DOM_ParentNode_append + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Implementation_createDocumentType, 0, 3, DOM\\DocumentType, 0) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, publicId, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, systemId, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOM_Document_createAttributeNS, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Implementation_createDocument, 0, 2, DOM\\XMLDocument, 0) ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, doctype, DOM\\DocumentType, 1, "null") ZEND_END_ARG_INFO() -#define arginfo_class_DOM_Document_createCDATASection arginfo_class_DOMCdataSection___construct +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Implementation_createHTMLDocument, 0, 0, DOM\\HTMLDocument, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, title, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() -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) +#define arginfo_class_DOM_Node___construct arginfo_class_DOMDocumentFragment___construct + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Node_getRootNode, 0, 0, DOM\\\116ode, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Node_hasChildNodes, 0, 0, _IS_BOOL, 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, "\"\"") +#define arginfo_class_DOM_Node_normalize arginfo_class_DOMChildNode_remove + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Node_cloneNode, 0, 0, DOM\\\116ode, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, deep, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Node_isEqualNode, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, otherNode, DOM\\\116ode, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_DOM_Node_isSameNode arginfo_class_DOM_Node_isEqualNode + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Node_compareDocumentPosition, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, other, DOM\\\116ode, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Node_contains, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, other, DOM\\\116ode, 1) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOM_Document_createElementNS, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Node_lookupPrefix, 0, 1, IS_STRING, 1) 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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Node_lookupNamespaceURI, 0, 1, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, prefix, IS_STRING, 1) 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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Node_isDefaultNamespace, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1) 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_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Node_insertBefore, 0, 2, DOM\\\116ode, 0) + ZEND_ARG_OBJ_INFO(0, node, DOM\\\116ode, 0) + ZEND_ARG_OBJ_INFO(0, child, DOM\\\116ode, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Node_appendChild, 0, 1, DOM\\\116ode, 0) + ZEND_ARG_OBJ_INFO(0, node, DOM\\\116ode, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Node_replaceChild, 0, 2, DOM\\\116ode, 0) + ZEND_ARG_OBJ_INFO(0, node, DOM\\\116ode, 0) + ZEND_ARG_OBJ_INFO(0, child, DOM\\\116ode, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Node_removeChild, 0, 1, DOM\\\116ode, 0) + ZEND_ARG_OBJ_INFO(0, child, DOM\\\116ode, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Node_getLineNo, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Node_getNodePath, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_DOM_Node_C14N, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, exclusive, _IS_BOOL, 0, "false") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, withComments, _IS_BOOL, 0, "false") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, xpath, IS_ARRAY, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nsPrefixes, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_DOM_Node_C14NFile, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, exclusive, _IS_BOOL, 0, "false") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, withComments, _IS_BOOL, 0, "false") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, xpath, IS_ARRAY, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nsPrefixes, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_DOM_Node___sleep arginfo_class_DOMNode___sleep + +#define arginfo_class_DOM_Node___wakeup arginfo_class_DOMChildNode_remove + +#define arginfo_class_DOM_NodeList_count arginfo_class_DOM_Node_getLineNo + +#define arginfo_class_DOM_NodeList_getIterator arginfo_class_DOMNodeList_getIterator + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_NodeList_item, 0, 1, DOM\\\116ode, 1) + ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_NamedNodeMap_item, 0, 1, DOM\\Attr, 1) + ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 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_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_NamedNodeMap_getNamedItem, 0, 1, DOM\\Attr, 1) 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_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_NamedNodeMap_getNamedItemNS, 0, 2, DOM\\Attr, 1) 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_NamedNodeMap_count arginfo_class_DOM_Node_getLineNo -#define arginfo_class_DOM_Document_normalizeDocument arginfo_class_DOMNode_normalize +#define arginfo_class_DOM_NamedNodeMap_getIterator arginfo_class_DOMNodeList_getIterator -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Document_registerNodeClass, 0, 2, IS_TRUE, 0) - ZEND_ARG_TYPE_INFO(0, baseClass, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, extendedClass, IS_STRING, 1) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DOM_DTDNamedNodeMap_item, 0, 1, DOM\\Entity|DOM\\\116otation, MAY_BE_NULL) + ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) 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_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DOM_DTDNamedNodeMap_getNamedItem, 0, 1, DOM\\Entity|DOM\\\116otation, MAY_BE_NULL) + ZEND_ARG_TYPE_INFO(0, qualifiedName, 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_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_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DOM_DTDNamedNodeMap_getNamedItemNS, 0, 2, DOM\\Entity|DOM\\\116otation, MAY_BE_NULL) + ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, localName, 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_relaxNGValidate, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) +#define arginfo_class_DOM_DTDNamedNodeMap_count arginfo_class_DOM_Node_getLineNo + +#define arginfo_class_DOM_DTDNamedNodeMap_getIterator arginfo_class_DOMNodeList_getIterator + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_HTMLCollection_item, 0, 1, DOM\\Element, 1) + ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 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) +#define arginfo_class_DOM_HTMLCollection_count arginfo_class_DOM_Node_getLineNo + +#define arginfo_class_DOM_HTMLCollection_getIterator arginfo_class_DOMNodeList_getIterator + +#define arginfo_class_DOM_Element_hasAttributes arginfo_class_DOM_Node_hasChildNodes + +#define arginfo_class_DOM_Element_getAttributeNames arginfo_class_DOMNode___sleep + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_getAttribute, 0, 1, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, qualifiedName, 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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_getAttributeNS, 0, 2, IS_STRING, 1) + 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_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_setAttribute, 0, 2, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_class_DOM_Document_append arginfo_class_DOMParentNode_append +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_setAttributeNS, 0, 3, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DOM_Document_prepend arginfo_class_DOMParentNode_append +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_removeAttribute, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DOM_Document_replaceChildren arginfo_class_DOMParentNode_append +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_removeAttributeNS, 0, 2, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, localName, IS_STRING, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_DOM_HTMLDocument___construct arginfo_class_DOMDocumentFragment___construct +#define arginfo_class_DOM_Element_toggleAttribute arginfo_class_DOMElement_toggleAttribute -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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_hasAttribute, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) 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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_hasAttributeNS, 0, 2, _IS_BOOL, 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_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") +#define arginfo_class_DOM_Element_getAttributeNode arginfo_class_DOM_NamedNodeMap_getNamedItem + +#define arginfo_class_DOM_Element_getAttributeNodeNS arginfo_class_DOM_NamedNodeMap_getNamedItemNS + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Element_setAttributeNode, 0, 1, DOM\\Attr, 1) + ZEND_ARG_OBJ_INFO(0, attr, DOM\\Attr, 0) 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") +#define arginfo_class_DOM_Element_setAttributeNodeNS arginfo_class_DOM_Element_setAttributeNode + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Element_removeAttributeNode, 0, 1, DOM\\Attr, 0) + ZEND_ARG_OBJ_INFO(0, attr, DOM\\Attr, 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_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Element_getElementsByTagName, 0, 1, DOM\\HTMLCollection, 0) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 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_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Element_getElementsByTagNameNS, 0, 2, DOM\\HTMLCollection, 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_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_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Element_insertAdjacentElement, 0, 2, DOM\\Element, 1) + ZEND_ARG_TYPE_INFO(0, where, IS_STRING, 0) + ZEND_ARG_OBJ_INFO(0, element, DOM\\Element, 0) ZEND_END_ARG_INFO() -#define arginfo_class_DOM_XMLDocument___construct arginfo_class_DOMDocumentFragment___construct +#define arginfo_class_DOM_Element_insertAdjacentText arginfo_class_DOMElement_insertAdjacentText -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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_setIdAttribute, 0, 2, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, isId, _IS_BOOL, 0) 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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_setIdAttributeNS, 0, 3, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, isId, _IS_BOOL, 0) 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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_setIdAttributeNode, 0, 2, IS_VOID, 0) + ZEND_ARG_OBJ_INFO(0, attr, DOM\\Attr, 0) + ZEND_ARG_TYPE_INFO(0, isId, _IS_BOOL, 0) ZEND_END_ARG_INFO() -#define arginfo_class_DOM_XMLDocument_createEntityReference arginfo_class_DOMDocument_createEntityReference +#define arginfo_class_DOM_Element_remove arginfo_class_DOMChildNode_remove + +#define arginfo_class_DOM_Element_before arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_Element_after arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_Element_replaceWith arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_Element_append arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_Element_prepend arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_Element_replaceChildren arginfo_class_DOM_ParentNode_append -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_XMLDocument_validate, 0, 0, _IS_BOOL, 0) +#define arginfo_class_DOM_Attr_isId arginfo_class_DOM_Node_hasChildNodes + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_CharacterData_substringData, 0, 2, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_class_DOM_XMLDocument_xinclude arginfo_class_DOMDocument_xinclude +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_CharacterData_appendData, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) +ZEND_END_ARG_INFO() -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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_CharacterData_insertData, 0, 2, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_class_DOM_XMLDocument_saveXMLFile arginfo_class_DOMDocument_save +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_CharacterData_deleteData, 0, 2, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0) +ZEND_END_ARG_INFO() -ZEND_FUNCTION(dom_import_simplexml); -ZEND_METHOD(DOMCdataSection, __construct); -ZEND_METHOD(DOMComment, __construct); -ZEND_METHOD(DOMNode, appendChild); +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_CharacterData_replaceData, 0, 3, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_DOM_CharacterData_remove arginfo_class_DOMChildNode_remove + +#define arginfo_class_DOM_CharacterData_before arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_CharacterData_after arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_CharacterData_replaceWith arginfo_class_DOM_ParentNode_append + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Text_splitText, 0, 1, DOM\\Text, 0) + ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_DOM_DocumentType_remove arginfo_class_DOMChildNode_remove + +#define arginfo_class_DOM_DocumentType_before arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_DocumentType_after arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_DocumentType_replaceWith arginfo_class_DOM_ParentNode_append + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_DocumentFragment_appendXML, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_DOM_DocumentFragment_append arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_DocumentFragment_prepend arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_DocumentFragment_replaceChildren arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_Document_getElementsByTagName arginfo_class_DOM_Element_getElementsByTagName + +#define arginfo_class_DOM_Document_getElementsByTagNameNS arginfo_class_DOM_Element_getElementsByTagNameNS + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createElement, 0, 1, DOM\\Element, 0) + ZEND_ARG_TYPE_INFO(0, localName, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createElementNS, 0, 2, DOM\\Element, 0) + ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createDocumentFragment, 0, 0, DOM\\DocumentFragment, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_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_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createCDATASection, 0, 1, DOM\\CDATASection, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_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_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createProcessingInstruction, 0, 2, DOM\\ProcessingInstruction, 0) + ZEND_ARG_TYPE_INFO(0, target, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_importNode, 0, 1, DOM\\\116ode, 0) + ZEND_ARG_OBJ_INFO(0, node, DOM\\\116ode, 1) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, deep, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() + +#define arginfo_class_DOM_Document_adoptNode arginfo_class_DOM_Node_appendChild + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createAttribute, 0, 1, DOM\\Attr, 0) + ZEND_ARG_TYPE_INFO(0, localName, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_createAttributeNS, 0, 2, DOM\\Attr, 0) + ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_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_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Document_registerNodeClass, 0, 2, IS_VOID, 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_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_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_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_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 + +#define arginfo_class_DOM_Document_append arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_Document_prepend arginfo_class_DOM_ParentNode_append + +#define arginfo_class_DOM_Document_replaceChildren arginfo_class_DOM_ParentNode_append + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_importLegacyNode, 0, 1, DOM\\\116ode, 0) + 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_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() + +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() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_XMLDocument_createEntityReference, 0, 1, DOM\\EntityReference, 0) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_DOM_XMLDocument_validate arginfo_class_DOM_Node_hasChildNodes + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_DOM_XMLDocument_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() + +#define arginfo_class_DOM_XMLDocument_saveXML arginfo_class_DOM_HTMLDocument_saveXML + +#define arginfo_class_DOM_XMLDocument_saveXMLFile arginfo_class_DOM_HTMLDocument_saveXMLFile + +#if defined(LIBXML_XPATH_ENABLED) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOM_XPath___construct, 0, 0, 1) + 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 + +#if defined(LIBXML_XPATH_ENABLED) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DOM_XPath_evaluate, 0, 1, DOM\\\116odeList, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_DOUBLE|MAY_BE_STRING) + ZEND_ARG_TYPE_INFO(0, expression, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, contextNode, DOM\\\116ode, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, registerNodeNS, _IS_BOOL, 0, "true") +ZEND_END_ARG_INFO() +#endif + +#if defined(LIBXML_XPATH_ENABLED) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_XPath_query, 0, 1, DOM\\\116odeList, 0) + ZEND_ARG_TYPE_INFO(0, expression, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, contextNode, DOM\\\116ode, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, registerNodeNS, _IS_BOOL, 0, "true") +ZEND_END_ARG_INFO() +#endif + +#if defined(LIBXML_XPATH_ENABLED) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_XPath_registerNamespace, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, prefix, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 0) +ZEND_END_ARG_INFO() +#endif + +#if defined(LIBXML_XPATH_ENABLED) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_XPath_registerPhpFunctions, 0, 0, IS_VOID, 0) + ZEND_ARG_TYPE_MASK(0, restrict, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_NULL, "null") +ZEND_END_ARG_INFO() +#endif + +#if defined(LIBXML_XPATH_ENABLED) +#define arginfo_class_DOM_XPath_registerPhpFunctionNS arginfo_class_DOMXPath_registerPhpFunctionNS +#endif + +#if defined(LIBXML_XPATH_ENABLED) +#define arginfo_class_DOM_XPath_quote arginfo_class_DOMXPath_quote +#endif + +ZEND_FUNCTION(dom_import_simplexml); +ZEND_FUNCTION(DOM_import_simplexml); +ZEND_METHOD(DOMCdataSection, __construct); +ZEND_METHOD(DOMComment, __construct); +ZEND_METHOD(DOMNode, appendChild); ZEND_METHOD(DOMNode, C14N); ZEND_METHOD(DOMNode, C14NFile); ZEND_METHOD(DOMNode, cloneNode); @@ -667,7 +1125,7 @@ ZEND_METHOD(DOMDocumentFragment, __construct); ZEND_METHOD(DOMDocumentFragment, appendXML); ZEND_METHOD(DOMElement, append); ZEND_METHOD(DOMElement, prepend); -ZEND_METHOD(DOM_Document, replaceChildren); +ZEND_METHOD(DOMDocument, replaceChildren); ZEND_METHOD(DOMNodeList, count); ZEND_METHOD(DOMNodeList, getIterator); ZEND_METHOD(DOMNodeList, item); @@ -707,9 +1165,22 @@ 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, 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); @@ -724,8 +1195,21 @@ 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); @@ -757,47 +1241,61 @@ ZEND_METHOD(DOMXPath, registerPhpFunctionNS); #if defined(LIBXML_XPATH_ENABLED) ZEND_METHOD(DOMXPath, quote); #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_Implementation, createDocumentType); +ZEND_METHOD(DOM_Implementation, createDocument); +ZEND_METHOD(DOM_Implementation, createHTMLDocument); +ZEND_METHOD(DOM_Node, __construct); +ZEND_METHOD(DOM_Node, isEqualNode); +ZEND_METHOD(DOM_Node, isSameNode); +ZEND_METHOD(DOM_Node, compareDocumentPosition); +ZEND_METHOD(DOM_Node, contains); +ZEND_METHOD(DOM_Node, lookupPrefix); +ZEND_METHOD(DOM_Node, isDefaultNamespace); +ZEND_METHOD(DOM_Node, insertBefore); +ZEND_METHOD(DOM_Node, appendChild); +ZEND_METHOD(DOM_Node, replaceChild); +ZEND_METHOD(DOM_Node, removeChild); +ZEND_METHOD(DOM_Node, getNodePath); +ZEND_METHOD(DOM_Element, removeAttribute); +ZEND_METHOD(DOM_Element, setAttributeNodeNS); +ZEND_METHOD(DOM_Element, removeAttributeNode); +ZEND_METHOD(DOM_Element, getElementsByTagName); +ZEND_METHOD(DOM_Element, getElementsByTagNameNS); +ZEND_METHOD(DOM_Element, insertAdjacentElement); +ZEND_METHOD(DOM_Element, setIdAttributeNode); +ZEND_METHOD(DOM_CharacterData, appendData); +ZEND_METHOD(DOM_CharacterData, insertData); +ZEND_METHOD(DOM_CharacterData, deleteData); +ZEND_METHOD(DOM_CharacterData, replaceData); 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_Document, registerNodeClass); +ZEND_METHOD(DOM_Document, importLegacyNode); ZEND_METHOD(DOM_HTMLDocument, createEmpty); ZEND_METHOD(DOM_HTMLDocument, createFromFile); ZEND_METHOD(DOM_HTMLDocument, createFromString); +ZEND_METHOD(DOM_XMLDocument, saveXML); 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); +#if defined(LIBXML_XPATH_ENABLED) +ZEND_METHOD(DOM_XPath, __construct); +#endif +#if defined(LIBXML_XPATH_ENABLED) +ZEND_METHOD(DOM_XPath, evaluate); +#endif +#if defined(LIBXML_XPATH_ENABLED) +ZEND_METHOD(DOM_XPath, query); +#endif static const zend_function_entry ext_functions[] = { ZEND_FE(dom_import_simplexml, arginfo_dom_import_simplexml) - ZEND_RAW_FENTRY(ZEND_NS_NAME("DOM", "import_simplexml"), zif_dom_import_simplexml, arginfo_DOM_import_simplexml, 0, NULL, NULL) + ZEND_RAW_FENTRY(ZEND_NS_NAME("DOM", "import_simplexml"), zif_DOM_import_simplexml, arginfo_DOM_import_simplexml, 0, NULL, NULL) ZEND_FE_END }; @@ -876,7 +1374,7 @@ static const zend_function_entry class_DOMDocumentFragment_methods[] = { ZEND_ME(DOMDocumentFragment, appendXML, arginfo_class_DOMDocumentFragment_appendXML, ZEND_ACC_PUBLIC) ZEND_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_DOMDocumentFragment_append, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_DOMDocumentFragment_prepend, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_RAW_FENTRY("replaceChildren", zim_DOM_Document_replaceChildren, arginfo_class_DOMDocumentFragment_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("replaceChildren", zim_DOMDocument_replaceChildren, arginfo_class_DOMDocumentFragment_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_FE_END }; @@ -942,9 +1440,24 @@ 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_RAW_FENTRY("getElementsByTagName", zim_DOMElement_getElementsByTagName, arginfo_class_DOMDocument_getElementsByTagName, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getElementsByTagNameNS", zim_DOMElement_getElementsByTagNameNS, arginfo_class_DOMDocument_getElementsByTagNameNS, ZEND_ACC_PUBLIC, NULL, NULL) + 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) @@ -959,8 +1472,24 @@ 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_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_DOMDocument_append, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_DOMDocument_prepend, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOMDocument, replaceChildren, arginfo_class_DOMDocument_replaceChildren, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -1016,66 +1545,254 @@ 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_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_DOM_Document_append, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_DOM_Document_prepend, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_ME(DOM_Document, replaceChildren, arginfo_class_DOM_Document_replaceChildren, ZEND_ACC_PUBLIC) +static const zend_function_entry class_DOM_ParentNode_methods[] = { + ZEND_RAW_FENTRY("append", NULL, arginfo_class_DOM_ParentNode_append, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) + ZEND_RAW_FENTRY("prepend", NULL, arginfo_class_DOM_ParentNode_prepend, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) + ZEND_RAW_FENTRY("replaceChildren", NULL, arginfo_class_DOM_ParentNode_replaceChildren, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) 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_RAW_FENTRY("saveXML", zim_DOMDocument_saveXML, arginfo_class_DOM_HTMLDocument_saveXML, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_RAW_FENTRY("saveXMLFile", zim_DOMDocument_save, arginfo_class_DOM_HTMLDocument_saveXMLFile, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_ME(DOM_HTMLDocument, saveHTML, arginfo_class_DOM_HTMLDocument_saveHTML, ZEND_ACC_PUBLIC) - ZEND_ME(DOM_HTMLDocument, saveHTMLFile, arginfo_class_DOM_HTMLDocument_saveHTMLFile, ZEND_ACC_PUBLIC) +static const zend_function_entry class_DOM_ChildNode_methods[] = { + ZEND_RAW_FENTRY("remove", NULL, arginfo_class_DOM_ChildNode_remove, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) + ZEND_RAW_FENTRY("before", NULL, arginfo_class_DOM_ChildNode_before, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) + ZEND_RAW_FENTRY("after", NULL, arginfo_class_DOM_ChildNode_after, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) + ZEND_RAW_FENTRY("replaceWith", NULL, arginfo_class_DOM_ChildNode_replaceWith, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) ZEND_FE_END }; -static const zend_function_entry class_DOM_XMLDocument_methods[] = { - ZEND_RAW_FENTRY("__construct", zim_DOM_HTMLDocument___construct, arginfo_class_DOM_XMLDocument___construct, ZEND_ACC_PRIVATE, NULL, NULL) - 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_RAW_FENTRY("createEntityReference", zim_DOMDocument_createEntityReference, arginfo_class_DOM_XMLDocument_createEntityReference, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_RAW_FENTRY("validate", zim_DOMDocument_validate, arginfo_class_DOM_XMLDocument_validate, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_RAW_FENTRY("xinclude", zim_DOMDocument_xinclude, arginfo_class_DOM_XMLDocument_xinclude, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_RAW_FENTRY("saveXML", zim_DOMDocument_saveXML, arginfo_class_DOM_XMLDocument_saveXML, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_RAW_FENTRY("saveXMLFile", zim_DOMDocument_save, arginfo_class_DOM_XMLDocument_saveXMLFile, ZEND_ACC_PUBLIC, NULL, NULL) +static const zend_function_entry class_DOM_Implementation_methods[] = { + ZEND_ME(DOM_Implementation, createDocumentType, arginfo_class_DOM_Implementation_createDocumentType, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Implementation, createDocument, arginfo_class_DOM_Implementation_createDocument, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Implementation, createHTMLDocument, arginfo_class_DOM_Implementation_createHTMLDocument, ZEND_ACC_PUBLIC) ZEND_FE_END }; +static const zend_function_entry class_DOM_Node_methods[] = { + ZEND_ME(DOM_Node, __construct, arginfo_class_DOM_Node___construct, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL) + ZEND_RAW_FENTRY("getRootNode", zim_DOMNode_getRootNode, arginfo_class_DOM_Node_getRootNode, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("hasChildNodes", zim_DOMNode_hasChildNodes, arginfo_class_DOM_Node_hasChildNodes, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("normalize", zim_DOMNode_normalize, arginfo_class_DOM_Node_normalize, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("cloneNode", zim_DOMNode_cloneNode, arginfo_class_DOM_Node_cloneNode, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Node, isEqualNode, arginfo_class_DOM_Node_isEqualNode, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Node, isSameNode, arginfo_class_DOM_Node_isSameNode, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Node, compareDocumentPosition, arginfo_class_DOM_Node_compareDocumentPosition, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Node, contains, arginfo_class_DOM_Node_contains, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Node, lookupPrefix, arginfo_class_DOM_Node_lookupPrefix, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("lookupNamespaceURI", zim_DOMNode_lookupNamespaceURI, arginfo_class_DOM_Node_lookupNamespaceURI, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Node, isDefaultNamespace, arginfo_class_DOM_Node_isDefaultNamespace, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Node, insertBefore, arginfo_class_DOM_Node_insertBefore, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Node, appendChild, arginfo_class_DOM_Node_appendChild, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Node, replaceChild, arginfo_class_DOM_Node_replaceChild, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Node, removeChild, arginfo_class_DOM_Node_removeChild, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("getLineNo", zim_DOMNode_getLineNo, arginfo_class_DOM_Node_getLineNo, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Node, getNodePath, arginfo_class_DOM_Node_getNodePath, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("C14N", zim_DOMNode_C14N, arginfo_class_DOM_Node_C14N, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("C14NFile", zim_DOMNode_C14NFile, arginfo_class_DOM_Node_C14NFile, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("__sleep", zim_DOMNode___sleep, arginfo_class_DOM_Node___sleep, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("__wakeup", zim_DOMNode___wakeup, arginfo_class_DOM_Node___wakeup, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_NodeList_methods[] = { + ZEND_RAW_FENTRY("count", zim_DOMNodeList_count, arginfo_class_DOM_NodeList_count, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getIterator", zim_DOMNodeList_getIterator, arginfo_class_DOM_NodeList_getIterator, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("item", zim_DOMNodeList_item, arginfo_class_DOM_NodeList_item, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_NamedNodeMap_methods[] = { + ZEND_RAW_FENTRY("item", zim_DOMNamedNodeMap_item, arginfo_class_DOM_NamedNodeMap_item, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getNamedItem", zim_DOMNamedNodeMap_getNamedItem, arginfo_class_DOM_NamedNodeMap_getNamedItem, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getNamedItemNS", zim_DOMNamedNodeMap_getNamedItemNS, arginfo_class_DOM_NamedNodeMap_getNamedItemNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("count", zim_DOMNamedNodeMap_count, arginfo_class_DOM_NamedNodeMap_count, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getIterator", zim_DOMNamedNodeMap_getIterator, arginfo_class_DOM_NamedNodeMap_getIterator, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_DTDNamedNodeMap_methods[] = { + ZEND_RAW_FENTRY("item", zim_DOMNamedNodeMap_item, arginfo_class_DOM_DTDNamedNodeMap_item, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getNamedItem", zim_DOMNamedNodeMap_getNamedItem, arginfo_class_DOM_DTDNamedNodeMap_getNamedItem, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getNamedItemNS", zim_DOMNamedNodeMap_getNamedItemNS, arginfo_class_DOM_DTDNamedNodeMap_getNamedItemNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("count", zim_DOMNamedNodeMap_count, arginfo_class_DOM_DTDNamedNodeMap_count, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getIterator", zim_DOMNamedNodeMap_getIterator, arginfo_class_DOM_DTDNamedNodeMap_getIterator, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_HTMLCollection_methods[] = { + ZEND_RAW_FENTRY("item", zim_DOMNodeList_item, arginfo_class_DOM_HTMLCollection_item, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("count", zim_DOMNodeList_count, arginfo_class_DOM_HTMLCollection_count, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getIterator", zim_DOMNodeList_getIterator, arginfo_class_DOM_HTMLCollection_getIterator, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_Element_methods[] = { + ZEND_RAW_FENTRY("hasAttributes", zim_DOMNode_hasAttributes, arginfo_class_DOM_Element_hasAttributes, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getAttributeNames", zim_DOMElement_getAttributeNames, arginfo_class_DOM_Element_getAttributeNames, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getAttribute", zim_DOMElement_getAttribute, arginfo_class_DOM_Element_getAttribute, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getAttributeNS", zim_DOMElement_getAttributeNS, arginfo_class_DOM_Element_getAttributeNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("setAttribute", zim_DOMElement_setAttribute, arginfo_class_DOM_Element_setAttribute, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("setAttributeNS", zim_DOMElement_setAttributeNS, arginfo_class_DOM_Element_setAttributeNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Element, removeAttribute, arginfo_class_DOM_Element_removeAttribute, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("removeAttributeNS", zim_DOMElement_removeAttributeNS, arginfo_class_DOM_Element_removeAttributeNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("toggleAttribute", zim_DOMElement_toggleAttribute, arginfo_class_DOM_Element_toggleAttribute, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("hasAttribute", zim_DOMElement_hasAttribute, arginfo_class_DOM_Element_hasAttribute, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("hasAttributeNS", zim_DOMElement_hasAttributeNS, arginfo_class_DOM_Element_hasAttributeNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getAttributeNode", zim_DOMElement_getAttributeNode, arginfo_class_DOM_Element_getAttributeNode, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getAttributeNodeNS", zim_DOMElement_getAttributeNodeNS, arginfo_class_DOM_Element_getAttributeNodeNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("setAttributeNode", zim_DOM_Element_setAttributeNodeNS, arginfo_class_DOM_Element_setAttributeNode, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Element, setAttributeNodeNS, arginfo_class_DOM_Element_setAttributeNodeNS, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Element, removeAttributeNode, arginfo_class_DOM_Element_removeAttributeNode, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Element, getElementsByTagName, arginfo_class_DOM_Element_getElementsByTagName, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Element, getElementsByTagNameNS, arginfo_class_DOM_Element_getElementsByTagNameNS, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Element, insertAdjacentElement, arginfo_class_DOM_Element_insertAdjacentElement, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("insertAdjacentText", zim_DOMElement_insertAdjacentText, arginfo_class_DOM_Element_insertAdjacentText, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("setIdAttribute", zim_DOMElement_setIdAttribute, arginfo_class_DOM_Element_setIdAttribute, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("setIdAttributeNS", zim_DOMElement_setIdAttributeNS, arginfo_class_DOM_Element_setIdAttributeNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Element, setIdAttributeNode, arginfo_class_DOM_Element_setIdAttributeNode, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("remove", zim_DOMElement_remove, arginfo_class_DOM_Element_remove, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("before", zim_DOMElement_before, arginfo_class_DOM_Element_before, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("after", zim_DOMElement_after, arginfo_class_DOM_Element_after, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("replaceWith", zim_DOMElement_replaceWith, arginfo_class_DOM_Element_replaceWith, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_DOM_Element_append, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_DOM_Element_prepend, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("replaceChildren", zim_DOMElement_replaceChildren, arginfo_class_DOM_Element_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_Attr_methods[] = { + ZEND_RAW_FENTRY("isId", zim_DOMAttr_isId, arginfo_class_DOM_Attr_isId, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_CharacterData_methods[] = { + ZEND_RAW_FENTRY("substringData", zim_DOMCharacterData_substringData, arginfo_class_DOM_CharacterData_substringData, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_CharacterData, appendData, arginfo_class_DOM_CharacterData_appendData, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_CharacterData, insertData, arginfo_class_DOM_CharacterData_insertData, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_CharacterData, deleteData, arginfo_class_DOM_CharacterData_deleteData, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_CharacterData, replaceData, arginfo_class_DOM_CharacterData_replaceData, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("remove", zim_DOMElement_remove, arginfo_class_DOM_CharacterData_remove, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("before", zim_DOMElement_before, arginfo_class_DOM_CharacterData_before, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("after", zim_DOMElement_after, arginfo_class_DOM_CharacterData_after, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("replaceWith", zim_DOMElement_replaceWith, arginfo_class_DOM_CharacterData_replaceWith, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_Text_methods[] = { + ZEND_RAW_FENTRY("splitText", zim_DOMText_splitText, arginfo_class_DOM_Text_splitText, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_CDATASection_methods[] = { + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_ProcessingInstruction_methods[] = { + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_Comment_methods[] = { + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_DocumentType_methods[] = { + ZEND_RAW_FENTRY("remove", zim_DOMElement_remove, arginfo_class_DOM_DocumentType_remove, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("before", zim_DOMElement_before, arginfo_class_DOM_DocumentType_before, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("after", zim_DOMElement_after, arginfo_class_DOM_DocumentType_after, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("replaceWith", zim_DOMElement_replaceWith, arginfo_class_DOM_DocumentType_replaceWith, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_DocumentFragment_methods[] = { + ZEND_RAW_FENTRY("appendXML", zim_DOMDocumentFragment_appendXML, arginfo_class_DOM_DocumentFragment_appendXML, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_DOM_DocumentFragment_append, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_DOM_DocumentFragment_prepend, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("replaceChildren", zim_DOMElement_replaceChildren, arginfo_class_DOM_DocumentFragment_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_Entity_methods[] = { + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_EntityReference_methods[] = { + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_Notation_methods[] = { + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_Document_methods[] = { + ZEND_RAW_FENTRY("getElementsByTagName", zim_DOM_Element_getElementsByTagName, arginfo_class_DOM_Document_getElementsByTagName, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getElementsByTagNameNS", zim_DOM_Element_getElementsByTagNameNS, arginfo_class_DOM_Document_getElementsByTagNameNS, ZEND_ACC_PUBLIC, NULL, NULL) + 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_RAW_FENTRY("createDocumentFragment", zim_DOMDocument_createDocumentFragment, arginfo_class_DOM_Document_createDocumentFragment, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("createTextNode", zim_DOMDocument_createTextNode, arginfo_class_DOM_Document_createTextNode, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("createCDATASection", zim_DOMDocument_createCDATASection, arginfo_class_DOM_Document_createCDATASection, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("createComment", zim_DOMDocument_createComment, arginfo_class_DOM_Document_createComment, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Document, createProcessingInstruction, arginfo_class_DOM_Document_createProcessingInstruction, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Document, importNode, arginfo_class_DOM_Document_importNode, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Document, adoptNode, arginfo_class_DOM_Document_adoptNode, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("createAttribute", zim_DOMDocument_createAttribute, arginfo_class_DOM_Document_createAttribute, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("createAttributeNS", zim_DOMDocument_createAttributeNS, arginfo_class_DOM_Document_createAttributeNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("getElementById", zim_DOMDocument_getElementById, arginfo_class_DOM_Document_getElementById, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Document, registerNodeClass, arginfo_class_DOM_Document_registerNodeClass, ZEND_ACC_PUBLIC) +#if defined(LIBXML_SCHEMAS_ENABLED) + ZEND_RAW_FENTRY("schemaValidate", zim_DOMDocument_schemaValidate, arginfo_class_DOM_Document_schemaValidate, ZEND_ACC_PUBLIC, NULL, NULL) +#endif +#if defined(LIBXML_SCHEMAS_ENABLED) + ZEND_RAW_FENTRY("schemaValidateSource", zim_DOMDocument_schemaValidateSource, arginfo_class_DOM_Document_schemaValidateSource, ZEND_ACC_PUBLIC, NULL, NULL) +#endif +#if defined(LIBXML_SCHEMAS_ENABLED) + ZEND_RAW_FENTRY("relaxNGValidate", zim_DOMDocument_relaxNGValidate, arginfo_class_DOM_Document_relaxNGValidate, ZEND_ACC_PUBLIC, NULL, NULL) +#endif +#if defined(LIBXML_SCHEMAS_ENABLED) + ZEND_RAW_FENTRY("relaxNGValidateSource", zim_DOMDocument_relaxNGValidateSource, arginfo_class_DOM_Document_relaxNGValidateSource, ZEND_ACC_PUBLIC, NULL, NULL) +#endif + ZEND_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_DOM_Document_append, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_DOM_Document_prepend, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("replaceChildren", zim_DOMDocument_replaceChildren, arginfo_class_DOM_Document_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Document, importLegacyNode, arginfo_class_DOM_Document_importLegacyNode, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_HTMLDocument_methods[] = { + 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_RAW_FENTRY("saveXML", zim_DOM_XMLDocument_saveXML, arginfo_class_DOM_HTMLDocument_saveXML, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("saveXMLFile", zim_DOMDocument_save, arginfo_class_DOM_HTMLDocument_saveXMLFile, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_HTMLDocument, saveHTML, arginfo_class_DOM_HTMLDocument_saveHTML, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_HTMLDocument, saveHTMLFile, arginfo_class_DOM_HTMLDocument_saveHTMLFile, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_DOM_XMLDocument_methods[] = { + 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_RAW_FENTRY("createEntityReference", zim_DOMDocument_createEntityReference, arginfo_class_DOM_XMLDocument_createEntityReference, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("validate", zim_DOMDocument_validate, arginfo_class_DOM_XMLDocument_validate, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("xinclude", zim_DOMDocument_xinclude, arginfo_class_DOM_XMLDocument_xinclude, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_XMLDocument, saveXML, arginfo_class_DOM_XMLDocument_saveXML, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("saveXMLFile", zim_DOMDocument_save, arginfo_class_DOM_XMLDocument_saveXMLFile, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_FE_END +}; + +#if defined(LIBXML_XPATH_ENABLED) +static const zend_function_entry class_DOM_XPath_methods[] = { + ZEND_ME(DOM_XPath, __construct, arginfo_class_DOM_XPath___construct, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_XPath, evaluate, arginfo_class_DOM_XPath_evaluate, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_XPath, query, arginfo_class_DOM_XPath_query, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("registerNamespace", zim_DOMXPath_registerNamespace, arginfo_class_DOM_XPath_registerNamespace, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("registerPhpFunctions", zim_DOMXPath_registerPhpFunctions, arginfo_class_DOM_XPath_registerPhpFunctions, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("registerPhpFunctionNS", zim_DOMXPath_registerPhpFunctionNS, arginfo_class_DOM_XPath_registerPhpFunctionNS, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("quote", zim_DOMXPath_quote, arginfo_class_DOM_XPath_quote, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL) + ZEND_FE_END +}; +#endif + static void register_php_dom_symbols(int module_number) { REGISTER_LONG_CONSTANT("XML_ELEMENT_NODE", XML_ELEMENT_NODE, CONST_PERSISTENT); @@ -1126,7 +1843,6 @@ 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); @@ -1141,7 +1857,6 @@ static void register_php_dom_symbols(int module_number) 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); } @@ -1152,7 +1867,6 @@ 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); @@ -1201,7 +1915,6 @@ 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; } @@ -1212,7 +1925,6 @@ 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; } @@ -1223,7 +1935,6 @@ 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; } @@ -1234,7 +1945,6 @@ 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; } @@ -1245,7 +1955,6 @@ 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); @@ -1366,8 +2075,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_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 *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_release(property_ownerDocument_name); zval property_namespaceURI_default_value; @@ -1409,7 +2118,6 @@ 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); @@ -1494,7 +2202,6 @@ 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); @@ -1526,7 +2233,6 @@ 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); @@ -1544,7 +2250,6 @@ 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); @@ -1581,7 +2286,6 @@ 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); @@ -1624,7 +2328,6 @@ 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); @@ -1687,12 +2390,20 @@ 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_DOM_Document) +static zend_class_entry *register_class_DOMDocument(zend_class_entry *class_entry_DOMNode, zend_class_entry *class_entry_DOMParentNode) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "DOMDocument", class_DOMDocument_methods); - class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Document); + 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); zval property_implementation_default_value; ZVAL_UNDEF(&property_implementation_default_value); @@ -1701,12 +2412,25 @@ 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); @@ -1737,6 +2461,18 @@ 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); @@ -1779,6 +2515,26 @@ 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; } @@ -1806,7 +2562,6 @@ 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); @@ -1824,7 +2579,6 @@ 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); @@ -1841,7 +2595,6 @@ 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); @@ -1888,7 +2641,6 @@ 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; } @@ -1899,7 +2651,6 @@ 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); @@ -1922,7 +2673,6 @@ 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); @@ -1947,13 +2697,12 @@ 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_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 *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_release(property_document_name); zval property_registerNodeNamespaces_default_value; @@ -1966,117 +2715,732 @@ static zend_class_entry *register_class_DOMXPath(void) } #endif -static zend_class_entry *register_class_DOM_Document(zend_class_entry *class_entry_DOMNode, zend_class_entry *class_entry_DOMParentNode) +static zend_class_entry *register_class_DOM_ParentNode(void) { 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_DOMNode); - class_entry->ce_flags |= ZEND_ACC_ABSTRACT; - 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_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); + INIT_NS_CLASS_ENTRY(ce, "DOM", "ParentNode", class_DOM_ParentNode_methods); + class_entry = zend_register_internal_interface(&ce); - 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); + return class_entry; +} - 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); +static zend_class_entry *register_class_DOM_ChildNode(void) +{ + zend_class_entry ce, *class_entry; - 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); + INIT_NS_CLASS_ENTRY(ce, "DOM", "ChildNode", class_DOM_ChildNode_methods); + class_entry = zend_register_internal_interface(&ce); return class_entry; } -static zend_class_entry *register_class_DOM_HTMLDocument(zend_class_entry *class_entry_DOM_Document) +static zend_class_entry *register_class_DOM_Implementation(void) { 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_Document); - class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + INIT_NS_CLASS_ENTRY(ce, "DOM", "Implementation", class_DOM_Implementation_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; return class_entry; } -static zend_class_entry *register_class_DOM_XMLDocument(zend_class_entry *class_entry_DOM_Document) +static zend_class_entry *register_class_DOM_Node(void) { 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_Document); - class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + INIT_NS_CLASS_ENTRY(ce, "DOM", "Node", class_DOM_Node_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= 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 const_DOCUMENT_POSITION_DISCONNECTED_value; + ZVAL_LONG(&const_DOCUMENT_POSITION_DISCONNECTED_value, 0x1); + zend_string *const_DOCUMENT_POSITION_DISCONNECTED_name = zend_string_init_interned("DOCUMENT_POSITION_DISCONNECTED", sizeof("DOCUMENT_POSITION_DISCONNECTED") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_DISCONNECTED_name, &const_DOCUMENT_POSITION_DISCONNECTED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_DOCUMENT_POSITION_DISCONNECTED_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 const_DOCUMENT_POSITION_PRECEDING_value; + ZVAL_LONG(&const_DOCUMENT_POSITION_PRECEDING_value, 0x2); + zend_string *const_DOCUMENT_POSITION_PRECEDING_name = zend_string_init_interned("DOCUMENT_POSITION_PRECEDING", sizeof("DOCUMENT_POSITION_PRECEDING") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_PRECEDING_name, &const_DOCUMENT_POSITION_PRECEDING_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_DOCUMENT_POSITION_PRECEDING_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 const_DOCUMENT_POSITION_FOLLOWING_value; + ZVAL_LONG(&const_DOCUMENT_POSITION_FOLLOWING_value, 0x4); + zend_string *const_DOCUMENT_POSITION_FOLLOWING_name = zend_string_init_interned("DOCUMENT_POSITION_FOLLOWING", sizeof("DOCUMENT_POSITION_FOLLOWING") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_FOLLOWING_name, &const_DOCUMENT_POSITION_FOLLOWING_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_DOCUMENT_POSITION_FOLLOWING_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 const_DOCUMENT_POSITION_CONTAINS_value; + ZVAL_LONG(&const_DOCUMENT_POSITION_CONTAINS_value, 0x8); + zend_string *const_DOCUMENT_POSITION_CONTAINS_name = zend_string_init_interned("DOCUMENT_POSITION_CONTAINS", sizeof("DOCUMENT_POSITION_CONTAINS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_CONTAINS_name, &const_DOCUMENT_POSITION_CONTAINS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_DOCUMENT_POSITION_CONTAINS_name); + + zval const_DOCUMENT_POSITION_CONTAINED_BY_value; + ZVAL_LONG(&const_DOCUMENT_POSITION_CONTAINED_BY_value, 0x10); + zend_string *const_DOCUMENT_POSITION_CONTAINED_BY_name = zend_string_init_interned("DOCUMENT_POSITION_CONTAINED_BY", sizeof("DOCUMENT_POSITION_CONTAINED_BY") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_CONTAINED_BY_name, &const_DOCUMENT_POSITION_CONTAINED_BY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_DOCUMENT_POSITION_CONTAINED_BY_name); + + zval const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_value; + ZVAL_LONG(&const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_value, 0x20); + zend_string *const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_name = zend_string_init_interned("DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", sizeof("DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_name, &const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_name); + + zval property_nodeType_default_value; + ZVAL_UNDEF(&property_nodeType_default_value); + zend_string *property_nodeType_name = zend_string_init("nodeType", sizeof("nodeType") - 1, 1); + zend_declare_typed_property(class_entry, property_nodeType_name, &property_nodeType_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_nodeType_name); + + zval property_nodeName_default_value; + ZVAL_UNDEF(&property_nodeName_default_value); + zend_string *property_nodeName_name = zend_string_init("nodeName", sizeof("nodeName") - 1, 1); + zend_declare_typed_property(class_entry, property_nodeName_name, &property_nodeName_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_nodeName_name); + + zval property_baseURI_default_value; + ZVAL_UNDEF(&property_baseURI_default_value); + zend_string *property_baseURI_name = zend_string_init("baseURI", sizeof("baseURI") - 1, 1); + zend_declare_typed_property(class_entry, property_baseURI_name, &property_baseURI_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_baseURI_name); + + zval property_isConnected_default_value; + ZVAL_UNDEF(&property_isConnected_default_value); + zend_string *property_isConnected_name = zend_string_init("isConnected", sizeof("isConnected") - 1, 1); + zend_declare_typed_property(class_entry, property_isConnected_name, &property_isConnected_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_isConnected_name); + + 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_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_parentNode_default_value; + ZVAL_UNDEF(&property_parentNode_default_value); + zend_string *property_parentNode_name = zend_string_init("parentNode", sizeof("parentNode") - 1, 1); + zend_string *property_parentNode_class_DOM_Node = zend_string_init("DOM\\\116ode", sizeof("DOM\\\116ode")-1, 1); + zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentNode_class_DOM_Node, 0, MAY_BE_NULL)); + zend_string_release(property_parentNode_name); + + zval property_parentElement_default_value; + ZVAL_UNDEF(&property_parentElement_default_value); + zend_string *property_parentElement_name = zend_string_init("parentElement", sizeof("parentElement") - 1, 1); + zend_string *property_parentElement_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentElement_class_DOM_Element, 0, MAY_BE_NULL)); + zend_string_release(property_parentElement_name); + + zval property_childNodes_default_value; + ZVAL_UNDEF(&property_childNodes_default_value); + zend_string *property_childNodes_name = zend_string_init("childNodes", sizeof("childNodes") - 1, 1); + zend_string *property_childNodes_class_DOM_NodeList = zend_string_init("DOM\\\116odeList", sizeof("DOM\\\116odeList")-1, 1); + zend_declare_typed_property(class_entry, property_childNodes_name, &property_childNodes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_childNodes_class_DOM_NodeList, 0, 0)); + zend_string_release(property_childNodes_name); + + zval property_firstChild_default_value; + ZVAL_UNDEF(&property_firstChild_default_value); + zend_string *property_firstChild_name = zend_string_init("firstChild", sizeof("firstChild") - 1, 1); + zend_string *property_firstChild_class_DOM_Node = zend_string_init("DOM\\\116ode", sizeof("DOM\\\116ode")-1, 1); + zend_declare_typed_property(class_entry, property_firstChild_name, &property_firstChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstChild_class_DOM_Node, 0, MAY_BE_NULL)); + zend_string_release(property_firstChild_name); + + zval property_lastChild_default_value; + ZVAL_UNDEF(&property_lastChild_default_value); + zend_string *property_lastChild_name = zend_string_init("lastChild", sizeof("lastChild") - 1, 1); + zend_string *property_lastChild_class_DOM_Node = zend_string_init("DOM\\\116ode", sizeof("DOM\\\116ode")-1, 1); + zend_declare_typed_property(class_entry, property_lastChild_name, &property_lastChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastChild_class_DOM_Node, 0, MAY_BE_NULL)); + zend_string_release(property_lastChild_name); + + zval property_previousSibling_default_value; + ZVAL_UNDEF(&property_previousSibling_default_value); + zend_string *property_previousSibling_name = zend_string_init("previousSibling", sizeof("previousSibling") - 1, 1); + zend_string *property_previousSibling_class_DOM_Node = zend_string_init("DOM\\\116ode", sizeof("DOM\\\116ode")-1, 1); + zend_declare_typed_property(class_entry, property_previousSibling_name, &property_previousSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previousSibling_class_DOM_Node, 0, MAY_BE_NULL)); + zend_string_release(property_previousSibling_name); + + zval property_nextSibling_default_value; + ZVAL_UNDEF(&property_nextSibling_default_value); + zend_string *property_nextSibling_name = zend_string_init("nextSibling", sizeof("nextSibling") - 1, 1); + zend_string *property_nextSibling_class_DOM_Node = zend_string_init("DOM\\\116ode", sizeof("DOM\\\116ode")-1, 1); + zend_declare_typed_property(class_entry, property_nextSibling_name, &property_nextSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextSibling_class_DOM_Node, 0, MAY_BE_NULL)); + zend_string_release(property_nextSibling_name); + + zval property_nodeValue_default_value; + ZVAL_UNDEF(&property_nodeValue_default_value); + zend_string *property_nodeValue_name = zend_string_init("nodeValue", sizeof("nodeValue") - 1, 1); + zend_declare_typed_property(class_entry, property_nodeValue_name, &property_nodeValue_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_nodeValue_name); + + zval property_textContent_default_value; + ZVAL_UNDEF(&property_textContent_default_value); + zend_string *property_textContent_name = zend_string_init("textContent", sizeof("textContent") - 1, 1); + zend_declare_typed_property(class_entry, property_textContent_name, &property_textContent_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_textContent_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_NodeList(zend_class_entry *class_entry_DOM_IteratorAggregate, zend_class_entry *class_entry_DOM_Countable) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "NodeList", class_DOM_NodeList_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + zend_class_implements(class_entry, 2, class_entry_DOM_IteratorAggregate, class_entry_DOM_Countable); + + zval property_length_default_value; + ZVAL_UNDEF(&property_length_default_value); + zend_string *property_length_name = zend_string_init("length", sizeof("length") - 1, 1); + zend_declare_typed_property(class_entry, property_length_name, &property_length_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_length_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_NamedNodeMap(zend_class_entry *class_entry_DOM_IteratorAggregate, zend_class_entry *class_entry_DOM_Countable) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "NamedNodeMap", class_DOM_NamedNodeMap_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + zend_class_implements(class_entry, 2, class_entry_DOM_IteratorAggregate, class_entry_DOM_Countable); + + zval property_length_default_value; + ZVAL_UNDEF(&property_length_default_value); + zend_string *property_length_name = zend_string_init("length", sizeof("length") - 1, 1); + zend_declare_typed_property(class_entry, property_length_name, &property_length_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_length_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_DTDNamedNodeMap(zend_class_entry *class_entry_DOM_IteratorAggregate, zend_class_entry *class_entry_DOM_Countable) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "DTDNamedNodeMap", class_DOM_DTDNamedNodeMap_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + zend_class_implements(class_entry, 2, class_entry_DOM_IteratorAggregate, class_entry_DOM_Countable); + + zval property_length_default_value; + ZVAL_UNDEF(&property_length_default_value); + zend_string *property_length_name = zend_string_init("length", sizeof("length") - 1, 1); + zend_declare_typed_property(class_entry, property_length_name, &property_length_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_length_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_HTMLCollection(zend_class_entry *class_entry_DOM_IteratorAggregate, zend_class_entry *class_entry_DOM_Countable) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "HTMLCollection", class_DOM_HTMLCollection_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + zend_class_implements(class_entry, 2, class_entry_DOM_IteratorAggregate, class_entry_DOM_Countable); + + zval property_length_default_value; + ZVAL_UNDEF(&property_length_default_value); + zend_string *property_length_name = zend_string_init("length", sizeof("length") - 1, 1); + zend_declare_typed_property(class_entry, property_length_name, &property_length_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_length_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_Element(zend_class_entry *class_entry_DOM_Node, zend_class_entry *class_entry_DOM_ParentNode, zend_class_entry *class_entry_DOM_ChildNode) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "Element", class_DOM_Element_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Node); + zend_class_implements(class_entry, 2, class_entry_DOM_ParentNode, class_entry_DOM_ChildNode); + + zval property_namespaceURI_default_value; + ZVAL_UNDEF(&property_namespaceURI_default_value); + zend_string *property_namespaceURI_name = zend_string_init("namespaceURI", sizeof("namespaceURI") - 1, 1); + zend_declare_typed_property(class_entry, property_namespaceURI_name, &property_namespaceURI_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_namespaceURI_name); + + zval property_prefix_default_value; + ZVAL_UNDEF(&property_prefix_default_value); + zend_string *property_prefix_name = zend_string_init("prefix", sizeof("prefix") - 1, 1); + zend_declare_typed_property(class_entry, property_prefix_name, &property_prefix_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_prefix_name); + + zval property_localName_default_value; + ZVAL_UNDEF(&property_localName_default_value); + zend_string *property_localName_name = zend_string_init("localName", sizeof("localName") - 1, 1); + zend_declare_typed_property(class_entry, property_localName_name, &property_localName_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_localName_name); + + zval property_tagName_default_value; + ZVAL_UNDEF(&property_tagName_default_value); + zend_string *property_tagName_name = zend_string_init("tagName", sizeof("tagName") - 1, 1); + zend_declare_typed_property(class_entry, property_tagName_name, &property_tagName_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_tagName_name); + + zval property_id_default_value; + ZVAL_UNDEF(&property_id_default_value); + zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, 1); + zend_declare_typed_property(class_entry, property_id_name, &property_id_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_id_name); + + zval property_className_default_value; + ZVAL_UNDEF(&property_className_default_value); + zend_string *property_className_name = zend_string_init("className", sizeof("className") - 1, 1); + zend_declare_typed_property(class_entry, property_className_name, &property_className_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_className_name); + + zval property_attributes_default_value; + ZVAL_UNDEF(&property_attributes_default_value); + zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); + zend_string *property_attributes_class_DOM_NamedNodeMap = zend_string_init("DOM\\\116amedNodeMap", sizeof("DOM\\\116amedNodeMap")-1, 1); + zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_attributes_class_DOM_NamedNodeMap, 0, 0)); + zend_string_release(property_attributes_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); + + zval property_previousElementSibling_default_value; + ZVAL_UNDEF(&property_previousElementSibling_default_value); + zend_string *property_previousElementSibling_name = zend_string_init("previousElementSibling", sizeof("previousElementSibling") - 1, 1); + zend_string *property_previousElementSibling_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previousElementSibling_class_DOM_Element, 0, MAY_BE_NULL)); + zend_string_release(property_previousElementSibling_name); + + zval property_nextElementSibling_default_value; + ZVAL_UNDEF(&property_nextElementSibling_default_value); + zend_string *property_nextElementSibling_name = zend_string_init("nextElementSibling", sizeof("nextElementSibling") - 1, 1); + zend_string *property_nextElementSibling_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextElementSibling_class_DOM_Element, 0, MAY_BE_NULL)); + zend_string_release(property_nextElementSibling_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_Attr(zend_class_entry *class_entry_DOM_Node) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "Attr", class_DOM_Attr_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Node); + + zval property_namespaceURI_default_value; + ZVAL_UNDEF(&property_namespaceURI_default_value); + zend_string *property_namespaceURI_name = zend_string_init("namespaceURI", sizeof("namespaceURI") - 1, 1); + zend_declare_typed_property(class_entry, property_namespaceURI_name, &property_namespaceURI_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_namespaceURI_name); + + zval property_prefix_default_value; + ZVAL_UNDEF(&property_prefix_default_value); + zend_string *property_prefix_name = zend_string_init("prefix", sizeof("prefix") - 1, 1); + zend_declare_typed_property(class_entry, property_prefix_name, &property_prefix_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_prefix_name); + + zval property_localName_default_value; + ZVAL_UNDEF(&property_localName_default_value); + zend_string *property_localName_name = zend_string_init("localName", sizeof("localName") - 1, 1); + zend_declare_typed_property(class_entry, property_localName_name, &property_localName_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_localName_name); + + zval property_name_default_value; + ZVAL_UNDEF(&property_name_default_value); + zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1); + zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_name_name); + + zval property_value_default_value; + ZVAL_UNDEF(&property_value_default_value); + zend_string *property_value_name = zend_string_init("value", sizeof("value") - 1, 1); + zend_declare_typed_property(class_entry, property_value_name, &property_value_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_value_name); + + zval property_ownerElement_default_value; + ZVAL_UNDEF(&property_ownerElement_default_value); + zend_string *property_ownerElement_name = zend_string_init("ownerElement", sizeof("ownerElement") - 1, 1); + zend_string *property_ownerElement_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_ownerElement_name, &property_ownerElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_ownerElement_class_DOM_Element, 0, MAY_BE_NULL)); + zend_string_release(property_ownerElement_name); + + zval property_specified_default_value; + ZVAL_TRUE(&property_specified_default_value); + zend_string *property_specified_name = zend_string_init("specified", sizeof("specified") - 1, 1); + zend_declare_typed_property(class_entry, property_specified_name, &property_specified_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_specified_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_CharacterData(zend_class_entry *class_entry_DOM_Node, zend_class_entry *class_entry_DOM_ChildNode) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "CharacterData", class_DOM_CharacterData_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Node); + zend_class_implements(class_entry, 1, class_entry_DOM_ChildNode); + + zval property_previousElementSibling_default_value; + ZVAL_UNDEF(&property_previousElementSibling_default_value); + zend_string *property_previousElementSibling_name = zend_string_init("previousElementSibling", sizeof("previousElementSibling") - 1, 1); + zend_string *property_previousElementSibling_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previousElementSibling_class_DOM_Element, 0, MAY_BE_NULL)); + zend_string_release(property_previousElementSibling_name); + + zval property_nextElementSibling_default_value; + ZVAL_UNDEF(&property_nextElementSibling_default_value); + zend_string *property_nextElementSibling_name = zend_string_init("nextElementSibling", sizeof("nextElementSibling") - 1, 1); + zend_string *property_nextElementSibling_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextElementSibling_class_DOM_Element, 0, MAY_BE_NULL)); + zend_string_release(property_nextElementSibling_name); + + zval property_data_default_value; + ZVAL_UNDEF(&property_data_default_value); + zend_string *property_data_name = zend_string_init("data", sizeof("data") - 1, 1); + zend_declare_typed_property(class_entry, property_data_name, &property_data_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_data_name); + + zval property_length_default_value; + ZVAL_UNDEF(&property_length_default_value); + zend_string *property_length_name = zend_string_init("length", sizeof("length") - 1, 1); + zend_declare_typed_property(class_entry, property_length_name, &property_length_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_length_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_Text(zend_class_entry *class_entry_DOM_CharacterData) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "Text", class_DOM_Text_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_CharacterData); + + zval property_wholeText_default_value; + ZVAL_UNDEF(&property_wholeText_default_value); + zend_string *property_wholeText_name = zend_string_init("wholeText", sizeof("wholeText") - 1, 1); + zend_declare_typed_property(class_entry, property_wholeText_name, &property_wholeText_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_wholeText_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_CDATASection(zend_class_entry *class_entry_DOM_Text) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "CDATASection", class_DOM_CDATASection_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Text); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_ProcessingInstruction(zend_class_entry *class_entry_DOM_CharacterData) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "ProcessingInstruction", class_DOM_ProcessingInstruction_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_CharacterData); + + zval property_target_default_value; + ZVAL_UNDEF(&property_target_default_value); + zend_string *property_target_name = zend_string_init("target", sizeof("target") - 1, 1); + zend_declare_typed_property(class_entry, property_target_name, &property_target_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_target_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_Comment(zend_class_entry *class_entry_DOM_CharacterData) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "Comment", class_DOM_Comment_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_CharacterData); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_DocumentType(zend_class_entry *class_entry_DOM_Node, zend_class_entry *class_entry_DOM_ChildNode) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "DocumentType", class_DOM_DocumentType_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Node); + zend_class_implements(class_entry, 1, class_entry_DOM_ChildNode); + + zval property_name_default_value; + ZVAL_UNDEF(&property_name_default_value); + zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1); + zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_name_name); + + zval property_entities_default_value; + ZVAL_UNDEF(&property_entities_default_value); + zend_string *property_entities_name = zend_string_init("entities", sizeof("entities") - 1, 1); + zend_string *property_entities_class_DOM_DTDNamedNodeMap = zend_string_init("DOM\\DTDNamedNodeMap", sizeof("DOM\\DTDNamedNodeMap")-1, 1); + zend_declare_typed_property(class_entry, property_entities_name, &property_entities_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_entities_class_DOM_DTDNamedNodeMap, 0, 0)); + zend_string_release(property_entities_name); + + zval property_notations_default_value; + ZVAL_UNDEF(&property_notations_default_value); + zend_string *property_notations_name = zend_string_init("notations", sizeof("notations") - 1, 1); + zend_string *property_notations_class_DOM_DTDNamedNodeMap = zend_string_init("DOM\\DTDNamedNodeMap", sizeof("DOM\\DTDNamedNodeMap")-1, 1); + zend_declare_typed_property(class_entry, property_notations_name, &property_notations_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_notations_class_DOM_DTDNamedNodeMap, 0, 0)); + zend_string_release(property_notations_name); + + zval property_publicId_default_value; + ZVAL_UNDEF(&property_publicId_default_value); + zend_string *property_publicId_name = zend_string_init("publicId", sizeof("publicId") - 1, 1); + zend_declare_typed_property(class_entry, property_publicId_name, &property_publicId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_publicId_name); + + zval property_systemId_default_value; + ZVAL_UNDEF(&property_systemId_default_value); + zend_string *property_systemId_name = zend_string_init("systemId", sizeof("systemId") - 1, 1); + zend_declare_typed_property(class_entry, property_systemId_name, &property_systemId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_systemId_name); + + zval property_internalSubset_default_value; + ZVAL_UNDEF(&property_internalSubset_default_value); + zend_string *property_internalSubset_name = zend_string_init("internalSubset", sizeof("internalSubset") - 1, 1); + zend_declare_typed_property(class_entry, property_internalSubset_name, &property_internalSubset_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_internalSubset_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_DocumentFragment(zend_class_entry *class_entry_DOM_Node, zend_class_entry *class_entry_DOM_ParentNode) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "DocumentFragment", class_DOM_DocumentFragment_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Node); + zend_class_implements(class_entry, 1, class_entry_DOM_ParentNode); + + 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_Entity(zend_class_entry *class_entry_DOM_Node) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "Entity", class_DOM_Entity_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Node); + + zval property_publicId_default_value; + ZVAL_UNDEF(&property_publicId_default_value); + zend_string *property_publicId_name = zend_string_init("publicId", sizeof("publicId") - 1, 1); + zend_declare_typed_property(class_entry, property_publicId_name, &property_publicId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_publicId_name); + + zval property_systemId_default_value; + ZVAL_UNDEF(&property_systemId_default_value); + zend_string *property_systemId_name = zend_string_init("systemId", sizeof("systemId") - 1, 1); + zend_declare_typed_property(class_entry, property_systemId_name, &property_systemId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_systemId_name); + + zval property_notationName_default_value; + ZVAL_UNDEF(&property_notationName_default_value); + zend_string *property_notationName_name = zend_string_init("notationName", sizeof("notationName") - 1, 1); + zend_declare_typed_property(class_entry, property_notationName_name, &property_notationName_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release(property_notationName_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_EntityReference(zend_class_entry *class_entry_DOM_Node) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "EntityReference", class_DOM_EntityReference_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Node); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_Notation(zend_class_entry *class_entry_DOM_Node) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "DOM", "Notation", class_DOM_Notation_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DOM_Node); + + zval property_publicId_default_value; + ZVAL_UNDEF(&property_publicId_default_value); + zend_string *property_publicId_name = zend_string_init("publicId", sizeof("publicId") - 1, 1); + zend_declare_typed_property(class_entry, property_publicId_name, &property_publicId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_publicId_name); + + zval property_systemId_default_value; + ZVAL_UNDEF(&property_systemId_default_value); + zend_string *property_systemId_name = zend_string_init("systemId", sizeof("systemId") - 1, 1); + zend_declare_typed_property(class_entry, property_systemId_name, &property_systemId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_systemId_name); + + return class_entry; +} + +static zend_class_entry *register_class_DOM_Document(zend_class_entry *class_entry_DOM_Node, zend_class_entry *class_entry_DOM_ParentNode) +{ + 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_Node); + class_entry->ce_flags |= ZEND_ACC_ABSTRACT; + zend_class_implements(class_entry, 1, class_entry_DOM_ParentNode); + + zval property_implementation_default_value; + ZVAL_UNDEF(&property_implementation_default_value); + zend_string *property_implementation_name = zend_string_init("implementation", sizeof("implementation") - 1, 1); + zend_string *property_implementation_class_DOM_Implementation = zend_string_init("DOM\\Implementation", sizeof("DOM\\Implementation")-1, 1); + 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_DOM_Implementation, 0, 0)); + zend_string_release(property_implementation_name); + + zval property_URL_default_value; + ZVAL_UNDEF(&property_URL_default_value); + zend_string *property_URL_name = zend_string_init("URL", sizeof("URL") - 1, 1); + zend_declare_typed_property(class_entry, property_URL_name, &property_URL_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_URL_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)); + zend_string_release(property_documentURI_name); + + zval property_characterSet_default_value; + ZVAL_UNDEF(&property_characterSet_default_value); + zend_string *property_characterSet_name = zend_string_init("characterSet", sizeof("characterSet") - 1, 1); + zend_declare_typed_property(class_entry, property_characterSet_name, &property_characterSet_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_characterSet_name); + + zval property_charset_default_value; + ZVAL_UNDEF(&property_charset_default_value); + zend_string *property_charset_name = zend_string_init("charset", sizeof("charset") - 1, 1); + zend_declare_typed_property(class_entry, property_charset_name, &property_charset_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_charset_name); + + zval property_inputEncoding_default_value; + ZVAL_UNDEF(&property_inputEncoding_default_value); + zend_string *property_inputEncoding_name = zend_string_init("inputEncoding", sizeof("inputEncoding") - 1, 1); + zend_declare_typed_property(class_entry, property_inputEncoding_name, &property_inputEncoding_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_inputEncoding_name); + + 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_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_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_Document); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + return class_entry; +} + +static zend_class_entry *register_class_DOM_XMLDocument(zend_class_entry *class_entry_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_Document); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + 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)); + zend_string_release(property_xmlEncoding_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_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_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)); zend_string_release(property_xmlVersion_name); zval property_formatOutput_default_value; @@ -2085,35 +3449,31 @@ static zend_class_entry *register_class_DOM_XMLDocument(zend_class_entry *class_ 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); + return class_entry; +} - 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); +#if defined(LIBXML_XPATH_ENABLED) +static zend_class_entry *register_class_DOM_XPath(void) +{ + zend_class_entry ce, *class_entry; - 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); + INIT_NS_CLASS_ENTRY(ce, "DOM", "XPath", class_DOM_XPath_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE; - 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_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_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_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); + zval property_registerNodeNamespaces_default_value; + ZVAL_UNDEF(&property_registerNodeNamespaces_default_value); + zend_string *property_registerNodeNamespaces_name = zend_string_init("registerNodeNamespaces", sizeof("registerNodeNamespaces") - 1, 1); + zend_declare_typed_property(class_entry, property_registerNodeNamespaces_name, &property_registerNodeNamespaces_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_registerNodeNamespaces_name); return class_entry; } +#endif diff --git a/ext/dom/serialize_common.h b/ext/dom/serialize_common.h new file mode 100644 index 0000000000000..35203592da9ce --- /dev/null +++ b/ext/dom/serialize_common.h @@ -0,0 +1,28 @@ +/* + +----------------------------------------------------------------------+ + | 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 SERIALIZE_COMMON_H +#define SERIALIZE_COMMON_H + +#include +#include + +static zend_always_inline 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; +} + +#endif diff --git a/ext/dom/tests/DOM4_ChildNode_wrong_document.phpt b/ext/dom/tests/DOM4_ChildNode_wrong_document.phpt index aa06d90bb26fb..cf51c3581a34d 100644 --- a/ext/dom/tests/DOM4_ChildNode_wrong_document.phpt +++ b/ext/dom/tests/DOM4_ChildNode_wrong_document.phpt @@ -6,34 +6,27 @@ dom loadXML(''); +function test($method) { + $dom1 = new DOMDocument; + $dom1->loadXML(''); -$dom2 = new DOMDocument; -$dom2->loadXML(''); + $dom2 = new DOMDocument; + $dom2->loadXML(''); -$element = $dom1->documentElement; + $element = $dom1->documentElement; -try { - $element->after($dom2->documentElement->firstChild); - echo "FAIL"; -} catch (DOMException $e) { - echo $e->getMessage() . "\n"; + try { + $element->$method($dom2->documentElement->firstChild); + echo "FAIL"; + } catch (DOMException $e) { + echo $e->getMessage() . "\n"; + } } -try { - $element->before($dom2->documentElement->firstChild); - echo "FAIL"; -} catch (DOMException $e) { - echo $e->getMessage() . "\n"; -} +test("after"); +test("before"); +test("replaceWith"); -try { - $element->replaceWith($dom2->documentElement->firstChild); - echo "FAIL"; -} catch (DOMException $e) { - echo $e->getMessage(); -} ?> --EXPECT-- Wrong Document Error diff --git a/ext/dom/tests/DOM4_ParentNode_append_wrong_document.phpt b/ext/dom/tests/DOM4_ParentNode_append_wrong_document.phpt index f9db709f0eb41..9df1603cab0c0 100644 --- a/ext/dom/tests/DOM4_ParentNode_append_wrong_document.phpt +++ b/ext/dom/tests/DOM4_ParentNode_append_wrong_document.phpt @@ -6,27 +6,25 @@ dom loadXML(''); +function test($method) { + $dom1 = new DOMDocument; + $dom1->loadXML(''); -$dom2 = new DOMDocument; -$dom2->loadXML(''); + $dom2 = new DOMDocument; + $dom2->loadXML(''); -$element = $dom1->documentElement; + $element = $dom1->documentElement; -try { - $element->append($dom2->documentElement->firstChild); - echo "FAIL"; -} catch (DOMException $e) { - echo $e->getMessage() . "\n"; + try { + $element->$method($dom2->documentElement->firstChild); + echo "FAIL"; + } catch (DOMException $e) { + echo $e->getMessage() . "\n"; + } } -try { - $element->prepend($dom2->documentElement->firstChild); - echo "FAIL"; -} catch (DOMException $e) { - echo $e->getMessage(); -} +test("append"); +test("prepend"); ?> --EXPECT-- Wrong Document Error diff --git a/ext/dom/tests/DOMDocument_adoptNode.phpt b/ext/dom/tests/DOMDocument_adoptNode.phpt index 81fe4c564971a..2382cabd5136f 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: DOM\Document::adoptNode(): Not Supported Error in %s on line %d +Warning: DOMDocument::adoptNode(): Not Supported Error in %s on line %d -- Adopt an attribute -- bool(true) bool(true) diff --git a/ext/dom/tests/DOMDocument_relaxNGValidateSource_error1.phpt b/ext/dom/tests/DOMDocument_relaxNGValidateSource_error1.phpt index 3baa24c50c65f..fd476749fe20e 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: DOM\Document::relaxNGValidateSource(): Did not expect element pear there in %s on line %d +Warning: DOMDocument::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 76aa3a04939d7..da00943c7caf2 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: DOM\Document::relaxNGValidateSource(): xmlRelaxNGParseElement: element has no content in %s on line %d +Warning: DOMDocument::relaxNGValidateSource(): xmlRelaxNGParseElement: element has no content in %s on line %d -Warning: DOM\Document::relaxNGValidateSource(): Invalid RelaxNG in %s on line %d +Warning: DOMDocument::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 aff2680b3daee..aa38ca5c1ed26 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: DOM\Document::relaxNGValidate(): Did not expect element pear there in %s on line %d +Warning: DOMDocument::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 5dde5e8ced926..1ad46e014a0a7 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: DOM\Document::relaxNGValidate(): I/O warning : failed to load external entity "%s/foo.rng" in %s on line %d +Warning: DOMDocument::relaxNGValidate(): I/O warning : failed to load external entity "%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(): xmlRelaxNGParse: could not load %s/foo.rng in %s on line %d -Warning: DOM\Document::relaxNGValidate(): Invalid RelaxNG in %s on line %d +Warning: DOMDocument::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 f71d192a616ed..c21403605647e 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: DOM\Document::schemaValidateSource(): Entity: line 1: parser error : Start tag expected, '<' not found in %s on line %d +Warning: DOMDocument::schemaValidateSource(): Entity: line 1: parser error : Start tag expected, '<' not found in %s on line %d -Warning: DOM\Document::schemaValidateSource(): string that is not a schema in %s on line %d +Warning: DOMDocument::schemaValidateSource(): string that is not a schema in %s on line %d -Warning: DOM\Document::schemaValidateSource(): ^ in %s on line %d +Warning: DOMDocument::schemaValidateSource(): ^ in %s on line %d -Warning: DOM\Document::schemaValidateSource(): Failed to parse the XML resource 'in_memory_buffer'. in %s on line %d +Warning: DOMDocument::schemaValidateSource(): Failed to parse the XML resource 'in_memory_buffer'. in %s on line %d -Warning: DOM\Document::schemaValidateSource(): Invalid Schema in %s on line %d +Warning: DOMDocument::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 b9fea75807a47..00b1a9b1dd115 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: DOM\Document::schemaValidateSource(): Element 'books': No matching global declaration available for the validation root. in %s on line %d +Warning: DOMDocument::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 bdeb74ff6c2c9..ec295a55e3391 100644 --- a/ext/dom/tests/DOMDocument_schemaValidateSource_error3.phpt +++ b/ext/dom/tests/DOMDocument_schemaValidateSource_error3.phpt @@ -20,4 +20,4 @@ try { ?> --EXPECT-- -DOM\Document::schemaValidateSource(): Argument #1 ($source) must not be empty +DOMDocument::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 2923943a3c8f5..b31c14651f293 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: 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(): %sbook-not-a-schema.xsd:1: parser error : Start tag expected, '<' not found in %s 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(): Let's see what happens upon parsing a file that doesn't contain a schema. in %s on line %d -Warning: DOM\Document::schemaValidate(): ^ in %s on line %d +Warning: DOMDocument::schemaValidate(): ^ in %s 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(): Failed to parse the XML resource '%sbook-not-a-schema.xsd'. in %s on line %d -Warning: DOM\Document::schemaValidate(): Invalid Schema in %s on line %d +Warning: DOMDocument::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 ddc491c70ca3f..47282575ef2b7 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: DOM\Document::schemaValidate(): Element 'books': No matching global declaration available for the validation root. in %s on line %d +Warning: DOMDocument::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 d48ed3d1963b2..274463e62e139 100644 --- a/ext/dom/tests/DOMDocument_schemaValidate_error3.phpt +++ b/ext/dom/tests/DOMDocument_schemaValidate_error3.phpt @@ -20,4 +20,4 @@ try { ?> --EXPECT-- -DOM\Document::schemaValidate(): Argument #1 ($filename) must not be empty +DOMDocument::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 2c179ed35bd45..3b277fd6cdb97 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: DOM\Document::schemaValidate(): I/O warning : failed to load external entity "%s/non-existent-file" in %s on line %d +Warning: DOMDocument::schemaValidate(): I/O warning : failed to load external entity "%snon-existent-file" in %s 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(): Failed to locate the main schema resource at '%snon-existent-file'. in %s on line %d -Warning: DOM\Document::schemaValidate(): Invalid Schema in %s on line %d +Warning: DOMDocument::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 3668921264cf2..c5d99b20a3fec 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-- -DOM\Document::schemaValidate(): Argument #1 ($filename) must not contain any null bytes +DOMDocument::schemaValidate(): Argument #1 ($filename) must not contain any null bytes -Warning: DOM\Document::schemaValidate(): Invalid Schema file source in %s on line %d +Warning: DOMDocument::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 3def63da9b5a9..b44d6395c309a 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: DOM\Document::createAttribute(): Invalid Character Error in %s on line %d +Warning: DOMDocument::createAttribute(): Invalid Character Error in %s on line %d diff --git a/ext/dom/tests/DOMElement_append_hierarchy_test.phpt b/ext/dom/tests/DOMElement_append_hierarchy_test.phpt index 2d70b10fe9f70..63583c05bf532 100644 --- a/ext/dom/tests/DOMElement_append_hierarchy_test.phpt +++ b/ext/dom/tests/DOMElement_append_hierarchy_test.phpt @@ -22,6 +22,13 @@ $b_world = $b_hello->nextSibling; $b_hello->append($b_world->firstChild); var_dump($dom->saveHTML()); +echo "-- Append hello with world's child and text --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +$b_hello->append($b_world->firstChild, "foo"); +var_dump($dom->saveHTML()); + echo "-- Append world's child with hello --\n"; $dom = clone $dom_original; $b_hello = $dom->firstChild->firstChild; @@ -39,6 +46,16 @@ try { } var_dump($dom->saveHTML()); +echo "-- Append hello with itself and text --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +try { + $b_hello->append($b_hello, "foo"); +} catch (\DOMException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom->saveHTML()); + echo "-- Append world's i tag with the parent --\n"; $dom = clone $dom_original; $b_hello = $dom->firstChild->firstChild; @@ -70,6 +87,9 @@ string(39) "

helloworld

-- Append hello with world's child -- string(39) "

helloworld

" +-- Append hello with world's child and text -- +string(42) "

helloworldfoo

+" -- Append world's child with hello -- string(39) "

worldhello

" @@ -77,6 +97,10 @@ string(39) "

worldhello

Hierarchy Request Error string(39) "

helloworld

" +-- Append hello with itself and text -- +Hierarchy Request Error +string(27) "

world

+" -- Append world's i tag with the parent -- Hierarchy Request Error string(39) "

helloworld

diff --git a/ext/dom/tests/DOMElement_get_or_has_attribute_ns_xmlns.phpt b/ext/dom/tests/DOMElement_get_or_has_attribute_ns_xmlns.phpt new file mode 100644 index 0000000000000..400f2f9908b13 --- /dev/null +++ b/ext/dom/tests/DOMElement_get_or_has_attribute_ns_xmlns.phpt @@ -0,0 +1,21 @@ +--TEST-- +DOMElement::{get,has}AttributeNS() with xmlns +--EXTENSIONS-- +dom +--FILE-- +loadXML(''); + +var_dump($dom->documentElement->getAttributeNS('http://www.w3.org/2000/xmlns/', 'a')); +var_dump($dom->documentElement->getAttributeNS('http://www.w3.org/2000/xmlns/', 'b')); +var_dump($dom->documentElement->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'a')); +var_dump($dom->documentElement->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'b')); + +?> +--EXPECT-- +string(5) "urn:a" +string(0) "" +bool(true) +bool(false) diff --git a/ext/dom/tests/DOMElement_insertAdjacentText.phpt b/ext/dom/tests/DOMElement_insertAdjacentText.phpt index 58af0812d6fe5..937f35f47acb9 100644 --- a/ext/dom/tests/DOMElement_insertAdjacentText.phpt +++ b/ext/dom/tests/DOMElement_insertAdjacentText.phpt @@ -78,4 +78,4 @@ Syntax Error --- Normal cases starting from empty element --- A BA -string(2) "BA" +string(1) "A" diff --git a/ext/dom/tests/DOMElement_prepend_hierarchy_test.phpt b/ext/dom/tests/DOMElement_prepend_hierarchy_test.phpt index 4d9cf24a61828..b1a9910e0ee1c 100644 --- a/ext/dom/tests/DOMElement_prepend_hierarchy_test.phpt +++ b/ext/dom/tests/DOMElement_prepend_hierarchy_test.phpt @@ -22,6 +22,13 @@ $b_world = $b_hello->nextSibling; $b_hello->prepend($b_world->firstChild); var_dump($dom->saveHTML()); +echo "-- Prepend hello with world's child and text --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +$b_hello->prepend($b_world->firstChild, "foo"); +var_dump($dom->saveHTML()); + echo "-- Prepend world's child with hello --\n"; $dom = clone $dom_original; $b_hello = $dom->firstChild->firstChild; @@ -29,6 +36,13 @@ $b_world = $b_hello->nextSibling; $b_world->firstChild->prepend($b_hello); var_dump($dom->saveHTML()); +echo "-- Prepend world's child with hello and text --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +$b_world->firstChild->prepend($b_hello, "foo"); +var_dump($dom->saveHTML()); + echo "-- Prepend hello with itself --\n"; $dom = clone $dom_original; $b_hello = $dom->firstChild->firstChild; @@ -39,6 +53,16 @@ try { } var_dump($dom->saveHTML()); +echo "-- Prepend hello with itself and text --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +try { + $b_hello->prepend($b_hello, "foo"); +} catch (\DOMException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom->saveHTML()); + echo "-- Prepend world's i tag with the parent --\n"; $dom = clone $dom_original; $b_hello = $dom->firstChild->firstChild; @@ -70,13 +94,23 @@ string(39) "

worldhello

-- Prepend hello with world's child -- string(39) "

worldhello

" +-- Prepend hello with world's child and text -- +string(42) "

worldfoohello

+" -- Prepend world's child with hello -- string(39) "

helloworld

" +-- Prepend world's child with hello and text -- +string(42) "

hellofooworld

+" -- Prepend hello with itself -- Hierarchy Request Error string(39) "

helloworld

" +-- Prepend hello with itself and text -- +Hierarchy Request Error +string(27) "

world

+" -- Prepend world's i tag with the parent -- Hierarchy Request Error string(39) "

helloworld

diff --git a/ext/dom/tests/DOMNode_normalize_advanced.phpt b/ext/dom/tests/DOMNode_normalize_advanced.phpt new file mode 100644 index 0000000000000..99cd922363529 --- /dev/null +++ b/ext/dom/tests/DOMNode_normalize_advanced.phpt @@ -0,0 +1,32 @@ +--TEST-- +DOMNode::normalize() advanced +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement("root")); +$child1 = $root->appendChild($dom->createElement("child1")); +$child2 = $root->appendChild($dom->createElement("child2")); + +$child1->appendChild($dom->createTextNode("abc")); +$child1->appendChild($dom->createTextNode("")); +$child1->appendChild($dom->createTextNode("def")); + +$child2->appendChild($dom->createTextNode("")); +$child2->setAttribute("foo", "bar"); + +echo $dom->saveXML(); + +$root->normalize(); + +var_dump($child1->childNodes[0]->textContent); +var_dump($child2->childNodes[0]); + +?> +--EXPECT-- + +abcdef +string(6) "abcdef" +NULL diff --git a/ext/dom/tests/dom_import_simplexml.phpt b/ext/dom/tests/dom_import_simplexml.phpt index c5dc5a3e4c0af..f38ea1227ca3b 100644 --- a/ext/dom/tests/dom_import_simplexml.phpt +++ b/ext/dom/tests/dom_import_simplexml.phpt @@ -12,6 +12,13 @@ if(!$s) { } $dom = dom_import_simplexml($s); print $dom->ownerDocument->saveXML(); + +// This should fail because it has been imported already above in legacy DOM +try { + DOM\import_simplexml($s); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} ?> --EXPECT-- @@ -25,3 +32,4 @@ print $dom->ownerDocument->saveXML(); John Steinbeck +DOM\import_simplexml(): Argument #1 ($node) must not be already imported as a DOMNode diff --git a/ext/dom/tests/domobject_debug_handler.phpt b/ext/dom/tests/domobject_debug_handler.phpt index c97655a63a14d..a1cbabffbb239 100644 --- a/ext/dom/tests/domobject_debug_handler.phpt +++ b/ext/dom/tests/domobject_debug_handler.phpt @@ -20,12 +20,14 @@ 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"]=> @@ -38,6 +40,12 @@ 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"]=> @@ -50,14 +58,6 @@ 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/gh11830/document_variation.phpt b/ext/dom/tests/gh11830/document_variation.phpt index 89eed3dff22c0..1f291b946878f 100644 --- a/ext/dom/tests/gh11830/document_variation.phpt +++ b/ext/dom/tests/gh11830/document_variation.phpt @@ -4,68 +4,80 @@ GH-11830 (ParentNode methods should perform their checks upfront) - document var dom --FILE-- loadXML(<< - -XML); - -$otherElement = $otherDoc->documentElement; +function test($method) { + $otherDoc = new DOMDocument; + $otherDoc->loadXML(<< + + XML); -$doc = new DOMDocument; -$doc->loadXML(<< - - - - -XML); + $otherElement = $otherDoc->documentElement; -$testElement = $doc->documentElement->firstElementChild->nextElementSibling->firstElementChild; + $doc = new DOMDocument; + $doc->loadXML(<< + + + + + XML); -try { - $doc->documentElement->firstElementChild->prepend($testElement, $otherElement); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; -} + $testElement = $doc->documentElement->firstElementChild->nextElementSibling->firstElementChild; -try { - $doc->documentElement->firstElementChild->append($testElement, $otherElement); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; -} + try { + $doc->documentElement->firstElementChild->$method($testElement, $otherElement); + } catch (\DOMException $e) { + echo $e->getMessage(), "\n"; + } -try { - $doc->documentElement->firstElementChild->before($testElement, $otherElement); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; + echo $otherDoc->saveXML(); + echo $doc->saveXML(); } -try { - $doc->documentElement->firstElementChild->after($testElement, $otherElement); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; -} - -try { - $doc->documentElement->firstElementChild->replaceWith($testElement, $otherElement); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; -} - -echo $otherDoc->saveXML(); -echo $doc->saveXML(); +test("prepend"); +test("append"); +test("before"); +test("after"); +test("replaceWith"); ?> --EXPECT-- Wrong Document Error + + + + + + + Wrong Document Error + + + + + + + Wrong Document Error + + + + + + + Wrong Document Error + + + + + + + Wrong Document Error - + diff --git a/ext/dom/tests/gh11830/hierarchy_variation.phpt b/ext/dom/tests/gh11830/hierarchy_variation.phpt index bd6534ee71b12..4430684618749 100644 --- a/ext/dom/tests/gh11830/hierarchy_variation.phpt +++ b/ext/dom/tests/gh11830/hierarchy_variation.phpt @@ -4,59 +4,43 @@ GH-11830 (ParentNode methods should perform their checks upfront) - hierarchy va dom --FILE-- loadXML(<< - - - - -XML); - -$container = $doc->documentElement; -$alone = $container->firstElementChild; -$testElement = $alone->nextElementSibling->firstElementChild; - -try { - $testElement->prepend($alone, $container); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; -} - -try { - $testElement->append($alone, $container); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; -} +function test($method) { + $doc = new DOMDocument; + $doc->loadXML(<< + + + + + XML); -try { - $testElement->before($alone, $container); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; -} + $container = $doc->documentElement; + $alone = $container->firstElementChild; + $testElement = $alone->nextElementSibling->firstElementChild; -try { - $testElement->after($alone, $container); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; -} + try { + $testElement->$method($alone, $container); + } catch (\DOMException $e) { + echo $e->getMessage(), "\n"; + } -try { - $testElement->replaceWith($alone, $container); -} catch (\DOMException $e) { - echo $e->getMessage(), "\n"; + echo $doc->saveXML(); } -echo $doc->saveXML(); +test("prepend"); +test("append"); +test("before"); +test("after"); +test("replaceWith"); ?> --EXPECT-- Hierarchy Request Error + Hierarchy Request Error + Hierarchy Request Error + Hierarchy Request Error + Hierarchy Request Error - - - - diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_GB18030.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_GB18030.phpt index c0139661e3077..05a348df0ce28 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_GB18030.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_GB18030.phpt @@ -6,7 +6,7 @@ dom encoding); +var_dump($dom->charset); $dom->documentElement->firstChild->nextElementSibling->textContent = "é"; $output = $dom->saveHTML(); echo $output, "\n"; @@ -14,7 +14,7 @@ $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"; +$dom->charset = "UTF-8"; echo $dom->saveHTML(), "\n"; ?> diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_Shift_JIS.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_Shift_JIS.phpt index c021d7022f275..84e835c1d8d89 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_Shift_JIS.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_Shift_JIS.phpt @@ -6,7 +6,7 @@ dom encoding); +var_dump($dom->charset); $dom->documentElement->firstChild->nextElementSibling->textContent .= "é"; $output = $dom->saveHTML(); echo $output, "\n"; @@ -14,7 +14,7 @@ $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"; +$dom->charset = "UTF-8"; echo $dom->saveHTML(), "\n"; ?> diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16BE_BOM.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16BE_BOM.phpt index 9e10859f1914c..315384345a4b9 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16BE_BOM.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16BE_BOM.phpt @@ -6,7 +6,7 @@ dom encoding); +var_dump($dom->characterSet); $dom->documentElement->firstChild->nextElementSibling->textContent = "é"; $output = $dom->saveHTML(); echo $output, "\n"; @@ -14,7 +14,7 @@ $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"; +$dom->characterSet = "UTF-8"; echo $dom->saveHTML(), "\n"; ?> diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16LE_BOM.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16LE_BOM.phpt index 7006163e3de07..8b0183a9f055e 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16LE_BOM.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF16LE_BOM.phpt @@ -6,7 +6,7 @@ dom encoding); +var_dump($dom->inputEncoding); $dom->documentElement->firstChild->nextElementSibling->textContent = "é"; $output = $dom->saveHTML(); echo $output, "\n"; @@ -14,7 +14,7 @@ $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"; +$dom->inputEncoding = "UTF-8"; echo $dom->saveHTML(), "\n"; ?> diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF8_BOM.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF8_BOM.phpt index 65326ad6b0370..c23ca5a9a47ec 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF8_BOM.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_UTF8_BOM.phpt @@ -6,7 +6,7 @@ dom encoding); +var_dump($dom->charset); $dom->documentElement->firstChild->nextElementSibling->textContent = "é"; $output = $dom->saveHTML(); echo $output, "\n"; @@ -14,7 +14,7 @@ $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"; +$dom->charset = "UTF-8"; echo $dom->saveHTML(), "\n"; ?> diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_Windows1251.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_Windows1251.phpt index 5b85639996d9c..98d313a554028 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_Windows1251.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_Windows1251.phpt @@ -6,7 +6,7 @@ dom encoding); +var_dump($dom->charset); $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"; @@ -14,7 +14,7 @@ $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"; +$dom->charset = "UTF-8"; echo $dom->saveHTML(), "\n"; ?> 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 index 130447228f4d8..8a4271c3a0a88 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_http_header.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_http_header.phpt @@ -63,7 +63,7 @@ foreach ($tests as $name => $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; + echo $result->getElementsByTagName("p")[0]->textContent, "\n"; } http_server_kill($pid); } 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 index 4f6f9943d2b62..ef1aca0f9f6f8 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_override_encoding.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_override_encoding.phpt @@ -14,12 +14,12 @@ try { // 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); +var_dump($dom->charset); // 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); +var_dump($dom->charset); ?> --EXPECT-- 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 index b276be453eec0..d68e0131df9d8 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromString_override_encoding.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromString_override_encoding.phpt @@ -14,12 +14,12 @@ try { // 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); +var_dump($dom->charset); // 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); +var_dump($dom->charset); ?> --EXPECT-- 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 index 3988767e58064..13fbdc684b336 100644 --- 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 @@ -8,9 +8,11 @@ dom // UTF-8 -> 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"); +$element = $dom->createElement("container"); +$dom->append($element); +$element->append(str_repeat("A", 4096 - 2 - strlen("")) . "\xf0\x90\x8d\x88AA"); var_dump($dom->saveHTML()); ?> --EXPECT-- -string(4100) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA𐍈AA" +string(4112) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA𐍈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 index 5ac5694a89dc3..c8e9736533ad0 100644 --- 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 @@ -7,9 +7,11 @@ dom // UTF-8 -> GB18030 $dom = DOM\HTMLDocument::createEmpty("GB18030"); +$element = $dom->createElement("container"); +$dom->append($element); // 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"); +$element->append(str_repeat("A", 4096 - 2 - strlen("")) . "\xf0\x90\x8d\x88AA"); var_dump($output = $dom->saveHTML()); // GB18030 encoding of the above UTF-8 symbol @@ -20,7 +22,7 @@ var_dump($output[4097] == "\x30"); ?> --EXPECT-- -string(4100) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�0�0AA" +string(4112) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�0�0AA" 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 index d68257ca80fee..5e66a0b0411cc 100644 --- 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 @@ -7,11 +7,13 @@ dom // UTF-8 -> GB18030 $dom = DOM\HTMLDocument::createEmpty("GB18030"); +$element = $dom->createElement("container"); +$dom->append($element); // 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"); +$element->append(str_repeat("A", 4096 - 2 - strlen("")) . "\xff\xff\xff"); var_dump($dom->saveHTML()); ?> --EXPECT-- -string(4106) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�1�7�1�7�1�7" +string(4118) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�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 index ccff90a83320f..1bde3303269b5 100644 --- 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 @@ -7,10 +7,12 @@ dom // UTF-8 -> UTF-8 $dom = DOM\HTMLDocument::createEmpty(); +$element = $dom->createElement("container"); +$dom->append($element); // 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"); +$element->append(str_repeat("A", 4096 - 2 - strlen("")) . "\xff\xff\xff"); var_dump($dom->saveHTML()); ?> --EXPECT-- -string(4103) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���" +string(4115) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���" 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 index e7da1e439b70a..0c44159c8b6ce 100644 --- 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 @@ -11,8 +11,8 @@ $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->charset); +$dom->charset = "UTF-8"; var_dump($dom->saveHTML()); ?> 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 index 0b472b466fbed..ea5962a1ae6a6 100644 --- 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 @@ -7,10 +7,12 @@ dom // UTF-8 -> UTF-8 $dom = DOM\HTMLDocument::createEmpty(); +$element = $dom->createElement("container"); +$dom->append($element); // 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"); +$element->append(str_repeat("A", 4096 - 1 - strlen("")) . "\xf0\x90"); var_dump($dom->saveHTML()); ?> --EXPECT-- -string(4101) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��" +string(4110) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�" 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 index 6cf2d1d8f5e4b..4f60cccaba155 100644 --- 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 @@ -11,8 +11,8 @@ $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->charset); +$dom->charset = "UTF-8"; var_dump($dom->saveHTML()); ?> 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 index bab3532565b82..c5c5aec382d58 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_field_test.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_field_test.phpt @@ -6,23 +6,23 @@ dom encoding); -$dom->encoding = "CSeuckr"; -var_dump($dom->encoding); +var_dump($dom->charset); +$dom->inputEncoding = "CSeuckr"; +var_dump($dom->characterSet); try { - $dom->encoding = "nope"; + $dom->charset = "nope"; } catch (ValueError $e) { echo $e->getMessage(), "\n"; } -var_dump($dom->encoding); -$dom->encoding = "Windows-1251"; -var_dump($dom->encoding); +var_dump($dom->charset); +$dom->inputEncoding = "Windows-1251"; +var_dump($dom->characterSet); try { - $dom->encoding = NULL; + $dom->charset = ""; } catch (ValueError $e) { echo $e->getMessage(), "\n"; } -var_dump($dom->encoding); +var_dump($dom->inputEncoding); echo $dom->saveHTML(); try { diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_fallback_encoding.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_fallback_encoding.phpt index 9ffd02dc5d7d8..f431b6cd31902 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_fallback_encoding.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_fallback_encoding.phpt @@ -6,7 +6,7 @@ dom encoding); +var_dump($dom->inputEncoding); echo $dom->saveHTML(); ?> diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_adopt_DOMDocument.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_adopt_DOMDocument.phpt deleted file mode 100644 index cff51dac28f6b..0000000000000 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_adopt_DOMDocument.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---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 index 1a04dd7beae29..e5420d06b9727 100644 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_clone.phpt +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_clone.phpt @@ -18,6 +18,6 @@ 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(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 index 9430aac9da30a..a24ebb7bf3416 100644 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_documentURI.phpt +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_documentURI.phpt @@ -42,6 +42,6 @@ var_dump($dom->documentURI); ?> --EXPECTF-- -string(%d) "file:/%stest%20foo.html" +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 index 3af5c2d01eb72..19759c95677ed 100644 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_getElementsByTagName.phpt +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_getElementsByTagName.phpt @@ -67,19 +67,19 @@ var_dump($dom->getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML", "cir ?> --EXPECT-- --- getElementsByTagName --- -string(1) "p" +string(1) "P" string(4) "math" string(6) "mtable" string(3) "svg" string(6) "circle" --- getElementsByTagNameNS (*) --- -string(1) "p" +string(1) "P" string(4) "math" string(6) "mtable" string(3) "svg" string(6) "circle" --- getElementsByTagNameNS (xhtml) --- -string(1) "p" +string(1) "P" NULL 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 index e7f4fb630f542..6ae7589270b73 100644 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_01.phpt +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_01.phpt @@ -10,8 +10,8 @@ $dom->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 +Fatal error: Uncaught TypeError: DOMDocument::registerNodeClass(): Argument #1 ($baseClass) must be a class name derived from DOMNode, DOM\HTMLDocument given in %s:%d Stack trace: -#0 %s(%d): DOM\Document->registerNodeClass('DOM\\HTMLDocumen...', 'DOMDocument') +#0 %s(%d): DOMDocument->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 index f9b8493cda34a..3eb1f0cae8af1 100644 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_02.phpt +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_02.phpt @@ -10,7 +10,7 @@ class Custom extends DOM\Document { } } -$dom = new DOMDocument(); +$dom = DOM\HTMLDocument::createEmpty(); try { $dom->registerNodeClass("DOM\\Document", "Custom"); } catch (ValueError $e) { @@ -28,9 +28,9 @@ $element->ownerDocument->foo(); ?> --EXPECTF-- DOM\Document::registerNodeClass(): Argument #1 ($baseClass) must not be an abstract class -string(11) "DOMDocument" +string(16) "DOM\HTMLDocument" -Fatal error: Uncaught Error: Call to undefined method DOMDocument::foo() in %s:%d +Fatal error: Uncaught Error: Call to undefined method DOM\HTMLDocument::foo() in %s:%d Stack trace: #0 {main} thrown in %s on line %d diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_03.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_03.phpt new file mode 100644 index 0000000000000..859212a086708 --- /dev/null +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_registerNodeClass_03.phpt @@ -0,0 +1,25 @@ +--TEST-- +DOM\HTMLDocument::registerNodeClass 03 +--EXTENSIONS-- +dom +--FILE-- +test); + return strrev($this->tagName); + } +} + +$dom = DOM\HTMLDocument::createFromString("
foo
", LIBXML_NOERROR); +$dom->registerNodeClass("DOM\\Element", "Custom"); + +var_dump($dom->getElementsByTagName('div')[0]->reverseTagName()); + +?> +--EXPECT-- +int(1) +string(3) "VID" 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 index 4a6e816313229..08e70cfa43019 100644 --- 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 @@ -5,45 +5,57 @@ dom --FILE-- foo

", LIBXML_NOERROR); -$dom->strictErrorChecking = false; +$dom->registerNodeClass("DOM\\Element", "MyElement"); // Destroy reference to the DOM $child = $dom->documentElement; unset($dom); // Regain reference using the ownerDocument property -// Should be a DOM\HTML5Document +// 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); +var_dump(get_class($dom->getElementsByTagName("p")->item(0))); ?> --EXPECT-- -object(DOM\HTMLDocument)#1 (26) { - ["encoding"]=> +object(DOM\HTMLDocument)#1 (25) { + ["implementation"]=> + string(22) "(object value omitted)" + ["URL"]=> + string(11) "about:blank" + ["documentURI"]=> + string(11) "about:blank" + ["characterSet"]=> + string(5) "UTF-8" + ["charset"]=> + string(5) "UTF-8" + ["inputEncoding"]=> 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) + ["nodeType"]=> + int(13) ["nodeName"]=> string(9) "#document" - ["nodeValue"]=> + ["baseURI"]=> + string(11) "about:blank" + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> NULL - ["nodeType"]=> - int(13) ["parentNode"]=> NULL ["parentElement"]=> @@ -58,21 +70,9 @@ object(DOM\HTMLDocument)#1 (26) { NULL ["nextSibling"]=> NULL - ["attributes"]=> - NULL - ["isConnected"]=> - bool(true) - ["ownerDocument"]=> - NULL - ["namespaceURI"]=> - NULL - ["prefix"]=> - string(0) "" - ["localName"]=> - NULL - ["baseURI"]=> + ["nodeValue"]=> NULL ["textContent"]=> - string(3) "foo" + NULL } -bool(false) +string(9) "MyElement" 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 index 9c4d016fef81c..379a3da4b2fa1 100644 --- 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 @@ -5,9 +5,11 @@ dom --FILE-- foo

", LIBXML_NOERROR); -$dom->strictErrorChecking = false; -$child = $dom->appendChild($dom->createElement('html')); +$dom->registerNodeClass("DOM\\Element", "MyElement"); +$child = $dom->documentElement->appendChild($dom->createElement('html')); // Destroy reference to the DOM unset($dom); @@ -17,33 +19,43 @@ unset($dom); $dom = $child->ownerDocument; var_dump($dom); // Test if property is preserved (any random doc_props property will do) -var_dump($dom->strictErrorChecking); +var_dump(get_class($dom->getElementsByTagName("p")->item(0))); ?> --EXPECT-- -object(DOM\HTMLDocument)#1 (26) { - ["encoding"]=> +object(DOM\HTMLDocument)#1 (25) { + ["implementation"]=> + string(22) "(object value omitted)" + ["URL"]=> + string(11) "about:blank" + ["documentURI"]=> + string(11) "about:blank" + ["characterSet"]=> + string(5) "UTF-8" + ["charset"]=> + string(5) "UTF-8" + ["inputEncoding"]=> 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) + int(1) + ["nodeType"]=> + int(13) ["nodeName"]=> string(9) "#document" - ["nodeValue"]=> + ["baseURI"]=> + string(11) "about:blank" + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> NULL - ["nodeType"]=> - int(13) ["parentNode"]=> NULL ["parentElement"]=> @@ -58,21 +70,9 @@ object(DOM\HTMLDocument)#1 (26) { NULL ["nextSibling"]=> NULL - ["attributes"]=> - NULL - ["isConnected"]=> - bool(true) - ["ownerDocument"]=> - NULL - ["namespaceURI"]=> - NULL - ["prefix"]=> - string(0) "" - ["localName"]=> - NULL - ["baseURI"]=> + ["nodeValue"]=> NULL ["textContent"]=> - string(3) "foo" + NULL } -bool(false) +string(9) "MyElement" diff --git a/ext/dom/tests/modern/html/interactions/noscript.phpt b/ext/dom/tests/modern/html/interactions/noscript.phpt index 839845e330f22..d93dae79c650f 100644 --- a/ext/dom/tests/modern/html/interactions/noscript.phpt +++ b/ext/dom/tests/modern/html/interactions/noscript.phpt @@ -9,27 +9,29 @@ echo "--- Parsing ---\n"; $dom = DOM\HTMLDocument::createFromString("", DOM\HTML_NO_DEFAULT_NS); var_dump($dom->documentElement->textContent); +var_dump($dom->documentElement->namespaceURI); echo $dom->saveHTML(), "\n"; -echo $dom->saveXML(); +echo $dom->saveXML(), "\n"; echo "--- Modifying the text content: tag ---\n"; -$xpath = new DOMXPath($dom); +$xpath = new DOM\XPath($dom); $noscript = $xpath->query("//noscript")[0]; $noscript->textContent = "

bye

"; echo $dom->saveHTML(), "\n"; -echo $dom->saveXML(); +echo $dom->saveXML(), "\n"; echo "--- Modifying the text content: trick ---\n"; $noscript->textContent = ""; echo $dom->saveHTML(), "\n"; -echo $dom->saveXML(); +echo $dom->saveXML(), "\n"; ?> --EXPECT-- --- Parsing --- string(2) "hi" +NULL 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 index 8d7d70e6d9f8c..b666167d2b4de 100644 --- 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 @@ -10,11 +10,13 @@ $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); +var_dump($dom->documentElement->textContent); + +echo $dom->saveHTML(), "\n"; file_put_contents(__DIR__ . "/BOM_edge.tmp", $data); $dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/BOM_edge.tmp"); -var_dump($dom->textContent); +var_dump($dom->documentElement->textContent); ?> --CLEAN-- @@ -23,4 +25,5 @@ var_dump($dom->textContent); ?> --EXPECT-- string(4052) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa string(4052) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" diff --git a/ext/dom/tests/modern/html/parser/HTMLDocument_createFromString_namespaced_attributes.phpt b/ext/dom/tests/modern/html/parser/HTMLDocument_createFromString_namespaced_attributes.phpt new file mode 100644 index 0000000000000..7562e14beb1ab --- /dev/null +++ b/ext/dom/tests/modern/html/parser/HTMLDocument_createFromString_namespaced_attributes.phpt @@ -0,0 +1,49 @@ +--TEST-- +HTMLDocument::createFromString() with namespaced attributes +--EXTENSIONS-- +dom +--FILE-- + + + + + + + + + +HTML); + +foreach (['svg', 'use', 'mo'] as $tag) { + $el = $dom->getElementsByTagName($tag)[0]; + foreach ($el->attributes as $attribute) { + echo "Attribute: \n"; + var_dump($attribute->name, $attribute->value, $attribute->namespaceURI); + } +} + +?> +--EXPECT-- +Attribute: +string(5) "width" +string(1) "1" +NULL +Attribute: +string(11) "xmlns:xlink" +string(28) "http://www.w3.org/1999/xlink" +string(29) "http://www.w3.org/2000/xmlns/" +Attribute: +string(10) "xlink:href" +string(5) "#test" +string(28) "http://www.w3.org/1999/xlink" +Attribute: +string(3) "foo" +string(3) "bar" +NULL +Attribute: +string(6) "accent" +string(4) "true" +NULL 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 index 55089d8fac345..4b370b417dd5e 100644 --- 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 @@ -6,32 +6,32 @@ dom 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 = new DOM\XPath($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) { +object(DOM\NodeList)#3 (1) { ["length"]=> int(0) } -object(DOMNodeList)#4 (1) { +object(DOM\NodeList)#4 (1) { ["length"]=> int(1) } -object(DOMNodeList)#3 (1) { +object(DOM\NodeList)#3 (1) { ["length"]=> int(1) } -object(DOMNodeList)#3 (1) { +object(DOM\NodeList)#3 (1) { ["length"]=> int(0) } 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 index 0e6839007fc8e..102f4df84c3e2 100644 --- 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 @@ -6,32 +6,32 @@ dom 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 = new DOM\XPath($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) { +object(DOM\NodeList)#3 (1) { ["length"]=> int(0) } -object(DOMNodeList)#4 (1) { +object(DOM\NodeList)#4 (1) { ["length"]=> int(1) } -object(DOMNodeList)#3 (1) { +object(DOM\NodeList)#3 (1) { ["length"]=> int(1) } -object(DOMNodeList)#3 (1) { +object(DOM\NodeList)#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 index 28146903105ec..c702c48ba3f64 100644 --- a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_COMPACT.phpt +++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_LIBXML_COMPACT.phpt @@ -23,7 +23,7 @@ $dom = DOM\HTMLDocument::createFromString(<< HTML, LIBXML_COMPACT); -$xpath = new DOMXPath($dom); +$xpath = new DOM\XPath($dom); foreach ($xpath->query("//*[name()='p']") as $p) { echo $p->textContent, "\n"; } 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 index ea384bb7885f3..fde8f40b90b0a 100644 --- 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 @@ -8,19 +8,19 @@ dom echo "--- No elements ---\n"; $dom = DOM\HTMLDocument::createFromString("", LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR); -echo $dom->saveXML(); +echo $dom->saveXML(), "\n"; echo "--- Single element ---\n"; $dom = DOM\HTMLDocument::createFromString("

foo

", LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR); -echo $dom->saveXML(); +echo $dom->saveXML(), "\n"; 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(); +echo $dom->saveXML(), "\n"; var_dump($dom->documentElement->namespaceURI); var_dump($dom->documentElement->prefix); var_dump($dom->documentElement->nextSibling->namespaceURI); @@ -30,16 +30,16 @@ var_dump($dom->documentElement->nextSibling->prefix); --EXPECT-- --- No elements --- + --- Single element ---

foo

string(28) "http://www.w3.org/1999/xhtml" -string(0) "" +NULL --- Multiple elements --- -

foo

-bar +

foo

bar string(28) "http://www.w3.org/1999/xhtml" -string(0) "" +NULL string(28) "http://www.w3.org/1999/xhtml" -string(0) "" +NULL 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 index ba47a715fb394..2d1ecab2cb1a2 100644 --- a/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_line_column.phpt +++ b/ext/dom/tests/modern/html/parser/HTMLDocument_fromString_line_column.phpt @@ -22,7 +22,7 @@ $dom = DOM\HTMLDocument::createFromString(<< HTML); -$xpath = new DOMXPath($dom); +$xpath = new DOM\XPath($dom); foreach ($xpath->query("//*") as $element) { echo "Element: '", $element->tagName, "', ", $element->getLineNo(), "\n"; @@ -44,13 +44,13 @@ foreach ($xpath->query("//comment()") as $comment) { ?> --EXPECT-- -Element: 'html', 1 -Element: 'head', 2 -Element: 'title', 3 -Element: 'body', 5 -Element: 'div', 6 -Element: 'p', 7 -Element: 'strong', 8 +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 diff --git a/ext/dom/tests/modern/html/parser/predefined_namespaces.phpt b/ext/dom/tests/modern/html/parser/predefined_namespaces.phpt index 39e8bf0131d09..a97385de37d91 100644 --- a/ext/dom/tests/modern/html/parser/predefined_namespaces.phpt +++ b/ext/dom/tests/modern/html/parser/predefined_namespaces.phpt @@ -28,7 +28,7 @@ $dom = DOM\HTMLDocument::createFromString(<<query("//*[name()='body']//*") as $node) { echo $node->nodeName, " ", $node->namespaceURI ?? "(NONE)", "\n"; foreach ($node->attributes as $attribute) { @@ -54,8 +54,8 @@ rect http://www.w3.org/2000/svg Attribute: y (NONE) Attribute: width (NONE) Attribute: height (NONE) -div http://www.w3.org/1999/xhtml -p http://www.w3.org/1999/xhtml +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) diff --git a/ext/dom/tests/modern/html/parser/xml_style_namespace.phpt b/ext/dom/tests/modern/html/parser/xml_style_namespace.phpt index 08c2756f2cdf3..3889d1fd2ff65 100644 --- a/ext/dom/tests/modern/html/parser/xml_style_namespace.phpt +++ b/ext/dom/tests/modern/html/parser/xml_style_namespace.phpt @@ -18,7 +18,7 @@ $dom = DOM\HTMLDocument::createFromString(<<query("//*[name()='body']//*") as $node) { echo $node->nodeName, " ", $node->namespaceURI ?? "(NONE)", "\n"; echo "prefix: \"", $node->prefix, "\"\n"; @@ -35,7 +35,7 @@ echo $dom->saveXML(); ?> --EXPECT-- --- Namespaces --- -foo:bar http://www.w3.org/1999/xhtml +FOO:BAR http://www.w3.org/1999/xhtml prefix: "" --- HTML serialization --- @@ -52,6 +52,6 @@ prefix: "" Test - + diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_entity_reference.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_entity_reference.phpt new file mode 100644 index 0000000000000..108c383467819 --- /dev/null +++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_entity_reference.phpt @@ -0,0 +1,22 @@ +--TEST-- +Serialize entity reference +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom1->createElement('root')); +$root->appendChild($dom1->createEntityReference('nbsp')); + +$dom2 = DOM\HTMLDocument::createEmpty(); +$dom2->appendChild($dom2->importLegacyNode($root, true)); + +echo $dom2->saveXML(), "\n"; +echo $dom2->saveHTML(), "\n"; + +?> +--EXPECT-- + +  +  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 index 9e8865f9971f8..dc8881bfc6a70 100644 --- a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_attribute_ns.phpt +++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_attribute_ns.phpt @@ -9,8 +9,7 @@ $dom = DOM\HTMLDocument::createEmpty(); $root = $dom->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")); $root->setAttributeNodeNS($dom->createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:f")); $root->setAttributeNodeNS($dom->createAttributeNS("http://www.w3.org/1999/xlink", "z:f")); @@ -19,4 +18,4 @@ 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 index 2951ff59e9d12..27824c61eeecf 100644 --- a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_cdata.phpt +++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_cdata.phpt @@ -5,10 +5,15 @@ dom --FILE-- createCDATASection("foobaré\"<>-&"); + $dom = DOM\HTMLDocument::createEmpty(); -$dom->appendChild($dom->createCDATASection("foobaré\"<>-&")); +$container = $dom->appendChild($dom->createElement("container")); +$container->appendChild($dom->importNode($cdata)); echo $dom->saveHTML(); ?> --EXPECT-- -foobaré"<>-& +foobaré"<>-& diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_doctype.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_doctype.phpt index 72823bb5b4bba..5be17d704191e 100644 --- a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_doctype.phpt +++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_doctype.phpt @@ -16,10 +16,10 @@ $dom = DOM\HTMLDocument::createFromString(<<saveXML(); +echo $dom->saveXML(), "\n"; 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(); +echo $dom->saveHTML(), "\n"; ?> --EXPECT-- 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 index 1395fe72f1e97..76321f473805f 100644 --- 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 @@ -8,7 +8,7 @@ dom $xml = DOM\XMLDocument::createFromString(''); $xml->documentElement->setAttributeNS("http://foo/", "foo:bar", "value"); $xml->documentElement->appendChild($xml->createElementNS('some:ns2', 'child')); -echo $xml->saveXML(); +echo $xml->saveXML(), "\n"; echo "--- After import into HTML ---\n"; @@ -17,7 +17,7 @@ $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->saveXML(), "\n"; echo $html->saveHTML(), "\n"; ?> @@ -26,5 +26,5 @@ echo $html->saveHTML(), "\n"; --- After import into HTML --- -

foo

+

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 index c7297805b0a30..94af74ffaeb2d 100644 --- 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 @@ -7,7 +7,7 @@ dom $xml = DOM\XMLDocument::createFromFile(__DIR__.'/sample.xml'); $xml->documentElement->appendChild($xml->createElementNS('some:ns2', 'child')); -echo $xml->saveXML(); +echo $xml->saveXML(), "\n"; echo "--- After import into HTML ---\n"; @@ -16,7 +16,7 @@ $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->saveXML(), "\n"; echo $html->saveHTML(), "\n"; ?> @@ -35,7 +35,7 @@ echo $html->saveHTML(), "\n";
--- 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 index f5a51c16b01e5..7f5b9ab822f21 100644 --- 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 @@ -7,7 +7,7 @@ dom $xml = DOM\XMLDocument::createFromFile(__DIR__.'/sample.xml'); $xml->documentElement->appendChild($xml->createElementNS('some:ns2', 'child')); -echo $xml->saveXML(); +echo $xml->saveXML(), "\n"; echo "--- After import into HTML ---\n"; @@ -16,7 +16,7 @@ $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->saveXML(), "\n"; echo $html->saveHTML(), "\n"; ?> @@ -35,5 +35,5 @@ echo $html->saveHTML(), "\n";
--- After import into HTML --- -

foo

+

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 index 9793043568c2d..d4ae507c76177 100644 --- 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 @@ -7,7 +7,7 @@ dom $xml = DOM\XMLDocument::createFromFile(__DIR__.'/sample.xml'); $xml->documentElement->appendChild($xml->createElementNS('some:ns2', 'child')); -echo $xml->saveXML(); +echo $xml->saveXML(), "\n"; echo "--- After import into HTML ---\n"; @@ -16,7 +16,7 @@ $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->saveXML(), "\n"; echo $html->saveHTML(), "\n"; ?> @@ -35,5 +35,5 @@ echo $html->saveHTML(), "\n";
--- After import into HTML --- -

foo

+

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 index 09810be1e13ed..2e9fd671775f6 100644 --- 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 @@ -7,7 +7,7 @@ dom $xml = DOM\XMLDocument::createFromFile(__DIR__.'/sample.xml'); $xml->documentElement->appendChild($xml->createElementNS('some:ns2', 'child')); -echo $xml->saveXML(); +echo $xml->saveXML(), "\n"; echo "--- After adoption into HTML ---\n"; @@ -16,7 +16,7 @@ $html = DOM\HTMLDocument::createFromString('

foo

', LIBXML_NOERROR); $p = $html->documentElement->firstChild->nextSibling->firstChild; $p->appendChild($html->adoptNode($xml->documentElement)); -echo $html->saveXML(); +echo $html->saveXML(), "\n"; echo $html->saveHTML(), "\n"; ?> @@ -35,7 +35,7 @@ echo $html->saveHTML(), "\n";
--- 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 index 5df88add914ee..753fa73633b96 100644 --- 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 @@ -6,17 +6,17 @@ dom documentElement->firstChild->appendChild($xml->createElementNS('some:ns2', 'child')); -echo $xml->saveXML(); +$xml->documentElement->firstElementChild->appendChild($xml->createElementNS('some:ns2', 'child')); +echo $xml->saveXML(), "\n"; echo "--- After clone + import into HTML ---\n"; $html = DOM\HTMLDocument::createFromString('

foo

', LIBXML_NOERROR); -$p = $html->documentElement->firstChild->nextSibling->firstChild; +$p = $html->documentElement->firstElementChild->nextElementSibling->firstElementChild; $p->appendChild($html->adoptNode($xml->documentElement->firstElementChild->cloneNode(true))); -echo $html->saveXML(); +echo $html->saveXML(), "\n"; echo $html->saveHTML(), "\n"; ?> @@ -31,11 +31,11 @@ echo $html->saveHTML(), "\n"; - +
--- After clone + import into HTML --- -

foo +

foo @@ -43,7 +43,7 @@ echo $html->saveHTML(), "\n"; -

+

foo @@ -52,4 +52,4 @@ echo $html->saveHTML(), "\n"; -

+

diff --git a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_roots.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_roots.phpt new file mode 100644 index 0000000000000..5b5959e5adb6c --- /dev/null +++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_roots.phpt @@ -0,0 +1,35 @@ +--TEST-- +DOM\HTMLDocument serialization of different roots +--EXTENSIONS-- +dom +--FILE-- +createCDATASection("cdata"); + +$dom = DOM\HTMLDocument::createEmpty(); +$container = $dom->appendChild($dom->createElement("container")); +$comment = $container->appendChild($dom->createComment("comment")); +$cdata = $container->appendChild($dom->importNode($cdata)); +$emptyElement = $container->appendChild($dom->createElement("empty")); +$text = $container->appendChild($dom->createTextNode("text")); +$pi = $container->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(14) "" +string(5) "cdata" +string(15) "" +string(4) "text" +string(14) "" +string(0) "" 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 deleted file mode 100644 index fd33e67d7e484..0000000000000 --- a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_roots_test_empty.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---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_02.phpt b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_02.phpt index 31e12bbd6ef47..115f3233eb2eb 100644 --- a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_02.phpt +++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_02.phpt @@ -10,7 +10,7 @@ $body = $dom->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")); + $body->append("\n"); } echo $dom->saveHTML(); 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 index fee3c34b3c5dd..ec6822d77582c 100644 --- a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_03.phpt +++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_text_03.phpt @@ -10,7 +10,7 @@ $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")); + $body->append("\n"); } echo $dom->saveHTML(); 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 index c5ac9a6d71a7c..e4acf241e7d08 100644 --- a/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_void_elements.phpt +++ b/ext/dom/tests/modern/html/serializer/HTMLDocument_serialize_void_elements.phpt @@ -6,6 +6,8 @@ dom createElement("container"); +$dom->append($container); $tags = [ "area", @@ -29,13 +31,13 @@ $tags = [ ]; foreach ($tags as $tag) { - $element = $dom->appendChild($dom->createElement($tag)); + $element = $container->appendChild($dom->createElement($tag)); $element->appendChild($dom->createElement("inner")); $element->after("\n"); echo "$tag: "; - var_dump(strlen($dom->saveHTML($element)) === 0); + var_dump($dom->saveHTML($element)); - $element = $dom->appendChild($dom->createElementNS("http://php.net/foo", "x:$tag")); + $element = $container->appendChild($dom->createElementNS("http://php.net/foo", "x:$tag")); $element->appendChild($dom->createElement("inner")); $element->after("\n"); } @@ -44,25 +46,25 @@ 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: string(6) "" +base: string(6) "" +br: string(4) "
" +col: string(5) "" +embed: string(7) "" +hr: string(4) "
" +img: string(5) "" +input: string(7) "" +link: string(6) "" +meta: string(6) "" +source: string(8) "" +track: string(7) "" +wbr: string(5) "" +basefont: string(10) "" +bgsound: string(9) "" +frame: string(7) "" +keygen: string(8) "" +param: string(7) "" + @@ -98,3 +100,4 @@ param: bool(true) + diff --git a/ext/dom/tests/modern/spec/Attr_name.phpt b/ext/dom/tests/modern/spec/Attr_name.phpt new file mode 100644 index 0000000000000..2510de3eb1c2c --- /dev/null +++ b/ext/dom/tests/modern/spec/Attr_name.phpt @@ -0,0 +1,15 @@ +--TEST-- +DOMAttr::$name +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement("foo")); +$foo->setAttributeNS("http://example.com", "foo:bar", "baz"); +var_dump($foo->attributes[0]->name); + +?> +--EXPECT-- +string(7) "foo:bar" diff --git a/ext/dom/tests/modern/spec/CharacterData_deleteData_negative_in_bounds_length.phpt b/ext/dom/tests/modern/spec/CharacterData_deleteData_negative_in_bounds_length.phpt new file mode 100644 index 0000000000000..924f58d703627 --- /dev/null +++ b/ext/dom/tests/modern/spec/CharacterData_deleteData_negative_in_bounds_length.phpt @@ -0,0 +1,32 @@ +--TEST-- +deleteData() negative in bounds length +--EXTENSIONS-- +dom +--FILE-- +createComment("foobarbaz"); +$comment->deleteData(3, -1); +echo $dom->saveHTML($comment), "\n"; + +echo "--- Legacy behaviour ---\n"; + +$dom = new DOMDocument; +$comment = $dom->createComment("foobarbaz"); +try { + $comment->deleteData(3, -1); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; + +?> +--EXPECT-- +--- Modern behaviour --- + +--- Legacy behaviour --- +Index Size Error + diff --git a/ext/dom/tests/modern/spec/CharacterData_insertData_edge_cases.phpt b/ext/dom/tests/modern/spec/CharacterData_insertData_edge_cases.phpt new file mode 100644 index 0000000000000..b89b389ee31a8 --- /dev/null +++ b/ext/dom/tests/modern/spec/CharacterData_insertData_edge_cases.phpt @@ -0,0 +1,28 @@ +--TEST-- +insertData() edge cases +--EXTENSIONS-- +dom +--SKIPIF-- + +--FILE-- +createComment("foobarbaz"); +try { + var_dump($comment->insertData(100, "data")); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +try { + var_dump($comment->insertData(2**31+1, "data")); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; + +?> +--EXPECT-- +Index Size Error +Index Size Error + diff --git a/ext/dom/tests/modern/spec/CharacterData_insertData_negative_offset.phpt b/ext/dom/tests/modern/spec/CharacterData_insertData_negative_offset.phpt new file mode 100644 index 0000000000000..4166c64f54495 --- /dev/null +++ b/ext/dom/tests/modern/spec/CharacterData_insertData_negative_offset.phpt @@ -0,0 +1,48 @@ +--TEST-- +insertData() negative offset +--EXTENSIONS-- +dom +--FILE-- +createComment("foobarbaz"); +try { + $comment->insertData(-1, "A"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; +$comment->insertData(-(2**32 - 1), "A"); +echo $dom->saveHTML($comment), "\n"; + +echo "--- Legacy behaviour ---\n"; + +$dom = new DOMDocument; +$comment = $dom->createComment("foobarbaz"); +try { + $comment->insertData(-1, "A"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; +try { + $comment->insertData(-(2**32 - 1), "A"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; + +?> +--EXPECT-- +--- Modern behaviour --- +Index Size Error + + +--- Legacy behaviour --- +Index Size Error + +Index Size Error + diff --git a/ext/dom/tests/modern/spec/CharacterData_replaceData_negative_count.phpt b/ext/dom/tests/modern/spec/CharacterData_replaceData_negative_count.phpt new file mode 100644 index 0000000000000..d673cb25198f0 --- /dev/null +++ b/ext/dom/tests/modern/spec/CharacterData_replaceData_negative_count.phpt @@ -0,0 +1,44 @@ +--TEST-- +replaceData() negative count +--EXTENSIONS-- +dom +--FILE-- +createComment("foobarbaz"); +$comment->replaceData(0, -1, "A"); +echo $dom->saveHTML($comment), "\n"; +$comment = $dom->createComment("foobarbaz"); +$comment->replaceData(2, -(2**32 - 2), "A"); +echo $dom->saveHTML($comment), "\n"; + +echo "--- Legacy behaviour ---\n"; + +$dom = new DOMDocument; +$comment = $dom->createComment("foobarbaz"); +try { + $comment->replaceData(0, -1, "A"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; +try { + $comment->replaceData(2, -(2**32 - 2), "A"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; + +?> +--EXPECT-- +--- Modern behaviour --- + + +--- Legacy behaviour --- +Index Size Error + +Index Size Error + diff --git a/ext/dom/tests/modern/spec/CharacterData_substringData_edge_cases.phpt b/ext/dom/tests/modern/spec/CharacterData_substringData_edge_cases.phpt new file mode 100644 index 0000000000000..82f1f9ed77d3d --- /dev/null +++ b/ext/dom/tests/modern/spec/CharacterData_substringData_edge_cases.phpt @@ -0,0 +1,34 @@ +--TEST-- +substringData() edge cases +--EXTENSIONS-- +dom +--SKIPIF-- + +--FILE-- +createComment("foobarbaz"); +try { + var_dump($comment->substringData(0, 2**31+1)); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +try { + var_dump($comment->substringData(2**31+1, 0)); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +try { + var_dump($comment->substringData(100, 0)); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; + +?> +--EXPECT-- +Index Size Error +Index Size Error +Index Size Error + diff --git a/ext/dom/tests/modern/spec/CharacterData_substringData_negative_arguments.phpt b/ext/dom/tests/modern/spec/CharacterData_substringData_negative_arguments.phpt new file mode 100644 index 0000000000000..756e3cf07678c --- /dev/null +++ b/ext/dom/tests/modern/spec/CharacterData_substringData_negative_arguments.phpt @@ -0,0 +1,57 @@ +--TEST-- +substringData() negative arguments +--EXTENSIONS-- +dom +--FILE-- +createComment("foobarbaz"); +var_dump($comment->substringData(0, -1)); +echo $dom->saveHTML($comment), "\n"; +var_dump($comment->substringData(2, -(2**32 - 2))); +echo $dom->saveHTML($comment), "\n"; +var_dump($comment->substringData(-(2**32 - 2), 2)); +echo $dom->saveHTML($comment), "\n"; + +echo "--- Legacy behaviour ---\n"; + +$dom = new DOMDocument; +$comment = $dom->createComment("foobarbaz"); +try { + var_dump($comment->substringData(0, -1)); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; +try { + var_dump($comment->substringData(2, -(2**32 - 2))); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; +try { + var_dump($comment->substringData(-(2**32 - 2), 2)); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveHTML($comment), "\n"; + +?> +--EXPECT-- +--- Modern behaviour --- +string(9) "foobarbaz" + +string(2) "ob" + +string(2) "ob" + +--- Legacy behaviour --- +Index Size Error + +Index Size Error + +Index Size Error + diff --git a/ext/dom/tests/modern/spec/Document_createAttribute.phpt b/ext/dom/tests/modern/spec/Document_createAttribute.phpt new file mode 100644 index 0000000000000..7a9ead916c8bb --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_createAttribute.phpt @@ -0,0 +1,43 @@ +--TEST-- +Document::createAttribute() +--EXTENSIONS-- +dom +--FILE-- +createAttribute('foo'); +dumpAttr($attr); +$attr = $dom->createAttribute('FoOo'); +dumpAttr($attr); + +echo "--- XML document ---\n"; + +$dom = DOM\XMLDocument::createEmpty(); +$attr = $dom->createAttribute('foo'); +dumpAttr($attr); +$attr = $dom->createAttribute('FoOo'); +dumpAttr($attr); +?> +--EXPECT-- +--- HTML document --- +Attr: foo +NULL +string(3) "foo" +NULL +Attr: fooo +NULL +string(4) "fooo" +NULL +--- XML document --- +Attr: foo +NULL +string(3) "foo" +NULL +Attr: FoOo +NULL +string(4) "FoOo" +NULL diff --git a/ext/dom/tests/modern/spec/Document_createAttributeNS.phpt b/ext/dom/tests/modern/spec/Document_createAttributeNS.phpt new file mode 100644 index 0000000000000..e0cb36c9ffd6e --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_createAttributeNS.phpt @@ -0,0 +1,127 @@ +--TEST-- +Document::createAttributeNS() +--EXTENSIONS-- +dom +--FILE-- +createAttributeNS($ns, $qname); + } catch (DOMException $e) { + $ns_readable = is_null($ns) ? 'null' : "\"$ns\""; + echo "($ns_readable, \"$qname\"): {$e->getMessage()}\n"; + } +} + +$dom = DOM\HTMLDocument::createEmpty(); +$attrs = []; +$attrs[] = $dom->createAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:foo'); +$attrs[] = $dom->createAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:bar'); +$attrs[] = $dom->createAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns'); +$attrs[] = $dom->createAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:foo'); +$attrs[] = $dom->createAttributeNS('urn:a', 'foo:bar'); +$attrs[] = $dom->createAttributeNS('urn:a', 'bar:bar'); +$attrs[] = $dom->createAttributeNS('http://www.w3.org/2000/xmlns', 'foo:bar'); + +echo "--- Error cases ---\n"; + +testErrorCase($dom, '', 'bar:bar'); +testErrorCase($dom, null, 'bar:bar'); +testErrorCase($dom, 'urn:a', '@'); +testErrorCase($dom, 'urn:a', 'foo:bar:baz'); +testErrorCase($dom, 'http://www.w3.org/2000/xmlns', 'xmlns'); +testErrorCase($dom, 'http://www.w3.org/2000/xmlns', 'xmlns:bar'); +testErrorCase($dom, 'http://www.w3.org/2000/xmlns', 'xml:foo'); + +echo "\n--- Normal cases ---\n"; + +// Test after creating to make sure they cannot corrupt each other +foreach ($attrs as $attr) { + dumpAttr($attr); +} + +// Test in document +$root = $dom->appendChild($dom->createElement('foo')); +foreach ($attrs as $attr) { + $root->setAttributeNodeNS($attr); +} + +echo $dom->saveHTML(), "\n"; + +echo "\n--- NULL prefix cases ---\n"; + +// Test multiple "null prefixes" after having a root +$attrs = []; +$attrs[] = $dom->createAttributeNS(null, 'baz1'); +$attrs[] = $dom->createAttributeNS(null, 'baz2'); +$attrs[] = $dom->createAttributeNS('', 'baz1'); +$attrs[] = $dom->createAttributeNS('', 'baz2'); +foreach ($attrs as $attr) { + dumpAttr($attr); + $root->setAttributeNodeNS($attr); +} + +echo $dom->saveHTML(), "\n"; +?> +--EXPECT-- +--- Error cases --- +("", "bar:bar"): Namespace Error +(null, "bar:bar"): Namespace Error +("urn:a", "@"): Invalid Character Error +("urn:a", "foo:bar:baz"): Invalid Character Error +("http://www.w3.org/2000/xmlns", "xmlns"): Namespace Error +("http://www.w3.org/2000/xmlns", "xmlns:bar"): Namespace Error +("http://www.w3.org/2000/xmlns", "xml:foo"): Namespace Error + +--- Normal cases --- +Attr: xmlns:foo +string(5) "xmlns" +string(9) "xmlns:foo" +string(29) "http://www.w3.org/2000/xmlns/" +Attr: xmlns:bar +string(5) "xmlns" +string(9) "xmlns:bar" +string(29) "http://www.w3.org/2000/xmlns/" +Attr: xmlns +NULL +string(5) "xmlns" +string(29) "http://www.w3.org/2000/xmlns/" +Attr: xml:foo +string(3) "xml" +string(7) "xml:foo" +string(36) "http://www.w3.org/XML/1998/namespace" +Attr: foo:bar +string(3) "foo" +string(7) "foo:bar" +string(5) "urn:a" +Attr: bar:bar +string(3) "bar" +string(7) "bar:bar" +string(5) "urn:a" +Attr: foo:bar +string(3) "foo" +string(7) "foo:bar" +string(28) "http://www.w3.org/2000/xmlns" + + +--- NULL prefix cases --- +Attr: baz1 +NULL +string(4) "baz1" +NULL +Attr: baz2 +NULL +string(4) "baz2" +NULL +Attr: baz1 +NULL +string(4) "baz1" +NULL +Attr: baz2 +NULL +string(4) "baz2" +NULL + diff --git a/ext/dom/tests/modern/spec/Document_createElement.phpt b/ext/dom/tests/modern/spec/Document_createElement.phpt new file mode 100644 index 0000000000000..72a330fa8e2f5 --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_createElement.phpt @@ -0,0 +1,68 @@ +--TEST-- +Document::createElement() +--EXTENSIONS-- +dom +--FILE-- +createElement("HTML"); +$element->textContent = "&hello"; +dumpElement($element); + +$element = $dom->createElement("HEad"); +dumpElement($element); + +echo "--- Into document with HTML root ---\n"; + +$dom = DOM\HTMLDocument::createEmpty(); +$element = $dom->createElement("HTML"); +$element->textContent = "&hello"; +$dom->appendChild($element); +$element = $dom->createElement("HEad"); +dumpElement($element); + +echo "--- Into document with non-HTML root ---\n"; + +$dom = DOM\HTMLDocument::createEmpty(); +$element = $dom->createElementNS("urn:a", "container"); +$dom->appendChild($element); +$element = $dom->createElement("HEad"); +dumpElement($element); + +?> +--EXPECT-- +--- Into rootless document --- +tagName: string(4) "HTML" +nodeName: string(4) "HTML" +textContent: string(6) "&hello" +prefix: NULL +namespaceURI: string(28) "http://www.w3.org/1999/xhtml" +&hello + +tagName: string(4) "HEAD" +nodeName: string(4) "HEAD" +textContent: string(0) "" +prefix: NULL +namespaceURI: string(28) "http://www.w3.org/1999/xhtml" + + +--- Into document with HTML root --- +tagName: string(4) "HEAD" +nodeName: string(4) "HEAD" +textContent: string(0) "" +prefix: NULL +namespaceURI: string(28) "http://www.w3.org/1999/xhtml" + + +--- Into document with non-HTML root --- +tagName: string(4) "HEAD" +nodeName: string(4) "HEAD" +textContent: string(0) "" +prefix: NULL +namespaceURI: string(28) "http://www.w3.org/1999/xhtml" + diff --git a/ext/dom/tests/modern/spec/Document_createElementNS.phpt b/ext/dom/tests/modern/spec/Document_createElementNS.phpt new file mode 100644 index 0000000000000..3eeb1bd2cbb8b --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_createElementNS.phpt @@ -0,0 +1,96 @@ +--TEST-- +Document::createElementNS() +--EXTENSIONS-- +dom +--FILE-- +createElementNS($namespaceURI, $qualifiedName); + } catch (DOMException $e) { + echo $e->getMessage(), "\n"; + } +} + +testError($dom, NULL, "prefix:name"); +testError($dom, "", "prefix:name"); +testError($dom, "urn:foo", "@"); +testError($dom, "http://www.w3.org/2000/xmlns/", "svg"); +testError($dom, "urn:foo", "xml:xml"); + +?> +--EXPECT-- +--- Normal cases --- +tagName: string(5) "qname" +nodeName: string(5) "qname" +textContent: string(6) "&hello" +prefix: NULL +namespaceURI: NULL +&hello + +tagName: string(7) "foo:bar" +nodeName: string(7) "foo:bar" +textContent: string(6) "&hello" +prefix: string(3) "foo" +namespaceURI: string(7) "urn:foo" +&hello + +tagName: string(7) "bar:BAR" +nodeName: string(7) "bar:BAR" +textContent: string(6) "&hello" +prefix: string(3) "bar" +namespaceURI: string(7) "urn:foo" +&hello + +tagName: string(7) "bar:BAR" +nodeName: string(7) "bar:BAR" +textContent: string(6) "&hello" +prefix: string(3) "bar" +namespaceURI: string(7) "urn:foo" +&hello + +tagName: string(5) "xmlns" +nodeName: string(5) "xmlns" +textContent: string(6) "&hello" +prefix: NULL +namespaceURI: string(29) "http://www.w3.org/2000/xmlns/" +&hello + +tagName: string(3) "xml" +nodeName: string(3) "xml" +textContent: string(6) "&hello" +prefix: NULL +namespaceURI: string(36) "http://www.w3.org/XML/1998/namespace" +&hello + +--- Error cases --- +(NULL, "prefix:name"): Namespace Error +("", "prefix:name"): Namespace Error +("urn:foo", "@"): Invalid Character Error +("http://www.w3.org/2000/xmlns/", "svg"): Namespace Error +("urn:foo", "xml:xml"): Namespace Error diff --git a/ext/dom/tests/modern/spec/Document_createElement_edge_cases.phpt b/ext/dom/tests/modern/spec/Document_createElement_edge_cases.phpt new file mode 100644 index 0000000000000..d2113b2d1ef13 --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_createElement_edge_cases.phpt @@ -0,0 +1,23 @@ +--TEST-- +Document::createElement() edge cases +--EXTENSIONS-- +dom +--FILE-- +createElement(''); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +try { + $dom->createElement('$'); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Invalid Character Error +Invalid Character Error diff --git a/ext/dom/tests/modern/spec/Document_getElementsByTagNameNS.phpt b/ext/dom/tests/modern/spec/Document_getElementsByTagNameNS.phpt new file mode 100644 index 0000000000000..0f18891e62c99 --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_getElementsByTagNameNS.phpt @@ -0,0 +1,90 @@ +--TEST-- +Document::getElementsByTagNameNS +--EXTENSIONS-- +dom +--FILE-- + + + + Test + + +

Hello World

+ + +HTML); + +$body = $dom->getElementsByTagName('body')->item(0); +$body->appendChild(createElementNS($dom, NULL, "p", "content 1")); +$body->appendChild(createElementNS($dom, "", "p", "content 2")); +$body->appendChild(createElementNS($dom, "http://www.w3.org/2000/svg", "svg:svg", "content 3")); + +function dump($dom, $uri, $local) { + $list = $dom->getElementsByTagNameNS($uri, $local); + + $uri_readable = is_null($uri) ? "NULL" : "\"$uri\""; + echo "--- ($uri_readable, \"$local\"): {$list->length} ---\n"; + + var_dump($list->length); + foreach ($list as $item) { + var_dump($item->textContent); + } +} + +dump($dom, 'http://www.w3.org/1999/xhtml', 'p'); +dump($dom, '*', 'p'); +dump($dom, '*', '*'); +dump($dom, '', '*'); +dump($dom, NULL, '*'); +dump($dom, '*', 'svg'); +dump($dom, '*', 'svg:svg'); + +?> +--EXPECT-- +--- ("http://www.w3.org/1999/xhtml", "p"): 1 --- +int(1) +string(11) "Hello World" +--- ("*", "p"): 3 --- +int(3) +string(11) "Hello World" +string(9) "content 1" +string(9) "content 2" +--- ("*", "*"): 8 --- +int(8) +string(56) " + Test + + + Hello World + +content 1content 2content 3" +string(10) " + Test +" +string(4) "Test" +string(45) " + Hello World + +content 1content 2content 3" +string(11) "Hello World" +string(9) "content 1" +string(9) "content 2" +string(9) "content 3" +--- ("", "*"): 2 --- +int(2) +string(9) "content 1" +string(9) "content 2" +--- (NULL, "*"): 2 --- +int(2) +string(9) "content 1" +string(9) "content 2" +--- ("*", "svg"): 1 --- +int(1) +string(9) "content 3" +--- ("*", "svg:svg"): 0 --- +int(0) diff --git a/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt b/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt new file mode 100644 index 0000000000000..a7025655ec406 --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt @@ -0,0 +1,115 @@ +--TEST-- +Document::$implementation createDocument +--EXTENSIONS-- +dom +--FILE-- +implementation->createDocument(null, "")); + +echo "--- (null, \"qname\") ---\n"; + +echo $dom->implementation->createDocument(null, "qname")->saveXML(), "\n"; + +echo "--- (\"\", \"qname\") ---\n"; + +echo $dom->implementation->createDocument("", "qname")->saveXML(), "\n"; + +echo "--- (\"urn:a\", \"qname\") ---\n"; + +echo $dom->implementation->createDocument("urn:a", "qname")->saveXML(), "\n"; + +echo "--- With doctype ---\n"; + +$dtd = $dom->implementation->createDocumentType("mydoc", "", ""); +echo $dom->implementation->createDocument(null, "", $dtd)->saveXML(), "\n"; + +echo "--- With auto-adopting doctype ---\n"; + +$dom2 = DOM\XMLDocument::createEmpty(); +$dtd = $dom2->implementation->createDocumentType("mydoc", "", ""); +echo $dom->implementation->createDocument(null, "", $dtd)->saveXML(), "\n"; + +?> +--EXPECT-- +--- (null, "") --- +object(DOM\XMLDocument)#3 (29) { + ["xmlEncoding"]=> + string(5) "UTF-8" + ["xmlStandalone"]=> + bool(false) + ["xmlVersion"]=> + string(3) "1.0" + ["formatOutput"]=> + bool(false) + ["implementation"]=> + string(22) "(object value omitted)" + ["URL"]=> + string(11) "about:blank" + ["documentURI"]=> + string(11) "about:blank" + ["characterSet"]=> + string(5) "UTF-8" + ["charset"]=> + string(5) "UTF-8" + ["inputEncoding"]=> + string(5) "UTF-8" + ["doctype"]=> + NULL + ["documentElement"]=> + NULL + ["firstElementChild"]=> + NULL + ["lastElementChild"]=> + NULL + ["childElementCount"]=> + int(0) + ["nodeType"]=> + int(9) + ["nodeName"]=> + string(9) "#document" + ["baseURI"]=> + string(11) "about:blank" + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> + NULL + ["parentNode"]=> + NULL + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["nodeValue"]=> + NULL + ["textContent"]=> + NULL +} +--- (null, "qname") --- + + +--- ("", "qname") --- + + +--- ("urn:a", "qname") --- + + +--- With doctype --- + + + +--- With auto-adopting doctype --- + + diff --git a/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt b/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt new file mode 100644 index 0000000000000..d396477c69418 --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt @@ -0,0 +1,230 @@ +--TEST-- +Document::$implementation createDocumentType +--EXTENSIONS-- +dom +--FILE-- + 'qname', 'public' => 'public', 'system' => 'system'], + ['uri' => 'qname', 'public' => 'public', 'system' => ''], + ['uri' => 'qname', 'public' => '', 'system' => 'system'], + ['uri' => 'qname', 'public' => '', 'system' => ''], +]; + +foreach ($test_matrix as $test_item) { + $dom = DOM\XMLDocument::createEmpty(); + $dtd = $dom->implementation->createDocumentType($test_item['uri'], $test_item['public'], $test_item['system']); + + var_dump($dtd); + + $dom->append($dom->importNode($dtd)); + + echo $dom->saveXML(), "\n"; +} + +?> +--EXPECT-- +object(DOM\DocumentType)#3 (23) { + ["name"]=> + string(5) "qname" + ["entities"]=> + string(22) "(object value omitted)" + ["notations"]=> + string(22) "(object value omitted)" + ["publicId"]=> + string(6) "public" + ["systemId"]=> + string(6) "system" + ["internalSubset"]=> + NULL + ["nodeName"]=> + string(5) "qname" + ["nodeValue"]=> + NULL + ["nodeType"]=> + int(10) + ["parentNode"]=> + NULL + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["attributes"]=> + NULL + ["isConnected"]=> + bool(false) + ["namespaceURI"]=> + NULL + ["prefix"]=> + string(0) "" + ["localName"]=> + NULL + ["baseURI"]=> + NULL + ["textContent"]=> + string(0) "" +} + + + +object(DOM\DocumentType)#2 (23) { + ["name"]=> + string(5) "qname" + ["entities"]=> + string(22) "(object value omitted)" + ["notations"]=> + string(22) "(object value omitted)" + ["publicId"]=> + string(6) "public" + ["systemId"]=> + string(0) "" + ["internalSubset"]=> + NULL + ["nodeName"]=> + string(5) "qname" + ["nodeValue"]=> + NULL + ["nodeType"]=> + int(10) + ["parentNode"]=> + NULL + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["attributes"]=> + NULL + ["isConnected"]=> + bool(false) + ["namespaceURI"]=> + NULL + ["prefix"]=> + string(0) "" + ["localName"]=> + NULL + ["baseURI"]=> + NULL + ["textContent"]=> + string(0) "" +} + + + +object(DOM\DocumentType)#1 (23) { + ["name"]=> + string(5) "qname" + ["entities"]=> + string(22) "(object value omitted)" + ["notations"]=> + string(22) "(object value omitted)" + ["publicId"]=> + string(0) "" + ["systemId"]=> + string(6) "system" + ["internalSubset"]=> + NULL + ["nodeName"]=> + string(5) "qname" + ["nodeValue"]=> + NULL + ["nodeType"]=> + int(10) + ["parentNode"]=> + NULL + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["attributes"]=> + NULL + ["isConnected"]=> + bool(false) + ["namespaceURI"]=> + NULL + ["prefix"]=> + string(0) "" + ["localName"]=> + NULL + ["baseURI"]=> + NULL + ["textContent"]=> + string(0) "" +} + + + +object(DOM\DocumentType)#4 (23) { + ["name"]=> + string(5) "qname" + ["entities"]=> + string(22) "(object value omitted)" + ["notations"]=> + string(22) "(object value omitted)" + ["publicId"]=> + string(0) "" + ["systemId"]=> + string(0) "" + ["internalSubset"]=> + NULL + ["nodeName"]=> + string(5) "qname" + ["nodeValue"]=> + NULL + ["nodeType"]=> + int(10) + ["parentNode"]=> + NULL + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["attributes"]=> + NULL + ["isConnected"]=> + bool(false) + ["namespaceURI"]=> + NULL + ["prefix"]=> + string(0) "" + ["localName"]=> + NULL + ["baseURI"]=> + NULL + ["textContent"]=> + string(0) "" +} + + diff --git a/ext/dom/tests/modern/spec/Document_implementation_createDocumentType_errors.phpt b/ext/dom/tests/modern/spec/Document_implementation_createDocumentType_errors.phpt new file mode 100644 index 0000000000000..622a690abc0ea --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_implementation_createDocumentType_errors.phpt @@ -0,0 +1,29 @@ +--TEST-- +Document::$implementation createDocumentType errors +--EXTENSIONS-- +dom +--FILE-- +implementation->createDocumentType("invalid name", "public", "system"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +try { + $dom->implementation->createDocumentType("", "public", "system"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +try { + $dom->implementation->createDocumentType("@", "", ""); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Namespace Error +Namespace Error +Namespace Error diff --git a/ext/dom/tests/modern/spec/Document_implementation_createDocument_errors.phpt b/ext/dom/tests/modern/spec/Document_implementation_createDocument_errors.phpt new file mode 100644 index 0000000000000..c66808e3895ef --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_implementation_createDocument_errors.phpt @@ -0,0 +1,25 @@ +--TEST-- +Document::$implementation createDocument errors +--EXTENSIONS-- +dom +--FILE-- +implementation->createDocument("urn:a", "@"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +try { + $dom->implementation->createDocument("", "foo:bar"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Invalid Character Error +Namespace Error diff --git a/ext/dom/tests/modern/spec/Document_implementation_createHTMLDocument.phpt b/ext/dom/tests/modern/spec/Document_implementation_createHTMLDocument.phpt new file mode 100644 index 0000000000000..fe3568bb0c262 --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_implementation_createHTMLDocument.phpt @@ -0,0 +1,35 @@ +--TEST-- +Document::$implementation createHTMLDocument +--EXTENSIONS-- +dom +--FILE-- +implementation->createHTMLDocument()->saveXML(), "\n"; + +echo "---\n"; + +echo $dom->implementation->createHTMLDocument("")->saveXML(), "\n"; + +echo "---\n"; + +echo $dom->implementation->createHTMLDocument("my ")->saveXML(), "\n"; + +?> +--EXPECT-- +--- +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body></body></html> +--- +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"><head><title> +--- + + +my <title> diff --git a/ext/dom/tests/modern/spec/Document_implementation_same_object.phpt b/ext/dom/tests/modern/spec/Document_implementation_same_object.phpt new file mode 100644 index 0000000000000..01e9dd2af6b9e --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_implementation_same_object.phpt @@ -0,0 +1,31 @@ +--TEST-- +Document::$implementation should return the same object +--EXTENSIONS-- +dom +--FILE-- +implementation === $dom->implementation); +$implementation = $dom->implementation; +var_dump($dom->implementation === $implementation); + +$dom2 = DOM\XMLDocument::createEmpty(); +var_dump($dom2->implementation === $dom->implementation); + +var_dump($implementation); + +try { + clone $implementation; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +bool(true) +bool(true) +bool(false) +object(DOM\Implementation)#2 (0) { +} +Trying to clone an uncloneable object of class DOM\Implementation diff --git a/ext/dom/tests/modern/spec/Document_importLegacyNode.phpt b/ext/dom/tests/modern/spec/Document_importLegacyNode.phpt new file mode 100644 index 0000000000000..55e53c3ccc169 --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_importLegacyNode.phpt @@ -0,0 +1,70 @@ +--TEST-- +Document::importLegacyNode +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + + + + + +
+XML); + +$new = DOM\XMLDocument::createEmpty(); +$new->append($new->importLegacyNode($old->documentElement, true)); + +unset($old); + +foreach ($new->getElementsByTagName('child') as $child) { + var_dump($child->attributes); + foreach ($child->attributes as $attr) { + echo "name: "; + var_dump($attr->name); + echo "prefix: "; + var_dump($attr->prefix); + echo "namespaceURI: "; + var_dump($attr->namespaceURI); + } +} + +echo $new->saveXML(), "\n"; + +?> +--EXPECT-- +object(DOM\NamedNodeMap)#5 (1) { + ["length"]=> + int(2) +} +name: string(5) "xmlns" +prefix: NULL +namespaceURI: string(29) "http://www.w3.org/2000/xmlns/" +name: string(1) "a" +prefix: NULL +namespaceURI: NULL +object(DOM\NamedNodeMap)#3 (1) { + ["length"]=> + int(3) +} +name: string(5) "xmlns" +prefix: NULL +namespaceURI: string(29) "http://www.w3.org/2000/xmlns/" +name: string(7) "xmlns:c" +prefix: string(5) "xmlns" +namespaceURI: string(29) "http://www.w3.org/2000/xmlns/" +name: string(3) "c:c" +prefix: string(1) "c" +namespaceURI: string(5) "urn:c" + + + + + + + + diff --git a/ext/dom/tests/modern/spec/Document_importNode_not_supported.phpt b/ext/dom/tests/modern/spec/Document_importNode_not_supported.phpt new file mode 100644 index 0000000000000..779a41efb5578 --- /dev/null +++ b/ext/dom/tests/modern/spec/Document_importNode_not_supported.phpt @@ -0,0 +1,24 @@ +--TEST-- +Document::importNode() with unsupported node type +--EXTENSIONS-- +dom +--FILE-- +importNode($dom)); + +// For modern documents this should throw +$dom = DOM\HTMLDocument::createEmpty(); +try { + $dom->importNode($dom); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +Warning: DOMDocument::importNode(): Cannot import: Node Type Not Supported in %s on line %d +bool(false) +Not Supported Error diff --git a/ext/dom/tests/modern/spec/Element_getAttribute.phpt b/ext/dom/tests/modern/spec/Element_getAttribute.phpt new file mode 100644 index 0000000000000..73dc64f503d47 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_getAttribute.phpt @@ -0,0 +1,51 @@ +--TEST-- +Element::getAttribute() +--EXTENSIONS-- +dom +--FILE-- +'); +$body = $dom->getElementsByTagName("body")[0]; +var_dump($body->getAttribute("align")); +var_dump($body->getAttribute("foo:bar")); + +echo "--- Get after creating without namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttribute("prefix:local")); +$attr->value = "A"; +var_dump($body->getAttribute("prefix:local")); + +echo "--- Get after creating with namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttributeNS("urn:a", "prefix:local2")); +$attr->value = "B"; +var_dump($body->getAttribute("prefix:local2")); +var_dump($body->getAttribute("Prefix:LOCAL2")); + +echo "--- Get after creating with namespace case sensitive ---\n"; + +$element = $dom->createElementNS("urn:a", "a:element"); +$attr = $dom->createAttributeNS("urn:a", "Prefix:local2"); +$element->setAttributeNode($attr); +$attr->value = "C"; +var_dump($element->getAttribute("Prefix:local2")); +var_dump($element->getAttribute("Prefix:LOCAL2")); +var_dump($element->getAttribute("prefix:local2")); + +?> +--EXPECT-- +--- Get after parsing --- +string(3) "foo" +string(3) "baz" +--- Get after creating without namespace --- +string(1) "A" +--- Get after creating with namespace --- +string(1) "B" +string(1) "B" +--- Get after creating with namespace case sensitive --- +string(1) "C" +NULL +NULL diff --git a/ext/dom/tests/modern/spec/Element_getAttributeNS.phpt b/ext/dom/tests/modern/spec/Element_getAttributeNS.phpt new file mode 100644 index 0000000000000..cfaae5bb89791 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_getAttributeNS.phpt @@ -0,0 +1,59 @@ +--TEST-- +Element::getAttributeNS() +--EXTENSIONS-- +dom +--FILE-- +'); +$body = $dom->getElementsByTagName("body")[0]; + +echo "--- After parsing, i.e. without namespace ---\n"; + +// Every pair of 2 calls should return the same result +var_dump($body->getAttributeNS(NULL, "align")); +var_dump($body->getAttributeNS("", "align")); +var_dump($body->getAttributeNS(NULL, "ALIGN")); +var_dump($body->getAttributeNS("", "ALIGN")); +var_dump($body->getAttributeNS(NULL, "foo:bar")); +var_dump($body->getAttributeNS("", "foo:bar")); +var_dump($body->getAttributeNS(NULL, "FOO:BAR")); +var_dump($body->getAttributeNS("", "FOO:BAR")); + +echo "--- Special legacy case ---\n"; + +var_dump($dom->documentElement->getAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns")); + +echo "--- Get after creating without namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttribute("prefix:local")); +$attr->value = "A"; +var_dump($body->getAttributeNS(NULL, "prefix:local")); +var_dump($body->getAttributeNS(NULL, "prefix:LOCAL")); + +echo "--- Get after creating with namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttributeNS("urn:a", "prefix:local2")); +$attr->value = "B"; +var_dump($body->getAttributeNS("urn:a", "local2")); +var_dump($body->getAttributeNS("urn:a", "LOCAL2")); + +?> +--EXPECT-- +--- After parsing, i.e. without namespace --- +string(3) "foo" +string(3) "foo" +NULL +NULL +string(3) "baz" +string(3) "baz" +NULL +NULL +--- Special legacy case --- +NULL +--- Get after creating without namespace --- +string(1) "A" +NULL +--- Get after creating with namespace --- +string(1) "B" +NULL diff --git a/ext/dom/tests/modern/spec/Element_getAttributeNode.phpt b/ext/dom/tests/modern/spec/Element_getAttributeNode.phpt new file mode 100644 index 0000000000000..4bf186ac2f415 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_getAttributeNode.phpt @@ -0,0 +1,51 @@ +--TEST-- +Element::getAttributeNode() +--EXTENSIONS-- +dom +--FILE-- +'); +$body = $dom->getElementsByTagName("body")[0]; +var_dump($body->getAttributeNode("align")->textContent); +var_dump($body->getAttributeNode("foo:bar")->textContent); + +echo "--- Get after creating without namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttribute("prefix:local")); +$attr->value = "A"; +var_dump($body->getAttributeNode("prefix:local")->textContent); + +echo "--- Get after creating with namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttributeNS("urn:a", "prefix:local2")); +$attr->value = "B"; +var_dump($body->getAttributeNode("prefix:local2")->textContent); +var_dump($body->getAttributeNode("Prefix:LOCAL2")->textContent); + +echo "--- Get after creating with namespace case sensitive ---\n"; + +$element = $dom->createElementNS("urn:a", "a:element"); +$attr = $dom->createAttributeNS("urn:a", "Prefix:local2"); +$element->setAttributeNode($attr); +$attr->value = "C"; +var_dump($element->getAttributeNode("Prefix:local2")->textContent); +var_dump($element->getAttributeNode("Prefix:LOCAL2")); +var_dump($element->getAttributeNode("prefix:local2")); + +?> +--EXPECT-- +--- Get after parsing --- +string(3) "foo" +string(3) "baz" +--- Get after creating without namespace --- +string(1) "A" +--- Get after creating with namespace --- +string(1) "B" +string(1) "B" +--- Get after creating with namespace case sensitive --- +string(1) "C" +NULL +NULL diff --git a/ext/dom/tests/modern/spec/Element_getAttributeNodeNS.phpt b/ext/dom/tests/modern/spec/Element_getAttributeNodeNS.phpt new file mode 100644 index 0000000000000..4cbc2d2558d9e --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_getAttributeNodeNS.phpt @@ -0,0 +1,62 @@ +--TEST-- +Element::getAttributeNodeNS() +--EXTENSIONS-- +dom +--FILE-- +'); +$body = $dom->getElementsByTagName("body")[0]; + +echo "--- After parsing, i.e. without namespace ---\n"; + +// Every pair of 2 calls should return the same result +var_dump($body->getAttributeNodeNS(NULL, "align")->textContent); +var_dump($body->getAttributeNodeNS("", "align")->textContent); +var_dump($body->getAttributeNodeNS(NULL, "ALIGN")); +var_dump($body->getAttributeNodeNS("", "ALIGN")); +var_dump($body->getAttributeNodeNS(NULL, "foo:bar")->textContent); +var_dump($body->getAttributeNodeNS("", "foo:bar")->textContent); +var_dump($body->getAttributeNodeNS(NULL, "FOO:BAR")); +var_dump($body->getAttributeNodeNS("", "FOO:BAR")); + +echo "--- Special legacy case ---\n"; + +var_dump($dom->documentElement->getAttributeNodeNS("http://www.w3.org/2000/xmlns/", "xmlns")); + +echo "--- Get after creating without namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttribute("prefix:local")); +$attr->value = "A"; +var_dump($body->getAttributeNodeNS(NULL, "prefix:local")->textContent); +var_dump($body->getAttributeNodeNS(NULL, "prefix:LOCAL")); + +echo "--- Get after creating with namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttributeNS("urn:a", "prefix:local2")); +$attr->value = "B"; +var_dump($body->getAttributeNodeNS("urn:a", "local2")->textContent); +var_dump($body->getAttributeNodeNS("urn:a", "LOCAL2")); + +?> +--EXPECT-- +--- Get after parsing --- +--- After parsing, i.e. without namespace --- +string(3) "foo" +string(3) "foo" +NULL +NULL +string(3) "baz" +string(3) "baz" +NULL +NULL +--- Special legacy case --- +NULL +--- Get after creating without namespace --- +string(1) "A" +NULL +--- Get after creating with namespace --- +string(1) "B" +NULL diff --git a/ext/dom/tests/modern/spec/Element_hasAttribute.phpt b/ext/dom/tests/modern/spec/Element_hasAttribute.phpt new file mode 100644 index 0000000000000..e72fd36672733 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_hasAttribute.phpt @@ -0,0 +1,51 @@ +--TEST-- +Element::hasAttribute() +--EXTENSIONS-- +dom +--FILE-- +'); +$body = $dom->getElementsByTagName("body")[0]; +var_dump($body->hasAttribute("align")); +var_dump($body->hasAttribute("foo:bar")); + +echo "--- Get after creating without namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttribute("prefix:local")); +$attr->value = "A"; +var_dump($body->hasAttribute("prefix:local")); + +echo "--- Get after creating with namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttributeNS("urn:a", "prefix:local2")); +$attr->value = "B"; +var_dump($body->hasAttribute("prefix:local2")); +var_dump($body->hasAttribute("Prefix:LOCAL2")); + +echo "--- Get after creating with namespace case sensitive ---\n"; + +$element = $dom->createElementNS("urn:a", "a:element"); +$attr = $dom->createAttributeNS("urn:a", "Prefix:local2"); +$element->setAttributeNode($attr); +$attr->value = "C"; +var_dump($element->hasAttribute("Prefix:local2")); +var_dump($element->hasAttribute("Prefix:LOCAL2")); +var_dump($element->hasAttribute("prefix:local2")); + +?> +--EXPECT-- +--- Get after parsing --- +bool(true) +bool(true) +--- Get after creating without namespace --- +bool(true) +--- Get after creating with namespace --- +bool(true) +bool(true) +--- Get after creating with namespace case sensitive --- +bool(true) +bool(false) +bool(false) diff --git a/ext/dom/tests/modern/spec/Element_hasAttributeNS.phpt b/ext/dom/tests/modern/spec/Element_hasAttributeNS.phpt new file mode 100644 index 0000000000000..6b8820f9452b0 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_hasAttributeNS.phpt @@ -0,0 +1,59 @@ +--TEST-- +Element::hasAttributeNS() +--EXTENSIONS-- +dom +--FILE-- +'); +$body = $dom->getElementsByTagName("body")[0]; + +echo "--- After parsing, i.e. without namespace ---\n"; + +// Every pair of 2 calls should return the same result +var_dump($body->hasAttributeNS(NULL, "align")); +var_dump($body->hasAttributeNS("", "align")); +var_dump($body->hasAttributeNS(NULL, "ALIGN")); +var_dump($body->hasAttributeNS("", "ALIGN")); +var_dump($body->hasAttributeNS(NULL, "foo:bar")); +var_dump($body->hasAttributeNS("", "foo:bar")); +var_dump($body->hasAttributeNS(NULL, "FOO:BAR")); +var_dump($body->hasAttributeNS("", "FOO:BAR")); + +echo "--- Special legacy case ---\n"; + +var_dump($dom->documentElement->hasAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns")); + +echo "--- Get after creating without namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttribute("prefix:local")); +$attr->value = "A"; +var_dump($body->hasAttributeNS(NULL, "prefix:local")); +var_dump($body->hasAttributeNS(NULL, "prefix:LOCAL")); + +echo "--- Get after creating with namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttributeNS("urn:a", "prefix:local2")); +$attr->value = "B"; +var_dump($body->hasAttributeNS("urn:a", "local2")); +var_dump($body->hasAttributeNS("urn:a", "LOCAL2")); + +?> +--EXPECT-- +--- After parsing, i.e. without namespace --- +bool(true) +bool(true) +bool(false) +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) +--- Special legacy case --- +bool(false) +--- Get after creating without namespace --- +bool(true) +bool(false) +--- Get after creating with namespace --- +bool(true) +bool(false) diff --git a/ext/dom/tests/modern/spec/Element_insertAdjacentText.phpt b/ext/dom/tests/modern/spec/Element_insertAdjacentText.phpt new file mode 100644 index 0000000000000..94bcaa13b8962 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_insertAdjacentText.phpt @@ -0,0 +1,29 @@ +--TEST-- +Element::insertAdjacentText() edge cases +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement("foo")); +try { + $foo->insertAdjacentText("beforebegin", "bar"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +$foo->insertAdjacentText("afterbegin", "bar"); +$foo->insertAdjacentText("beforeend", "baz"); + +echo $dom->saveHTML(), "\n"; + +var_dump($foo->firstChild->textContent); +var_dump($foo->firstChild->nextSibling->textContent); + +?> +--EXPECT-- +Cannot insert text as a child of a document +barbaz +string(3) "bar" +string(3) "baz" diff --git a/ext/dom/tests/modern/spec/Element_prefix_readonly.phpt b/ext/dom/tests/modern/spec/Element_prefix_readonly.phpt new file mode 100644 index 0000000000000..37ceda2e4c057 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_prefix_readonly.phpt @@ -0,0 +1,18 @@ +--TEST-- +Ensure that DOM\Node::$prefix is read-only +--EXTENSIONS-- +dom +--FILE-- +createElement('div'); +try { + $div->prefix = "foo"; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +echo $dom->saveXML(); +?> +--EXPECT-- +Cannot modify readonly property DOM\Element::$prefix + diff --git a/ext/dom/tests/modern/spec/Element_removeAttribute.phpt b/ext/dom/tests/modern/spec/Element_removeAttribute.phpt new file mode 100644 index 0000000000000..20ccb6c109b90 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_removeAttribute.phpt @@ -0,0 +1,65 @@ +--TEST-- +Element::removeAttribute() +--EXTENSIONS-- +dom +--FILE-- +'); +$body = $dom->getElementsByTagName("body")[0]; +var_dump($body->removeAttribute("align")); +var_dump($body->removeAttribute("foo:bar")); +echo $dom->saveHTML($body), "\n"; + +echo "--- Remove after creating without namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttribute("prefix:local")); +$attr->value = "A"; +var_dump($body->removeAttribute("prefix:local")); +echo $dom->saveHTML($body), "\n"; + +echo "--- Remove after creating with namespace ---\n"; + +$body->setAttributeNode($attr = $dom->createAttributeNS("urn:a", "prefix:local2")); +$attr->value = "B"; +var_dump($body->removeAttribute("prefix:local2")); +echo $dom->saveHTML($body), "\n"; +$body->setAttributeNode($attr = $dom->createAttributeNS("urn:a", "prefix:local2")); +$attr->value = "B"; +var_dump($body->removeAttribute("Prefix:LOCAL2")); +echo $dom->saveHTML($body), "\n"; + +echo "--- Remove after creating with namespace case sensitive ---\n"; + +$element = $dom->createElementNS("urn:a", "a:element"); +$attr = $dom->createAttributeNS("urn:a", "Prefix:local2"); +$element->setAttributeNode($attr); +echo $dom->saveHTML($element), "\n"; +$attr->value = "C"; +var_dump($element->removeAttribute("Prefix:local2")); +var_dump($element->removeAttribute("Prefix:LOCAL2")); +var_dump($element->removeAttribute("prefix:local2")); +echo $dom->saveHTML($element), "\n"; + +?> +--EXPECT-- +--- Remove after parsing --- +NULL +NULL + +--- Remove after creating without namespace --- +NULL + +--- Remove after creating with namespace --- +NULL + +NULL + +--- Remove after creating with namespace case sensitive --- + +NULL +NULL +NULL + diff --git a/ext/dom/tests/modern/spec/Element_removeAttributeNS.phpt b/ext/dom/tests/modern/spec/Element_removeAttributeNS.phpt new file mode 100644 index 0000000000000..7f31eacda5166 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_removeAttributeNS.phpt @@ -0,0 +1,94 @@ +--TEST-- +Element::removeAttributeNS() +--EXTENSIONS-- +dom +--FILE-- +'); + $body = $dom->getElementsByTagName("body")[0]; + return $body; +} + +echo "--- After parsing, i.e. without namespace ---\n"; + +$body = createFreshBody(); +$body->removeAttributeNS(NULL, "align"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +$body = createFreshBody(); +$body->removeAttributeNS("", "align"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +$body = createFreshBody(); +$body->removeAttributeNS(NULL, "ALIGN"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +$body = createFreshBody(); +$body->removeAttributeNS("", "ALIGN"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +$body = createFreshBody(); +$body->removeAttributeNS(NULL, "foo:bar"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +$body = createFreshBody(); +$body->removeAttributeNS("", "foo:bar"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +$body = createFreshBody(); +$body->removeAttributeNS(NULL, "FOO:BAR"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +$body = createFreshBody(); +$body->removeAttributeNS("", "FOO:BAR"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +echo "--- Special legacy case ---\n"; + +$body = createFreshBody(); +$body->ownerDocument->documentElement->removeAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns"); + +echo "--- Remove after creating without namespace ---\n"; + +$body = createFreshBody(); +$body->setAttributeNode($attr = $body->ownerDocument->createAttribute("prefix:local")); +$attr->value = "A"; +echo $body->ownerDocument->saveHTML($body), "\n"; +$body->removeAttributeNS(NULL, "prefix:LOCAL"); +echo $body->ownerDocument->saveHTML($body), "\n"; +$body->removeAttributeNS(NULL, "prefix:local"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +echo "--- Remove after creating with namespace ---\n"; + +$body = createFreshBody(); +$body->setAttributeNode($attr = $body->ownerDocument->createAttributeNS("urn:a", "prefix:local2")); +$attr->value = "B"; +echo $body->ownerDocument->saveHTML($body), "\n"; +$body->removeAttributeNS("urn:a", "LOCAL2"); +echo $body->ownerDocument->saveHTML($body), "\n"; +$body->removeAttributeNS("urn:a", "local2"); +echo $body->ownerDocument->saveHTML($body), "\n"; + +?> +--EXPECT-- +--- After parsing, i.e. without namespace --- + + + + + + + + +--- Special legacy case --- +--- Remove after creating without namespace --- + + + +--- Remove after creating with namespace --- + + + diff --git a/ext/dom/tests/modern/spec/Element_removeAttribute_edge_cases.phpt b/ext/dom/tests/modern/spec/Element_removeAttribute_edge_cases.phpt new file mode 100644 index 0000000000000..fc5dbeae04058 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_removeAttribute_edge_cases.phpt @@ -0,0 +1,17 @@ +--TEST-- +Element::removeAttribute() edge cases +--EXTENSIONS-- +dom +--FILE-- +'); +try { + $dom->documentElement->removeAttributeNode($dom->createAttribute('test')); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Not Found Error diff --git a/ext/dom/tests/modern/spec/Element_setAttributeNS.phpt b/ext/dom/tests/modern/spec/Element_setAttributeNS.phpt new file mode 100644 index 0000000000000..e23492303ac34 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_setAttributeNS.phpt @@ -0,0 +1,72 @@ +--TEST-- +Element::setAttributeNS() +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement("container")); + +echo "--- xmlns attribute ---\n"; + +$container->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:foo", "1"); +echo $dom->saveHTML($container), "\n"; +dumpAttrs($container); + +echo "--- name validation ---\n"; + +try { + $container->setAttributeNS("urn:a", "a:b:c", ""); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- ns attributes with same namespace but different prefix ---\n"; + +$dom = DOM\HTMLDocument::createEmpty(); +$container = $dom->appendChild($dom->createElement("container")); + +$container->setAttributeNS("urn:a", "x:foo", "1"); +$container->setAttributeNS("urn:a", "y:foo", "2"); +echo $dom->saveHTML($container), "\n"; +dumpAttrs($container); + +echo "--- ns attributes with different namespace but same prefix ---\n"; + +$dom = DOM\HTMLDocument::createEmpty(); +$container = $dom->appendChild($dom->createElement("container")); + +$container->setAttributeNS("urn:a", "x:foo", "1"); +$container->setAttributeNS("urn:b", "x:foo", "2"); +echo $dom->saveHTML($container), "\n"; +dumpAttrs($container); + +?> +--EXPECT-- +--- xmlns attribute --- + +Attr: xmlns:foo +string(5) "xmlns" +string(9) "xmlns:foo" +string(29) "http://www.w3.org/2000/xmlns/" +--- name validation --- +Invalid Character Error +--- ns attributes with same namespace but different prefix --- + +Attr: y:foo +string(1) "y" +string(5) "y:foo" +string(5) "urn:a" +--- ns attributes with different namespace but same prefix --- + +Attr: x:foo +string(1) "x" +string(5) "x:foo" +string(5) "urn:a" +Attr: x:foo +string(1) "x" +string(5) "x:foo" +string(5) "urn:b" diff --git a/ext/dom/tests/modern/spec/Element_setAttributeNode.phpt b/ext/dom/tests/modern/spec/Element_setAttributeNode.phpt new file mode 100644 index 0000000000000..3379d6b8b3677 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_setAttributeNode.phpt @@ -0,0 +1,48 @@ +--TEST-- +DOM\Element::setAttributeNode(NS) in the same document +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom1->createElement("container")); + +echo "--- Without namespace ---\n"; + +$attribute1 = $dom1->createAttribute("my-attribute"); +$attribute1->value = "1"; +$container->setAttributeNode($attribute1); +var_dump($container->setAttributeNode($attribute1) === null); +$attribute2 = $dom1->createAttribute("my-attribute"); +$attribute2->value = "2"; +var_dump($container->setAttributeNode($attribute2) === $attribute1); +$attribute3 = $dom1->createAttributeNS("", "my-ATTRIBUTE"); +$attribute3->value = "3"; +var_dump($container->setAttributeNode($attribute3) === null); + +echo "--- With namespace ---\n"; + +$attribute4 = $dom1->createAttributeNS("urn:a", "my-attribute"); +$attribute4->value = "4"; +$container->setAttributeNode($attribute4); +var_dump($container->setAttributeNode($attribute4) === null); +$attribute5 = $dom1->createAttributeNS("urn:b", "my-attribute"); +$attribute5->value = "5"; +var_dump($container->setAttributeNodeNS($attribute5) === null); + +echo "--- Resulting document ---\n"; + +echo $dom1->saveHTML(), "\n"; + +?> +--EXPECT-- +--- Without namespace --- +bool(true) +bool(true) +bool(true) +--- With namespace --- +bool(true) +bool(true) +--- Resulting document --- + diff --git a/ext/dom/tests/modern/spec/Element_setAttributeNode_adoption.phpt b/ext/dom/tests/modern/spec/Element_setAttributeNode_adoption.phpt new file mode 100644 index 0000000000000..d395c16355b94 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_setAttributeNode_adoption.phpt @@ -0,0 +1,55 @@ +--TEST-- +DOM\Element::setAttributeNode(NS) adopting from another document +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom1->createElement("container")); + +echo "--- Without namespace ---\n"; + +$dom2 = DOM\HTMLDocument::createEmpty(); +$attr = $dom2->createAttribute("my-attribute"); +$attr->value = "1"; + +$container->setAttributeNode($attr); +var_dump($attr->ownerDocument === $dom1); + +// Should not cause problems +unset($dom2); +unset($attr); + +echo "--- With namespace ---\n"; + +$dom2 = DOM\HTMLDocument::createEmpty(); +$attr2 = $dom2->createAttributeNS("urn:a", "a:my-attribute"); +$attr2->value = "2"; +$dom1->documentElement->setAttributeNode($attr2); + +$dom2 = DOM\HTMLDocument::createEmpty(); +$attr3 = $dom2->createAttributeNS("urn:b", "a:my-attribute"); +$attr3->value = "3"; +$dom1->documentElement->setAttributeNode($attr3); + +var_dump($attr2->prefix, $attr2->namespaceURI, $attr2->localName); +var_dump($attr3->prefix, $attr3->namespaceURI, $attr3->localName); + +echo "--- Resulting document ---\n"; + +echo $dom1->saveHTML(), "\n"; + +?> +--EXPECT-- +--- Without namespace --- +bool(true) +--- With namespace --- +string(1) "a" +string(5) "urn:a" +string(12) "my-attribute" +string(1) "a" +string(5) "urn:b" +string(12) "my-attribute" +--- Resulting document --- + diff --git a/ext/dom/tests/modern/spec/Element_setAttributeNode_inuse.phpt b/ext/dom/tests/modern/spec/Element_setAttributeNode_inuse.phpt new file mode 100644 index 0000000000000..1e2e5e3b15fdc --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_setAttributeNode_inuse.phpt @@ -0,0 +1,24 @@ +--TEST-- +DOM\Element::setAttributeNode(NS) inuse error +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom1->createElement("container")); +$attr1 = $dom1->createAttribute("my-attribute"); +$attr1->value = "1"; +$container->setAttributeNode($attr1); +$element = $container->appendChild($dom1->createElement("element")); +try { + $element->setAttributeNode($attr1); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $dom1->saveHTML(), "\n"; + +?> +--EXPECT-- +Inuse Attribute Error + diff --git a/ext/dom/tests/modern/spec/Element_setAttribute_with_namespace.phpt b/ext/dom/tests/modern/spec/Element_setAttribute_with_namespace.phpt new file mode 100644 index 0000000000000..d480dbaac5ee2 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_setAttribute_with_namespace.phpt @@ -0,0 +1,78 @@ +--TEST-- +Element::setAttribute() with namespace +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElementNS("urn:a", "container")); + +$attrs = []; + +function setAttribute($container, string $name, string $value): DOM\Attr { + $container->setAttribute($name, $value); + return $container->getAttributeNode($name); +} + +$attrs[] = setAttribute($container, "foo:bar", "&hello1"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "foo:bar", "&hello2"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "bar", "&hello3"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "xmlns", "&hello4"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "XMLns", "&hello5"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "BAR", "&hello6"); +echo $dom->saveHTML($container), "\n"; + +$container->setAttributeNode($dom->createAttributeNS("urn:b", "in:ns")); +$attrs[] = setAttribute($container, "in:ns", "&hello7"); +echo $dom->saveHTML($container), "\n"; + +// Dump at the end to check whether they influenced each other +foreach ($attrs as $attr) { + dumpAttr($attr); +} + +?> +--EXPECT-- + + + + + + + +Attr: foo:bar +NULL +string(7) "foo:bar" +NULL +Attr: foo:bar +NULL +string(7) "foo:bar" +NULL +Attr: bar +NULL +string(3) "bar" +NULL +Attr: xmlns +NULL +string(5) "xmlns" +NULL +Attr: XMLns +NULL +string(5) "XMLns" +NULL +Attr: BAR +NULL +string(3) "BAR" +NULL +Attr: in:ns +string(2) "in" +string(5) "in:ns" +string(5) "urn:b" diff --git a/ext/dom/tests/modern/spec/Element_setAttribute_without_namespace.phpt b/ext/dom/tests/modern/spec/Element_setAttribute_without_namespace.phpt new file mode 100644 index 0000000000000..7619988f4f15c --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_setAttribute_without_namespace.phpt @@ -0,0 +1,69 @@ +--TEST-- +Element::setAttribute() without namespace +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement("container")); + +$attrs = []; + +function setAttribute($container, string $name, string $value): DOM\Attr { + $container->setAttribute($name, $value); + return $container->getAttributeNode($name); +} + +$attrs[] = setAttribute($container, "foo:bar", "&hello1"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "foo:bar", "&hello2"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "bar", "&hello3"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "xmlns", "&hello4"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "XMLns", "&hello5"); +echo $dom->saveHTML($container), "\n"; +$attrs[] = setAttribute($container, "BAR", "&hello6"); +echo $dom->saveHTML($container), "\n"; + +// Dump at the end to check whether they influenced each other +foreach ($attrs as $attr) { + dumpAttr($attr); +} + +?> +--EXPECT-- + + + + + + +Attr: foo:bar +NULL +string(7) "foo:bar" +NULL +Attr: foo:bar +NULL +string(7) "foo:bar" +NULL +Attr: bar +NULL +string(3) "bar" +NULL +Attr: xmlns +NULL +string(5) "xmlns" +NULL +Attr: xmlns +NULL +string(5) "xmlns" +NULL +Attr: bar +NULL +string(3) "bar" +NULL diff --git a/ext/dom/tests/modern/spec/Element_tagName.phpt b/ext/dom/tests/modern/spec/Element_tagName.phpt new file mode 100644 index 0000000000000..f364ebf3be43e --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_tagName.phpt @@ -0,0 +1,42 @@ +--TEST-- +DOM\Element::$tagName HTML-uppercased qualified name +--EXTENSIONS-- +dom +--FILE-- +createElementNS('http://www.w3.org/1999/xhtml', 'html')->tagName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml/', 'html')->tagName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml', 'foo:html')->tagName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml/', 'foo:html')->tagName); +var_dump($dom->createElementNS('', 'html')->tagName); +var_dump($dom->createElementNS(null, 'html')->tagName); + +echo "--- For XML document ---\n"; + +$dom = DOM\XMLDocument::createEmpty(); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml', 'html')->tagName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml/', 'html')->tagName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml', 'foo:html')->tagName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml/', 'foo:html')->tagName); +var_dump($dom->createElementNS('', 'html')->tagName); +var_dump($dom->createElementNS(null, 'html')->tagName); +?> +--EXPECT-- +--- For HTML document --- +string(4) "HTML" +string(4) "html" +string(8) "FOO:HTML" +string(8) "foo:html" +string(4) "html" +string(4) "html" +--- For XML document --- +string(4) "html" +string(4) "html" +string(8) "foo:html" +string(8) "foo:html" +string(4) "html" +string(4) "html" diff --git a/ext/dom/tests/modern/spec/Element_toggleAttribute.phpt b/ext/dom/tests/modern/spec/Element_toggleAttribute.phpt new file mode 100644 index 0000000000000..457663b3cd250 --- /dev/null +++ b/ext/dom/tests/modern/spec/Element_toggleAttribute.phpt @@ -0,0 +1,28 @@ +--TEST-- +Element::toggleAttribute() xmlns spec compliance +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement('container')); + +$container->toggleAttribute('hidden'); +$container->toggleAttribute('xmlns:foo'); +$container->toggleAttribute('xmlns:bar'); +$container->toggleAttribute('xmlns'); + +echo $dom->saveHTML(), "\n"; + +$container->toggleAttribute('hidden'); +$container->toggleAttribute('xmlns:foo'); +$container->toggleAttribute('xmlns:bar'); +$container->toggleAttribute('xmlns'); + +echo $dom->saveHTML(), "\n"; + +?> +--EXPECT-- + + diff --git a/ext/dom/tests/modern/spec/HTMLDocument_createCDATASection.phpt b/ext/dom/tests/modern/spec/HTMLDocument_createCDATASection.phpt new file mode 100644 index 0000000000000..de9989510ad79 --- /dev/null +++ b/ext/dom/tests/modern/spec/HTMLDocument_createCDATASection.phpt @@ -0,0 +1,36 @@ +--TEST-- +DOM\HTMLDocument::createCDATASection() +--EXTENSIONS-- +dom +--FILE-- +createCDATASection("foo"); +} catch (DOMException $e) { + var_dump($e->getCode()); + echo $e->getMessage(), "\n"; +} +try { + $dom->createCDATASection("]]>"); +} catch (DOMException $e) { + var_dump($e->getCode()); + echo $e->getMessage(), "\n"; +} + +$dom = DOM\XMLDocument::createEmpty(); +try { + $dom->createCDATASection("]]>"); +} catch (DOMException $e) { + var_dump($e->getCode()); + echo $e->getMessage(), "\n"; +} +$dom->createCDATASection("]>"); +?> +--EXPECT-- +int(9) +This operation is not supported for HTML documents +int(9) +This operation is not supported for HTML documents +int(5) +Invalid character sequence "]]>" in CDATA section diff --git a/ext/dom/tests/modern/spec/HTMLDocument_createProcessingInstruction.phpt b/ext/dom/tests/modern/spec/HTMLDocument_createProcessingInstruction.phpt new file mode 100644 index 0000000000000..7cb459f9866de --- /dev/null +++ b/ext/dom/tests/modern/spec/HTMLDocument_createProcessingInstruction.phpt @@ -0,0 +1,37 @@ +--TEST-- +DOM\HTMLDocument::createProcessingInstruction() +--EXTENSIONS-- +dom +--FILE-- +createProcessingInstruction("?>", ""); +} catch (DOMException $e) { + var_dump($e->getCode()); + echo $e->getMessage(), "\n"; +} +try { + $dom->createProcessingInstruction("?>", "?>"); +} catch (DOMException $e) { + var_dump($e->getCode()); + echo $e->getMessage(), "\n"; +} +try { + $dom->createProcessingInstruction("target", "?>"); +} catch (DOMException $e) { + var_dump($e->getCode()); + echo $e->getMessage(), "\n"; +} +$dom->appendChild($dom->createProcessingInstruction("foo", "")); +$dom->appendChild($dom->createProcessingInstruction("foo", "bar")); +echo $dom->saveHTML(); +?> +--EXPECT-- +int(5) +Invalid Character Error +int(5) +Invalid Character Error +int(5) +Invalid character sequence "?>" in processing instruction + diff --git a/ext/dom/tests/modern/spec/HTMLDocument_getElementsByTagName.phpt b/ext/dom/tests/modern/spec/HTMLDocument_getElementsByTagName.phpt new file mode 100644 index 0000000000000..c19a94ada25b8 --- /dev/null +++ b/ext/dom/tests/modern/spec/HTMLDocument_getElementsByTagName.phpt @@ -0,0 +1,70 @@ +--TEST-- +HTMLDocument::getElementsByTagName +--EXTENSIONS-- +dom +--FILE-- +appendChild(createElement($dom, "container")); +$container->appendChild(createElement($dom, "HTML", "1")); +$container->appendChild(createElementNS($dom, "http://www.w3.org/1999/xhtml", "html", "2")); +$container->appendChild(createElementNS($dom, NULL, "html", "3")); +$container->appendChild(createElementNS($dom, NULL, "HTML", "4")); +$container->appendChild(createElementNS($dom, "urn:foo", "htML", "5")); +$container->appendChild(createElement($dom, "foo:HTML", "6")); +$container->appendChild(createElementNS($dom, "urn:a", "foo:HTML", "7")); // Should never match in this test file +$container->appendChild(createElementNS($dom, "http://www.w3.org/1999/xhtml", "bar:HTML", "8")); +$container->appendChild(createElementNS($dom, "http://www.w3.org/1999/xhtml", "bar:html", "9")); + +dumpNodeList($dom->getElementsByTagName("HTml")); +dumpNodeList($dom->getElementsByTagName("htML")); +dumpNodeList($dom->getElementsByTagName("html")); +dumpNodeList($dom->getElementsByTagName("HTML")); +// Matches 6 instead of 7 because the input to this function is lowercased +// _and_ createElement also lowercases. +// This is in contrast to createElementNS that won't lowercase. +dumpNodeList($dom->getElementsByTagName("foo:html")); +// This will match both 6 and 7. 7 is now matched because the casing is right. +dumpNodeList($dom->getElementsByTagName("foo:HTML")); +// Both of the following calls will match 9 because 8 isn't lowercased due to the use of createElementNS. +dumpNodeList($dom->getElementsByTagName("bar:HTML")); +dumpNodeList($dom->getElementsByTagName("bar:html")); + +?> +--EXPECT-- +Node list length: int(2) + HTML 1 + HTML 2 +--- +Node list length: int(3) + HTML 1 + HTML 2 + htML 5 +--- +Node list length: int(3) + HTML 1 + HTML 2 + html 3 +--- +Node list length: int(3) + HTML 1 + HTML 2 + HTML 4 +--- +Node list length: int(1) + FOO:HTML 6 +--- +Node list length: int(2) + FOO:HTML 6 + foo:HTML 7 +--- +Node list length: int(1) + BAR:HTML 9 +--- +Node list length: int(1) + BAR:HTML 9 +--- diff --git a/ext/dom/tests/modern/spec/HTMLDocument_importNode_01.phpt b/ext/dom/tests/modern/spec/HTMLDocument_importNode_01.phpt new file mode 100644 index 0000000000000..8108567a04f30 --- /dev/null +++ b/ext/dom/tests/modern/spec/HTMLDocument_importNode_01.phpt @@ -0,0 +1,46 @@ +--TEST-- +HTMLDocument::importNode() with namespace should not shift namespaces +--EXTENSIONS-- +dom +--FILE-- + + + +

hello world.

+

test

+
+

test 2

+ namespace prefixed + + +XML); + +// Note the HTMLDocument class! +$dom2 = DOM\HTMLDocument::createEmpty(); +$imported = $dom2->importNode($dom->documentElement, true); +$dom2->appendChild($imported); + +$body = $dom2->getElementsByTagName("body")[0]; +$default_p = $body->lastElementChild; +var_dump($default_p->prefix); +var_dump($default_p->namespaceURI); + +echo $dom2->saveXML(); + +?> +--EXPECT-- +string(7) "default" +string(28) "http://www.w3.org/1999/xhtml" + + + +

hello world.

+

test

+
+

test 2

+

namespace prefixed

+ + diff --git a/ext/dom/tests/modern/spec/NamedNodeMap_dimensions.phpt b/ext/dom/tests/modern/spec/NamedNodeMap_dimensions.phpt new file mode 100644 index 0000000000000..eb015911baac4 --- /dev/null +++ b/ext/dom/tests/modern/spec/NamedNodeMap_dimensions.phpt @@ -0,0 +1,95 @@ +--TEST-- +NamedNodeMap dimensions +--EXTENSIONS-- +dom +--FILE-- +'); +$attributes = $dom->documentElement->attributes; + +$test_values = [-1, 0, 1, 2, 3, 1.0, 1.1, true, null, "0", "", "1", "8", "a", "b", "c", "d"]; + +foreach ($test_values as $value) { + echo "--- ", json_encode($value), " ---\n"; + try { + var_dump($attributes[$value] ? $attributes[$value]->nodeName : "N/A", isset($attributes[$value]), empty($attributes[$value])); + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } +} + +?> +--EXPECTF-- +--- -1 --- +string(1) "a" +bool(false) +bool(true) +--- 0 --- +string(1) "a" +bool(true) +bool(false) +--- 1 --- +string(1) "b" +bool(true) +bool(false) +--- 2 --- +string(1) "c" +bool(true) +bool(false) +--- 3 --- +string(3) "N/A" +bool(false) +bool(true) +--- 1 --- +string(1) "b" +bool(true) +bool(false) +--- 1.1 --- + +Deprecated: Implicit conversion from float 1.1 to int loses precision in %s on line %d + +Deprecated: Implicit conversion from float 1.1 to int loses precision in %s on line %d + +Deprecated: Implicit conversion from float 1.1 to int loses precision in %s on line %d + +Deprecated: Implicit conversion from float 1.1 to int loses precision in %s on line %d +string(1) "b" +bool(true) +bool(false) +--- true --- +Cannot access offset of type bool on DOM\NamedNodeMap +--- null --- +Cannot access offset of type null on DOM\NamedNodeMap +--- "0" --- +string(1) "a" +bool(true) +bool(false) +--- "" --- +string(3) "N/A" +bool(false) +bool(true) +--- "1" --- +string(1) "b" +bool(true) +bool(false) +--- "8" --- +string(3) "N/A" +bool(false) +bool(true) +--- "a" --- +string(1) "a" +bool(true) +bool(false) +--- "b" --- +string(1) "b" +bool(true) +bool(false) +--- "c" --- +string(1) "c" +bool(true) +bool(false) +--- "d" --- +string(3) "N/A" +bool(false) +bool(true) diff --git a/ext/dom/tests/modern/spec/NamedNodeMap_dimensions_errors.phpt b/ext/dom/tests/modern/spec/NamedNodeMap_dimensions_errors.phpt new file mode 100644 index 0000000000000..8f5101c55e4f2 --- /dev/null +++ b/ext/dom/tests/modern/spec/NamedNodeMap_dimensions_errors.phpt @@ -0,0 +1,19 @@ +--TEST-- +NamedNodeMap dimensions errors +--EXTENSIONS-- +dom +--FILE-- +'); +$attributes = $dom->documentElement->attributes; + +try { + $attributes[][0] = 1; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Cannot append to DOM\NamedNodeMap diff --git a/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItem.phpt b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItem.phpt new file mode 100644 index 0000000000000..ab6f7e027d34f --- /dev/null +++ b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItem.phpt @@ -0,0 +1,80 @@ +--TEST-- +DOMNamedNodeMap::getNamedItem() +--EXTENSIONS-- +dom +--FILE-- + + + + Test + + + + +HTML); + +echo "--- On HTML document ---\n"; + +$body = $dom->getElementsByTagName('body')->item(0); +$body->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:foo", "http://example.com/foo"); +$attributes = $body->attributes; + +var_dump($attributes->getNamedItem("FOO:BAR")->value); +var_dump($attributes->getNamedItem("foo:BAR")->value); +var_dump($attributes->getNamedItem("foo:bar")->value); +var_dump($attributes->getNamedItem("XmLnS:foo")->value); +var_dump($attributes->getNamedItem("xmlns:foo")->value); + +var_dump($attributes["FOO:BAR"]->value); +var_dump($attributes["foo:BAR"]->value); +var_dump($attributes["foo:bar"]->value); +var_dump($attributes["XmLnS:foo"]->value); +var_dump($attributes["xmlns:foo"]->value); + +echo "--- On XML document ---\n"; + +$dom = DOM\XMLDocument::createEmpty(); +$dom->appendChild($dom->importNode($body, true)); + +$body = $dom->getElementsByTagName('body')->item(0); +$attributes = $body->attributes; + +var_dump($attributes->getNamedItem("FOO:BAR")); +var_dump($attributes->getNamedItem("foo:BAR")); +var_dump($attributes->getNamedItem("foo:bar")->value); +var_dump($attributes->getNamedItem("XmLnS:foo")); +var_dump($attributes->getNamedItem("xmlns:foo")->value); + +var_dump($attributes["FOO:BAR"]); +var_dump($attributes["foo:BAR"]); +var_dump($attributes["foo:bar"]->value); +var_dump($attributes["XmLnS:foo"]); +var_dump($attributes["xmlns:foo"]->value); + +?> +--EXPECT-- +--- On HTML document --- +string(3) "baz" +string(3) "baz" +string(3) "baz" +string(22) "http://example.com/foo" +string(22) "http://example.com/foo" +string(3) "baz" +string(3) "baz" +string(3) "baz" +string(22) "http://example.com/foo" +string(22) "http://example.com/foo" +--- On XML document --- +NULL +NULL +string(3) "baz" +NULL +string(22) "http://example.com/foo" +NULL +NULL +string(3) "baz" +NULL +string(22) "http://example.com/foo" diff --git a/ext/dom/tests/modern/spec/NodeList_dimensions.phpt b/ext/dom/tests/modern/spec/NodeList_dimensions.phpt new file mode 100644 index 0000000000000..a20e1c98ed846 --- /dev/null +++ b/ext/dom/tests/modern/spec/NodeList_dimensions.phpt @@ -0,0 +1,75 @@ +--TEST-- +NodeList dimensions +--EXTENSIONS-- +dom +--FILE-- +'); +$children = $dom->documentElement->childNodes; + +$test_values = [-1, 0, 1, 2, 3, 1.0, 1.1, true, null, "0", "1", "", "foo"]; + +foreach ($test_values as $value) { + echo "--- ", json_encode($value), " ---\n"; + try { + var_dump($children[$value] ? $children[$value]->nodeName : "N/A", isset($children[$value]), empty($children[$value])); + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } +} + +?> +--EXPECTF-- +--- -1 --- +string(3) "N/A" +bool(false) +bool(true) +--- 0 --- +string(1) "a" +bool(true) +bool(false) +--- 1 --- +string(1) "b" +bool(true) +bool(false) +--- 2 --- +string(1) "c" +bool(true) +bool(false) +--- 3 --- +string(3) "N/A" +bool(false) +bool(true) +--- 1 --- +string(1) "b" +bool(true) +bool(false) +--- 1.1 --- + +Deprecated: Implicit conversion from float 1.1 to int loses precision in %s on line %d + +Deprecated: Implicit conversion from float 1.1 to int loses precision in %s on line %d + +Deprecated: Implicit conversion from float 1.1 to int loses precision in %s on line %d + +Deprecated: Implicit conversion from float 1.1 to int loses precision in %s on line %d +string(1) "b" +bool(true) +bool(false) +--- true --- +Cannot access offset of type bool on DOM\NodeList +--- null --- +Cannot access offset of type null on DOM\NodeList +--- "0" --- +string(1) "a" +bool(true) +bool(false) +--- "1" --- +string(1) "b" +bool(true) +bool(false) +--- "" --- +Cannot access offset of type string on DOM\NodeList +--- "foo" --- +Cannot access offset of type string on DOM\NodeList diff --git a/ext/dom/tests/modern/spec/NodeList_dimensions_errors.phpt b/ext/dom/tests/modern/spec/NodeList_dimensions_errors.phpt new file mode 100644 index 0000000000000..9c0b10e6f7545 --- /dev/null +++ b/ext/dom/tests/modern/spec/NodeList_dimensions_errors.phpt @@ -0,0 +1,19 @@ +--TEST-- +NodeList dimensions errors +--EXTENSIONS-- +dom +--FILE-- +'); +$children = $dom->documentElement->childNodes; + +try { + $children[][0] = 1; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Cannot append to DOM\NodeList diff --git a/ext/dom/tests/modern/spec/Node_appendChild_attribute.phpt b/ext/dom/tests/modern/spec/Node_appendChild_attribute.phpt new file mode 100644 index 0000000000000..c18e2527c0201 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_appendChild_attribute.phpt @@ -0,0 +1,24 @@ +--TEST-- +DOM\Node::appendChild() with attribute should fail +--EXTENSIONS-- +dom +--FILE-- +createElement('foo'); +$attr = $dom->createAttribute('bar'); +$attr->value = "hello"; +try { + $element->appendChild($attr); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +var_dump($attr->value); + +?> +--EXPECT-- +Hierarchy Request Error +string(5) "hello" diff --git a/ext/dom/tests/modern/spec/Node_appendChild_fragment_multiple_elements.phpt b/ext/dom/tests/modern/spec/Node_appendChild_fragment_multiple_elements.phpt new file mode 100644 index 0000000000000..86b90e1608715 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_appendChild_fragment_multiple_elements.phpt @@ -0,0 +1,25 @@ +--TEST-- +DOM\Node::appendChild() with fragment of multiple elements +--EXTENSIONS-- +dom +--FILE-- +createDocumentFragment(); +$fragment->appendChild($dom->createElement('foo')); +$fragment->appendChild($dom->createElement('bar')); + +try { + $dom->appendChild($fragment); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo $dom->saveXML($fragment), "\n"; + +?> +--EXPECT-- +Cannot have more than one element child in a document + diff --git a/ext/dom/tests/modern/spec/Node_appendChild_invalid_parent.phpt b/ext/dom/tests/modern/spec/Node_appendChild_invalid_parent.phpt new file mode 100644 index 0000000000000..6f2e3f3762a0d --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_appendChild_invalid_parent.phpt @@ -0,0 +1,44 @@ +--TEST-- +DOM\Node::appendChild() invalid parent +--EXTENSIONS-- +dom +--FILE-- +createTextNode('Hello World'); +$cdata = $dom->createCDATASection('Hello World'); +$pi = $dom->createProcessingInstruction('Hello', ''); +$attr = $dom->createAttribute('Hello'); + +try { + $text->appendChild($dom->createElement('br')); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cdata->appendChild($dom->createElement('br')); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +try { + $pi->appendChild($dom->createElement('br')); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +try { + $attr->appendChild($dom->createElement('br')); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Hierarchy Request Error +Hierarchy Request Error +Hierarchy Request Error +Hierarchy Request Error diff --git a/ext/dom/tests/modern/spec/Node_baseURI.phpt b/ext/dom/tests/modern/spec/Node_baseURI.phpt new file mode 100644 index 0000000000000..80a07fa280db1 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_baseURI.phpt @@ -0,0 +1,29 @@ +--TEST-- +Node::$baseURI +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement('html')); +var_dump($html->baseURI); + +$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/../../empty.html", LIBXML_NOERROR); +var_dump($dom->documentElement->baseURI); + +$dom = DOM\HTMLDocument::createFromString(<< + + + + + +HTML); +var_dump($dom->documentElement->baseURI); + +?> +--EXPECTF-- +string(11) "about:blank" +string(%d) "file://%sempty.html" +string(19) "http://example.com/" diff --git a/ext/dom/tests/modern/spec/Node_cloneNode_copy_document_properties.phpt b/ext/dom/tests/modern/spec/Node_cloneNode_copy_document_properties.phpt new file mode 100644 index 0000000000000..a6928b4a697e0 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_cloneNode_copy_document_properties.phpt @@ -0,0 +1,21 @@ +--TEST-- +DOM\Node::cloneNode() should copy the document properties +--EXTENSIONS-- +dom +--FILE-- +cloneNode(); + +$dom->appendChild($dom->createElement("foo")); + +try { + $dom->prepend($dom->createElement("bar")); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Cannot have more than one element child in a document diff --git a/ext/dom/tests/modern/spec/Node_getRootNode.phpt b/ext/dom/tests/modern/spec/Node_getRootNode.phpt new file mode 100644 index 0000000000000..826a83055feec --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_getRootNode.phpt @@ -0,0 +1,33 @@ +--TEST-- +DOM\Node::getRootNode() +--EXTENSIONS-- +dom +--FILE-- +'); + +var_dump($dom->documentElement->firstElementChild->getRootNode() === $dom); +$p = $dom->createElement('p'); +var_dump($p->getRootNode() === $p); +$dom->documentElement->appendChild($p); +var_dump($p->getRootNode() === $dom); +$dom->documentElement->remove(); +var_dump($p->getRootNode() === $p); + +$fragment = $dom->createDocumentFragment(); +var_dump($fragment->getRootNode() === $fragment); +$div = $fragment->appendChild($dom->createElement('div')); +$div->appendChild($p); +var_dump($p->getRootNode() === $fragment); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/dom/tests/modern/spec/Node_insertBefore_01.phpt b/ext/dom/tests/modern/spec/Node_insertBefore_01.phpt new file mode 100644 index 0000000000000..7672e8e9719a0 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_insertBefore_01.phpt @@ -0,0 +1,25 @@ +--TEST-- +DOM\Node::insertBefore() with DocumentFragment and dtd +--EXTENSIONS-- +dom +--FILE-- +'); +$dom->documentElement->remove(); + +$fragment = $dom->createDocumentFragment(); +$fragment->appendChild($dom->createElement('a')); + +try { + $dom->insertBefore($fragment, $dom->doctype); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo $dom->saveHTML(), "\n"; + +?> +--EXPECT-- +Document types must be the first child in a document + diff --git a/ext/dom/tests/modern/spec/Node_insertBefore_02.phpt b/ext/dom/tests/modern/spec/Node_insertBefore_02.phpt new file mode 100644 index 0000000000000..aa252e7eed098 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_insertBefore_02.phpt @@ -0,0 +1,24 @@ +--TEST-- +DOM\Node::insertBefore() with DocumentFragment and a document element +--EXTENSIONS-- +dom +--FILE-- +'); + +$fragment = $dom->createDocumentFragment(); +$fragment->appendChild($dom->createElement('a')); + +try { + $dom->insertBefore($fragment, $dom->documentElement); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo $dom->saveHTML(), "\n"; + +?> +--EXPECT-- +Cannot have more than one element child in a document + diff --git a/ext/dom/tests/modern/spec/Node_isDefaultNamespace.phpt b/ext/dom/tests/modern/spec/Node_isDefaultNamespace.phpt new file mode 100644 index 0000000000000..56eee90553c5b --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_isDefaultNamespace.phpt @@ -0,0 +1,100 @@ +--TEST-- +DOM\Node::isDefaultNamespace() +--EXTENSIONS-- +dom +--FILE-- + + + + + + + +HTML); + +function dump($node) { + echo "svg NS: "; + var_dump($node->isDefaultNamespace('http://www.w3.org/2000/svg')); + echo "xhtml NS: "; + var_dump($node->isDefaultNamespace('http://www.w3.org/1999/xhtml')); + echo "mathml NS: "; + var_dump($node->isDefaultNamespace('http://www.w3.org/1998/Math/MathML')); + echo "empty NS: "; + var_dump($node->isDefaultNamespace('')); +} + +echo "--- Document ---\n"; +dump($dom); +echo "--- svg element ---\n"; +dump($dom->getElementsByTagName('svg')[0]); +echo "--- math element ---\n"; +dump($dom->getElementsByTagName('math')[0]); +echo "--- svg attribute ---\n"; +dump($dom->getElementsByTagName('svg')[0]->attributes[0]); +echo "--- empty fragment ---\n"; +dump($dom->createDocumentFragment()); +echo "--- doctype ---\n"; +dump($dom->doctype); +echo "--- detached attribute ---\n"; +dump($dom->createAttribute('foo')); +echo "--- custom namespace with a prefix ---\n"; +var_dump($dom->createElementNS('urn:a', 'q:name')->isDefaultNamespace('urn:a')); +echo "--- custom namespace with a prefix and with attributes ---\n"; +$element = $dom->createElementNS('urn:a', 'q:name'); +$element->setAttributeNS("urn:x", "x:foo", ""); +$element->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:q", "urn:test"); +$element->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", "urn:test2"); +var_dump($element->isDefaultNamespace('urn:a')); +var_dump($element->isDefaultNamespace('urn:test')); +var_dump($element->isDefaultNamespace('urn:test2')); +echo "--- custom namespace without a prefix ---\n"; +var_dump($dom->createElementNS('urn:a', 'name')->isDefaultNamespace('urn:a')); + +?> +--EXPECT-- +--- Document --- +svg NS: bool(false) +xhtml NS: bool(true) +mathml NS: bool(false) +empty NS: bool(false) +--- svg element --- +svg NS: bool(true) +xhtml NS: bool(false) +mathml NS: bool(false) +empty NS: bool(false) +--- math element --- +svg NS: bool(false) +xhtml NS: bool(false) +mathml NS: bool(true) +empty NS: bool(false) +--- svg attribute --- +svg NS: bool(true) +xhtml NS: bool(false) +mathml NS: bool(false) +empty NS: bool(false) +--- empty fragment --- +svg NS: bool(false) +xhtml NS: bool(false) +mathml NS: bool(false) +empty NS: bool(true) +--- doctype --- +svg NS: bool(false) +xhtml NS: bool(false) +mathml NS: bool(false) +empty NS: bool(true) +--- detached attribute --- +svg NS: bool(false) +xhtml NS: bool(false) +mathml NS: bool(false) +empty NS: bool(true) +--- custom namespace with a prefix --- +bool(false) +--- custom namespace with a prefix and with attributes --- +bool(false) +bool(false) +bool(true) +--- custom namespace without a prefix --- +bool(true) diff --git a/ext/dom/tests/modern/spec/Node_isEqualNode_01.phpt b/ext/dom/tests/modern/spec/Node_isEqualNode_01.phpt new file mode 100644 index 0000000000000..51b5648d8a1b8 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_isEqualNode_01.phpt @@ -0,0 +1,30 @@ +--TEST-- +DOM\Node::isEqualNode edge cases 01 +--EXTENSIONS-- +dom +--FILE-- +append($dom->createElement("container")); +$e1 = $dom->documentElement->appendChild($dom->createElementNS("urn:example1", "example:foo")); +$e1->after("\n"); +$e2 = $dom->documentElement->appendChild($dom->createElementNS("urn:example2", "example:foo")); + +$e2->setAttributeNS("urn:example2", "example2:bar", "baz1"); +$e2->setAttributeNS("urn:example1", "example2:bar", "baz2"); + +$dom2 = DOM\HTMLDocument::createFromString("" . $dom->saveHTML()); + +// Shouldn't match because the attributes can't roundtrip. +var_dump($dom->saveHTML($dom->getElementsByTagName("container")[0])); +var_dump($dom2->saveHTML($dom2->getElementsByTagName("container")[0])); +var_dump($dom->getElementsByTagName("container")[0]->isEqualNode($dom2->getElementsByTagName("container")[0])); + +?> +--EXPECT-- +string(118) " +" +string(98) " +" +bool(false) diff --git a/ext/dom/tests/modern/spec/Node_isEqualNode_02.phpt b/ext/dom/tests/modern/spec/Node_isEqualNode_02.phpt new file mode 100644 index 0000000000000..bc7f23a7d9871 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_isEqualNode_02.phpt @@ -0,0 +1,32 @@ +--TEST-- +DOM\Node::isEqualNode edge cases 02 +--EXTENSIONS-- +dom +--FILE-- +append($dom->createElement("container")); +$e1 = $dom->documentElement->appendChild($dom->createElement("foo1")); +$e1->after("\n"); +$e2 = $dom->documentElement->appendChild($dom->createElement("foo2")); + +$e2->setAttribute("bar1", "baz"); +$e2->setAttribute("bar2", "baz"); +// Internal namespace node creation +$e2->setAttributeNodeNS($child = $dom->createAttributeNS("http://example.com", "foo:bar")); +$e2->removeAttributeNode($child); + +$dom2 = DOM\HTMLDocument::createFromString("" . $dom->saveHTML()); + +var_dump($dom->saveHTML($dom->getElementsByTagName("container")[0])); +var_dump($dom2->saveHTML($dom2->getElementsByTagName("container")[0])); +var_dump($dom->getElementsByTagName("container")[0]->isEqualNode($dom2->getElementsByTagName("container")[0])); + +?> +--EXPECT-- +string(72) " +" +string(72) " +" +bool(true) diff --git a/ext/dom/tests/modern/spec/Node_lookupNamespaceURI.phpt b/ext/dom/tests/modern/spec/Node_lookupNamespaceURI.phpt new file mode 100644 index 0000000000000..c4e1015144d9a --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_lookupNamespaceURI.phpt @@ -0,0 +1,95 @@ +--TEST-- +DOM\Node::lookupNamespaceURI() +--EXTENSIONS-- +dom +--FILE-- + + + + + + + +HTML); + +$body = $dom->getElementsByTagName("body")[0]; +$namespaceless = $body->appendChild($dom->createElementNS(NULL, "foo")); +$prefixed = $body->appendChild($dom->createElementNS("urn:a", "a:a")); + +echo "--- Hardcoded prefixes ---\n"; +var_dump($dom->lookupNamespaceURI("xml")); +var_dump($dom->lookupNamespaceURI("xmlns")); + +echo "--- Default prefix ---\n"; +var_dump($dom->lookupNamespaceURI("")); +var_dump($dom->lookupNamespaceURI(NULL)); + +echo "--- NULL namespace should propagate up ---\n"; +var_dump($namespaceless->lookupNamespaceURI("")); +var_dump($namespaceless->lookupNamespaceURI(NULL)); +var_dump($namespaceless->lookupNamespaceURI("a")); + +echo "--- Prefixed element ---\n"; +var_dump($prefixed->lookupNamespaceURI("")); +var_dump($prefixed->lookupNamespaceURI(NULL)); +var_dump($prefixed->lookupNamespaceURI("a")); + +echo "--- Prefixed element custom xmlns attribute should not change ns ---\n"; +$prefixed->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:a", "urn:another"); +var_dump($prefixed->lookupNamespaceURI("")); +var_dump($prefixed->lookupNamespaceURI(NULL)); +var_dump($prefixed->lookupNamespaceURI("a")); + +echo "--- xmlns attribute defines new namespace ---\n"; +$body->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:a", "urn:another"); +var_dump($body->lookupNamespaceURI("")); +var_dump($body->lookupNamespaceURI(NULL)); +var_dump($body->lookupNamespaceURI("a")); + +echo "--- empty xmlns attribute defines no new namespace ---\n"; +$body->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:a", ""); +var_dump($body->lookupNamespaceURI("")); +var_dump($body->lookupNamespaceURI(NULL)); +var_dump($body->lookupNamespaceURI("a")); + +echo "--- lookup in empty document ---\n"; +$dom = DOM\HTMLDocument::createEmpty(); +var_dump($dom->lookupNamespaceURI("")); +var_dump($dom->lookupNamespaceURI(NULL)); +var_dump($dom->lookupNamespaceURI("a")); + +?> +--EXPECT-- +--- Hardcoded prefixes --- +string(36) "http://www.w3.org/XML/1998/namespace" +string(29) "http://www.w3.org/2000/xmlns/" +--- Default prefix --- +string(28) "http://www.w3.org/1999/xhtml" +string(28) "http://www.w3.org/1999/xhtml" +--- NULL namespace should propagate up --- +string(28) "http://www.w3.org/1999/xhtml" +string(28) "http://www.w3.org/1999/xhtml" +NULL +--- Prefixed element --- +string(28) "http://www.w3.org/1999/xhtml" +string(28) "http://www.w3.org/1999/xhtml" +string(5) "urn:a" +--- Prefixed element custom xmlns attribute should not change ns --- +string(28) "http://www.w3.org/1999/xhtml" +string(28) "http://www.w3.org/1999/xhtml" +string(5) "urn:a" +--- xmlns attribute defines new namespace --- +string(28) "http://www.w3.org/1999/xhtml" +string(28) "http://www.w3.org/1999/xhtml" +string(11) "urn:another" +--- empty xmlns attribute defines no new namespace --- +string(28) "http://www.w3.org/1999/xhtml" +string(28) "http://www.w3.org/1999/xhtml" +NULL +--- lookup in empty document --- +NULL +NULL +NULL diff --git a/ext/dom/tests/modern/spec/Node_lookupPrefix.phpt b/ext/dom/tests/modern/spec/Node_lookupPrefix.phpt new file mode 100644 index 0000000000000..4bb52004027a0 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_lookupPrefix.phpt @@ -0,0 +1,64 @@ +--TEST-- +DOM\Node::lookupPrefix() +--EXTENSIONS-- +dom +--FILE-- + + + + + +

+ +

+ + +XML); + +$body = $dom->getElementsByTagName("body")[0]; +$body->setAttribute("xmlns:a", "urn:a"); + +echo "--- NULL case because invalid node type ---\n"; + +var_dump($dom->doctype->lookupPrefix("")); + +echo "--- NULL case because xmlns attribute not in xmlns namespace ---\n"; + +var_dump($body->lookupPrefix("urn:a")); + +echo "--- svg case ---\n"; + +$svg = $dom->getElementsByTagNameNS("*", "svg")[0]; + +var_dump($svg->lookupPrefix("")); +var_dump($svg->lookupPrefix("http://www.w3.org/2000/svg")); +var_dump($svg->lookupPrefix("1")); + +echo "--- search for \"test\" ---\n"; + +foreach (['x', 'p', 'html'] as $name) { + $x = $dom->getElementsByTagNameNS("*", $name)[0]; + var_dump($x->lookupPrefix("")); + var_dump($x->lookupPrefix("test")); +} + +?> +--EXPECT-- +--- NULL case because invalid node type --- +NULL +--- NULL case because xmlns attribute not in xmlns namespace --- +NULL +--- svg case --- +NULL +string(3) "svg" +NULL +--- search for "test" --- +NULL +string(1) "y" +NULL +string(1) "y" +NULL +string(1) "x" diff --git a/ext/dom/tests/modern/spec/Node_nodeName.phpt b/ext/dom/tests/modern/spec/Node_nodeName.phpt new file mode 100644 index 0000000000000..84343731a00a0 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_nodeName.phpt @@ -0,0 +1,62 @@ +--TEST-- +DOM\Node::$nodeName +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement('container')); +var_dump($dom->createAttributeNS('http://www.w3.org/1999/xhtml', 'html')->nodeName); +var_dump($dom->createAttributeNS('http://www.w3.org/1999/xhtml', 'foo:html')->nodeName); + +echo "\n"; + +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml', 'html')->nodeName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml/', 'html')->nodeName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml', 'foo:html')->nodeName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml/', 'foo:html')->nodeName); +var_dump($dom->createElementNS('', 'html')->nodeName); +var_dump($dom->createElementNS(null, 'html')->nodeName); + +echo "--- For XML document ---\n"; + +$dom = DOM\XMLDocument::createEmpty(); + +$dom->appendChild($dom->createElement('container')); +var_dump($dom->createAttributeNS('http://www.w3.org/1999/xhtml', 'html')->nodeName); +var_dump($dom->createAttributeNS('http://www.w3.org/1999/xhtml', 'foo:html')->nodeName); + +echo "\n"; + +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml', 'html')->nodeName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml/', 'html')->nodeName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml', 'foo:html')->nodeName); +var_dump($dom->createElementNS('http://www.w3.org/1999/xhtml/', 'foo:html')->nodeName); +var_dump($dom->createElementNS('', 'html')->nodeName); +var_dump($dom->createElementNS(null, 'html')->nodeName); +?> +--EXPECT-- +--- For HTML document --- +string(4) "html" +string(8) "foo:html" + +string(4) "HTML" +string(4) "html" +string(8) "FOO:HTML" +string(8) "foo:html" +string(4) "html" +string(4) "html" +--- For XML document --- +string(4) "html" +string(8) "foo:html" + +string(4) "html" +string(4) "html" +string(8) "foo:html" +string(8) "foo:html" +string(4) "html" +string(4) "html" diff --git a/ext/dom/tests/modern/spec/Node_nodeValue_element.phpt b/ext/dom/tests/modern/spec/Node_nodeValue_element.phpt new file mode 100644 index 0000000000000..749614ea9fc71 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_nodeValue_element.phpt @@ -0,0 +1,15 @@ +--TEST-- +DOM\Node::$nodeValue on an element +--EXTENSIONS-- +dom +--FILE-- +createElement("foo"); +$foo->textContent = "bar"; +var_dump($foo->nodeValue); + +?> +--EXPECT-- +NULL diff --git a/ext/dom/tests/modern/spec/Node_normalize.phpt b/ext/dom/tests/modern/spec/Node_normalize.phpt new file mode 100644 index 0000000000000..04848e5d019a7 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_normalize.phpt @@ -0,0 +1,64 @@ +--TEST-- +DOM\Node::normalize() +--EXTENSIONS-- +dom +--FILE-- + + * + * This is the title + * + * Calculate the number of title text nodes (1). + * Add another text node to title. Calculate the number of title text nodes (2). + * Normalize author. Calculate the number of title text nodes (2). + * Normalize title. Calculate the number of title text nodes (1). +*/ + +$doc = DOM\XMLDocument::createEmpty(); + +$root = $doc->createElement('book'); +$doc->appendChild($root); + +$title = $doc->createElement('title'); +$root->appendChild($title); + +$author = $doc->createElement('author'); +$root->appendChild($author); + +$text = $doc->createTextNode('This is the first title'); +$title->appendChild($text); + +echo "Number of child nodes of title = "; +var_dump($title->childNodes->length); + +// add a second text node to title +$text = $doc->createTextNode('This is the second title'); +$title->appendChild($text); + +echo "Number of child nodes of title after adding second title = "; +var_dump($title->childNodes->length); + +// should do nothing +$author->normalize(); + +echo "Number of child nodes of title after normalizing author = "; +var_dump($title->childNodes->length); + + +// should concatenate first and second title text nodes +$title->normalize(); + +echo "Number of child nodes of title after normalizing title = "; +var_dump($title->childNodes->length); + +?> +--EXPECT-- +Number of child nodes of title = int(1) +Number of child nodes of title after adding second title = int(2) +Number of child nodes of title after normalizing author = int(2) +Number of child nodes of title after normalizing title = int(1) diff --git a/ext/dom/tests/modern/spec/Node_normalize_edge_case.phpt b/ext/dom/tests/modern/spec/Node_normalize_edge_case.phpt new file mode 100644 index 0000000000000..03b75ede7f392 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_normalize_edge_case.phpt @@ -0,0 +1,25 @@ +--TEST-- +DOM\Node::normalize() edge case +--EXTENSIONS-- +dom +--FILE-- +appendChild($doc->createElement("container")); + +$container->appendChild($doc->createTextNode("")); +$middle = $container->appendChild($doc->createTextNode("foo")); +$container->appendChild($doc->createTextNode("")); + +$doc->normalize(); + +echo $doc->saveXML(), "\n"; + +var_dump($middle->textContent); + +?> +--EXPECT-- + +foo +string(3) "foo" diff --git a/ext/dom/tests/modern/spec/Node_replaceChild_edge_cases.phpt b/ext/dom/tests/modern/spec/Node_replaceChild_edge_cases.phpt new file mode 100644 index 0000000000000..e76fdc62ee730 --- /dev/null +++ b/ext/dom/tests/modern/spec/Node_replaceChild_edge_cases.phpt @@ -0,0 +1,146 @@ +--TEST-- +DOM\Node::replaceChild() edge cases +--EXTENSIONS-- +dom +--FILE-- +"); +$dom->documentElement->remove(); +$parent = $dom->createElement("parent"); +$child = $dom->createElement("child"); +$parent->appendChild($child); +$dom->appendChild($parent); + +echo "--- Wrong parent node type ---\n"; + +$comment = $dom->createComment('This is a comment'); +try { + $comment->replaceChild($comment, $dom->createElement("old-child")); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Node is an inclusive ancestor of parent ---\n"; + +try { + $parent->replaceChild($parent, $child); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +try { + $parent->replaceChild($dom, $child); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Child's parent is not parent ---\n"; + +try { + $parent->replaceChild($dom->createElement("new-child"), $dom->createElement("old-child")); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Invalid child to replace with ---\n"; + +try { + $entityReference = $dom->importNode(DOM\XMLDocument::createEmpty()->createEntityReference("foo")); + $parent->replaceChild($entityReference, $child); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Replace element with text in document root ---\n"; + +try { + $dom->replaceChild($dom->createTextNode("text"), $parent); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Replace child element with doctype inside element ---\n"; + +try { + $parent->replaceChild($dom->doctype, $child); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Replace element with fragment containing multiple elements ---\n"; + +$fragment = $dom->createDocumentFragment(); +$fragment->appendChild($dom->createElement("new-child1")); +$fragment->appendChild($dom->createElement("new-child2")); + +try { + $dom->replaceChild($fragment, $parent); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Replace comment in document causing more than two elements ---\n"; + +$comment = $dom->appendChild($dom->createComment("comment")); +try { + $dom->replaceChild($dom->createElement("new-child"), $comment); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Replace dtd with element ---\n"; + +try { + $dom->replaceChild($dom->createElement("new-child"), $dom->doctype); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Replace element with another dtd ---\n"; + +try { + $dom->replaceChild($dom->doctype, $parent); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Replace parent with itself ---\n"; + +$dom->replaceChild($parent, $parent); +echo $dom->saveHTML(), "\n"; + +echo "--- Replace parent with single-child fragment ---\n"; + +$fragment = $dom->createDocumentFragment(); +$fragment->appendChild($dom->createElement("new-child")); +$dom->replaceChild($fragment, $parent); +echo $dom->saveHTML(), "\n"; + +?> +--EXPECT-- +--- Wrong parent node type --- +Hierarchy Request Error +--- Node is an inclusive ancestor of parent --- +Hierarchy Request Error +Hierarchy Request Error +--- Child's parent is not parent --- +Not Found Error +--- Invalid child to replace with --- +Hierarchy Request Error +--- Replace element with text in document root --- +Cannot insert text as a child of a document +--- Replace child element with doctype inside element --- +Cannot insert a document type into anything other than a document +--- Replace element with fragment containing multiple elements --- +Cannot have more than one element child in a document +--- Replace comment in document causing more than two elements --- +Cannot have more than one element child in a document +--- Replace dtd with element --- +Cannot have more than one element child in a document +--- Replace element with another dtd --- +Document types must be the first child in a document +--- Replace parent with itself --- + +--- Replace parent with single-child fragment --- + diff --git a/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt b/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt new file mode 100644 index 0000000000000..a8a65ca7bc58b --- /dev/null +++ b/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt @@ -0,0 +1,56 @@ +--TEST-- +Consistency of parent node after exception in ParentNode::append() +--EXTENSIONS-- +dom +--FILE-- +createDocumentFragment(); +$fragment->append($element = $dom->createElement("foo")); +$fragment->append($dom->createElement("bar")); +try { + $dom->append($fragment); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +var_dump($element->parentNode); +?> +--EXPECT-- +Exception: Cannot have more than one element child in a document +object(DOM\DocumentFragment)#2 (17) { + ["firstElementChild"]=> + string(22) "(object value omitted)" + ["lastElementChild"]=> + string(22) "(object value omitted)" + ["childElementCount"]=> + int(2) + ["nodeType"]=> + int(11) + ["nodeName"]=> + string(18) "#document-fragment" + ["baseURI"]=> + string(11) "about:blank" + ["isConnected"]=> + bool(false) + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["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 + ["nodeValue"]=> + NULL + ["textContent"]=> + string(0) "" +} diff --git a/ext/dom/tests/modern/spec/ParentNode_edge_case.phpt b/ext/dom/tests/modern/spec/ParentNode_edge_case.phpt new file mode 100644 index 0000000000000..5df27d5a27657 --- /dev/null +++ b/ext/dom/tests/modern/spec/ParentNode_edge_case.phpt @@ -0,0 +1,18 @@ +--TEST-- +HierarchyRequestError on appending document to element +--EXTENSIONS-- +dom +--FILE-- +createElement('container'); +try { + $container->append($dom); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Hierarchy Request Error diff --git a/ext/dom/tests/modern/spec/ParentNode_hierarchy_text_nodes.phpt b/ext/dom/tests/modern/spec/ParentNode_hierarchy_text_nodes.phpt new file mode 100644 index 0000000000000..a4ebc59450700 --- /dev/null +++ b/ext/dom/tests/modern/spec/ParentNode_hierarchy_text_nodes.phpt @@ -0,0 +1,47 @@ +--TEST-- +ParentNode hierarchy exceptions with temporary and non-temporary text nodes +--EXTENSIONS-- +dom +--FILE-- +append("bar"); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +try { + $dom->append($dom->createTextNode("bar")); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +$text = $dom->createTextNode("bar"); +try { + $dom->append($text); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +var_dump($text->parentNode); +var_dump($text->textContent); + +$element = $dom->createElement("container"); +$text = $element->appendChild($dom->createTextNode("text")); +try { + $dom->append($text); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Exception: Cannot insert text as a child of a document +Exception: Cannot insert text as a child of a document +Exception: Cannot insert text as a child of a document +NULL +string(3) "bar" +Exception: Cannot insert text as a child of a document diff --git a/ext/dom/tests/modern/spec/ProcessingInstruction_data_manipulation.phpt b/ext/dom/tests/modern/spec/ProcessingInstruction_data_manipulation.phpt new file mode 100644 index 0000000000000..498561a8b7885 --- /dev/null +++ b/ext/dom/tests/modern/spec/ProcessingInstruction_data_manipulation.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test processing instruction character data manipulation +--EXTENSIONS-- +dom +--FILE-- +'); + +$pi = $dom->documentElement->firstChild; + +echo $pi->substringData(0, 3), "\n"; +$pi->appendData('foobar'); +echo $pi->textContent, "\n"; +$pi->insertData(6, 'oooooo'); +echo $pi->textContent, "\n"; +$pi->deleteData(0, strlen('value')); +echo $pi->textContent, "\n"; +$pi->replaceData(1, 8, 'oo'); +echo $pi->textContent, "\n"; + +?> +--EXPECT-- +val +valuefoobar +valuefoooooooobar +foooooooobar +foobar diff --git a/ext/dom/tests/modern/spec/Text_splitText_edge_case.phpt b/ext/dom/tests/modern/spec/Text_splitText_edge_case.phpt new file mode 100644 index 0000000000000..5b46a2ba8dcfe --- /dev/null +++ b/ext/dom/tests/modern/spec/Text_splitText_edge_case.phpt @@ -0,0 +1,24 @@ +--TEST-- +DOM\Text:splitText() edge case +--EXTENSIONS-- +dom +--FILE-- +createTextNode('Hello World!'); +var_dump($text->splitText(5)->wholeText); +try { + var_dump($text->splitText(-1)); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + var_dump($text->splitText(200)); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +string(7) " World!" +DOM\Text::splitText(): Argument #1 ($offset) must be greater than or equal to 0 +Index Size Error diff --git a/ext/dom/tests/modern/spec/XMLDocument_getElementsByTagName.phpt b/ext/dom/tests/modern/spec/XMLDocument_getElementsByTagName.phpt new file mode 100644 index 0000000000000..ca521c6ff2cbd --- /dev/null +++ b/ext/dom/tests/modern/spec/XMLDocument_getElementsByTagName.phpt @@ -0,0 +1,58 @@ +--TEST-- +XMLDocument::getElementsByTagName +--EXTENSIONS-- +dom +--FILE-- +appendChild(createElement($dom, "container")); +$container->appendChild(createElement($dom, "HTML", "1")); +$container->appendChild(createElementNS($dom, "http://www.w3.org/1999/xhtml", "html", "2")); +$container->appendChild(createElementNS($dom, NULL, "html", "3")); +$container->appendChild(createElementNS($dom, NULL, "HTML", "4")); +$container->appendChild(createElementNS($dom, "urn:foo", "htML", "5")); +$container->appendChild(createElement($dom, "foo:HTML", "6")); +$container->appendChild(createElementNS($dom, "urn:a", "foo:HTML", "7")); // Should never match in this test file +$container->appendChild(createElementNS($dom, "http://www.w3.org/1999/xhtml", "bar:HTML", "8")); +$container->appendChild(createElementNS($dom, "http://www.w3.org/1999/xhtml", "bar:html", "9")); + +dumpNodeList($dom->getElementsByTagName("HTml")); +dumpNodeList($dom->getElementsByTagName("htML")); +dumpNodeList($dom->getElementsByTagName("html")); +dumpNodeList($dom->getElementsByTagName("HTML")); +dumpNodeList($dom->getElementsByTagName("foo:html")); +dumpNodeList($dom->getElementsByTagName("foo:HTML")); +dumpNodeList($dom->getElementsByTagName("bar:HTML")); +dumpNodeList($dom->getElementsByTagName("bar:html")); + +?> +--EXPECT-- +Node list length: int(0) +--- +Node list length: int(1) + htML 5 +--- +Node list length: int(2) + html 2 + html 3 +--- +Node list length: int(2) + HTML 1 + HTML 4 +--- +Node list length: int(0) +--- +Node list length: int(2) + foo:HTML 6 + foo:HTML 7 +--- +Node list length: int(1) + bar:HTML 8 +--- +Node list length: int(1) + bar:html 9 +--- diff --git a/ext/dom/tests/modern/spec/XMLDocument_version.phpt b/ext/dom/tests/modern/spec/XMLDocument_version.phpt new file mode 100644 index 0000000000000..accc06ce3d77f --- /dev/null +++ b/ext/dom/tests/modern/spec/XMLDocument_version.phpt @@ -0,0 +1,30 @@ +--TEST-- +XMLDocument::$version +--EXTENSIONS-- +dom +--FILE-- +'); +var_dump($dom->xmlVersion); + +foreach (['0.1', '1.0', '1.1', '', 'foo'] as $version) { + try { + $dom->xmlVersion = $version; + } catch (ValueError $e) { + echo $e->getMessage(), "\n"; + } + var_dump($dom->xmlVersion); +} + +?> +--EXPECT-- +string(3) "1.0" +Invalid XML version +string(3) "1.0" +string(3) "1.0" +string(3) "1.1" +Invalid XML version +string(3) "1.1" +Invalid XML version +string(3) "1.1" diff --git a/ext/dom/tests/modern/spec/appendChild_dtd_legacy.phpt b/ext/dom/tests/modern/spec/appendChild_dtd_legacy.phpt new file mode 100644 index 0000000000000..25a106479f16f --- /dev/null +++ b/ext/dom/tests/modern/spec/appendChild_dtd_legacy.phpt @@ -0,0 +1,39 @@ +--TEST-- +appendChild() with DocumentType +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + +]> + +XML); +$doctype = $original->doctype->cloneNode(); +foreach ($doctype->entities as $entity) { + echo "Found entity: ", $entity->nodeName, "\n"; +} + +$other = new DOMDocument(); +$doctype = $other->importNode($original->doctype); +$other->appendChild($doctype); +$other->appendChild($doctype); +try { + $other->appendChild($other->implementation->createDocumentType('doc', '', '')); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +echo $other->saveXML(); + +?> +--EXPECT-- +Found entity: foo +A document may only contain one document type + + +]> diff --git a/ext/dom/tests/modern/spec/append_text_nodes_invalid_hierarchy.phpt b/ext/dom/tests/modern/spec/append_text_nodes_invalid_hierarchy.phpt new file mode 100644 index 0000000000000..5b66c030a4f19 --- /dev/null +++ b/ext/dom/tests/modern/spec/append_text_nodes_invalid_hierarchy.phpt @@ -0,0 +1,28 @@ +--TEST-- +Document::append() with text nodes in an invalid hierarchy +--EXTENSIONS-- +dom +--FILE-- +append("foo", "bar", "baz", $dom); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +try { + $dom->append("foo", "bar", "baz"); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +var_dump($dom->saveHTML()); + +?> +--EXPECT-- +Hierarchy Request Error +Cannot insert text as a child of a document +string(0) "" diff --git a/ext/dom/tests/modern/spec/attribute_entity_expansion.phpt b/ext/dom/tests/modern/spec/attribute_entity_expansion.phpt new file mode 100644 index 0000000000000..19a8f91024eb3 --- /dev/null +++ b/ext/dom/tests/modern/spec/attribute_entity_expansion.phpt @@ -0,0 +1,49 @@ +--TEST-- +Attribute entity expansion +--EXTENSIONS-- +dom +--FILE-- +createElement('elt'); +$doc->appendChild($elt); +$elt->setAttribute('a','&'); +print $doc->saveHTML($elt) . "\n"; + +$attr = $elt->getAttributeNode('a'); +$attr->value = '&'; +print "$attr->value\n"; +print $doc->saveHTML($elt) . "\n"; + +$attr->removeChild($attr->firstChild); +print $doc->saveHTML($elt) . "\n"; + +$attr->nodeValue = '&'; +print "$attr->nodeValue\n"; +print $doc->saveHTML($elt) . "\n"; + +$attr->nodeValue = '&'; +print "$attr->nodeValue\n"; +print $doc->saveHTML($elt) . "\n"; + +$elt->removeAttributeNode($attr); +$elt->setAttributeNS('http://www.w3.org/2000/svg', 'svg:id','&'); +print $doc->saveHTML($elt) . "\n"; + +$attr = $elt->getAttributeNodeNS('http://www.w3.org/2000/svg', 'id'); +$attr->value = '<&'; +print "$attr->value\n"; +print $doc->saveHTML($elt) . "\n"; +?> +--EXPECT-- + +& + + +& + +& + + +<& + diff --git a/ext/dom/tests/modern/spec/attribute_entity_expansion_legacy.phpt b/ext/dom/tests/modern/spec/attribute_entity_expansion_legacy.phpt new file mode 100644 index 0000000000000..9b8ed50801f5e --- /dev/null +++ b/ext/dom/tests/modern/spec/attribute_entity_expansion_legacy.phpt @@ -0,0 +1,51 @@ +--TEST-- +Attribute entity expansion in a legacy document +--EXTENSIONS-- +dom +--FILE-- +createElement('elt'); +$doc->appendChild($elt); +$elt->setAttribute('a','&'); +print $doc->saveXML($elt) . "\n"; + +$attr = $elt->getAttributeNode('a'); +$attr->value = '&'; +print "$attr->value\n"; +print $doc->saveXML($elt) . "\n"; + +$attr->removeChild($attr->firstChild); +print $doc->saveXML($elt) . "\n"; + +$attr->nodeValue = '&'; +print "$attr->nodeValue\n"; +print $doc->saveXML($elt) . "\n"; + +$attr->nodeValue = '&'; +print "$attr->nodeValue\n"; +print $doc->saveXML($elt) . "\n"; + +$elt->removeAttributeNode($attr); +$elt->setAttributeNS('http://www.w3.org/2000/svg', 'svg:id','&'); +print $doc->saveXML($elt) . "\n"; + +$attr = $elt->getAttributeNodeNS('http://www.w3.org/2000/svg', 'id'); +$attr->value = '<&'; +print "$attr->value\n"; +print $doc->saveXML($elt) . "\n"; +?> +--EXPECTF-- + +& + + + +Warning: main(): unterminated entity reference in %s on line %d + + +& + + +<& + diff --git a/ext/dom/tests/modern/spec/bug47530.phpt b/ext/dom/tests/modern/spec/bug47530.phpt new file mode 100644 index 0000000000000..16d3078d07b98 --- /dev/null +++ b/ext/dom/tests/modern/spec/bug47530.phpt @@ -0,0 +1,146 @@ +--TEST-- +Bug #47530 (Importing objects into document fragments creates bogus "default" namespace) +--EXTENSIONS-- +dom +--FILE-- +'); + $root = $doc->documentElement; + $frag = $doc->createDocumentFragment(); + $frag->appendChild($doc->importNode($root->firstChild)); + $root->appendChild($frag); + echo $doc->saveXML(), "\n"; +} + +function test_document_fragment_without_import() { + $doc = DOM\XMLDocument::createFromString(''); + $frag = $doc->createDocumentFragment(); + $frag->appendChild($doc->createElementNS('https://php.net/bar', 'bar')); + $frag->appendChild($doc->createElementNS('', 'bar')); + $element = $doc->documentElement->firstChild; + $element->appendChild($frag); + unset($frag); // Free fragment, should not break getting the namespaceURI below + echo $doc->saveXML(), "\n"; + unset($doc); + var_dump($element->firstChild->tagName); + var_dump($element->firstChild->namespaceURI); + var_dump($element->firstChild->nextSibling->tagName); + var_dump($element->firstChild->nextSibling->namespaceURI); +} + +function test_document_import() { + $xml = << + +
+

Test-Text

+
+
+XML; + + $dom = DOM\XMLDocument::createFromString($xml); + + $dom2 = DOM\XMLDocument::createEmpty(); + $importedNode = $dom2->importNode($dom->documentElement, true); + $dom2->appendChild($importedNode); + + echo $dom2->saveXML(), "\n"; +} + +function test_partial_document_import() { + $xml = << + +
+

Test-Text

+ More test text + Even more test text +
+
+XML; + + $dom = DOM\XMLDocument::createFromString($xml); + + $dom2 = DOM\XMLDocument::createFromString(''); + $importedNode = $dom2->importNode($dom->documentElement, true); + $dom2->documentElement->appendChild($importedNode); + + // Freeing the original document shouldn't break the other document + unset($importedNode); + unset($dom); + + echo $dom2->saveXML(), "\n"; +} + +function test_document_import_with_attributes() { + $dom = DOM\XMLDocument::createFromString('

'); + $dom2 = DOM\XMLDocument::createFromString('
'); + $dom2->documentElement->appendChild($dom2->importNode($dom->documentElement->firstChild)); + echo $dom2->saveXML(), "\n"; + + $dom2->documentElement->firstChild->appendChild($dom2->importNode($dom->documentElement->firstChild->nextSibling)); + echo $dom2->saveXML(), "\n"; +} + +function test_appendChild_with_shadowing() { + $dom = DOM\XMLDocument::createFromString(''); + + $a = $dom->documentElement->firstElementChild; + $b = $a->nextSibling; + $b->remove(); + $a->appendChild($b); + + echo $dom->saveXML(), "\n"; +} + +echo "-- Test document fragment with import --\n"; +test_document_fragment_with_import(); +echo "-- Test document fragment without import --\n"; +test_document_fragment_without_import(); +echo "-- Test document import --\n"; +test_document_import(); +echo "-- Test partial document import --\n"; +test_partial_document_import(); +echo "-- Test document import with attributes --\n"; +test_document_import_with_attributes(); +echo "-- Test appendChild with shadowing --\n"; +test_appendChild_with_shadowing(); + +?> +--EXPECT-- +-- Test document fragment with import -- + + +-- Test document fragment without import -- + + +string(3) "bar" +string(19) "https://php.net/bar" +string(3) "bar" +NULL +-- Test document import -- + + +
+

Test-Text

+
+
+-- Test partial document import -- + + +
+

Test-Text

+ More test text + Even more test text +
+
+-- Test document import with attributes -- + +

+ +

+-- Test appendChild with shadowing -- + +
diff --git a/ext/dom/tests/bug47847.phpt b/ext/dom/tests/modern/spec/bug47847.phpt similarity index 56% rename from ext/dom/tests/bug47847.phpt rename to ext/dom/tests/modern/spec/bug47847.phpt index 1c9ec95418e01..70a0d7bc13589 100644 --- a/ext/dom/tests/bug47847.phpt +++ b/ext/dom/tests/modern/spec/bug47847.phpt @@ -2,12 +2,9 @@ Bug #47847 (importNode loses the namespace of an XML element) --EXTENSIONS-- dom ---XFAIL-- -See https://github.com/php/php-src/pull/12308 --FILE-- loadXML(<< @@ -16,14 +13,17 @@ $fromdom->loadXML(<< XML); -$aDOM = new DOMDocument(); +$aDOM = DOM\XMLDocument::createEmpty(); $imported = $aDOM->importNode($fromdom->documentElement->firstElementChild, true); $aDOM->appendChild($imported); -echo $aDOM->saveXML(); +echo $aDOM->saveXML(), "\n"; + +var_dump($aDOM->documentElement->firstElementChild->prefix); ?> --EXPECT-- - - - + + + +string(2) "ns" diff --git a/ext/dom/tests/bug55294.phpt b/ext/dom/tests/modern/spec/bug55294.phpt similarity index 77% rename from ext/dom/tests/bug55294.phpt rename to ext/dom/tests/modern/spec/bug55294.phpt index eb198a78ddee3..b0acd5f2118cf 100644 --- a/ext/dom/tests/bug55294.phpt +++ b/ext/dom/tests/modern/spec/bug55294.phpt @@ -2,13 +2,10 @@ Bug #55294 (DOMDocument::importNode shifts namespaces when "default" namespace exists) --EXTENSIONS-- dom ---XFAIL-- -See https://github.com/php/php-src/pull/12308 --FILE-- loadXML(<< @@ -17,7 +14,7 @@ $aDOM->loadXML(<<importNode($aDOM->getElementsByTagNameNS('http://example.com/A', 'B')->item(0), true); $bDOM->appendChild($node); @@ -25,7 +22,7 @@ echo $bDOM->saveXML(), "\n"; ?> --EXPECT-- - + diff --git a/ext/dom/tests/modern/spec/bug81468.phpt b/ext/dom/tests/modern/spec/bug81468.phpt new file mode 100644 index 0000000000000..3e0835ece1c49 --- /dev/null +++ b/ext/dom/tests/modern/spec/bug81468.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #81468 (Inconsistent default namespace inheritance) +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElementNS('some:namespace', 'foo')) + ->appendChild($dom->createElement('bar')); +echo ($xml = $dom->saveXML()), "\n"; + +$xpath = new DOM\XPath($dom); +$xpath->registerNamespace('n', 'some:namespace'); +echo "/n:foo/bar -> ", count($xpath->query('/n:foo/bar')), "\n"; +echo "/n:foo/n:bar -> ", count($xpath->query('/n:foo/n:bar')), "\n"; + +// + +$dom = DOM\XMLDocument::createFromString($xml); +echo ($xml = $dom->saveXML()), "\n"; + +$xpath = new DOM\XPath($dom); +$xpath->registerNamespace('n', 'some:namespace'); +echo "/n:foo/bar -> ", count($xpath->query('/n:foo/bar')), "\n"; +echo "/n:foo/n:bar -> ", count($xpath->query('/n:foo/n:bar')), "\n"; + +?> +--EXPECT-- + + +/n:foo/bar -> 1 +/n:foo/n:bar -> 0 + + +/n:foo/bar -> 1 +/n:foo/n:bar -> 0 diff --git a/ext/dom/tests/modern/spec/clone_conflicting_namespace_prefix.phpt b/ext/dom/tests/modern/spec/clone_conflicting_namespace_prefix.phpt new file mode 100644 index 0000000000000..b2afdfad3b74d --- /dev/null +++ b/ext/dom/tests/modern/spec/clone_conflicting_namespace_prefix.phpt @@ -0,0 +1,58 @@ +--TEST-- +Test clone with conflicting namespace prefix +--EXTENSIONS-- +dom +--FILE-- + + + + + + bar + + + + +XML); + +$dom->documentElement->firstElementChild->setAttributeNS("urn:y", "a:foo", "bar"); + +echo $dom->saveXML(), "\n"; + +$clone = clone $dom->documentElement->firstElementChild; +var_dump($clone->nodeName, $clone->namespaceURI); + +foreach ($clone->attributes as $attr) { + var_dump($attr->name, $attr->namespaceURI); +} + +echo $dom->saveXML($clone), "\n"; + +?> +--EXPECT-- + + + + + + bar + + + + +string(7) "a:root1" +string(5) "urn:a" +string(7) "xmlns:a" +string(29) "http://www.w3.org/2000/xmlns/" +string(5) "a:foo" +string(5) "urn:y" + + + + bar + + + diff --git a/ext/dom/tests/modern/spec/clone_document.phpt b/ext/dom/tests/modern/spec/clone_document.phpt new file mode 100644 index 0000000000000..8a855bcb23ce4 --- /dev/null +++ b/ext/dom/tests/modern/spec/clone_document.phpt @@ -0,0 +1,68 @@ +--TEST-- +Cloning a whole document +--EXTENSIONS-- +dom +--FILE-- + + + + +]> + + afoob + + + + &foo; + +XML); + +echo "---\n"; + +var_dump($dom->getElementsByTagName("child2")[0]->firstChild->nodeName); + +echo "---\n"; + +$clone = clone $dom; +echo $clone->saveXML(), "\n"; + +var_dump($clone->getElementsByTagName("child2")[0]->firstChild->nodeName); + +echo "---\n"; + +$clone = $dom->cloneNode(false); +echo $clone->saveXML(), "\n"; + +echo "---\n"; + +$clone = $dom->documentElement->cloneNode(false); +echo $clone->ownerDocument->saveXML($clone), "\n"; + +?> +--EXPECT-- +--- +string(3) "foo" +--- + + + + +]> + + afoob + + + + &foo; + +string(3) "foo" +--- + + +--- + diff --git a/ext/dom/tests/modern/spec/clone_dtd_node.phpt b/ext/dom/tests/modern/spec/clone_dtd_node.phpt new file mode 100644 index 0000000000000..7fd74022eab45 --- /dev/null +++ b/ext/dom/tests/modern/spec/clone_dtd_node.phpt @@ -0,0 +1,28 @@ +--TEST-- +Cloning a dtd node +--EXTENSIONS-- +dom +--FILE-- +'); + +$dt1 = clone $dom->doctype; +var_dump($dt1 === $dom->doctype); +var_dump($dt1->name); + +echo "\n"; + +$dt2 = $dom->doctype->cloneNode(); +var_dump($dt2 === $dt1); +var_dump($dt2 === $dom->doctype); +var_dump($dt2->name); + +?> +--EXPECT-- +bool(false) +string(4) "html" + +bool(false) +bool(false) +string(4) "html" diff --git a/ext/dom/tests/modern/spec/clone_entity_reference.phpt b/ext/dom/tests/modern/spec/clone_entity_reference.phpt new file mode 100644 index 0000000000000..b42d33e10059e --- /dev/null +++ b/ext/dom/tests/modern/spec/clone_entity_reference.phpt @@ -0,0 +1,59 @@ +--TEST-- +Cloning an entity reference without also cloning the document +--EXTENSIONS-- +dom +--FILE-- + + +]> +&foo; +XML); + +$clone = $dom->documentElement->cloneNode(true); +echo $dom->saveXML($clone), "\n"; + +var_dump($clone->firstChild->firstChild); + +?> +--EXPECTF-- +&foo; +object(DOM\Entity)#4 (17) { + ["publicId"]=> + NULL + ["systemId"]=> + NULL + ["notationName"]=> + NULL + ["nodeType"]=> + int(17) + ["nodeName"]=> + string(3) "foo" + ["baseURI"]=> + string(%d) "%s" + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + string(22) "(object value omitted)" + ["lastChild"]=> + string(22) "(object value omitted)" + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["nodeValue"]=> + NULL + ["textContent"]=> + NULL +} diff --git a/ext/dom/tests/modern/spec/clone_text_nodes.phpt b/ext/dom/tests/modern/spec/clone_text_nodes.phpt new file mode 100644 index 0000000000000..6809dc7690050 --- /dev/null +++ b/ext/dom/tests/modern/spec/clone_text_nodes.phpt @@ -0,0 +1,43 @@ +--TEST-- +Cloning text nodes should not merge adjacent text nodes +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElementNS("urn:a", "a:root")); +$root->setAttribute("foo", "bar"); + +$root->appendChild($dom->createTextNode("a")); +$root->appendChild($dom->createTextNode("foo")); +$root->appendChild($dom->createTextNode("b")); +$child = $root->appendChild($dom->createElement("child")); +$child->appendChild($dom->createTextNode("c")); +$child2 = $root->appendChild($dom->createElement("child2")); +$child2->appendChild($dom->createTextNode("d")); + +echo $dom->saveXML(), "\n"; + +$clone = clone $root; +var_dump($clone->firstChild->textContent); +var_dump($clone->firstChild->nextSibling->textContent); +var_dump($clone->firstChild->nextSibling->nextSibling->textContent); +echo $dom->saveXML($clone), "\n"; + +$clone = $child2->cloneNode(true); +echo $dom->saveXML($clone), "\n"; + +$clone = $child2->cloneNode(false); +echo $dom->saveXML($clone), "\n"; + +?> +--EXPECT-- + +afoobcd +string(1) "a" +string(3) "foo" +string(1) "b" +afoobcd +d + diff --git a/ext/dom/tests/modern/spec/create_element_util.inc b/ext/dom/tests/modern/spec/create_element_util.inc new file mode 100644 index 0000000000000..92a62059ca657 --- /dev/null +++ b/ext/dom/tests/modern/spec/create_element_util.inc @@ -0,0 +1,12 @@ +createElement($name); + if ($value) $element->textContent = $value; + return $element; +} + +function createElementNS($doc, $ns, $name, ?string $value = null) { + $element = $doc->createElementNS($ns, $name); + if ($value) $element->textContent = $value; + return $element; +} diff --git a/ext/dom/tests/modern/spec/default_namespace_move.phpt b/ext/dom/tests/modern/spec/default_namespace_move.phpt new file mode 100644 index 0000000000000..ee6d43fce82a0 --- /dev/null +++ b/ext/dom/tests/modern/spec/default_namespace_move.phpt @@ -0,0 +1,26 @@ +--TEST-- +Default namespace move +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElementNS('urn:a', 'foo:bar')); +$child = $root->appendChild($dom->createElementNS('urn:a', 'foo:baz')); +$child->setAttributeNS("urn:b", "foo:attr", "value"); +$root->remove(); +$dom->appendChild($child); +echo $dom->saveXML(), "\n"; + +$dom2 = DOM\XMLDocument::createEmpty(); +$child = $dom2->importNode($child, true); +$dom2->appendChild($child); +echo $dom2->saveXML(), "\n"; + +?> +--EXPECT-- + + + + diff --git a/ext/dom/tests/modern/spec/documentURI_URL.phpt b/ext/dom/tests/modern/spec/documentURI_URL.phpt new file mode 100644 index 0000000000000..20ba9bbb04493 --- /dev/null +++ b/ext/dom/tests/modern/spec/documentURI_URL.phpt @@ -0,0 +1,36 @@ +--TEST-- +Document URI/URL +--EXTENSIONS-- +dom +--FILE-- +'); +var_dump($dom->documentURI); +var_dump($dom->URL); + +try { + $dom->URL = NULL; + var_dump($dom->documentURI); + var_dump($dom->URL); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +$dom->URL = ""; +var_dump($dom->documentURI); +var_dump($dom->URL); + +$dom->URL = "http://example.com/"; +var_dump($dom->documentURI); +var_dump($dom->URL); + +?> +--EXPECTF-- +string(%d) "%s" +string(%d) "%s" +Cannot assign null to property DOM\Document::$URL of type string +string(0) "" +string(0) "" +string(%d) "%s" +string(%d) "%s" diff --git a/ext/dom/tests/modern/spec/dom_parsing_gh44.phpt b/ext/dom/tests/modern/spec/dom_parsing_gh44.phpt new file mode 100644 index 0000000000000..e25e48a1aa11e --- /dev/null +++ b/ext/dom/tests/modern/spec/dom_parsing_gh44.phpt @@ -0,0 +1,15 @@ +--TEST-- +DOM-Parsing GH-44 (It's possible for 'generate a prefix' algorithm to generate a prefix conflicting with an existing one) +--EXTENSIONS-- +dom +--FILE-- +')->documentElement; +$root->firstChild->setAttributeNS('uri3', 'attr1', 'value1'); +echo $root->ownerDocument->saveXML(), "\n"; + +?> +--EXPECT-- + + diff --git a/ext/dom/tests/modern/spec/dom_parsing_gh45.phpt b/ext/dom/tests/modern/spec/dom_parsing_gh45.phpt new file mode 100644 index 0000000000000..bd13c848e0e4f --- /dev/null +++ b/ext/dom/tests/modern/spec/dom_parsing_gh45.phpt @@ -0,0 +1,18 @@ +--TEST-- +DOM-Parsing GH-45 (It's possible that 'retrieve a preferred prefix string' returns a wrong prefix for the specified namespace) +--EXTENSIONS-- +dom +--FILE-- +')->documentElement; +$root->firstChild->setAttributeNS('u1', 'name', 'v'); +$attr = $root->firstChild->getAttributeNodeNS("u1", "name"); +var_dump($attr->prefix, $attr->namespaceURI); +echo $root->ownerDocument->saveXML($root), "\n"; + +?> +--EXPECT-- +NULL +string(2) "u1" + diff --git a/ext/dom/tests/modern/spec/dom_parsing_gh47.phpt b/ext/dom/tests/modern/spec/dom_parsing_gh47.phpt new file mode 100644 index 0000000000000..16cd4abd41307 --- /dev/null +++ b/ext/dom/tests/modern/spec/dom_parsing_gh47.phpt @@ -0,0 +1,58 @@ +--TEST-- +DOM-Parsing GH-47 (XML null namespaces need to be preserved) +--EXTENSIONS-- +dom +--FILE-- + + + + + + Smith + Joe + + + Jones + John + + + + +XML); + +echo $document->saveXML(), "\n"; + +$people = $document->getElementsByTagNameNS(null, 'people')->item(0); +echo $document->saveXML($people), "\n"; + +?> +--EXPECT-- + + + + + + + Smith + Joe + + + Jones + John + + + + + + + Smith + Joe + + + Jones + John + + diff --git a/ext/dom/tests/modern/spec/dom_parsing_gh47_bis.phpt b/ext/dom/tests/modern/spec/dom_parsing_gh47_bis.phpt new file mode 100644 index 0000000000000..c142702ebda93 --- /dev/null +++ b/ext/dom/tests/modern/spec/dom_parsing_gh47_bis.phpt @@ -0,0 +1,21 @@ +--TEST-- +DOM-Parsing GH-47 bis (XML null namespaces need to be preserved) +--EXTENSIONS-- +dom +--FILE-- +'); +echo $dom->saveXML(), "\n"; + +$dom = DOM\XMLDocument::createFromString(''); +echo $dom->saveXML(), "\n"; + +?> +--EXPECT-- + + + + diff --git a/ext/dom/tests/modern/spec/dump_attr.inc b/ext/dom/tests/modern/spec/dump_attr.inc new file mode 100644 index 0000000000000..fe3effd492b92 --- /dev/null +++ b/ext/dom/tests/modern/spec/dump_attr.inc @@ -0,0 +1,13 @@ +name}\n"; + var_dump($attr->prefix); + var_dump($attr->nodeName); + var_dump($attr->namespaceURI); +} + +function dumpAttrs(DOM\Element $element) { + foreach ($element->attributes as $attr) { + dumpAttr($attr); + } +} diff --git a/ext/dom/tests/modern/spec/element_dump.inc b/ext/dom/tests/modern/spec/element_dump.inc new file mode 100644 index 0000000000000..652debbfbabd4 --- /dev/null +++ b/ext/dom/tests/modern/spec/element_dump.inc @@ -0,0 +1,9 @@ +tagName); + echo "nodeName: ", var_dump($element->nodeName); + echo "textContent: ", var_dump($element->textContent); + echo "prefix: ", var_dump($element->prefix); + echo "namespaceURI: ", var_dump($element->namespaceURI); + echo $element->ownerDocument->saveHTML($element), "\n\n"; +} diff --git a/ext/dom/tests/modern/spec/gh11404_1.phpt b/ext/dom/tests/modern/spec/gh11404_1.phpt new file mode 100644 index 0000000000000..884f3560c064e --- /dev/null +++ b/ext/dom/tests/modern/spec/gh11404_1.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-11404 (DOMDocument::savexml and friends ommit xmlns="" declaration for null namespace, creating incorrect xml representation of the DOM) +--EXTENSIONS-- +dom +--FILE-- +'); + +$nodeA = $dom1->createElement('none'); +$nodeB = $dom1->createElementNS(null, 'none'); + +$dom1->documentElement->appendChild($nodeA); +$dom1->documentElement->appendChild($nodeB); + +echo $dom1->saveXML(), "\n"; + +var_dump($nodeA->namespaceURI, $nodeB->namespaceURI); + +?> +--EXPECT-- + + +NULL +NULL diff --git a/ext/dom/tests/modern/spec/gh11404_2.phpt b/ext/dom/tests/modern/spec/gh11404_2.phpt new file mode 100644 index 0000000000000..601c2d4e5ac1c --- /dev/null +++ b/ext/dom/tests/modern/spec/gh11404_2.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-11404 (DOMDocument::savexml and friends ommit xmlns="" declaration for null namespace, creating incorrect xml representation of the DOM) +--EXTENSIONS-- +dom +--FILE-- +'); + +$dom2 = DOM\XMLDocument::createFromString(''); + +$dom1->documentElement->append( + $dom1->importNode( + $dom2->documentElement + ) +); + +echo $dom1->saveXML(); +?> +--EXPECT-- + + diff --git a/ext/dom/tests/modern/spec/import_entity_reference.phpt b/ext/dom/tests/modern/spec/import_entity_reference.phpt new file mode 100644 index 0000000000000..02d51af68aad6 --- /dev/null +++ b/ext/dom/tests/modern/spec/import_entity_reference.phpt @@ -0,0 +1,54 @@ +--TEST-- +Importing an entity reference without also importing the document +--EXTENSIONS-- +dom +--FILE-- + + +]> +&foo; +XML); + +$importer = DOM\XMLDocument::createEmpty(); +$imported = $importer->importNode($dom->documentElement, true); +echo $importer->saveXML($imported), "\n"; + +var_dump($imported->firstChild); + +?> +--EXPECT-- +&foo; +object(DOM\EntityReference)#3 (14) { + ["nodeType"]=> + int(5) + ["nodeName"]=> + string(3) "foo" + ["baseURI"]=> + string(11) "about:blank" + ["isConnected"]=> + bool(false) + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" + ["parentElement"]=> + string(22) "(object value omitted)" + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["nodeValue"]=> + NULL + ["textContent"]=> + NULL +} diff --git a/ext/dom/tests/modern/spec/import_of_lone_attribute.phpt b/ext/dom/tests/modern/spec/import_of_lone_attribute.phpt new file mode 100644 index 0000000000000..b612c409e0435 --- /dev/null +++ b/ext/dom/tests/modern/spec/import_of_lone_attribute.phpt @@ -0,0 +1,27 @@ +--TEST-- +Import of lone attribute +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement('root')); + +$root->setAttributeNS("urn:a", "a:foo", "bar"); +$attr = $root->getAttributeNodeNS("urn:a", "foo"); + +$importer = DOM\XMLDocument::createEmpty(); +$attr = $importer->importNode($attr, true); +echo $importer->saveXML($attr), "\n"; + +unset($dom); +unset($root); + +var_dump($attr->prefix, $attr->namespaceURI); + +?> +--EXPECT-- +a:foo="bar" +string(1) "a" +string(5) "urn:a" diff --git a/ext/dom/tests/modern/spec/import_of_namepace_conflicting_attributes.phpt b/ext/dom/tests/modern/spec/import_of_namepace_conflicting_attributes.phpt new file mode 100644 index 0000000000000..e66ec34efa713 --- /dev/null +++ b/ext/dom/tests/modern/spec/import_of_namepace_conflicting_attributes.phpt @@ -0,0 +1,53 @@ +--TEST-- +Import of namespace-conflicting attributes +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement('root')); + +$root->setAttributeNS("urn:a", "a:root1", "bar"); +$root1 = $root->getAttributeNodeNS("urn:a", "root1"); +$root->setAttributeNS("urn:b", "a:root2", "bar"); +$root2 = $root->getAttributeNodeNS("urn:b", "root2"); + +$child = $root->appendChild($dom->createElement("child")); +$child->setAttributeNS("urn:x", "a:child1", "bar"); +$child1 = $child->getAttributeNodeNS("urn:x", "child1"); + +echo $dom->saveXML(), "\n"; + +var_dump($root1->prefix, $root1->namespaceURI); +var_dump($root2->prefix, $root2->namespaceURI); + +$child->removeAttribute("a:child1"); +$root->setAttributeNodeNS($child1); + +$importer = DOM\XMLDocument::createEmpty(); +$imported = $importer->importNode($root, true); +echo $importer->saveXML($imported), "\n"; + +var_dump($imported->getAttributeNodeNS("urn:a", "root1")->prefix); +var_dump($imported->getAttributeNodeNS("urn:a", "root1")->namespaceURI); +var_dump($imported->getAttributeNodeNS("urn:b", "root2")->prefix); +var_dump($imported->getAttributeNodeNS("urn:b", "root2")->namespaceURI); +var_dump($imported->getAttributeNodeNS("urn:x", "child1")->prefix); +var_dump($imported->getAttributeNodeNS("urn:x", "child1")->namespaceURI); + +?> +--EXPECT-- + + +string(1) "a" +string(5) "urn:a" +string(1) "a" +string(5) "urn:b" + +string(1) "a" +string(5) "urn:a" +string(1) "a" +string(5) "urn:b" +string(1) "a" +string(5) "urn:x" diff --git a/ext/dom/tests/modern/spec/node_list_dump.inc b/ext/dom/tests/modern/spec/node_list_dump.inc new file mode 100644 index 0000000000000..2aa1e12da7164 --- /dev/null +++ b/ext/dom/tests/modern/spec/node_list_dump.inc @@ -0,0 +1,9 @@ +length); + foreach ($nodeList as $node) { + echo "\t{$node->nodeName} {$node->textContent}\n"; + } + echo "---\n"; +} diff --git a/ext/dom/tests/modern/spec/pre_insertion_validation.phpt b/ext/dom/tests/modern/spec/pre_insertion_validation.phpt new file mode 100644 index 0000000000000..b0f426be5cecd --- /dev/null +++ b/ext/dom/tests/modern/spec/pre_insertion_validation.phpt @@ -0,0 +1,117 @@ +--TEST-- +ParentNode/ChildNode pre-insertion validation +--EXTENSIONS-- +dom +--FILE-- +"); +$doctype = $dom->doctype; +$dom->removeChild($doctype); + +echo "--- Trying to insert text node into the document ---\n"; + +try { + $dom->append("foo", "bar", "baz"); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} +try { + $dom->append($dom->createTextNode("text node")); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} +try { + $dom->append($dom->createCDATASection("text node")); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +echo "--- Trying to insert doctype into not a document ---\n"; + +$element = $dom->createElement("foo"); +try { + $element->append($doctype); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +echo "--- Trying to insert doctype at the wrong place in a document ---\n"; + +try { + $dom->append($doctype); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +echo "--- Prepend doctype in a document should work ---\n"; + +$dom->prepend($doctype); + +echo "--- Trying to create multiple document roots ---\n"; + +try { + $dom->append($dom->createElement("foo")); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +echo "--- Trying to insert an element before a document type ---\n"; + +$dom->documentElement->remove(); +try { + $dom->prepend($element); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +echo "--- Document output ---\n"; + +echo $dom->saveXML(), "\n"; + +echo "--- Document fragment edge cases with multiple elements ---\n"; + +$dom = DOM\XMLDocument::createEmpty(); +$fragment = $dom->createDocumentFragment(); +$fragment->append($dom->createElement("foo")); +$fragment->append($dom->createElement("bar")); +try { + $dom->append($fragment); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +echo "--- Document fragment edge cases with text ---\n"; + +$dom = DOM\XMLDocument::createEmpty(); +$fragment = $dom->createDocumentFragment(); +$fragment->append("foo"); +$fragment->append($dom->createCDATASection("bar")); +try { + $dom->append($fragment); +} catch (DOMException $e) { + echo "Exception: " . $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +--- Trying to insert text node into the document --- +Exception: Cannot insert text as a child of a document +Exception: Cannot insert text as a child of a document +Exception: Cannot insert text as a child of a document +--- Trying to insert doctype into not a document --- +Exception: Cannot insert a document type into anything other than a document +--- Trying to insert doctype at the wrong place in a document --- +Exception: Document types must be the first child in a document +--- Prepend doctype in a document should work --- +--- Trying to create multiple document roots --- +Exception: Cannot have more than one element child in a document +--- Trying to insert an element before a document type --- +Exception: Document types must be the first child in a document +--- Document output --- + + + +--- Document fragment edge cases with multiple elements --- +Exception: Cannot have more than one element child in a document +--- Document fragment edge cases with text --- +Exception: Cannot insert text as a child of a document diff --git a/ext/dom/tests/modern/spec/reconciliation_of_attributes.phpt b/ext/dom/tests/modern/spec/reconciliation_of_attributes.phpt new file mode 100644 index 0000000000000..be2db111092fc --- /dev/null +++ b/ext/dom/tests/modern/spec/reconciliation_of_attributes.phpt @@ -0,0 +1,53 @@ +--TEST-- +Reconciliation of attributes +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement('root')); + +$root->setAttributeNS("urn:a", "a:root1", "bar"); +$root1 = $root->getAttributeNodeNS("urn:a", "root1"); +$root->setAttributeNS("urn:b", "a:root2", "bar"); +$root2 = $root->getAttributeNodeNS("urn:b", "root2"); +$root->setAttributeNS("urn:a", "a:root3", "bar"); +$root3 = $root->getAttributeNodeNS("urn:a", "root3"); + +$child = $root->appendChild($dom->createElement("child")); +$child->setAttributeNS("urn:x", "a:child1", "bar"); +$child1 = $child->getAttributeNodeNS("urn:x", "child1"); +$child->setAttributeNS("urn:a", "a:child2", "bar"); +$child2 = $child->getAttributeNodeNS("urn:a", "child2"); + +echo $dom->saveXML(), "\n"; + +var_dump($root1->prefix, $root1->namespaceURI); +var_dump($root2->prefix, $root2->namespaceURI); +var_dump($root3->prefix, $root3->namespaceURI); + +$child->removeAttribute("a:child1"); +$root->setAttributeNodeNS($child1); + +var_dump($child1->prefix, $child1->namespaceURI); +var_dump($child2->prefix, $child2->namespaceURI); + +echo $dom->saveXML(), "\n"; + +?> +--EXPECT-- + + +string(1) "a" +string(5) "urn:a" +string(1) "a" +string(5) "urn:b" +string(1) "a" +string(5) "urn:a" +string(1) "a" +string(5) "urn:x" +string(1) "a" +string(5) "urn:a" + + diff --git a/ext/dom/tests/modern/spec/reconciliation_of_elements.phpt b/ext/dom/tests/modern/spec/reconciliation_of_elements.phpt new file mode 100644 index 0000000000000..f05ad8669c954 --- /dev/null +++ b/ext/dom/tests/modern/spec/reconciliation_of_elements.phpt @@ -0,0 +1,51 @@ +--TEST-- +Reconciliation of elements +--EXTENSIONS-- +dom +--FILE-- + + + + + + bar + + + + +XML); + +$root = $dom->documentElement; +$a_child = $dom->getElementsByTagName("a:child")[0]; + +$root->append($a_child); + +echo $dom->saveXML(), "\n"; + +echo "---\n"; + +// Trigger reconciliation via detach logic in ext/libxml +$root->remove(); +unset($root); + +echo $dom->saveXML($a_child), "\n"; + +?> +--EXPECT-- + + + + + + + + + bar + +--- + + bar + diff --git a/ext/dom/tests/modern/spec/serialize_default_and_prefixed_xmlns.phpt b/ext/dom/tests/modern/spec/serialize_default_and_prefixed_xmlns.phpt new file mode 100644 index 0000000000000..3bb61d1f63ae1 --- /dev/null +++ b/ext/dom/tests/modern/spec/serialize_default_and_prefixed_xmlns.phpt @@ -0,0 +1,26 @@ +--TEST-- +Serialize default and prefixed xmlns on elements +--EXTENSIONS-- +dom +--FILE-- + + + + + + +XML); + +echo $dom->saveXML(); + +?> +--EXPECT-- + + + + + + diff --git a/ext/dom/tests/modern/spec/serialize_element_unqualified.phpt b/ext/dom/tests/modern/spec/serialize_element_unqualified.phpt new file mode 100644 index 0000000000000..23ecc1321a825 --- /dev/null +++ b/ext/dom/tests/modern/spec/serialize_element_unqualified.phpt @@ -0,0 +1,22 @@ +--TEST-- +Serialize element choose nearest prefix if unqualified name +--EXTENSIONS-- +dom +--FILE-- +'); +echo $dom->saveXML(), "\n"; + +$dom = DOM\XMLDocument::createFromString(''); +$root = $dom->documentElement; +$child2 = $root->ownerDocument->createElementNS('u1', 'child2'); +$root->firstChild->appendChild($child2); +echo $dom->saveXML(), "\n"; + +?> +--EXPECT-- + + + + diff --git a/ext/dom/tests/modern/spec/serialize_non_default_empty_xmlns.phpt b/ext/dom/tests/modern/spec/serialize_non_default_empty_xmlns.phpt new file mode 100644 index 0000000000000..ab91d3e104b97 --- /dev/null +++ b/ext/dom/tests/modern/spec/serialize_non_default_empty_xmlns.phpt @@ -0,0 +1,16 @@ +--TEST-- +Serialize non-default but empty xmlns +--EXTENSIONS-- +dom +--FILE-- +'); + +echo $dom->saveXML(), "\n"; + +?> +--EXPECTF-- +Warning: DOM\XMLDocument::createFromString(): xmlns:a: ' ' is not a valid URI in Entity, line: 1 in %s on line %d + + diff --git a/ext/dom/tests/modern/spec/serialize_xml_attribute.phpt b/ext/dom/tests/modern/spec/serialize_xml_attribute.phpt new file mode 100644 index 0000000000000..8fd583c6b5f18 --- /dev/null +++ b/ext/dom/tests/modern/spec/serialize_xml_attribute.phpt @@ -0,0 +1,17 @@ +--TEST-- +Serialize XML attribute +--EXTENSIONS-- +dom +--FILE-- +"); +$root = $dom->documentElement; +$root->setAttribute("foo", "<>\"& \t\n\rfoo "); + +echo $dom->saveXML(), "\n"; + +?> +--EXPECT-- + + diff --git a/ext/dom/tests/modern/spec/serialize_xml_namespace_on_elements.phpt b/ext/dom/tests/modern/spec/serialize_xml_namespace_on_elements.phpt new file mode 100644 index 0000000000000..7e0ab39c666d1 --- /dev/null +++ b/ext/dom/tests/modern/spec/serialize_xml_namespace_on_elements.phpt @@ -0,0 +1,17 @@ +--TEST-- +Serialize XML namespace on elements +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElementNS("http://www.w3.org/XML/1998/namespace", "xml:test")); +$root->appendChild($dom->createElementNS("http://www.w3.org/XML/1998/namespace", "xml:child")); + +echo $dom->saveXML(), "\n"; + +?> +--EXPECT-- + + diff --git a/ext/dom/tests/modern/spec/serialize_xml_ns_decl.phpt b/ext/dom/tests/modern/spec/serialize_xml_ns_decl.phpt new file mode 100644 index 0000000000000..dc308b1ca829b --- /dev/null +++ b/ext/dom/tests/modern/spec/serialize_xml_ns_decl.phpt @@ -0,0 +1,16 @@ +--TEST-- +Document::saveXML() with XML namespace declaration +--EXTENSIONS-- +dom +--FILE-- +'); +$root = $dom->documentElement; +$root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xml', 'http://www.w3.org/XML/1998/namespace'); +echo $dom->saveXML(), "\n"; + +?> +--EXPECT-- + + diff --git a/ext/dom/tests/modern/spec/textContent_edge_cases.phpt b/ext/dom/tests/modern/spec/textContent_edge_cases.phpt new file mode 100644 index 0000000000000..b482ce01c790d --- /dev/null +++ b/ext/dom/tests/modern/spec/textContent_edge_cases.phpt @@ -0,0 +1,48 @@ +--TEST-- +textContent edge cases +--EXTENSIONS-- +dom +--FILE-- +text'); + +echo "document text content: "; +var_dump($dom->textContent); +try { + $dom->textContent = "foo"; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +$container = $dom->documentElement; + +$text = $container->firstChild; +$pi = $text->nextSibling; + +echo "text node text content: "; +var_dump($text->textContent); +echo "pi node text content: "; +var_dump($pi->textContent); + +$text->textContent = NULL; +$pi->textContent = NULL; + +echo "text node text content: "; +var_dump($text->textContent); +echo "pi node text content: "; +var_dump($pi->textContent); + +$container->textContent = NULL; +echo $dom->saveXML(), "\n"; + +?> +--EXPECT-- +document text content: NULL +Cannot modify readonly property DOM\XMLDocument::$textContent +text node text content: string(4) "text" +pi node text content: string(5) "value" +text node text content: string(0) "" +pi node text content: string(0) "" + + diff --git a/ext/dom/tests/modern/spec/text_merging.phpt b/ext/dom/tests/modern/spec/text_merging.phpt new file mode 100644 index 0000000000000..b3cd382158b3a --- /dev/null +++ b/ext/dom/tests/modern/spec/text_merging.phpt @@ -0,0 +1,36 @@ +--TEST-- +Appending should not merge text nodes +--EXTENSIONS-- +dom +--FILE-- +createElement("div"); +$span = $dom->createElement("span"); +$span->textContent = "qux"; +$element->append("foo", "bar", "baz", $span, $dom->createCDATASection("a"), $dom->createCDATASection("b")); +$dom->append($element); +echo $dom->saveXML(), "\n"; + +var_dump($element->firstChild->textContent); +var_dump($element->firstChild->nextSibling->textContent); +var_dump($element->firstChild->nextSibling->nextSibling->textContent); + +$dom->normalize(); +echo $dom->saveXML(), "\n"; + +var_dump($element->firstChild->textContent); +var_dump($element->firstChild->nextSibling->textContent); + +?> +--EXPECT-- + +
foobarbazqux
+string(3) "foo" +string(3) "bar" +string(3) "baz" + +
foobarbazqux
+string(9) "foobarbaz" +string(3) "qux" diff --git a/ext/dom/tests/modern/spec/xml_serialize_LIBXML_NOEMPTYTAG.phpt b/ext/dom/tests/modern/spec/xml_serialize_LIBXML_NOEMPTYTAG.phpt new file mode 100644 index 0000000000000..9ce097a40d68e --- /dev/null +++ b/ext/dom/tests/modern/spec/xml_serialize_LIBXML_NOEMPTYTAG.phpt @@ -0,0 +1,22 @@ +--TEST-- +XML Serialization with LIBXML_NOEMPTYTAG +--EXTENSIONS-- +dom +--FILE-- +
'); +echo $doc->saveXML($doc, LIBXML_NOEMPTYTAG), "\n"; + +$doc->formatOutput = true; +echo $doc->saveXML($doc, LIBXML_NOEMPTYTAG), "\n"; + +?> +--EXPECT-- + +

+ + + +

+
diff --git a/ext/dom/tests/modern/spec/xml_serialize_formatting.phpt b/ext/dom/tests/modern/spec/xml_serialize_formatting.phpt new file mode 100644 index 0000000000000..c704157b5b08a --- /dev/null +++ b/ext/dom/tests/modern/spec/xml_serialize_formatting.phpt @@ -0,0 +1,72 @@ +--TEST-- +XML Serialization formatting +--EXTENSIONS-- +dom +--FILE-- +
', + ' ', + '', + 'text&', + 'text', + ' ?>', + << + + + + + XML, +]; + +foreach ($tests as $test) { + echo "---\n"; + $dom = DOM\XMLDocument::createFromString($test); + $dom->formatOutput = true; + echo $dom->saveXML(), "\n"; +} + +?> +--EXPECT-- +--- + + +--- + + +--- + + + + + +--- + + + text + + & + +--- + +text +--- + + + + + + ?> + + + + +--- + + + + + + diff --git a/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt b/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt new file mode 100644 index 0000000000000..29a251003cc97 --- /dev/null +++ b/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt @@ -0,0 +1,180 @@ +--TEST-- +DTDNamedNodeMap +--EXTENSIONS-- +dom +--FILE-- + + + + +]> + +XML); + +$doctype = $dom->doctype; + +var_dump($doctype); + +var_dump($doctype->entities["test"]); +var_dump($doctype->entities["myimage"]); +// TODO: isConnected returning false is a bug +var_dump($doctype->notations["GIF"]); + +?> +--EXPECTF-- +object(DOM\DocumentType)#2 (24) { + ["name"]=> + string(4) "root" + ["entities"]=> + string(22) "(object value omitted)" + ["notations"]=> + string(22) "(object value omitted)" + ["publicId"]=> + string(0) "" + ["systemId"]=> + string(0) "" + ["internalSubset"]=> + string(105) " + +" + ["nodeName"]=> + string(4) "root" + ["nodeValue"]=> + NULL + ["nodeType"]=> + int(10) + ["parentNode"]=> + string(22) "(object value omitted)" + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + NULL + ["nextSibling"]=> + string(22) "(object value omitted)" + ["attributes"]=> + NULL + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["namespaceURI"]=> + NULL + ["prefix"]=> + string(0) "" + ["localName"]=> + NULL + ["baseURI"]=> + string(%d) "%s" + ["textContent"]=> + NULL +} +object(DOM\Entity)#4 (17) { + ["publicId"]=> + NULL + ["systemId"]=> + NULL + ["notationName"]=> + NULL + ["nodeType"]=> + int(17) + ["nodeName"]=> + string(4) "test" + ["baseURI"]=> + string(%d) "%s" + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + NULL + ["nextSibling"]=> + string(22) "(object value omitted)" + ["nodeValue"]=> + NULL + ["textContent"]=> + NULL +} +object(DOM\Entity)#3 (17) { + ["publicId"]=> + string(1) "-" + ["systemId"]=> + string(13) "mypicture.gif" + ["notationName"]=> + string(3) "GIF" + ["nodeType"]=> + int(17) + ["nodeName"]=> + string(7) "myimage" + ["baseURI"]=> + string(%d) "%smypicture.gif" + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + string(22) "(object value omitted)" + ["nextSibling"]=> + NULL + ["nodeValue"]=> + NULL + ["textContent"]=> + NULL +} +object(DOM\Notation)#4 (13) { + ["nodeType"]=> + int(12) + ["nodeName"]=> + string(3) "GIF" + ["baseURI"]=> + NULL + ["isConnected"]=> + bool(false) + ["parentNode"]=> + NULL + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + NULL + ["lastChild"]=> + NULL + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["nodeValue"]=> + NULL + ["textContent"]=> + string(0) "" +} diff --git a/ext/dom/tests/modern/xml/XMLDocument_createFromFile_empty_input.phpt b/ext/dom/tests/modern/xml/XMLDocument_createFromFile_empty_input.phpt new file mode 100644 index 0000000000000..9b7be6c2e0cb4 --- /dev/null +++ b/ext/dom/tests/modern/xml/XMLDocument_createFromFile_empty_input.phpt @@ -0,0 +1,18 @@ +--TEST-- +XMLDocument::createFromFile() - empty input +--EXTENSIONS-- +dom +--FILE-- +documentURI); +fclose($memory); +?> +--EXPECTF-- +Warning: DOM\XMLDocument::createFromFile(): Document is empty in php://memory, line: 1 in %s on line %d + +Fatal error: Uncaught Exception: XML document is malformed in %s:%d +Stack trace: +#0 %s(%d): DOM\XMLDocument::createFromFile('php://memory') +#1 {main} + thrown in %s on line %d diff --git a/ext/dom/tests/modern/xml/XMLDocument_createFromFile_override_encoding.phpt b/ext/dom/tests/modern/xml/XMLDocument_createFromFile_override_encoding.phpt index 9ca93b56d5879..59f96a97a09b5 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_createFromFile_override_encoding.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_createFromFile_override_encoding.phpt @@ -14,12 +14,12 @@ try { // 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); +var_dump($dom->charset); // 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); +var_dump($dom->charset); ?> --EXPECT-- diff --git a/ext/dom/tests/modern/xml/XMLDocument_createFromString_override_encoding.phpt b/ext/dom/tests/modern/xml/XMLDocument_createFromString_override_encoding.phpt index 29ce548292e9a..b972aec07cb57 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_createFromString_override_encoding.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_createFromString_override_encoding.phpt @@ -14,12 +14,12 @@ try { // 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); +var_dump($dom->charset); // 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); +var_dump($dom->charset); ?> --EXPECT-- diff --git a/ext/dom/tests/modern/xml/XMLDocument_debug.phpt b/ext/dom/tests/modern/xml/XMLDocument_debug.phpt index 2fbe33cd73e3f..bc277f0d05265 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_debug.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_debug.phpt @@ -10,51 +10,47 @@ var_dump($dom); ?> --EXPECT-- -object(DOM\XMLDocument)#1 (37) { - ["encoding"]=> - string(5) "UTF-8" +object(DOM\XMLDocument)#1 (29) { ["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) + ["implementation"]=> + string(22) "(object value omitted)" + ["URL"]=> + string(11) "about:blank" + ["documentURI"]=> + string(11) "about:blank" + ["characterSet"]=> + string(5) "UTF-8" + ["charset"]=> + string(5) "UTF-8" + ["inputEncoding"]=> + string(5) "UTF-8" ["doctype"]=> NULL ["documentElement"]=> NULL - ["strictErrorChecking"]=> - bool(true) - ["documentURI"]=> - NULL ["firstElementChild"]=> NULL ["lastElementChild"]=> NULL ["childElementCount"]=> int(0) + ["nodeType"]=> + int(9) ["nodeName"]=> string(9) "#document" - ["nodeValue"]=> + ["baseURI"]=> + string(11) "about:blank" + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> NULL - ["nodeType"]=> - int(9) ["parentNode"]=> NULL ["parentElement"]=> @@ -69,20 +65,8 @@ object(DOM\XMLDocument)#1 (37) { NULL ["nextSibling"]=> NULL - ["attributes"]=> - NULL - ["isConnected"]=> - bool(true) - ["ownerDocument"]=> - NULL - ["namespaceURI"]=> - NULL - ["prefix"]=> - string(0) "" - ["localName"]=> - NULL - ["baseURI"]=> + ["nodeValue"]=> NULL ["textContent"]=> - string(0) "" + NULL } diff --git a/ext/dom/tests/modern/xml/XMLDocument_documentURI.phpt b/ext/dom/tests/modern/xml/XMLDocument_documentURI.phpt new file mode 100644 index 0000000000000..f31c19d0ef650 --- /dev/null +++ b/ext/dom/tests/modern/xml/XMLDocument_documentURI.phpt @@ -0,0 +1,13 @@ +--TEST-- +DOM\XMLDocument::documentURI +--EXTENSIONS-- +dom +--FILE-- +documentURI); + +?> +--EXPECTF-- +string(%d) "file://%stest%20foo.xml" diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt index 5f7a604bb12b2..cda5210f83849 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt @@ -10,51 +10,47 @@ var_dump($dom); ?> --EXPECT-- -object(DOM\XMLDocument)#1 (37) { - ["encoding"]=> - string(5) "UTF-8" +object(DOM\XMLDocument)#1 (29) { ["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) + ["implementation"]=> + string(22) "(object value omitted)" + ["URL"]=> + string(11) "about:blank" + ["documentURI"]=> + string(11) "about:blank" + ["characterSet"]=> + string(5) "UTF-8" + ["charset"]=> + string(5) "UTF-8" + ["inputEncoding"]=> + string(5) "UTF-8" ["doctype"]=> NULL ["documentElement"]=> NULL - ["strictErrorChecking"]=> - bool(true) - ["documentURI"]=> - NULL ["firstElementChild"]=> NULL ["lastElementChild"]=> NULL ["childElementCount"]=> int(0) + ["nodeType"]=> + int(9) ["nodeName"]=> string(9) "#document" - ["nodeValue"]=> + ["baseURI"]=> + string(11) "about:blank" + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> NULL - ["nodeType"]=> - int(9) ["parentNode"]=> NULL ["parentElement"]=> @@ -69,20 +65,8 @@ object(DOM\XMLDocument)#1 (37) { NULL ["nextSibling"]=> NULL - ["attributes"]=> - NULL - ["isConnected"]=> - bool(true) - ["ownerDocument"]=> - NULL - ["namespaceURI"]=> - NULL - ["prefix"]=> - string(0) "" - ["localName"]=> - NULL - ["baseURI"]=> + ["nodeValue"]=> NULL ["textContent"]=> - string(0) "" + NULL } diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_03.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_03.phpt index 590fdd976bd09..bbbace2b06488 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_03.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_03.phpt @@ -6,10 +6,10 @@ dom append("foo"); +$dom->append($dom->createElement("foo")); echo $dom->saveXML(); ?> --EXPECT-- -foo + diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromString_04.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromString_04.phpt index 6058d9d8bee82..a124d2e4df1e5 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_fromString_04.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_fromString_04.phpt @@ -10,7 +10,7 @@ if (getenv('SKIP_ASAN') && LIBXML_VERSION < 20911) die('xleak leaks under libxml '); -var_dump($dom->encoding); +var_dump($dom->charset); echo $dom->saveXML(); ?> 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 index eacdbedd67ef6..0a0b541476a75 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt @@ -13,51 +13,47 @@ var_dump($element->ownerDocument); ?> --EXPECTF-- -object(DOM\XMLDocument)#1 (37) { - ["encoding"]=> - string(5) "UTF-8" +object(DOM\XMLDocument)#1 (29) { ["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) + ["implementation"]=> + string(22) "(object value omitted)" + ["URL"]=> + string(%d) "%s" + ["documentURI"]=> + string(%d) "%s" + ["characterSet"]=> + string(5) "UTF-8" + ["charset"]=> + string(5) "UTF-8" + ["inputEncoding"]=> + string(5) "UTF-8" ["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) + ["nodeType"]=> + int(9) ["nodeName"]=> string(9) "#document" - ["nodeValue"]=> + ["baseURI"]=> + string(%d) "%s" + ["isConnected"]=> + bool(true) + ["ownerDocument"]=> NULL - ["nodeType"]=> - int(9) ["parentNode"]=> NULL ["parentElement"]=> @@ -72,20 +68,8 @@ object(DOM\XMLDocument)#1 (37) { NULL ["nextSibling"]=> NULL - ["attributes"]=> - NULL - ["isConnected"]=> - bool(true) - ["ownerDocument"]=> - NULL - ["namespaceURI"]=> - NULL - ["prefix"]=> - string(0) "" - ["localName"]=> + ["nodeValue"]=> NULL - ["baseURI"]=> - string(%d) %s ["textContent"]=> - string(0) "" + NULL } diff --git a/ext/dom/tests/modern/xml/XMLDocument_xpath.phpt b/ext/dom/tests/modern/xml/XMLDocument_xpath.phpt new file mode 100644 index 0000000000000..6a7d903948f63 --- /dev/null +++ b/ext/dom/tests/modern/xml/XMLDocument_xpath.phpt @@ -0,0 +1,73 @@ +--TEST-- +DOM\XMLDocument interaction with XPath +--EXTENSIONS-- +dom +--FILE-- + + +

hi

+ + + + +
+XML); + +$xpath = new DOM\XPath($dom); + +echo "--- Get data of the paragraph ---\n"; + +$result = $xpath->query("//p"); +var_dump($result); +var_dump($result->item(0)->textContent); + +$result = $xpath->evaluate("//p"); +var_dump($result); +var_dump($result->item(0)->textContent); + +var_dump($xpath->evaluate("string(//p)")); +var_dump($xpath->evaluate("string-length(//p)")); +var_dump($xpath->evaluate("boolean(//p)")); + +echo "--- Get data of special nodes ---\n"; + +$result = $xpath->query("//*/comment()|//*/processing-instruction()"); +foreach ($result as $item) { + var_dump(get_class($item)); + var_dump($item->textContent); +} + +echo "--- Get a namespace node ---\n"; + +// Namespace nodes don't exist in modern day DOM. +var_dump($xpath->evaluate("//*/namespace::*")); + +?> +--EXPECT-- +--- Get data of the paragraph --- +object(DOM\NodeList)#4 (1) { + ["length"]=> + int(1) +} +string(2) "hi" +object(DOM\NodeList)#5 (1) { + ["length"]=> + int(1) +} +string(2) "hi" +string(2) "hi" +float(2) +bool(true) +--- Get data of special nodes --- +string(25) "DOM\ProcessingInstruction" +string(4) "data" +string(11) "DOM\Comment" +string(9) " comment " +--- Get a namespace node --- +object(DOM\NodeList)#5 (1) { + ["length"]=> + int(0) +} diff --git a/ext/dom/tests/modern/xml/XMLDocument_xpath_errors.phpt b/ext/dom/tests/modern/xml/XMLDocument_xpath_errors.phpt new file mode 100644 index 0000000000000..6eeac8c83b749 --- /dev/null +++ b/ext/dom/tests/modern/xml/XMLDocument_xpath_errors.phpt @@ -0,0 +1,21 @@ +--TEST-- +DOM\XMLDocument interaction with XPath evaluate - errors +--EXTENSIONS-- +dom +--FILE-- +'); + +$xpath = new DOM\XPath($dom); + +try { + var_dump($xpath->evaluate('-')); +} catch (Error $e) { + var_dump($e->getMessage()); +} + +?> +--EXPECTF-- +Warning: DOM\XPath::evaluate(): Invalid expression in %s on line %d +string(35) "Could not evaluate XPath expression" diff --git a/ext/dom/tests/modern/xml/simplexml_interop.phpt b/ext/dom/tests/modern/xml/simplexml_interop.phpt new file mode 100644 index 0000000000000..80bb478060ff4 --- /dev/null +++ b/ext/dom/tests/modern/xml/simplexml_interop.phpt @@ -0,0 +1,41 @@ +--TEST-- +Interoperability with SimpleXML +--EXTENSIONS-- +dom +simplexml +--FILE-- +foo
'); + +$element = DOM\import_simplexml($sxe); +var_dump($element->attributes); +echo $element->ownerDocument->saveXML($element), "\n"; + +$element->appendChild($element->ownerDocument->createElementNS('urn:a', 'child')); +echo $element->ownerDocument->saveXML($element), "\n"; + +$sxe->addChild('name', 'value'); +echo $element->ownerDocument->saveXML($element), "\n"; + +// This should fail because it has been imported already above in modern DOM +try { + dom_import_simplexml($sxe); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +// Import again and compare +var_dump(DOM\import_simplexml($sxe) === $element); + +?> +--EXPECT-- +object(DOM\NamedNodeMap)#3 (1) { + ["length"]=> + int(1) +} +foo +foo +foovalue +dom_import_simplexml(): Argument #1 ($node) must not be already imported as a DOM\Node +bool(true) diff --git a/ext/dom/tests/modern/xml/test foo.xml b/ext/dom/tests/modern/xml/test foo.xml new file mode 100644 index 0000000000000..23105488cb155 --- /dev/null +++ b/ext/dom/tests/modern/xml/test foo.xml @@ -0,0 +1,2 @@ + + diff --git a/ext/dom/tests/modern/xml/xpath_query_context_in_scope_ns.phpt b/ext/dom/tests/modern/xml/xpath_query_context_in_scope_ns.phpt new file mode 100644 index 0000000000000..d5e7d8d7f3847 --- /dev/null +++ b/ext/dom/tests/modern/xml/xpath_query_context_in_scope_ns.phpt @@ -0,0 +1,98 @@ +--TEST-- +Test DOM\XPath::query() with registering a context node's in-scope namespaces +--EXTENSIONS-- +dom +--FILE-- +query($query, $context); + foreach ($nodes as $node) { + echo "nodeName: "; + var_dump($node->nodeName); + echo "prefix: "; + var_dump($node->prefix); + echo "namespaceURI: "; + var_dump($node->namespaceURI); + } + if (count($nodes) === 0) { + echo "No nodes found\n"; + } +} + +$dom = DOM\XMLDocument::createFromString(<< + + + + + + + +
+ + + +XML); + +$c1 = $dom->getElementsByTagNameNS('*', 'c1')->item(0); +$c2 = $dom->getElementsByTagNameNS('*', 'c1')->item(0); + +$xpath = new DOM\XPath($dom); + +echo "=== Tests with c1 ===\n\n"; + +dump($xpath, '//b:bar', $c1); +dump($xpath, '//c:c1', $c1); +dump($xpath, '//c:c2', $c1); +dump($xpath, '//c:c3', $c1); +dump($xpath, '//c:c4', $c1); +dump($xpath, '//d:d', $c1); +dump($xpath, '//a', $c1); + +echo "\n=== Tests with c2 ===\n\n"; + +dump($xpath, '//c:c1', $c2); +dump($xpath, '//c:c2', $c2); +dump($xpath, '//d:d', $c2); + +?> +--EXPECT-- +=== Tests with c1 === + +--- //b:bar --- +nodeName: string(5) "b:bar" +prefix: string(1) "b" +namespaceURI: string(5) "urn:b" +--- //c:c1 --- +nodeName: string(4) "c:c1" +prefix: string(1) "c" +namespaceURI: string(6) "urn:c1" +--- //c:c2 --- +No nodes found +--- //c:c3 --- +No nodes found +--- //c:c4 --- +nodeName: string(4) "c:c4" +prefix: string(1) "c" +namespaceURI: string(6) "urn:c1" +--- //d:d --- +nodeName: string(3) "d:d" +prefix: string(1) "d" +namespaceURI: string(5) "urn:d" +--- //a --- +No nodes found + +=== Tests with c2 === + +--- //c:c1 --- +nodeName: string(4) "c:c1" +prefix: string(1) "c" +namespaceURI: string(6) "urn:c1" +--- //c:c2 --- +No nodes found +--- //d:d --- +nodeName: string(3) "d:d" +prefix: string(1) "d" +namespaceURI: string(5) "urn:d" diff --git a/ext/dom/tests/not_serializable.phpt b/ext/dom/tests/not_serializable.phpt index 3d542861db0aa..bce87754dba1d 100644 --- a/ext/dom/tests/not_serializable.phpt +++ b/ext/dom/tests/not_serializable.phpt @@ -34,9 +34,24 @@ try { echo $e->getMessage(), "\n"; } +$dom = DOM\XMLDocument::createEmpty(); +try { + serialize($dom); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +try { + serialize(new DOM\XPath($dom)); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + ?> --EXPECT-- Serialization of 'DOMDocument' is not allowed, unless serialization methods are implemented in a subclass Serialization of 'DOMElement' is not allowed, unless serialization methods are implemented in a subclass Serialization of 'DOMXPath' is not allowed Serialization of 'DOMNameSpaceNode' is not allowed, unless serialization methods are implemented in a subclass +Serialization of 'DOM\XMLDocument' is not allowed, unless serialization methods are implemented in a subclass +Serialization of 'DOM\XPath' is not allowed diff --git a/ext/dom/tests/property_write_errors.phpt b/ext/dom/tests/property_write_errors.phpt index 9713c80e86292..58cf800728a6b 100644 --- a/ext/dom/tests/property_write_errors.phpt +++ b/ext/dom/tests/property_write_errors.phpt @@ -49,8 +49,8 @@ try { ?> --EXPECT-- Cannot assign array to property DOMNode::$nodeValue of type ?string -Cannot write read-only property DOMDocument::$nodeType -Cannot write read-only property DOMDocument::$xmlEncoding -Cannot write read-only property DOMEntity::$actualEncoding -Cannot write read-only property DOMEntity::$encoding -Cannot write read-only property DOMEntity::$version +Cannot modify readonly property DOMDocument::$nodeType +Cannot modify readonly property DOMDocument::$xmlEncoding +Cannot modify readonly property DOMEntity::$actualEncoding +Cannot modify readonly property DOMEntity::$encoding +Cannot modify readonly property DOMEntity::$version diff --git a/ext/dom/tests/registerNodeClass_abstract_class.phpt b/ext/dom/tests/registerNodeClass_abstract_class.phpt index 3c384f3f5d522..24124d712ea09 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: DOM\Document::registerNodeClass(): Argument #2 ($extendedClass) must not be an abstract class +ValueError: DOMDocument::registerNodeClass(): Argument #2 ($extendedClass) must not be an abstract class diff --git a/ext/dom/text.c b/ext/dom/text.c index 1f2a6443ff40e..0be749a8bdfa6 100644 --- a/ext/dom/text.c +++ b/ext/dom/text.c @@ -100,6 +100,7 @@ zend_result dom_text_whole_text_read(dom_object *obj, zval *retval) /* }}} */ /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-38853C1D +Modern spec URL: https://dom.spec.whatwg.org/#dom-text-splittext Since: */ PHP_METHOD(DOMText, splitText) @@ -125,20 +126,17 @@ PHP_METHOD(DOMText, splitText) RETURN_THROWS(); } - if (node->type != XML_TEXT_NODE && node->type != XML_CDATA_SECTION_NODE) { - /* TODO Add warning? */ - RETURN_FALSE; - } - cur = node->content; if (cur == NULL) { - /* TODO Add warning? */ - RETURN_FALSE; + /* TODO: is this even possible? */ + cur = BAD_CAST ""; } length = xmlUTF8Strlen(cur); if (ZEND_LONG_INT_OVFL(offset) || (int)offset > length) { - /* TODO Add warning? */ + if (php_dom_follow_spec_intern(intern)) { + php_dom_throw_error(INDEX_SIZE_ERR, /* strict */ true); + } RETURN_FALSE; } diff --git a/ext/dom/xml_document.c b/ext/dom/xml_document.c index 3e50f3caa19a5..f7f0e27ac6b46 100644 --- a/ext/dom/xml_document.c +++ b/ext/dom/xml_document.c @@ -67,14 +67,14 @@ static bool check_options_validity(uint32_t arg_num, zend_long options) /* 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 + * However, step 10 in https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token (Date 2023-12-15) * requires us to have the declaration as an attribute available */ -static void dom_mark_namespaces_as_attributes_too(xmlDocPtr doc) +static void dom_mark_namespaces_as_attributes_too(php_dom_libxml_ns_mapper *ns_mapper, xmlDocPtr doc) { xmlNodePtr node = doc->children; while (node != NULL) { if (node->type == XML_ELEMENT_NODE) { - dom_ns_compat_mark_attribute_list(node->nsDef); + php_dom_ns_compat_mark_attribute_list(ns_mapper, node); if (node->children) { node = node->children; @@ -82,53 +82,7 @@ static void dom_mark_namespaces_as_attributes_too(xmlDocPtr doc) } } - 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; - } + node = php_dom_next_in_tree_order(node, NULL); } } @@ -164,7 +118,8 @@ PHP_METHOD(DOM_XMLDocument, createEmpty) (xmlNodePtr) lxml_doc, NULL ); - intern->document->is_modern_api_class = true; + intern->document->class_type = PHP_LIBXML_CLASS_MODERN; + intern->document->private_data = php_dom_libxml_ns_mapper_header(php_dom_libxml_ns_mapper_create()); return; oom: @@ -220,12 +175,16 @@ static void load_from_helper(INTERNAL_FUNCTION_PARAMETERS, int mode) } xmlDocPtr lxml_doc = dom_document_parser(NULL, mode, source, source_len, options, encoding); - if (UNEXPECTED(lxml_doc == NULL)) { + if (UNEXPECTED(lxml_doc == NULL || lxml_doc == DOM_DOCUMENT_MALFORMED)) { if (!EG(exception)) { - if (mode == DOM_LOAD_FILE) { - zend_throw_exception_ex(NULL, 0, "Cannot open file '%s'", source); + if (lxml_doc == DOM_DOCUMENT_MALFORMED) { + zend_throw_exception_ex(NULL, 0, "XML document is malformed"); } else { - php_dom_throw_error(INVALID_STATE_ERR, 1); + 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(); @@ -237,14 +196,46 @@ static void load_from_helper(INTERNAL_FUNCTION_PARAMETERS, int mode) lxml_doc->encoding = xmlStrdup((const xmlChar *) "UTF-8"); } } + if (mode == DOM_LOAD_FILE && lxml_doc->URL != NULL) { + if (!php_is_stream_path((char *) lxml_doc->URL)) { + /* Check for "file:/" instead of "file://" because of libxml2 quirk */ + if (strncmp((const char *) lxml_doc->URL, "file:/", sizeof("file:/") - 1) != 0) { +#if PHP_WIN32 + xmlChar *buffer = xmlStrdup((const xmlChar *) "file:///"); +#else + xmlChar *buffer = xmlStrdup((const xmlChar *) "file://"); +#endif + if (buffer != NULL) { + xmlChar *new_buffer = xmlStrcat(buffer, lxml_doc->URL); + if (new_buffer != NULL) { + xmlFree(BAD_CAST lxml_doc->URL); + lxml_doc->URL = new_buffer; + } else { + xmlFree(buffer); + } + } + } else { +#if PHP_WIN32 + lxml_doc->URL = php_dom_libxml_fix_file_path(BAD_CAST lxml_doc->URL); +#endif + } + } + } 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); + intern->document->class_type = PHP_LIBXML_CLASS_MODERN; + dom_document_convert_to_modern(intern->document, lxml_doc); +} + +void dom_document_convert_to_modern(php_libxml_ref_obj *document, xmlDocPtr lxml_doc) +{ + php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create(); + document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper); + dom_mark_namespaces_as_attributes_too(ns_mapper, lxml_doc); } PHP_METHOD(DOM_XMLDocument, createFromString) diff --git a/ext/dom/xml_serializer.c b/ext/dom/xml_serializer.c new file mode 100644 index 0000000000000..a36e883fa6bc0 --- /dev/null +++ b/ext/dom/xml_serializer.c @@ -0,0 +1,1168 @@ +/* + +----------------------------------------------------------------------+ + | 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 "xml_serializer.h" +#include "namespace_compat.h" +#include "serialize_common.h" +#include "internal_helpers.h" + +// TODO: implement iterative approach instead of recursive? + +/* This file implements the XML serialization algorithm. + * https://w3c.github.io/DOM-Parsing/#dom-xmlserializer-serializetostring (Date 2021-05-02) + * + * The following are spec issues that were fixed in this implementation, but are not yet fixed + * in the spec itself: + * https://github.com/w3c/DOM-Parsing/issues/28 + * https://github.com/w3c/DOM-Parsing/issues/29 + * https://github.com/w3c/DOM-Parsing/issues/38 + * https://github.com/w3c/DOM-Parsing/issues/43 + * https://github.com/w3c/DOM-Parsing/issues/44 + * https://github.com/w3c/DOM-Parsing/issues/45 + * https://github.com/w3c/DOM-Parsing/issues/47 + * https://github.com/w3c/DOM-Parsing/issues/50 + * https://github.com/w3c/DOM-Parsing/issues/52 + * https://github.com/w3c/DOM-Parsing/issues/59 + * https://github.com/w3c/DOM-Parsing/issues/71 + */ + +#define TRY(x) do { if (UNEXPECTED(x < 0)) { return -1; } } while (0) +#define TRY_OR_CLEANUP(x) do { if (UNEXPECTED(x < 0)) { goto cleanup; } } while (0) + +/* https://w3c.github.io/DOM-Parsing/#dfn-namespace-prefix-map + * This associates a namespace uri with a list of possible prefixes. */ +typedef struct { + HashTable *ht; +} dom_xml_ns_prefix_map; + +/* https://w3c.github.io/DOM-Parsing/#dfn-local-prefixes-map */ +typedef struct { + HashTable ht; +} dom_xml_local_prefix_map; + +typedef struct { + const xmlChar *prefix, *name; +} dom_qname_pair; + +static int dom_xml_serialization_algorithm( + xmlSaveCtxtPtr ctxt, + xmlOutputBufferPtr out, + dom_xml_ns_prefix_map *namespace_prefix_map, + xmlNodePtr node, + const xmlChar *namespace, + unsigned int *prefix_index, + int indent +); + +static bool dom_xml_str_equals_treat_nulls_as_empty(const xmlChar *s1, const xmlChar *s2) +{ + if (s1 == s2) { + return true; + } + if (s1 == NULL) { + return s2 == NULL || *s2 == '\0'; + } + if (s2 == NULL) { + /* Note: at this point we know that s1 != NULL. */ + return *s1 == '\0'; + } + return strcmp((const char *) s1, (const char *) s2) == 0; +} + +static zend_always_inline bool dom_xml_str_equals_treat_nulls_as_nulls(const xmlChar *s1, const xmlChar *s2) +{ + if (s1 == s2) { + return true; + } + if (s1 == NULL || s2 == NULL) { + return false; + } + return strcmp((const char *) s1, (const char *) s2) == 0; +} + +static zend_always_inline void dom_xml_ns_prefix_map_ctor(dom_xml_ns_prefix_map *map) +{ + ALLOC_HASHTABLE(map->ht); + zend_hash_init(map->ht, 8, NULL, NULL, false); +} + +static void dom_xml_ns_prefix_map_destroy(dom_xml_ns_prefix_map *map) +{ + HashTable *list; + ZEND_HASH_MAP_FOREACH_PTR(map->ht, list) { + if (GC_DELREF(list) == 0) { + zval *tmp; + ZEND_HASH_PACKED_FOREACH_VAL(list, tmp) { + if (DOM_Z_IS_OWNED(tmp)) { + efree(Z_PTR_P(tmp)); + } + } ZEND_HASH_FOREACH_END(); + + zend_hash_destroy(list); + efree(list); + } + } ZEND_HASH_FOREACH_END(); + + zend_hash_destroy(map->ht); + efree(map->ht); + map->ht = NULL; +} + +static zend_always_inline void dom_xml_ns_prefix_map_dtor(dom_xml_ns_prefix_map *map) +{ + if (GC_DELREF(map->ht) == 0) { + dom_xml_ns_prefix_map_destroy(map); + } +} + +static zend_always_inline void dom_xml_ns_prefix_map_copy(dom_xml_ns_prefix_map *dst, const dom_xml_ns_prefix_map *src) +{ + dst->ht = src->ht; + GC_ADDREF(dst->ht); +} + +static zend_always_inline void dom_xml_local_prefix_map_ctor(dom_xml_local_prefix_map *map) +{ + zend_hash_init(&map->ht, 8, NULL, NULL, false); +} + +static zend_always_inline void dom_xml_local_prefix_map_dtor(dom_xml_local_prefix_map *map) +{ + zend_hash_destroy(&map->ht); +} + +static zend_always_inline void dom_xml_local_prefix_map_add( + dom_xml_local_prefix_map *map, + const xmlChar *prefix, + size_t prefix_len, + const xmlChar *ns +) +{ + ZEND_ASSERT(prefix != NULL); + zend_hash_str_add_ptr(&map->ht, (const char *) prefix, prefix_len, (void *) ns); +} + +static zend_always_inline const xmlChar *dom_xml_local_prefix_map_find( + const dom_xml_local_prefix_map *map, + const xmlChar *prefix, + size_t prefix_len +) +{ + ZEND_ASSERT(prefix != NULL); + return zend_hash_str_find_ptr(&map->ht, (const char *) prefix, prefix_len); +} + +static zend_always_inline bool dom_xml_local_prefix_map_conflicts( + const dom_xml_local_prefix_map *map, + const xmlChar *prefix, + size_t prefix_len, + const xmlChar *ns +) +{ + const xmlChar *result = dom_xml_local_prefix_map_find(map, prefix, prefix_len); + if (result == NULL) { + return false; + } + return !dom_xml_str_equals_treat_nulls_as_empty(result, ns); +} + +static zend_always_inline bool dom_xml_local_prefix_map_contains( + const dom_xml_local_prefix_map *map, + const xmlChar *prefix, + size_t prefix_len +) +{ + return dom_xml_local_prefix_map_find(map, prefix, prefix_len) != NULL; +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-add */ +static void dom_xml_ns_prefix_map_add( + dom_xml_ns_prefix_map *map, + const xmlChar *prefix, + bool prefix_owned, + const xmlChar *ns, + size_t ns_length +) +{ + ZEND_ASSERT(map->ht != NULL); + ZEND_ASSERT(prefix != NULL); + + if (ns == NULL) { + ns = BAD_CAST ""; + } + + if (GC_REFCOUNT(map->ht) > 1) { + GC_DELREF(map->ht); + map->ht = zend_array_dup(map->ht); + + HashTable *list; + ZEND_HASH_MAP_FOREACH_PTR(map->ht, list) { + GC_ADDREF(list); + } ZEND_HASH_FOREACH_END(); + } + + /* 1. Let candidates list be the result of retrieving a list from map where there exists a key in map + * that matches the value of ns + * or if there is no such key, then let candidates list be null. */ + HashTable *list = zend_hash_str_find_ptr(map->ht, (const char *) ns, ns_length); + + /* 2. If candidates list is null, then create a new list with prefix as the only item in the list, + * and associate that list with a new key ns in map. */ + if (list == NULL) { + ALLOC_HASHTABLE(list); + zend_hash_init(list, 8, NULL, NULL, false); + zend_hash_str_add_new_ptr(map->ht, (const char *) ns, ns_length, list); + } else if (GC_REFCOUNT(list) > 1) { + GC_DELREF(list); + list = zend_array_dup(list); + zend_hash_str_update_ptr(map->ht, (const char *) ns, ns_length, list); + } + + /* 3. (Otherwise), append prefix to the end of candidates list. */ + zval tmp; + if (prefix_owned) { + DOM_Z_OWNED(&tmp, prefix); + } else { + DOM_Z_UNOWNED(&tmp, prefix); + } + zend_hash_next_index_insert_new(list, &tmp); +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-found */ +static zend_always_inline HashTable *dom_get_candidates_list(dom_xml_ns_prefix_map *map, const xmlChar *ns, size_t ns_length) +{ + ZEND_ASSERT(map->ht != NULL); + + /* 1. Let candidates list be the result of retrieving a list from map where there exists a key in map that matches + * the value of ns + * or if there is no such key, then let candidates list be null. */ + return zend_hash_str_find_ptr(map->ht, (const char *) ns, ns_length); +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-found */ +static zend_always_inline bool dom_prefix_in_candidate_list(const HashTable *list, const xmlChar *prefix) +{ + ZEND_ASSERT(prefix != NULL); + + if (list == NULL) { + return false; + } + + /* 2. If the value of prefix occurs at least once in candidates list, return true, otherwise return false. */ + const char *tmp; + ZEND_HASH_PACKED_FOREACH_PTR(list, tmp) { + if (dom_xml_str_equals_treat_nulls_as_empty(BAD_CAST tmp, prefix)) { + return true; + } + } ZEND_HASH_FOREACH_END(); + + return false; +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-found */ +static zend_always_inline bool dom_prefix_found_in_ns_prefix_map( + dom_xml_ns_prefix_map *map, + const xmlChar *prefix, + const xmlChar *ns, + size_t ns_length +) +{ + ZEND_ASSERT(ns != NULL); + HashTable *list = dom_get_candidates_list(map, ns, ns_length); + return dom_prefix_in_candidate_list(list, prefix); +} + +/* Helper to get the attribute value, will return "" instead of NULL for empty values, to mimic getAttribute()'s behaviour. */ +static zend_always_inline const xmlChar *dom_get_attribute_value(const xmlAttr *attr) +{ + if (attr->children == NULL) { + return BAD_CAST ""; + } + return attr->children->content ? attr->children->content : BAD_CAST ""; +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-recording-the-namespace-information */ +static const xmlChar *dom_recording_the_namespace_information( + dom_xml_ns_prefix_map *namespace_prefix_map, + dom_xml_local_prefix_map *local_prefixes_map, + xmlNodePtr element +) +{ + ZEND_ASSERT(element->type == XML_ELEMENT_NODE); + + /* 1. Let default namespace attr value be null. */ + const xmlChar *default_namespace_attr_value = NULL; + + /* 2. [MAIN] For each attribute attr in element's attributes, in the order they are specified in the element's attribute list: */ + for (xmlAttrPtr attr = element->properties; attr != NULL; attr = attr->next) { + /* Steps 2.1-2.2 fetch namespace information from the attribute, but let's defer that for simplicity to the if's body. */ + + /* 2.3. If the attribute namespace is the XMLNS namespace, then: */ + if (php_dom_ns_is_fast((xmlNodePtr) attr, php_dom_ns_is_xmlns_magic_token)) { + /* 2.3.1. If attribute prefix is null, then attr is a default namespace declaration. + * Set the default namespace attr value to attr's value and stop running these steps, + * returning to Main to visit the next attribute. */ + if (attr->ns->prefix == NULL) { + default_namespace_attr_value = dom_get_attribute_value(attr); + continue; + } + /* 2.3.2. Otherwise, the attribute prefix is not null and attr is a namespace prefix definition. + * Run the following steps: */ + else { + /* 2.3.2.1. Let prefix definition be the value of attr's localName. */ + const xmlChar *prefix_definition = attr->name; + ZEND_ASSERT(prefix_definition != NULL); + + /* 2.3.2.2. Let namespace definition be the value of attr's value. */ + const xmlChar *namespace_definition = dom_get_attribute_value(attr); + ZEND_ASSERT(namespace_definition != NULL); + + /* 2.3.2.3. If namespace definition is the XML namespace, then stop running these steps, + * and return to Main to visit the next attribute. */ + if (strcmp((const char *) namespace_definition, DOM_XML_NS_URI) == 0) { + continue; + } + + /* 2.3.2.4. If namespace definition is the empty string (the declarative form of having no namespace), + * then let namespace definition be null instead. */ + if (*namespace_definition == '\0') { + namespace_definition = NULL; + } + + size_t namespace_definition_length = namespace_definition == NULL ? 0 : strlen((const char *) namespace_definition); + + /* 2.3.2.5. If prefix definition is found in map given the namespace namespace definition, + * then stop running these steps, and return to Main to visit the next attribute. */ + if (dom_prefix_found_in_ns_prefix_map(namespace_prefix_map, prefix_definition, namespace_definition, namespace_definition_length)) { + continue; + } + + /* 2.3.2.6. Add the prefix prefix definition to map given namespace namespace definition. */ + dom_xml_ns_prefix_map_add(namespace_prefix_map, prefix_definition, false, namespace_definition, namespace_definition_length); + + /* 2.3.2.7. Add the value of prefix definition as a new key to the local prefixes map, + * with the namespace definition as the key's value replacing the value of null with the empty string if applicable. */ + size_t prefix_definition_length = strlen((const char *) prefix_definition); + namespace_definition = namespace_definition == NULL ? BAD_CAST "" : namespace_definition; + dom_xml_local_prefix_map_add(local_prefixes_map, prefix_definition, prefix_definition_length, namespace_definition); + } + } + } + + /* 3. Return the value of default namespace attr value. */ + return default_namespace_attr_value; +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-retrieving-a-preferred-prefix-string */ +static const xmlChar *dom_retrieve_a_preferred_prefix_string( + dom_xml_ns_prefix_map *namespace_prefix_map, + dom_xml_local_prefix_map *local_prefixes_map, + const xmlChar *preferred_prefix, + const xmlChar *ns, + size_t ns_length +) +{ + ZEND_ASSERT(namespace_prefix_map->ht != NULL); + + if (ns == NULL) { + ns = BAD_CAST ""; + } + + /* 1. Let candidates list be the result of retrieving a list from map where there exists a key in map that matches + * the value of ns or if there is no such key, then stop running these steps, and return the null value. */ + HashTable *list = dom_get_candidates_list(namespace_prefix_map, ns, ns_length); + if (list == NULL) { + return NULL; + } + + /* 2. Otherwise, for each prefix value prefix in candidates list, iterating from beginning to end: */ + const xmlChar *prefix = NULL; + const xmlChar *last_non_conflicting_in_list = NULL; + + /* Reverse so that the "nearest" ns gets priority: https://github.com/w3c/DOM-Parsing/issues/45 */ + ZEND_HASH_PACKED_REVERSE_FOREACH_PTR(list, prefix) { + ZEND_ASSERT(prefix != NULL); + + /* 2.1. If prefix matches preferred prefix, then stop running these steps and return prefix. */ + /* Adapted for https://github.com/w3c/DOM-Parsing/issues/45 */ + if (!dom_xml_local_prefix_map_conflicts(local_prefixes_map, prefix, strlen((const char *) prefix), ns)) { + if (dom_xml_str_equals_treat_nulls_as_empty(preferred_prefix, prefix)) { + return prefix; + } + + if (last_non_conflicting_in_list == NULL) { + last_non_conflicting_in_list = prefix; + } + } + } ZEND_HASH_FOREACH_END(); + + /* 2.2. If prefix is the last item in the candidates list, then stop running these steps and return prefix. */ + /* Note: previously the last item was "prefix", but we loop backwards now. */ + return last_non_conflicting_in_list; +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-generating-a-prefix */ +static xmlChar *dom_xml_generate_a_prefix( + dom_xml_ns_prefix_map *map, + dom_xml_local_prefix_map *local_prefixes_map, + const xmlChar *new_namespace, + size_t new_namespace_length, + unsigned int *prefix_index +) +{ + /* 1. Let generated prefix be the concatenation of the string "ns" and the current numerical value of prefix index. */ + char buffer[32]; + buffer[0] = 'n'; + buffer[1] = 's'; + size_t length; + do { + length = snprintf(buffer + 2, sizeof(buffer) - 2, "%u", *prefix_index) + 2; + + /* 2. Let the value of prefix index be incremented by one. */ + (*prefix_index)++; + + /* Loop condition is for https://github.com/w3c/DOM-Parsing/issues/44 */ + } while (dom_xml_local_prefix_map_contains(local_prefixes_map, (const xmlChar *) buffer, length)); + + xmlChar *generated_prefix = emalloc(length + 1); + memcpy(generated_prefix, buffer, length + 1); + + /* 3. Add to map the generated prefix given the new namespace namespace. */ + dom_xml_ns_prefix_map_add(map, generated_prefix, true, new_namespace, new_namespace_length); + /* Continuation of https://github.com/w3c/DOM-Parsing/issues/44 */ + dom_xml_local_prefix_map_add(local_prefixes_map, generated_prefix, length, new_namespace); + + /* 4. Return the value of generated prefix. */ + return generated_prefix; +} + +static int dom_xml_output_qname(xmlOutputBufferPtr out, const dom_qname_pair *qname) +{ + if (qname->prefix != NULL) { + TRY(xmlOutputBufferWriteString(out, (const char *) qname->prefix)); + TRY(xmlOutputBufferWrite(out, strlen(":"), ":")); + } + return xmlOutputBufferWriteString(out, (const char *) qname->name); +} + +/* This is a utility method used by both + * https://w3c.github.io/DOM-Parsing/#dfn-xml-serializing-an-element-node + * and https://w3c.github.io/DOM-Parsing/#dfn-serializing-an-attribute-value */ +static int dom_xml_common_text_serialization(xmlOutputBufferPtr out, const char *content, bool attribute_mode) +{ + if (content == NULL) { + return 0; + } + + const char *last_output = content; + const char *mask = attribute_mode ? "&<>\"\t\n\r" : "&<>"; + + while (true) { + size_t chunk_length = strcspn(content, mask); + + content += chunk_length; + if (*content == '\0') { + break; + } + + TRY(xmlOutputBufferWrite(out, content - last_output, last_output)); + + switch (*content) { + case '&': { + TRY(xmlOutputBufferWrite(out, strlen("&"), "&")); + break; + } + + case '<': { + TRY(xmlOutputBufferWrite(out, strlen("<"), "<")); + break; + } + + case '>': { + TRY(xmlOutputBufferWrite(out, strlen(">"), ">")); + break; + } + + case '"': { + TRY(xmlOutputBufferWrite(out, strlen("""), """)); + break; + } + + /* The following three are added to address https://github.com/w3c/DOM-Parsing/issues/59 */ + + case '\t': { + TRY(xmlOutputBufferWrite(out, strlen(" "), " ")); + break; + } + + case '\n': { + TRY(xmlOutputBufferWrite(out, strlen(" "), " ")); + break; + } + + case '\r': { + TRY(xmlOutputBufferWrite(out, strlen(" "), " ")); + break; + } + } + + content++; + last_output = content; + } + + return xmlOutputBufferWrite(out, content - last_output, last_output); +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serializing-an-element-node */ +static zend_always_inline int dom_xml_serialize_text_node(xmlOutputBufferPtr out, xmlNodePtr text) +{ + /* 1. If the require well-formed flag is set ... + * => N/A */ + + return dom_xml_common_text_serialization(out, (const char *) text->content, false); +} + +/* Spec says to do nothing, but that's inconsistent/wrong, see https://github.com/w3c/DOM-Parsing/issues/28 */ +static int dom_xml_serialize_attribute_node(xmlOutputBufferPtr out, xmlNodePtr attr) +{ + if (attr->ns != NULL && attr->ns->prefix != NULL) { + TRY(xmlOutputBufferWriteString(out, (const char *) attr->ns->prefix)); + TRY(xmlOutputBufferWrite(out, strlen(":"), ":")); + } + TRY(xmlOutputBufferWriteString(out, (const char *) attr->name)); + TRY(xmlOutputBufferWrite(out, strlen("=\""), "=\"")); + TRY(dom_xml_common_text_serialization(out, (const char *) dom_get_attribute_value((xmlAttrPtr) attr), true)); + return xmlOutputBufferWrite(out, strlen("\""), "\""); +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serializing-a-comment-node */ +static int dom_xml_serialize_comment_node(xmlOutputBufferPtr out, xmlNodePtr comment) +{ + /* 1. If the require well-formed flag is set ... + * => N/A */ + + TRY(xmlOutputBufferWrite(out, strlen(""), "-->"); +} + +/* https://w3c.github.io/DOM-Parsing/#xml-serializing-a-processinginstruction-node */ +static int dom_xml_serialize_processing_instruction(xmlOutputBufferPtr out, xmlNodePtr pi) +{ + /* Steps 1-2 deal with well-formed flag + * => N/A */ + + TRY(xmlOutputBufferWrite(out, strlen("name)); + TRY(xmlOutputBufferWrite(out, strlen(" "), " ")); + if (EXPECTED(pi->content != NULL)) { + TRY(xmlOutputBufferWriteString(out, (const char *) pi->content)); + } + return xmlOutputBufferWrite(out, strlen("?>"), "?>"); +} + +/* https://github.com/w3c/DOM-Parsing/issues/38 + * and https://github.com/w3c/DOM-Parsing/blob/ab8d1ac9699ed43ae6de9f4be2b0f3cfc5f3709e/index.html#L1510 */ +static int dom_xml_serialize_cdata_section_node(xmlOutputBufferPtr out, xmlNodePtr cdata) +{ + TRY(xmlOutputBufferWrite(out, strlen("content != NULL)) { + TRY(xmlOutputBufferWriteString(out, (const char *) cdata->content)); + } + return xmlOutputBufferWrite(out, strlen("]]>"), "]]>"); +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serialization-of-the-attributes */ +static int dom_xml_serialize_attributes( + xmlOutputBufferPtr out, + xmlNodePtr element, + dom_xml_ns_prefix_map *map, + dom_xml_local_prefix_map *local_prefixes_map, + unsigned int *prefix_index, + bool ignore_namespace_definition_attribute +) +{ + /* 1. Let result be the empty string. + * => We're going to write directly to the output buffer. */ + + /* 2. Let localname set be a new empty namespace localname set. + * => N/A this is only required for well-formedness */ + + /* 3. [LOOP] For each attribute attr in element's attributes, in the order they are specified in the element's attribute list: */ + for (xmlAttrPtr attr = element->properties; attr != NULL; attr = attr->next) { + /* 3.1. If the require well-formed flag is set ... + * => N/A */ + + /* 3.2. Create a new tuple consisting of attr's namespaceURI attribute and localName attribute, and add it to the localname set. + * => N/A this is only required for well-formedness */ + + /* 3.3. Let attribute namespace be the value of attr's namespaceURI value. */ + const xmlChar *attribute_namespace = attr->ns == NULL ? NULL : attr->ns->href; + + /* 3.4. Let candidate prefix be null. */ + const xmlChar *candidate_prefix = NULL; + + /* 3.5. If attribute namespace is not null, then run these sub-steps: */ + if (attribute_namespace != NULL) { + /* 3.5.1. Let candidate prefix be the result of retrieving a preferred prefix string from map + * given namespace attribute namespace with preferred prefix being attr's prefix value. */ + candidate_prefix = dom_retrieve_a_preferred_prefix_string( + map, + local_prefixes_map, + attr->ns->prefix, + attribute_namespace, + strlen((const char *) attribute_namespace) + ); + + /* 3.5.2. If the value of attribute namespace is the XMLNS namespace, then run these steps: */ + if (php_dom_ns_is_fast((xmlNodePtr) attr, php_dom_ns_is_xmlns_magic_token)) { + const xmlChar *attr_value = dom_get_attribute_value(attr); + + /* 3.5.2.1. If any of the following are true, then stop running these steps and goto Loop to visit the next attribute: */ + /* the attr's value is the XML namespace; */ + if (strcmp((const char *) attr_value, DOM_XML_NS_URI) == 0) { + continue; + } + /* the attr's prefix is null and the ignore namespace definition attribute flag is true */ + if (ignore_namespace_definition_attribute && attr->ns->prefix == NULL) { + /* https://github.com/w3c/DOM-Parsing/issues/47 */ + if (!dom_xml_str_equals_treat_nulls_as_empty(element->ns == NULL ? NULL : element->ns->href, attr_value)) { + continue; + } + } + /* the attr's prefix is not null and either */ + if (attr->ns->prefix != NULL) { + /* the attr's localName is not a key contained in the local prefixes map + * or the attr's localName is present in the local prefixes map but the value of the key does not match attr's value + * and furthermore that the attr's localName (as the prefix to find) is found in the namespace prefix map + * given the namespace consisting of the attr's value */ + const xmlChar *value = dom_xml_local_prefix_map_find(local_prefixes_map, attr->name, strlen((const char *) attr->name)); + if (value == NULL || strcmp((const char *) value, (const char *) attr_value) != 0) { + if (dom_prefix_found_in_ns_prefix_map(map, attr->name, attr_value, strlen((const char *) attr_value))) { + continue; + } + } + } + + /* 3.5.2.2. If the require well-formed flag is set ... + * => N/A */ + /* 3.5.2.3. If the require well-formed flag is set ... + * => N/A */ + + /* 3.5.2.4. the attr's prefix matches the string "xmlns", then let candidate prefix be the string "xmlns". */ + if (attr->ns->prefix != NULL && strcmp((const char *) attr->ns->prefix, "xmlns") == 0) { + candidate_prefix = BAD_CAST "xmlns"; + } + } + /* 3.5.3. Otherwise, the attribute namespace in not the XMLNS namespace. Run these steps: */ + else if (candidate_prefix == NULL) { /* https://github.com/w3c/DOM-Parsing/issues/29 */ + /* Continuation of https://github.com/w3c/DOM-Parsing/issues/29 */ + if (attr->ns->prefix == NULL + || dom_xml_local_prefix_map_contains(local_prefixes_map, attr->ns->prefix, strlen((const char *) attr->ns->prefix))) { + /* 3.5.3.1. Let candidate prefix be the result of generating a prefix providing map, + * attribute namespace, and prefix index as input. */ + candidate_prefix = dom_xml_generate_a_prefix( + map, + local_prefixes_map, + attribute_namespace, + strlen((const char *) attribute_namespace), + prefix_index + ); + } else { + candidate_prefix = attr->ns->prefix; + /* Continuation of https://github.com/w3c/DOM-Parsing/issues/29 */ + dom_xml_ns_prefix_map_add( + map, + candidate_prefix, + false, + attribute_namespace, + strlen((const char *) attribute_namespace) + ); + dom_xml_local_prefix_map_add( + local_prefixes_map, + candidate_prefix, + strlen((const char *) candidate_prefix), + attribute_namespace + ); + } + + /* 3.5.3.2. Append the following to result, in the order listed: */ + TRY(xmlOutputBufferWrite(out, strlen(" xmlns:"), " xmlns:")); + TRY(xmlOutputBufferWriteString(out, (const char *) candidate_prefix)); + TRY(xmlOutputBufferWrite(out, strlen("=\""), "=\"")); + TRY(dom_xml_common_text_serialization(out, (const char *) attribute_namespace, true)); + TRY(xmlOutputBufferWrite(out, strlen("\""), "\"")); + } + } + + /* 3.6. Append a " " (U+0020 SPACE) to result. */ + TRY(xmlOutputBufferWrite(out, strlen(" "), " ")); + + /* 3.7. If candidate prefix is not null, then append to result the concatenation of candidate prefix with ":" (U+003A COLON). */ + if (candidate_prefix != NULL) { + TRY(xmlOutputBufferWriteString(out, (const char *) candidate_prefix)); + TRY(xmlOutputBufferWrite(out, strlen(":"), ":")); + } + + /* 3.8. If the require well-formed flag is set ... + * => N/A */ + + /* 3.9. Append the following strings to result, in the order listed: */ + TRY(xmlOutputBufferWriteString(out, (const char *) attr->name)); + TRY(xmlOutputBufferWrite(out, strlen("=\""), "=\"")); + TRY(dom_xml_common_text_serialization(out, (const char *) dom_get_attribute_value(attr), true)); + TRY(xmlOutputBufferWrite(out, strlen("\""), "\"")); + } + + /* 4. Return the value of result. + * => We're writing directly to the output buffer. */ + return 0; +} + +/* Only format output if there are no text/entityrefs/cdata nodes as children. */ +static bool dom_xml_should_format_element(xmlNodePtr element) +{ + xmlNodePtr child = element->children; + ZEND_ASSERT(child != NULL); + do { + if (child->type == XML_TEXT_NODE || child->type == XML_ENTITY_REF_NODE || child->type == XML_CDATA_SECTION_NODE) { + return false; + } + child = child->next; + } while (child != NULL); + return true; +} + +static int dom_xml_output_indents(xmlOutputBufferPtr out, int indent) +{ + TRY(xmlOutputBufferWrite(out, strlen("\n"), "\n")); + for (int i = 0; i < indent; i++) { + TRY(xmlOutputBufferWrite(out, strlen(" "), " ")); + } + return 0; +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serializing-an-element-node */ +static int dom_xml_serialize_element_node( + xmlSaveCtxtPtr ctxt, + xmlOutputBufferPtr out, + const xmlChar *namespace, + dom_xml_ns_prefix_map *namespace_prefix_map, + xmlNodePtr element, + unsigned int *prefix_index, + int indent +) +{ + bool should_format = indent >= 0 && element->children != NULL && dom_xml_should_format_element(element); + + /* 1. If the require well-formed flag is set ... + * => N/A */ + + /* 2. Let markup be the string "<" (U+003C LESS-THAN SIGN). */ + TRY(xmlOutputBufferWrite(out, strlen("<"), "<")); + + /* 3. Let qualified name be an empty string. + * => We're going to do it a bit differently. + * To avoid string allocations, we're going to store the qualified name separately as prefix+name. + * If the prefix is NULL then the qualified name will be == name, otherwise == prefix:name. */ + dom_qname_pair qualified_name = { NULL, NULL }; + + /* 4. Let skip end tag be a boolean flag with value false. */ + bool skip_end_tag = false; + + /* 5. Let ignore namespace definition attribute be a boolean flag with value false. */ + bool ignore_namespace_definition_attribute = false; + + /* 6. Given prefix map, copy a namespace prefix map and let map be the result. */ + dom_xml_ns_prefix_map map; + dom_xml_ns_prefix_map_copy(&map, namespace_prefix_map); + + /* 7. Let local prefixes map be an empty map. */ + dom_xml_local_prefix_map local_prefixes_map; + dom_xml_local_prefix_map_ctor(&local_prefixes_map); + + /* 8. Let local default namespace be the result of recording the namespace information for node given map and local prefixes map. */ + const xmlChar *local_default_namespace = dom_recording_the_namespace_information(&map, &local_prefixes_map, element); + + /* 9. Let inherited ns be a copy of namespace. */ + const xmlChar *inherited_ns = namespace; + + /* 10. Let ns be the value of node's namespaceURI attribute. */ + const xmlChar *const ns = element->ns == NULL ? NULL : element->ns->href; + + /* 11. If inherited ns is equal to ns, then: */ + if (dom_xml_str_equals_treat_nulls_as_nulls(inherited_ns, ns)) { + /* 11.1. If local default namespace is not null, then set ignore namespace definition attribute to true. */ + if (local_default_namespace != NULL) { + ignore_namespace_definition_attribute = true; + } + + /* 11.2. If ns is the XML namespace, + * then append to qualified name the concatenation of the string "xml:" and the value of node's localName. */ + if (php_dom_ns_is_fast(element, php_dom_ns_is_xml_magic_token)) { + qualified_name.prefix = BAD_CAST "xml"; + qualified_name.name = element->name; + } + /* 11.3. Otherwise, append to qualified name the value of node's localName. */ + else { + qualified_name.name = element->name; + } + + /* 11.4. Append the value of qualified name to markup. */ + TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name)); + } + /* 12. Otherwise, inherited ns is not equal to ns */ + else { + /* 12.1. Let prefix be the value of node's prefix attribute. */ + const xmlChar *prefix = element->ns == NULL ? NULL : element->ns->prefix; + + /* 12.2. Let candidate prefix be the result of retrieving a preferred prefix string prefix from map given namespace ns. */ + /* https://github.com/w3c/DOM-Parsing/issues/52 */ + const xmlChar *candidate_prefix; + if (prefix == NULL && dom_xml_str_equals_treat_nulls_as_empty(ns, local_default_namespace)) { + candidate_prefix = NULL; + } else { + size_t ns_length = ns == NULL ? 0 : strlen((const char *) ns); + candidate_prefix = dom_retrieve_a_preferred_prefix_string(&map, &local_prefixes_map, prefix, ns, ns_length); + } + + /* 12.3. If the value of prefix matches "xmlns", then run the following steps: */ + if (prefix != NULL && strcmp((const char *) prefix, "xmlns") == 0) { + /* Step 1 deals with well-formedness, which we don't implement here. */ + + /* 12.3.2. Let candidate prefix be the value of prefix. */ + candidate_prefix = prefix; + } + + /* 12.4. if candidate prefix is not null (a namespace prefix is defined which maps to ns), then: */ + if (candidate_prefix != NULL) { + /* 12.4.1. Append to qualified name the concatenation of candidate prefix, ":" (U+003A COLON), and node's localName. */ + qualified_name.prefix = candidate_prefix; + qualified_name.name = element->name; + + /* 12.4.2. If the local default namespace is not null (there exists a locally-defined default namespace declaration attribute) + * and its value is not the XML namespace ... */ + if (local_default_namespace != NULL && strcmp((const char *) local_default_namespace, DOM_XML_NS_URI) != 0) { + if (*local_default_namespace == '\0') { + inherited_ns = NULL; + } else { + inherited_ns = local_default_namespace; + } + } + + /* 12.4.3. Append the value of qualified name to markup. */ + TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name)); + } + /* 12.5. Otherwise, if prefix is not null, then: */ + else if (prefix != NULL) { + size_t ns_length = ns == NULL ? 0 : strlen((const char *) ns); + + /* 12.5.1. If the local prefixes map contains a key matching prefix, ... */ + size_t prefix_length = strlen((const char *) prefix); + if (dom_xml_local_prefix_map_contains(&local_prefixes_map, prefix, prefix_length)) { + prefix = dom_xml_generate_a_prefix(&map, &local_prefixes_map, ns, ns_length, prefix_index); + } else { /* else branch fixes spec issue: generating a prefix already adds it to the maps. */ + /* 12.5.2. Add prefix to map given namespace ns. */ + dom_xml_ns_prefix_map_add(&map, prefix, false, ns, ns_length); + /* This is not spelled out in spec, but we have to do this to avoid conflicts (see default_namespace_move.phpt). */ + dom_xml_local_prefix_map_add(&local_prefixes_map, prefix, prefix_length, ns); + } + + /* 12.5.3. Append to qualified name the concatenation of prefix, ":" (U+003A COLON), and node's localName. */ + qualified_name.prefix = prefix; + qualified_name.name = element->name; + + /* 12.5.4. Append the value of qualified name to markup. */ + TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name)); + + /* 12.5.5. Append the following to markup, in the order listed: ... */ + TRY_OR_CLEANUP(xmlOutputBufferWrite(out, strlen(" xmlns:"), " xmlns:")); /* 12.5.5.1 - 12.5.5.2 */ + TRY_OR_CLEANUP(xmlOutputBufferWriteString(out, (const char *) prefix)); + TRY_OR_CLEANUP(xmlOutputBufferWrite(out, strlen("=\""), "=\"")); + TRY_OR_CLEANUP(dom_xml_common_text_serialization(out, (const char *) ns, true)); + TRY_OR_CLEANUP(xmlOutputBufferWrite(out, strlen("\""), "\"")); + + /* 12.5.6. If local default namespace is not null ... (editorial numbering error: https://github.com/w3c/DOM-Parsing/issues/43) */ + if (local_default_namespace != NULL) { + if (*local_default_namespace == '\0') { + inherited_ns = NULL; + } else { + inherited_ns = local_default_namespace; + } + } + } + /* 12.6. Otherwise, if local default namespace is null, or local default namespace is not null and its value is not equal to ns, then: */ + /* Note: https://github.com/w3c/DOM-Parsing/issues/47 */ + else if (local_default_namespace == NULL || !dom_xml_str_equals_treat_nulls_as_empty(local_default_namespace, ns)) { + /* 12.6.1. Set the ignore namespace definition attribute flag to true. */ + ignore_namespace_definition_attribute = true; + + /* 12.6.2. Append to qualified name the value of node's localName. */ + qualified_name.name = element->name; + + /* 12.6.3. Let the value of inherited ns be ns. */ + inherited_ns = ns; + + /* 12.6.4. Append the value of qualified name to markup. */ + TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name)); + + /* 12.6.5. Append the following to markup, in the order listed: ... */ + TRY_OR_CLEANUP(xmlOutputBufferWrite(out, strlen(" xmlns=\""), " xmlns=\"")); /* 12.6.5.1 - 12.6.5.2 */ + TRY_OR_CLEANUP(dom_xml_common_text_serialization(out, (const char *) ns, true)); + TRY_OR_CLEANUP(xmlOutputBufferWrite(out, strlen("\""), "\"")); + } + /* 12.7. Otherwise, the node has a local default namespace that matches ns ... */ + else { + qualified_name.name = element->name; + inherited_ns = ns; + TRY_OR_CLEANUP(dom_xml_output_qname(out, &qualified_name)); + } + } + + /* 13. Append to markup the result of the XML serialization of node's attributes given map, prefix index, + * local prefixes map, ignore namespace definition attribute flag, and require well-formed flag. */ + TRY_OR_CLEANUP(dom_xml_serialize_attributes(out, element, &map, &local_prefixes_map, prefix_index, ignore_namespace_definition_attribute)); + + /* 14. If ns is the HTML namespace, and the node's list of children is empty, and the node's localName matches + * any one of the following void elements: ... */ + if (element->children == NULL) { + if (xmlSaveNoEmptyTags) { + /* Do nothing, use the closing style. */ + } else if (php_dom_ns_is_fast(element, php_dom_ns_is_html_magic_token)) { + size_t name_length = strlen((const char *) element->name); + if (dom_local_name_compare_ex(element, "area", strlen("area"), name_length) + || dom_local_name_compare_ex(element, "base", strlen("base"), name_length) + || dom_local_name_compare_ex(element, "basefont", strlen("basefont"), name_length) + || dom_local_name_compare_ex(element, "bgsound", strlen("bgsound"), name_length) + || dom_local_name_compare_ex(element, "br", strlen("br"), name_length) + || dom_local_name_compare_ex(element, "col", strlen("col"), name_length) + || dom_local_name_compare_ex(element, "embed", strlen("embed"), name_length) + || dom_local_name_compare_ex(element, "frame", strlen("frame"), name_length) + || dom_local_name_compare_ex(element, "hr", strlen("hr"), name_length) + || dom_local_name_compare_ex(element, "img", strlen("img"), name_length) + || dom_local_name_compare_ex(element, "input", strlen("input"), name_length) + || dom_local_name_compare_ex(element, "keygen", strlen("keygen"), name_length) + || dom_local_name_compare_ex(element, "link", strlen("link"), name_length) + || dom_local_name_compare_ex(element, "menuitem", strlen("menuitem"), name_length) + || dom_local_name_compare_ex(element, "meta", strlen("meta"), name_length) + || dom_local_name_compare_ex(element, "param", strlen("param"), name_length) + || dom_local_name_compare_ex(element, "source", strlen("source"), name_length) + || dom_local_name_compare_ex(element, "track", strlen("track"), name_length) + || dom_local_name_compare_ex(element, "wbr", strlen("wbr"), name_length)) { + TRY_OR_CLEANUP(xmlOutputBufferWrite(out, strlen(" /"), " /")); + skip_end_tag = true; + } + } else { + /* 15. If ns is not the HTML namespace, and the node's list of children is empty, + * then append "/" (U+002F SOLIDUS) to markup and set the skip end tag flag to true. */ + TRY_OR_CLEANUP(xmlOutputBufferWrite(out, strlen("/"), "/")); + skip_end_tag = true; + } + } + + /* 16. Append ">" (U+003E GREATER-THAN SIGN) to markup. */ + TRY_OR_CLEANUP(xmlOutputBufferWrite(out, strlen(">"), ">")); + + /* 17. If the value of skip end tag is true, then return the value of markup and skip the remaining steps. */ + if (!skip_end_tag) { + /* Step 18 deals with template elements which we don't support. */ + + if (should_format) { + indent++; + } else { + indent = -1; + } + + /* 19. Otherwise, append to markup the result of running the XML serialization algorithm on each of node's children. */ + for (xmlNodePtr child = element->children; child != NULL; child = child->next) { + if (should_format) { + TRY_OR_CLEANUP(dom_xml_output_indents(out, indent)); + } + TRY_OR_CLEANUP(dom_xml_serialization_algorithm(ctxt, out, &map, child, inherited_ns, prefix_index, indent)); + } + + if (should_format) { + indent--; + TRY_OR_CLEANUP(dom_xml_output_indents(out, indent)); + } + + /* 20. Append the following to markup, in the order listed: */ + TRY_OR_CLEANUP(xmlOutputBufferWrite(out, strlen(""), ">")); + } + + /* 21. Return the value of markup. + * => We use the output buffer instead. */ + dom_xml_ns_prefix_map_dtor(&map); + dom_xml_local_prefix_map_dtor(&local_prefixes_map); + return 0; + +cleanup: + dom_xml_ns_prefix_map_dtor(&map); + dom_xml_local_prefix_map_dtor(&local_prefixes_map); + return -1; +} + +/* https://w3c.github.io/DOM-Parsing/#xml-serializing-a-documentfragment-node */ +static int dom_xml_serializing_a_document_fragment_node( + xmlSaveCtxtPtr ctxt, + xmlOutputBufferPtr out, + dom_xml_ns_prefix_map *namespace_prefix_map, + xmlNodePtr node, + const xmlChar *namespace, + unsigned int *prefix_index, + int indent +) +{ + /* 1. Let markup the empty string. + * => We use the output buffer instead. */ + + /* 2. For each child child of node, in tree order, run the XML serialization algorithm on the child ... */ + xmlNodePtr child = node->children; + while (child != NULL) { + TRY(dom_xml_serialization_algorithm(ctxt, out, namespace_prefix_map, child, namespace, prefix_index, indent)); + child = child->next; + } + + /* 3. Return the value of markup + * => We use the output buffer instead. */ + return 0; +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serializing-a-document-node */ +static int dom_xml_serializing_a_document_node( + xmlSaveCtxtPtr ctxt, + xmlOutputBufferPtr out, + dom_xml_ns_prefix_map *namespace_prefix_map, + xmlNodePtr node, + const xmlChar *namespace, + unsigned int *prefix_index, + int indent +) +{ + /* 1. Let serialized document be an empty string. + * => We use the output buffer instead. */ + + xmlNodePtr child = node->children; + node->children = NULL; + + /* https://github.com/w3c/DOM-Parsing/issues/50 */ + TRY(xmlOutputBufferFlush(out)); + TRY(xmlSaveDoc(ctxt, (xmlDocPtr) node)); + TRY(xmlSaveFlush(ctxt)); + + node->children = child; + + /* 2. For each child child of node, in tree order, run the XML serialization algorithm on the child passing along the provided arguments, + * and append the result to serialized document. */ + while (child != NULL) { + TRY(dom_xml_serialization_algorithm(ctxt, out, namespace_prefix_map, child, namespace, prefix_index, indent)); + child = child->next; + } + + /* 3. Return the value of serialized document. + * => We use the output buffer instead. */ + return 0; +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serialization-algorithm */ +static int dom_xml_serialization_algorithm( + xmlSaveCtxtPtr ctxt, + xmlOutputBufferPtr out, + dom_xml_ns_prefix_map *namespace_prefix_map, + xmlNodePtr node, + const xmlChar *namespace, + unsigned int *prefix_index, + int indent +) +{ + /* If node's interface is: */ + switch (node->type) { + case XML_ELEMENT_NODE: + return dom_xml_serialize_element_node(ctxt, out, namespace, namespace_prefix_map, node, prefix_index, indent); + + case XML_DOCUMENT_FRAG_NODE: + return dom_xml_serializing_a_document_fragment_node(ctxt, out, namespace_prefix_map, node, namespace, prefix_index, indent); + + case XML_HTML_DOCUMENT_NODE: + case XML_DOCUMENT_NODE: + return dom_xml_serializing_a_document_node(ctxt, out, namespace_prefix_map, node, namespace, prefix_index, indent); + + case XML_TEXT_NODE: + return dom_xml_serialize_text_node(out, node); + + case XML_COMMENT_NODE: + return dom_xml_serialize_comment_node(out, node); + + case XML_PI_NODE: + return dom_xml_serialize_processing_instruction(out, node); + + case XML_CDATA_SECTION_NODE: + return dom_xml_serialize_cdata_section_node(out, node); + + case XML_ATTRIBUTE_NODE: + return dom_xml_serialize_attribute_node(out, node); + + default: + TRY(xmlOutputBufferFlush(out)); + TRY(xmlSaveTree(ctxt, node)); + TRY(xmlSaveFlush(ctxt)); + if (node->type == XML_DTD_NODE) { + return xmlOutputBufferWrite(out, strlen("\n"), "\n"); + } + return 0; + } + + ZEND_UNREACHABLE(); +} + +/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serialization + * Assumes well-formed == false. */ +int dom_xml_serialize(xmlSaveCtxtPtr ctxt, xmlOutputBufferPtr out, xmlNodePtr node, bool format) +{ + /* 1. Let namespace be a context namespace with value null. */ + const xmlChar *namespace = NULL; + + /* 2. Let prefix map be a new namespace prefix map. */ + dom_xml_ns_prefix_map namespace_prefix_map; + dom_xml_ns_prefix_map_ctor(&namespace_prefix_map); + + /* 3. Add the XML namespace with prefix value "xml" to prefix map. */ + dom_xml_ns_prefix_map_add(&namespace_prefix_map, BAD_CAST "xml", false, BAD_CAST DOM_XML_NS_URI, strlen(DOM_XML_NS_URI)); + + /* 4. Let prefix index be a generated namespace prefix index with value 1. */ + unsigned int prefix_index = 1; + + /* 5. Return the result of running the XML serialization algorithm ... */ + int indent = format ? 0 : -1; + int result = dom_xml_serialization_algorithm(ctxt, out, &namespace_prefix_map, node, namespace, &prefix_index, indent); + + dom_xml_ns_prefix_map_dtor(&namespace_prefix_map); + + return result; +} + +#endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/ext/dom/xml_serializer.h b/ext/dom/xml_serializer.h new file mode 100644 index 0000000000000..a371049082d4d --- /dev/null +++ b/ext/dom/xml_serializer.h @@ -0,0 +1,27 @@ +/* + +----------------------------------------------------------------------+ + | 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 XML_SERIALIZER_H +#define XML_SERIALIZER_H + +#include +#include +#include +#include + +int dom_xml_serialize(xmlSaveCtxtPtr ctx, xmlOutputBufferPtr out, xmlNodePtr node, bool format); + +#endif diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index 5a84fb5d8688b..0feb307612c9d 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -22,6 +22,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "namespace_compat.h" #define PHP_DOM_XPATH_QUERY 0 #define PHP_DOM_XPATH_EVALUATE 1 @@ -113,14 +114,14 @@ static void dom_xpath_ext_function_trampoline(xmlXPathParserContextPtr ctxt, int } /* {{{ */ -PHP_METHOD(DOMXPath, __construct) +static void dom_xpath_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *document_ce) { zval *doc; bool register_node_ns = true; xmlDocPtr docp = NULL; dom_object *docobj; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &doc, dom_abstract_base_document_class_entry, ®ister_node_ns) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &doc, document_ce, ®ister_node_ns) != SUCCESS) { RETURN_THROWS(); } @@ -154,6 +155,16 @@ PHP_METHOD(DOMXPath, __construct) intern->register_node_ns = register_node_ns; php_libxml_increment_doc_ref((php_libxml_node_object *) &intern->dom, docp); } + +PHP_METHOD(DOMXPath, __construct) +{ + dom_xpath_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_document_class_entry); +} + +PHP_METHOD(DOM_XPath, __construct) +{ + dom_xpath_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_abstract_base_document_class_entry); +} /* }}} end DOMXPath::__construct */ /* {{{ document DOMDocument*/ @@ -225,19 +236,18 @@ static void dom_xpath_iter(zval *baseobj, dom_object *intern) /* {{{ */ } /* }}} */ -static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ +static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool modern) /* {{{ */ { zval *context = NULL; xmlNodePtr nodep = NULL; - size_t expr_len, nsnbr = 0, xpath_type; + size_t expr_len, xpath_type; dom_object *nodeobj; char *expr; - xmlNsPtr *ns = NULL; dom_xpath_object *intern = Z_XPATHOBJ_P(ZEND_THIS); bool register_node_ns = intern->register_node_ns; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|O!b", &expr, &expr_len, &context, dom_node_class_entry, ®ister_node_ns) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|O!b", &expr, &expr_len, &context, modern ? dom_modern_node_class_entry : dom_node_class_entry, ®ister_node_ns) == FAILURE) { RETURN_THROWS(); } @@ -249,8 +259,13 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ xmlDocPtr docp = ctxp->doc; if (docp == NULL) { - php_error_docref(NULL, E_WARNING, "Invalid XPath Document Pointer"); - RETURN_FALSE; + if (modern) { + zend_throw_error(NULL, "Invalid XPath Document Pointer"); + RETURN_THROWS(); + } else { + php_error_docref(NULL, E_WARNING, "Invalid XPath Document Pointer"); + RETURN_FALSE; + } } if (context != NULL) { @@ -268,32 +283,37 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ ctxp->node = nodep; - if (register_node_ns) { - /* Register namespaces in the node */ - ns = xmlGetNsList(docp, nodep); - - if (ns != NULL) { - while (ns[nsnbr] != NULL) { - nsnbr++; - } + php_dom_in_scope_ns in_scope_ns; + if (register_node_ns && nodep != NULL) { + if (modern) { + php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(&intern->dom); + in_scope_ns = php_dom_get_in_scope_ns(ns_mapper, nodep); + } else { + in_scope_ns = php_dom_get_in_scope_ns_legacy(nodep); } + ctxp->namespaces = in_scope_ns.list; + ctxp->nsNr = in_scope_ns.count; } - ctxp->namespaces = ns; - ctxp->nsNr = nsnbr; - xmlXPathObjectPtr xpathobjp = xmlXPathEvalExpression((xmlChar *) expr, ctxp); ctxp->node = NULL; - if (ns != NULL) { - xmlFree(ns); + if (register_node_ns && nodep != NULL) { + php_dom_in_scope_ns_destroy(&in_scope_ns); ctxp->namespaces = NULL; ctxp->nsNr = 0; } if (! xpathobjp) { - /* TODO Add Warning? */ - RETURN_FALSE; + if (modern) { + if (!EG(exception)) { + zend_throw_error(NULL, "Could not evaluate XPath expression"); + } + RETURN_THROWS(); + } else { + /* Should have already emit a warning by libxml */ + RETURN_FALSE; + } } if (type == PHP_DOM_XPATH_QUERY) { @@ -317,6 +337,10 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ zval child; if (node->type == XML_NAMESPACE_DECL) { + if (modern) { + continue; + } + xmlNodePtr nsparent = node->_private; xmlNsPtr original = (xmlNsPtr) node; @@ -334,7 +358,7 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ } else { ZVAL_EMPTY_ARRAY(&retval); } - php_dom_create_iterator(return_value, DOM_NODELIST); + php_dom_create_iterator(return_value, DOM_NODELIST, modern); nodeobj = Z_DOMOBJ_P(return_value); dom_xpath_iter(&retval, nodeobj); break; @@ -364,14 +388,24 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ /* {{{ */ PHP_METHOD(DOMXPath, query) { - php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_QUERY); + php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_QUERY, false); +} + +PHP_METHOD(DOM_XPath, query) +{ + php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_QUERY, true); } /* }}} end dom_xpath_query */ /* {{{ */ PHP_METHOD(DOMXPath, evaluate) { - php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_EVALUATE); + php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_EVALUATE, false); +} + +PHP_METHOD(DOM_XPath, evaluate) +{ + php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_EVALUATE, true); } /* }}} end dom_xpath_evaluate */ diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 723779eebd5b3..15f6e0cb830ac 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -188,124 +188,122 @@ static void php_libxml_unlink_entity_decl(xmlEntityPtr entity) static void php_libxml_node_free(xmlNodePtr node) { - if(node) { - if (node->_private != NULL) { - ((php_libxml_node_ptr *) node->_private)->node = NULL; - } - switch (node->type) { - case XML_ATTRIBUTE_NODE: - xmlFreeProp((xmlAttrPtr) node); - break; - /* libxml2 has a peculiarity where if you unlink an entity it'll only unlink it from the dtd if the - * dtd is attached to the document. This works around the issue by inspecting the parent directly. */ - case XML_ENTITY_DECL: { - xmlEntityPtr entity = (xmlEntityPtr) node; - if (entity->etype != XML_INTERNAL_PREDEFINED_ENTITY) { - php_libxml_unlink_entity_decl(entity); + if (node->_private != NULL) { + ((php_libxml_node_ptr *) node->_private)->node = NULL; + } + switch (node->type) { + case XML_ATTRIBUTE_NODE: + xmlFreeProp((xmlAttrPtr) node); + break; + /* libxml2 has a peculiarity where if you unlink an entity it'll only unlink it from the dtd if the + * dtd is attached to the document. This works around the issue by inspecting the parent directly. */ + case XML_ENTITY_DECL: { + xmlEntityPtr entity = (xmlEntityPtr) node; + if (entity->etype != XML_INTERNAL_PREDEFINED_ENTITY) { + php_libxml_unlink_entity_decl(entity); #if LIBXML_VERSION >= 21200 - xmlFreeEntity(entity); + xmlFreeEntity(entity); #else - if (entity->children != NULL && entity->owner && entity == (xmlEntityPtr) entity->children->parent) { - xmlFreeNodeList(entity->children); - } - xmlDictPtr dict = entity->doc != NULL ? entity->doc->dict : NULL; - if (dict == NULL || !xmlDictOwns(dict, entity->name)) { - xmlFree((xmlChar *) entity->name); - } - if (dict == NULL || !xmlDictOwns(dict, entity->ExternalID)) { - xmlFree((xmlChar *) entity->ExternalID); - } - if (dict == NULL || !xmlDictOwns(dict, entity->SystemID)) { - xmlFree((xmlChar *) entity->SystemID); - } - if (dict == NULL || !xmlDictOwns(dict, entity->URI)) { - xmlFree((xmlChar *) entity->URI); - } - if (dict == NULL || !xmlDictOwns(dict, entity->content)) { - xmlFree(entity->content); - } - if (dict == NULL || !xmlDictOwns(dict, entity->orig)) { - xmlFree(entity->orig); - } - xmlFree(entity); -#endif + if (entity->children != NULL && entity->owner && entity == (xmlEntityPtr) entity->children->parent) { + xmlFreeNodeList(entity->children); } - break; - } - case XML_NOTATION_NODE: { - /* See create_notation(), these aren't regular XML_NOTATION_NODE, but entities in disguise... */ - xmlEntityPtr entity = (xmlEntityPtr) node; - if (node->name != NULL) { - xmlFree((char *) node->name); + xmlDictPtr dict = entity->doc != NULL ? entity->doc->dict : NULL; + if (dict == NULL || !xmlDictOwns(dict, entity->name)) { + xmlFree((xmlChar *) entity->name); } - if (entity->ExternalID != NULL) { - xmlFree((char *) entity->ExternalID); + if (dict == NULL || !xmlDictOwns(dict, entity->ExternalID)) { + xmlFree((xmlChar *) entity->ExternalID); } - if (entity->SystemID != NULL) { - xmlFree((char *) entity->SystemID); + if (dict == NULL || !xmlDictOwns(dict, entity->SystemID)) { + xmlFree((xmlChar *) entity->SystemID); } - xmlFree(node); - break; - } - case XML_ELEMENT_DECL: - case XML_ATTRIBUTE_DECL: - break; - case XML_NAMESPACE_DECL: - if (node->ns) { - xmlFreeNs(node->ns); - node->ns = NULL; + if (dict == NULL || !xmlDictOwns(dict, entity->URI)) { + xmlFree((xmlChar *) entity->URI); } - node->type = XML_ELEMENT_NODE; - xmlFreeNode(node); - break; - case XML_DTD_NODE: { - xmlDtdPtr dtd = (xmlDtdPtr) node; - if (dtd->_private == NULL) { - /* There's no userland reference to the dtd, - * but there might be entities referenced from userland. Unlink those. */ - xmlHashScan(dtd->entities, php_libxml_unlink_entity, dtd->entities); - xmlHashScan(dtd->pentities, php_libxml_unlink_entity, dtd->pentities); - /* No unlinking of notations, see remark above at case XML_NOTATION_NODE. */ + if (dict == NULL || !xmlDictOwns(dict, entity->content)) { + xmlFree(entity->content); } - xmlFreeNode(node); - break; - } - case XML_ELEMENT_NODE: - if (node->nsDef && node->doc) { - /* Make the namespace declaration survive the destruction of the holding element. - * This prevents a use-after-free on the namespace declaration. - * - * The main problem is that libxml2 doesn't have a reference count on the namespace declaration. - * We don't actually need to save the namespace declaration if we know the subtree it belongs to - * has no references from userland. However, we can't know that without traversing the whole subtree - * (=> slow), or without adding some subtree metadata (=> also slow). - * So we have to assume we need to save everything. - * - * However, namespace declarations are quite rare in comparison to other node types. - * Most node types are either elements, text or attributes. - * And you only need one namespace declaration per namespace (in principle). - * So I expect the number of namespace declarations to be low for an average XML document. - * - * In the worst possible case we have to save all namespace declarations when we for example remove - * the whole document. But given the above reasoning this likely won't be a lot of declarations even - * in the worst case. - * A single declaration only takes about 48 bytes of memory, and I don't expect the worst case to occur - * very often (why would you remove the whole document?). - */ - xmlNsPtr ns = node->nsDef; - xmlNsPtr last = ns; - while (last->next) { - last = last->next; - } - php_libxml_set_old_ns_list(node->doc, ns, last); - node->nsDef = NULL; + if (dict == NULL || !xmlDictOwns(dict, entity->orig)) { + xmlFree(entity->orig); } - xmlFreeNode(node); - break; - default: - xmlFreeNode(node); - break; + xmlFree(entity); +#endif + } + break; + } + case XML_NOTATION_NODE: { + /* See create_notation(), these aren't regular XML_NOTATION_NODE, but entities in disguise... */ + xmlEntityPtr entity = (xmlEntityPtr) node; + if (node->name != NULL) { + xmlFree((char *) node->name); + } + if (entity->ExternalID != NULL) { + xmlFree((char *) entity->ExternalID); + } + if (entity->SystemID != NULL) { + xmlFree((char *) entity->SystemID); + } + xmlFree(node); + break; + } + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + break; + case XML_NAMESPACE_DECL: + if (node->ns) { + xmlFreeNs(node->ns); + node->ns = NULL; + } + node->type = XML_ELEMENT_NODE; + xmlFreeNode(node); + break; + case XML_DTD_NODE: { + xmlDtdPtr dtd = (xmlDtdPtr) node; + if (dtd->_private == NULL) { + /* There's no userland reference to the dtd, + * but there might be entities referenced from userland. Unlink those. */ + xmlHashScan(dtd->entities, php_libxml_unlink_entity, dtd->entities); + xmlHashScan(dtd->pentities, php_libxml_unlink_entity, dtd->pentities); + /* No unlinking of notations, see remark above at case XML_NOTATION_NODE. */ + } + xmlFreeDtd(dtd); + break; } + case XML_ELEMENT_NODE: + if (node->nsDef && node->doc) { + /* Make the namespace declaration survive the destruction of the holding element. + * This prevents a use-after-free on the namespace declaration. + * + * The main problem is that libxml2 doesn't have a reference count on the namespace declaration. + * We don't actually need to save the namespace declaration if we know the subtree it belongs to + * has no references from userland. However, we can't know that without traversing the whole subtree + * (=> slow), or without adding some subtree metadata (=> also slow). + * So we have to assume we need to save everything. + * + * However, namespace declarations are quite rare in comparison to other node types. + * Most node types are either elements, text or attributes. + * And you only need one namespace declaration per namespace (in principle). + * So I expect the number of namespace declarations to be low for an average XML document. + * + * In the worst possible case we have to save all namespace declarations when we for example remove + * the whole document. But given the above reasoning this likely won't be a lot of declarations even + * in the worst case. + * A single declaration only takes about 48 bytes of memory, and I don't expect the worst case to occur + * very often (why would you remove the whole document?). + */ + xmlNsPtr ns = node->nsDef; + xmlNsPtr last = ns; + while (last->next) { + last = last->next; + } + php_libxml_set_old_ns_list(node->doc, ns, last); + node->nsDef = NULL; + } + xmlFreeNode(node); + break; + default: + xmlFreeNode(node); + break; } } @@ -324,13 +322,11 @@ PHP_LIBXML_API void php_libxml_node_free_list(xmlNodePtr node) if (curnode->type == XML_ELEMENT_NODE) { /* This ensures that namespace references in this subtree are defined within this subtree, * otherwise a use-after-free would be possible when the original namespace holder gets freed. */ -#if 0 - xmlDOMWrapCtxt dummy_ctxt = {0}; - xmlDOMWrapReconcileNamespaces(&dummy_ctxt, curnode, /* options */ 0); -#else - /* See php_dom.c */ - xmlReconciliateNs(curnode->doc, curnode); -#endif + php_libxml_node_ptr *ptr = curnode->_private; + php_libxml_node_object *obj = ptr->_private; + if (!obj->document || obj->document->class_type < PHP_LIBXML_CLASS_MODERN) { + xmlReconciliateNs(curnode->doc, curnode); + } } /* Skip freeing */ curnode = next; @@ -1358,7 +1354,31 @@ 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; + object->document->private_data = NULL; + object->document->class_type = PHP_LIBXML_CLASS_UNSET; + } + + return ret_refcount; +} + +PHP_LIBXML_API int php_libxml_decrement_doc_ref_directly(php_libxml_ref_obj *document) +{ + int ret_refcount = --document->refcount; + if (ret_refcount == 0) { + if (document->ptr != NULL) { + xmlFreeDoc((xmlDoc *) document->ptr); + } + if (document->doc_props != NULL) { + if (document->doc_props->classmap) { + zend_hash_destroy(document->doc_props->classmap); + FREE_HASHTABLE(document->doc_props->classmap); + } + efree(document->doc_props); + } + if (document->private_data != NULL) { + document->private_data->dtor(document->private_data); + } + efree(document); } return ret_refcount; @@ -1369,20 +1389,7 @@ PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object) int ret_refcount = -1; if (object != NULL && object->document != NULL) { - ret_refcount = --object->document->refcount; - if (ret_refcount == 0) { - if (object->document->ptr != NULL) { - xmlFreeDoc((xmlDoc *) object->document->ptr); - } - if (object->document->doc_props != NULL) { - if (object->document->doc_props->classmap) { - zend_hash_destroy(object->document->doc_props->classmap); - FREE_HASHTABLE(object->document->doc_props->classmap); - } - efree(object->document->doc_props); - } - efree(object->document); - } + ret_refcount = php_libxml_decrement_doc_ref_directly(object->document); object->document = NULL; } diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index f7aabc00bf4ea..9385c1848a920 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -62,12 +62,34 @@ typedef struct { size_t modification_nr; } php_libxml_cache_tag; +typedef struct _php_libxml_private_data_header { + void (*dtor)(struct _php_libxml_private_data_header *); + /* extra fields */ +} php_libxml_private_data_header; + +/** + * Multiple representations are possible of the same underlying node data. + * This is the case for example when a SimpleXML node is imported into DOM. + * It must not be possible to obtain both a legacy and a modern representation + * of the same node, as they have different assumptions. The class_type field + * allows us to pin the representation to one of the two. If it is unset, no + * representation has been forced upon the node yet, and thus no assumptions + * have yet been made. This is the case for example when a SimpleXML node is + * created by SimpleXML itself and never leaves SimpleXML. + */ +typedef enum _php_libxml_class_type { + PHP_LIBXML_CLASS_UNSET = 0, + PHP_LIBXML_CLASS_LEGACY = 1, + PHP_LIBXML_CLASS_MODERN = 2, +} php_libxml_class_type; + typedef struct _php_libxml_ref_obj { void *ptr; libxml_doc_props *doc_props; php_libxml_cache_tag cache_tag; + php_libxml_private_data_header *private_data; int refcount; - bool is_modern_api_class; + php_libxml_class_type class_type; } php_libxml_ref_obj; typedef struct _php_libxml_node_ptr { @@ -122,6 +144,7 @@ typedef void * (*php_libxml_export_node) (zval *object); PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data); PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object); PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp); +PHP_LIBXML_API int php_libxml_decrement_doc_ref_directly(php_libxml_ref_obj *document); PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object); PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object); PHP_LIBXML_API zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function); diff --git a/ext/simplexml/tests/simplexml_import_new_dom.phpt b/ext/simplexml/tests/simplexml_import_new_dom.phpt new file mode 100644 index 0000000000000..b0e00c731e503 --- /dev/null +++ b/ext/simplexml/tests/simplexml_import_new_dom.phpt @@ -0,0 +1,32 @@ +--TEST-- +SimpleXML [interop]: simplexml_import_dom (new DOM) +--EXTENSIONS-- +simplexml +dom +--FILE-- +book; +foreach ($books as $book) { + echo "{$book->title} was written by {$book->author}\n"; +} + +$s->book[0]->title = "test"; +echo $dom->saveXML(); + +?> +--EXPECT-- +The Grapes of Wrath was written by John Steinbeck +The Pearl was written by John Steinbeck + + + + test + John Steinbeck + + + The Pearl + John Steinbeck + + diff --git a/ext/simplexml/tests/simplexml_import_dom.phpt b/ext/simplexml/tests/simplexml_import_old_dom.phpt similarity index 89% rename from ext/simplexml/tests/simplexml_import_dom.phpt rename to ext/simplexml/tests/simplexml_import_old_dom.phpt index 3ac15aad0cb9e..26f30b368164e 100644 --- a/ext/simplexml/tests/simplexml_import_dom.phpt +++ b/ext/simplexml/tests/simplexml_import_old_dom.phpt @@ -1,5 +1,5 @@ --TEST-- -SimpleXML [interop]: simplexml_import_dom +SimpleXML [interop]: simplexml_import_dom (old DOM) --EXTENSIONS-- simplexml dom diff --git a/ext/xsl/php_xsl.c b/ext/xsl/php_xsl.c index 2699ec2779dd2..5beccee2e62ab 100644 --- a/ext/xsl/php_xsl.c +++ b/ext/xsl/php_xsl.c @@ -59,6 +59,21 @@ static HashTable *xsl_objects_get_gc(zend_object *object, zval **table, int *n) return php_dom_xpath_callbacks_get_gc_for_whole_object(&intern->xpath_callbacks, object, table, n); } +void xsl_free_sheet(xsl_object *intern) +{ + if (intern->ptr) { + xsltStylesheetPtr sheet = intern->ptr; + + /* Free wrapper */ + if (sheet->_private != NULL) { + sheet->_private = NULL; + } + + xsltFreeStylesheet(sheet); + intern->ptr = NULL; + } +} + /* {{{ xsl_objects_free_storage */ void xsl_objects_free_storage(zend_object *object) { @@ -73,20 +88,17 @@ void xsl_objects_free_storage(zend_object *object) php_dom_xpath_callbacks_dtor(&intern->xpath_callbacks); + xsl_free_sheet(intern); + if (intern->doc) { php_libxml_decrement_doc_ref(intern->doc); efree(intern->doc); } - if (intern->ptr) { - /* free wrapper */ - if (((xsltStylesheetPtr) intern->ptr)->_private != NULL) { - ((xsltStylesheetPtr) intern->ptr)->_private = NULL; - } - - xsltFreeStylesheet((xsltStylesheetPtr) intern->ptr); - intern->ptr = NULL; + if (intern->sheet_ref_obj) { + php_libxml_decrement_doc_ref_directly(intern->sheet_ref_obj); } + if (intern->profiling) { efree(intern->profiling); } diff --git a/ext/xsl/php_xsl.h b/ext/xsl/php_xsl.h index b7a52dac5ac2f..accdecf46bdb2 100644 --- a/ext/xsl/php_xsl.h +++ b/ext/xsl/php_xsl.h @@ -56,6 +56,7 @@ typedef struct _xsl_object { void *ptr; HashTable *parameter; bool hasKeys; + php_libxml_ref_obj *sheet_ref_obj; zend_long securityPrefs; php_dom_xpath_callbacks xpath_callbacks; php_libxml_node_object *doc; @@ -70,6 +71,7 @@ static inline xsl_object *php_xsl_fetch_object(zend_object *obj) { #define Z_XSL_P(zv) php_xsl_fetch_object(Z_OBJ_P((zv))) void php_xsl_set_object(zval *wrapper, void *obj); +void xsl_free_sheet(xsl_object *intern); void xsl_objects_free_storage(zend_object *object); void xsl_ext_function_string_php(xmlXPathParserContextPtr ctxt, int nargs); diff --git a/ext/xsl/tests/XSLTProcessor_namespace_mapper_lifetime.phpt b/ext/xsl/tests/XSLTProcessor_namespace_mapper_lifetime.phpt new file mode 100644 index 0000000000000..2bd7c30dc8fd6 --- /dev/null +++ b/ext/xsl/tests/XSLTProcessor_namespace_mapper_lifetime.phpt @@ -0,0 +1,36 @@ +--TEST-- +XSLTProcessor namespace mapper lifetime +--EXTENSIONS-- +dom +xsl +--FILE-- + + World + +XML); + +$xslXML = << + + + + + +XML; + +$processor = new XSLTProcessor(); +// The fact that this is a temporary is important! +// And yes, this is done twice on purpose to check for leaks +$processor->importStylesheet(DOM\XMLDocument::createFromString($xslXML)); +$processor->importStylesheet(DOM\XMLDocument::createFromString($xslXML)); +$processor->registerPHPFunctions(); +echo $processor->transformToXML($input), "\n"; + +?> +--EXPECT-- +WORLD diff --git a/ext/xsl/tests/auto_registration_namespaces_new_dom.phpt b/ext/xsl/tests/auto_registration_namespaces_new_dom.phpt new file mode 100644 index 0000000000000..f3e7f6b0601d3 --- /dev/null +++ b/ext/xsl/tests/auto_registration_namespaces_new_dom.phpt @@ -0,0 +1,39 @@ +--TEST-- +Auto-registration of namespaces in XSL stylesheet with new DOM +--EXTENSIONS-- +dom +xsl +--FILE-- + + + + + + + +XML); + +// Make sure it will auto-register urn:test +$sheet->documentElement->append($sheet->createElementNS('urn:test', 'test:dummy')); + +$input = DOM\XMLDocument::createFromString(<< + World + +XML); + +$processor = new XSLTProcessor(); +$processor->importStylesheet($sheet); +$processor->registerPHPFunctions(); +$processor->registerPHPFunctionNS('urn:test', 'reverse', 'strrev'); +echo $processor->transformToXML($input), "\n"; + +?> +--EXPECT-- +WORLDdlroW diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c index ce07e75748eb0..e7a04e673a25c 100644 --- a/ext/xsl/xsltprocessor.c +++ b/ext/xsl/xsltprocessor.c @@ -23,6 +23,8 @@ #include "php_xsl.h" #include #include "ext/libxml/php_libxml.h" +#include "ext/dom/php_dom.h" +#include "ext/dom/namespace_compat.h" static zend_result php_xsl_xslt_apply_params(xsltTransformContextPtr ctxt, HashTable *params) @@ -122,6 +124,72 @@ static void xsl_ext_function_trampoline(xmlXPathParserContextPtr ctxt, int nargs } } +static void xsl_add_ns_to_map(xmlHashTablePtr table, xsltStylesheetPtr sheet, const xmlNode *cur, const xmlChar *prefix, const xmlChar *uri) +{ + const xmlChar *existing_url = xmlHashLookup(table, prefix); + if (existing_url != NULL && !xmlStrEqual(existing_url, uri)) { + xsltTransformError(NULL, sheet, (xmlNodePtr) cur, "Namespaces prefix %s used for multiple namespaces\n", prefix); + sheet->warnings++; + } else if (existing_url == NULL) { + xmlHashUpdateEntry(table, prefix, (void *) uri, NULL); + } +} + +/* Adds all namespace declaration (not using nsDef) into a hash map that maps prefix to uri. Warns on conflicting declarations. */ +static void xsl_build_ns_map(xmlHashTablePtr table, xsltStylesheetPtr sheet, php_dom_libxml_ns_mapper *ns_mapper, const xmlDoc *doc) +{ + const xmlNode *cur = xmlDocGetRootElement(doc); + + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (cur->ns != NULL && cur->ns->prefix != NULL) { + xsl_add_ns_to_map(table, sheet, cur, cur->ns->prefix, cur->ns->href); + } + + for (const xmlAttr *attr = cur->properties; attr != NULL; attr = attr->next) { + if (attr->ns != NULL && attr->ns->prefix != NULL && php_dom_ns_is_fast_ex(attr->ns, php_dom_ns_is_xmlns_magic_token) + && attr->children != NULL && attr->children->content != NULL) { + /* This attribute declares a namespace, get the relevant instance. + * The declared namespace is not the same as the namespace of this attribute (which is xmlns). */ + const xmlChar *prefix = attr->name; + xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ns_mapper, (const char *) prefix, (const char *) attr->children->content); + xsl_add_ns_to_map(table, sheet, cur, prefix, ns->href); + } + } + + if (cur->children != NULL) { + cur = cur->children; + continue; + } + } + + cur = php_dom_next_in_tree_order(cur, (const xmlNode *) doc); + } +} + +/* Apply namespace corrections for new DOM */ +typedef enum { + XSL_NS_HASH_CORRECTION_NONE = 0, + XSL_NS_HASH_CORRECTION_APPLIED = 1, + XSL_NS_HASH_CORRECTION_FAILED = 2 +} xsl_ns_hash_correction_status; + +static zend_always_inline xsl_ns_hash_correction_status xsl_apply_ns_hash_corrections(xsltStylesheetPtr sheetp, xmlNodePtr nodep, xmlDocPtr doc) +{ + if (sheetp->nsHash == NULL) { + dom_object *node_intern = php_dom_object_get_data(nodep); + if (node_intern != NULL && php_dom_follow_spec_intern(node_intern)) { + sheetp->nsHash = xmlHashCreate(10); + if (UNEXPECTED(!sheetp->nsHash)) { + return XSL_NS_HASH_CORRECTION_FAILED; + } + xsl_build_ns_map(sheetp->nsHash, sheetp, php_dom_get_ns_mapper(node_intern), doc); + return XSL_NS_HASH_CORRECTION_APPLIED; + } + } + return XSL_NS_HASH_CORRECTION_NONE; +} + /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html# Since: */ @@ -129,8 +197,7 @@ PHP_METHOD(XSLTProcessor, importStylesheet) { zval *id, *docp = NULL; xmlDoc *doc = NULL, *newdoc = NULL; - xsltStylesheetPtr sheetp, oldsheetp; - xsl_object *intern; + xsltStylesheetPtr sheetp; int clone_docu = 0; xmlNode *nodep = NULL; zval *cloneDocu, rv; @@ -169,7 +236,23 @@ PHP_METHOD(XSLTProcessor, importStylesheet) RETURN_FALSE; } - intern = Z_XSL_P(id); + xsl_object *intern = Z_XSL_P(id); + + xsl_ns_hash_correction_status status = xsl_apply_ns_hash_corrections(sheetp, nodep, doc); + if (UNEXPECTED(status == XSL_NS_HASH_CORRECTION_FAILED)) { + xsltFreeStylesheet(sheetp); + xmlFreeDoc(newdoc); + RETURN_FALSE; + } else if (status == XSL_NS_HASH_CORRECTION_APPLIED) { + /* The namespace mappings need to be kept alive. + * This is stored in the ref obj outside of libxml2, but that means that the sheet won't keep it alive + * unlike with namespaces from old DOM. */ + if (intern->sheet_ref_obj) { + php_libxml_decrement_doc_ref_directly(intern->sheet_ref_obj); + } + intern->sheet_ref_obj = Z_LIBXML_NODE_P(docp)->document; + intern->sheet_ref_obj->refcount++; + } member = ZSTR_INIT_LITERAL("cloneDocument", 0); cloneDocu = zend_std_read_property(Z_OBJ_P(id), member, BP_VAR_R, NULL, &rv); @@ -194,14 +277,7 @@ PHP_METHOD(XSLTProcessor, importStylesheet) intern->hasKeys = true; } - if ((oldsheetp = (xsltStylesheetPtr)intern->ptr)) { - /* free wrapper */ - if (((xsltStylesheetPtr) intern->ptr)->_private != NULL) { - ((xsltStylesheetPtr) intern->ptr)->_private = NULL; - } - xsltFreeStylesheet((xsltStylesheetPtr) intern->ptr); - intern->ptr = NULL; - } + xsl_free_sheet(intern); php_xsl_set_object(id, sheetp); RETVAL_TRUE;