[RFC] Add a way to opt-in ext/dom spec compliance (#13031)

RFC: https://wiki.php.net/rfc/opt_in_dom_spec_compliance
This commit is contained in:
Niels Dossche 2024-03-09 16:56:00 +01:00 committed by GitHub
parent f438b3bc69
commit 14b6c981c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
255 changed files with 14751 additions and 2642 deletions

1
NEWS
View file

@ -36,6 +36,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)

View file

@ -186,14 +186,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
@ -453,6 +445,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
========================================

View file

@ -143,6 +143,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
@ -176,6 +177,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.

View file

@ -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;
}
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 @@ Since:
*/
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;
}

View file

@ -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,6 +114,7 @@ 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)
@ -104,12 +123,13 @@ PHP_METHOD(DOMCharacterData, substringData)
xmlChar *cur;
xmlChar *substring;
xmlNodePtr node;
zend_long offset, count;
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);
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);
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);
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,8 +413,20 @@ PHP_METHOD(DOMCharacterData, replaceData)
}
xmlFree(substring);
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 */
#endif

View file

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

View file

@ -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"

View file

@ -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 <libxml/SAX.h>
#include <libxml/xmlsave.h>
#ifdef LIBXML_SCHEMAS_ENABLED
#include <libxml/relaxng.h>
#include <libxml/xmlschemas.h>
#endif
typedef struct _idsIterator idsIterator;
struct _idsIterator {
xmlChar *elementId;
xmlNode *element;
};
/*
* class DOMDocument extends DOMNode
*
@ -76,10 +72,9 @@ Since:
*/
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 );
}
@ -433,9 +436,13 @@ zend_result dom_document_document_uri_read(dom_object *obj, zval *retval)
url = (char *) docp->URL;
if (url != NULL) {
ZVAL_STRING(retval, url);
} else {
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(DOM_Document, createElement)
PHP_METHOD(DOMDocument, createElement)
{
zval *id;
xmlNode *node;
xmlDocPtr docp;
dom_object *intern;
int ret;
size_t name_len, value_len;
char *name, *value = NULL;
size_t value_len;
char *value = NULL;
zend_string *name;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &name, &name_len, &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
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, id, xmlDocPtr, intern);
DOM_GET_OBJ(docp, ZEND_THIS, 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 = xmlNewDocNode(docp, NULL, (xmlChar *) name, (xmlChar *) value);
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)
{
xmlNode *node;
xmlDocPtr docp;
dom_object *intern;
int ret;
zend_string *name;
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();
}
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);
}
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((xmlNodePtr) retnodep, &ret, intern);
DOM_RET_OBJ(retnodep, &ret, intern);
}
/* }}} end dom_document_import_node */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS
Since: DOM Level 2
*/
PHP_METHOD(DOM_Document, createElementNS)
static void dom_modern_document_import_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce)
{
zval *id;
zval *node;
xmlDocPtr docp;
xmlNodePtr nodep = NULL;
xmlNodePtr nodep, retnodep;
dom_object *intern, *nodeobj;
int ret;
size_t uri_len = 0, name_len = 0, value_len = 0;
char *uri, *name, *value = NULL;
char *localname = NULL, *prefix = NULL;
int errorcode;
dom_object *intern;
bool recursive = 0;
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(), "O|b", &node, node_ce, &recursive) != SUCCESS) {
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);
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(DOMDocument, createElementNS)
{
xmlDocPtr docp;
xmlNodePtr nodep = NULL;
int ret;
size_t value_len = 0;
char *value = NULL;
int errorcode;
dom_object *intern;
zend_string *name = NULL, *uri;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!S|s", &uri, &name, &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
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);
}
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,44 +1029,28 @@ 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"))) {
if (!php_dom_follow_spec_intern(intern)) {
/* legacy behaviour */
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;
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) {
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 {
nsptr = xmlSearchNsByHref(docp, root, BAD_CAST ZSTR_VAL(uri));
if (zend_string_equals_literal(name, "xmlns") || xmlStrEqual(BAD_CAST prefix, BAD_CAST "xml")) {
@ -943,13 +1060,14 @@ PHP_METHOD(DOM_Document, createAttributeNS)
}
} else {
if (nsptr == NULL || nsptr->prefix == NULL) {
nsptr = dom_get_ns_unchecked(root, ZSTR_VAL(uri), prefix ? prefix : "default");
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);
}
if (errorcode != 0) {
xmlFreeProp((xmlAttrPtr) nodep);
@ -972,31 +1088,10 @@ error:
}
/* }}} 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,18 +1156,66 @@ 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 (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);
} else {
@ -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)) {
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) xmlSaveClose(ctxt);
}
if (UNEXPECTED(status < 0)) {
xmlBufferFree(buf);
php_error_docref(NULL, E_WARNING, "Could not save document");
RETURN_FALSE;
}
(void) xmlSaveFlush(ctxt);
(void) xmlSaveClose(ctxt);
}
mem = xmlBufferContent(buf);
if (!mem) {
xmlBufferFree(buf);
@ -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;

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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 {

View file

@ -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);

View file

@ -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. documents origin is thiss associated documents origin.
* => We don't store the origin in ext/dom. */
/* 7. documents 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 docs 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. docs origin is thiss associated documents 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
*/

File diff suppressed because it is too large Load diff

View file

@ -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 <lexbor/html/parser.h>
#include <lexbor/html/interfaces/element.h>
#include <libxml/tree.h>
#include <libxml/parserInternals.h>
#include <libxml/HTMLtree.h>
#include <Zend/zend.h>
@ -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);

View file

@ -17,6 +17,7 @@
#ifndef HTML5_PARSER_H
#define HTML5_PARSER_H
#include "namespace_compat.h"
#include <lexbor/html/parser.h>
#include <libxml/tree.h>
#include <Zend/zend_portability.h>
@ -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,

View file

@ -23,25 +23,15 @@
#include "php_dom.h"
#include "html5_serializer.h"
#include "namespace_compat.h"
#include "serialize_common.h"
#include <lexbor/encoding/encoding.h>
/* This file implements the HTML 5 serialization algorithm.
* https://html.spec.whatwg.org/multipage/parsing.html#serialising-html-fragments (Date 2023-12-14)
*/
#define TRY(x) do { if (UNEXPECTED((x) != SUCCESS)) { return FAILURE; } } while (0)
static bool dom_is_ns(const xmlNode *node, const char *uri)
{
return node->ns != NULL && strcmp((const char *) node->ns->href, uri) == 0;
}
static bool dom_is_html_ns(const xmlNode *node)
{
return node->ns == NULL || dom_is_ns(node, DOM_XHTML_NS_URI);
}
static bool dom_local_name_compare_ex(const xmlNode *node, const char *tag, size_t tag_length, size_t name_length)
{
return name_length == tag_length && zend_binary_strcmp((const char *) node->name, name_length, tag, tag_length) == 0;
}
static zend_result dom_html5_serialize_doctype(dom_html5_serialize_context *ctx, const xmlDtd *dtd)
{
TRY(ctx->write_string_len(ctx->application_data, "<!DOCTYPE ", strlen("<!DOCTYPE ")));
@ -61,10 +51,19 @@ static zend_result dom_html5_serialize_processing_instruction(dom_html5_serializ
TRY(ctx->write_string_len(ctx->application_data, "<?", strlen("<?")));
TRY(ctx->write_string(ctx->application_data, (const char *) node->name));
TRY(ctx->write_string_len(ctx->application_data, " ", strlen(" ")));
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 */

View file

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

View file

@ -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;
}
}
}
}
}
@ -720,18 +726,10 @@ PHP_METHOD(DOM_HTMLDocument, createEmpty)
RETURN_THROWS();
}
#ifdef LIBXML_HTML_ENABLED
xmlDocPtr lxml_doc = htmlNewDocNoDtD(NULL, NULL);
xmlDocPtr lxml_doc = php_dom_create_html_doc();
if (UNEXPECTED(lxml_doc == NULL)) {
goto oom;
}
#else
xmlDocPtr lxml_doc = xmlNewDoc((const xmlChar *) "1.0");
if (UNEXPECTED(lxml_doc == NULL)) {
goto oom;
}
lxml_doc->type = XML_HTML_DOCUMENT_NODE;
#endif
lxml_doc->encoding = xmlStrdup((const xmlChar *) encoding);
@ -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 @@ fail_oom:
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);
lxb_html_document_destroy(document);
if (stream) {
php_stream_close(stream);
fail_general:
if (ns_mapper != NULL) {
php_dom_libxml_ns_mapper_destroy(ns_mapper);
}
lxb_html_document_destroy(document);
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);

View file

@ -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 <nielsdos@php.net> |
+----------------------------------------------------------------------+
*/
#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

View file

@ -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 @@ Since:
*/
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);
}
}
}

View file

@ -22,32 +22,514 @@
#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;
void dom_ns_compat_mark_attribute(xmlNsPtr ns) {
ns->_private = (void *) 1;
}
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;
};
void dom_ns_compat_mark_attribute_list(xmlNsPtr ns) {
while (ns != NULL) {
dom_ns_compat_mark_attribute(ns);
ns = ns->next;
static void php_dom_libxml_ns_mapper_prefix_map_element_dtor(zval *zv)
{
if (DOM_Z_IS_OWNED(zv)) {
efree(Z_PTR_P(zv));
}
}
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);
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);
}
copy = copy->next;
original = original->next;
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;
}
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);
}
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;
}
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);
}
}

View file

@ -17,7 +17,7 @@
#ifndef NAMESPACE_COMPAT_H
#define NAMESPACE_COMPAT_H
#include <libxml/tree.h>
#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/"
/* 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. */
struct php_dom_ns_magic_token;
typedef struct php_dom_ns_magic_token php_dom_ns_magic_token;
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);
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
* 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;
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

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -13,6 +13,7 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Benjamin Eberlei <beberlei@php.net> |
| Niels Dossche <nielsdos@php.net> |
+----------------------------------------------------------------------+
*/
@ -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,75 +148,20 @@ 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)
{
xmlDoc *documentNode;
xmlNode *fragment;
xmlNode *newNode;
dom_object *newNodeObj;
documentNode = dom_doc_from_context_node(contextNode);
fragment = xmlNewDocFragment(documentNode);
if (!fragment) {
return NULL;
}
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 (newNode->parent != NULL) {
xmlUnlinkNode(newNode);
}
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;
}
newNode = next;
}
} else if (!xmlAddChild(fragment, newNode)) {
if (will_free) {
xmlFreeNode(newNode);
}
goto err;
}
* 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 {
ZEND_ASSERT(Z_TYPE(nodes[i]) == IS_STRING);
newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[i]));
if (!xmlAddChild(fragment, newNode)) {
xmlFreeNode(newNode);
goto err;
xmlNodePtr last = parent->last;
last->next = child;
child->prev = last;
}
}
}
return fragment;
err:
xmlFreeNode(fragment);
return NULL;
parent->last = child;
child->parent = parent;
}
static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fragment)
@ -229,43 +176,294 @@ static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fr
}
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)
/* 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 (UNEXPECTED(parentNode == NULL)) {
/* No error required, this must be a no-op per spec */
return FAILURE;
/* 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;
}
xmlDocPtr documentNode = dom_doc_from_context_node(parentNode);
/* 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 *newNode;
dom_object *newNodeObj;
documentNode = dom_doc_from_context_node(contextNode);
/* 1. Let node be null. */
xmlNodePtr node = NULL;
/* 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);
}
newNodeObj->document = document;
xmlSetTreeDoc(newNode, documentNode);
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);
dom_add_child_without_merging(node, newNode);
newNode = next;
}
} 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);
/* 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);
}
}
/* 5. Return node. */
return node;
err:
/* 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 zend_result dom_sanity_check_node_list_types(zval *nodes, int nodesc, zend_class_entry *node_ce)
{
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);
}
}
/* 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. */
if (node->type == XML_DOCUMENT_FRAG_NODE) {
/* Steps 2-3 are not applicable here, the condition is impossible. */
xmlNodePtr newchild = node->children;
/* 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 (node->_private == NULL) {
xmlFree(node);
} else {
node->children = NULL;
node->last = NULL;
}
} else {
/* 2. Let referenceChild be child.
* 3. If referenceChild is node, then set referenceChild to nodes next sibling. */
if (insertion_point == node) {
insertion_point = node->next;
}
/* 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);
}
}
}
}
/* 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;
}
xmlNode *parentNode = dom_object_get_node(context);
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 thiss node document. */
xmlNodePtr node = dom_zvals_to_single_node(context->document, parentNode, nodes, nodesc);
if (UNEXPECTED(node == NULL)) {
return;
}
newchild = fragment->children;
prevsib = parentNode->last;
if (newchild) {
if (prevsib != NULL) {
prevsib->next = newchild;
} else {
parentNode->children = newchild;
}
xmlNodePtr last = fragment->last;
parentNode->last = last;
newchild->prev = prevsib;
dom_fragment_assign_parent_node(parentNode, fragment);
dom_reconcile_ns_list(parentNode->doc, newchild, last);
}
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 thiss 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 thiss 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 */
xmlNode *prevsib = dom_object_get_node(context);
xmlNodePtr newchild, parentNode;
xmlNode *fragment;
xmlDoc *doc;
/* Spec step 1 */
parentNode = prevsib->parent;
/* Sanity check for fragment, includes spec step 2 */
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;
}
/* 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;
xmlNode *thisp = dom_object_get_node(context);
/* 1. Let parent be thiss parent. */
xmlNodePtr parentNode = thisp->parent;
/* 2. If parent is null, then return. */
if (UNEXPECTED(parentNode == NULL)) {
return;
}
/* 3. Let viableNextSibling be thiss 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);
/* 4. Let node be the result of converting nodes into a node, given nodes and thiss node document. */
xmlNodePtr fragment = dom_zvals_to_single_node(context->document, parentNode, nodes, nodesc);
if (fragment == NULL) {
return;
}
newchild = fragment->children;
if (newchild) {
xmlNodePtr last = fragment->last;
/* 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 */
xmlNode *nextsib = dom_object_get_node(context);
xmlNodePtr newchild, parentNode;
xmlNode *fragment;
xmlDoc *doc;
/* Spec step 1 */
parentNode = nextsib->parent;
/* Sanity check for fragment, includes spec step 2 */
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;
}
/* 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;
xmlNode *thisp = dom_object_get_node(context);
/* 1. Let parent be thiss parent. */
xmlNodePtr parentNode = thisp->parent;
/* 2. If parent is null, then return. */
if (UNEXPECTED(parentNode == NULL)) {
return;
}
/* 3. Let viablePreviousSibling be thiss 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);
/* 4. Let node be the result of converting nodes into a node, given nodes and thiss node document. */
xmlNodePtr fragment = dom_zvals_to_single_node(context->document, parentNode, nodes, nodesc);
if (fragment == NULL) {
return;
}
newchild = fragment->children;
if (newchild) {
xmlNodePtr last = fragment->last;
/* Step 5: if viable_previous_sibling is null, set it to the parent's first child, otherwise viable_previous_sibling's next sibling */
/* 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);
}
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 */
xmlNodePtr child = dom_object_get_node(context);
/* Spec step 1 */
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)) {
if (UNEXPECTED(dom_sanity_check_node_list_types(nodes, nodesc, dom_get_node_ce(php_dom_follow_spec_doc_ref(context->document))) != SUCCESS)) {
return;
}
int stricterror = dom_get_strict_error(context->document);
xmlNodePtr child = dom_object_get_node(context);
/* 1. Let parent be thiss parent. */
xmlNodePtr parentNode = child->parent;
/* 2. If parent is null, then return. */
if (UNEXPECTED(parentNode == NULL)) {
return;
}
/* 3. Let viableNextSibling be thiss first following sibling not in nodes; otherwise null. */
xmlNodePtr viable_next_sibling = child->next;
while (viable_next_sibling && dom_is_node_in_list(nodes, nodesc, viable_next_sibling)) {
viable_next_sibling = viable_next_sibling->next;
}
bool stricterror = dom_get_strict_error(context->document);
if (UNEXPECTED(dom_child_removal_preconditions(child, stricterror) != SUCCESS)) {
return;
}
/* Spec step 3: find first following child 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;
}
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)) {
/* 4. Let node be the result of converting nodes into a node, given nodes and thiss node document. */
xmlNodePtr node = dom_zvals_to_single_node(context->document, parentNode, nodes, nodesc);
if (UNEXPECTED(node == NULL)) {
return;
}
/* Spec step 5: perform the replacement */
xmlNodePtr newchild = fragment->children;
/* 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 != fragment) {
if (child->parent != node) {
xmlUnlinkNode(child);
}
if (newchild) {
xmlNodePtr last = fragment->last;
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);
dom_insert_node_list_unchecked(context->document, node, parentNode, viable_next_sibling);
} else {
dom_insert_node_list_cleanup(node);
}
}
/* 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 */
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 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)) {
return;
}
xmlNodePtr fragment = dom_zvals_to_fragment(context->document, thisp, nodes, nodesc);
if (UNEXPECTED(fragment == NULL)) {
return;
}
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 thiss 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

File diff suppressed because it is too large Load diff

View file

@ -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);

File diff suppressed because it is too large Load diff

1766
ext/dom/php_dom_arginfo.h generated

File diff suppressed because it is too large Load diff

View file

@ -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 <nielsdos@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef SERIALIZE_COMMON_H
#define SERIALIZE_COMMON_H
#include <Zend/zend_types.h>
#include <libxml/tree.h>
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

View file

@ -6,6 +6,7 @@ dom
<?php
require_once("dom_test.inc");
function test($method) {
$dom1 = new DOMDocument;
$dom1->loadXML('<test/>');
@ -15,25 +16,17 @@ $dom2->loadXML('<test><foo /></test>');
$element = $dom1->documentElement;
try {
$element->after($dom2->documentElement->firstChild);
$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";
}
try {
$element->replaceWith($dom2->documentElement->firstChild);
echo "FAIL";
} catch (DOMException $e) {
echo $e->getMessage();
}
test("after");
test("before");
test("replaceWith");
?>
--EXPECT--
Wrong Document Error

View file

@ -6,6 +6,7 @@ dom
<?php
require_once("dom_test.inc");
function test($method) {
$dom1 = new DOMDocument;
$dom1->loadXML('<test/>');
@ -15,18 +16,15 @@ $dom2->loadXML('<test><foo /></test>');
$element = $dom1->documentElement;
try {
$element->append($dom2->documentElement->firstChild);
$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

View file

@ -141,7 +141,7 @@ string(27) "<?xml version="1.0"?>
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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

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

View file

@ -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)

View file

@ -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)

View file

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

View file

@ -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)

View file

@ -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)

View file

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

View file

@ -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) "<p><b>hello<b><i>world</i></b></b></p>
-- Append hello with world's child --
string(39) "<p><b>hello<i>world</i></b><b></b></p>
"
-- Append hello with world's child and text --
string(42) "<p><b>hello<i>world</i>foo</b><b></b></p>
"
-- Append world's child with hello --
string(39) "<p><b><i>world<b>hello</b></i></b></p>
"
@ -77,6 +97,10 @@ string(39) "<p><b><i>world<b>hello</b></i></b></p>
Hierarchy Request Error
string(39) "<p><b>hello</b><b><i>world</i></b></p>
"
-- Append hello with itself and text --
Hierarchy Request Error
string(27) "<p><b><i>world</i></b></p>
"
-- Append world's i tag with the parent --
Hierarchy Request Error
string(39) "<p><b>hello</b><b><i>world</i></b></p>

View file

@ -0,0 +1,21 @@
--TEST--
DOMElement::{get,has}AttributeNS() with xmlns
--EXTENSIONS--
dom
--FILE--
<?php
$dom = new DOMDocument;
$dom->loadXML('<root xmlns:a="urn:a"/>');
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)

View file

@ -78,4 +78,4 @@ Syntax Error
--- Normal cases starting from empty element ---
<empty>A</empty>
<empty>BA</empty>
string(2) "BA"
string(1) "A"

View file

@ -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) "<p><b><b><i>world</i></b>hello</b></p>
-- Prepend hello with world's child --
string(39) "<p><b><i>world</i>hello</b><b></b></p>
"
-- Prepend hello with world's child and text --
string(42) "<p><b><i>world</i>foohello</b><b></b></p>
"
-- Prepend world's child with hello --
string(39) "<p><b><i><b>hello</b>world</i></b></p>
"
-- Prepend world's child with hello and text --
string(42) "<p><b><i><b>hello</b>fooworld</i></b></p>
"
-- Prepend hello with itself --
Hierarchy Request Error
string(39) "<p><b>hello</b><b><i>world</i></b></p>
"
-- Prepend hello with itself and text --
Hierarchy Request Error
string(27) "<p><b><i>world</i></b></p>
"
-- Prepend world's i tag with the parent --
Hierarchy Request Error
string(39) "<p><b>hello</b><b><i>world</i></b></p>

View file

@ -0,0 +1,32 @@
--TEST--
DOMNode::normalize() advanced
--EXTENSIONS--
dom
--FILE--
<?php
$dom = new DOMDocument;
$root = $dom->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--
<?xml version="1.0"?>
<root><child1>abcdef</child1><child2 foo="bar"></child2></root>
string(6) "abcdef"
NULL

View file

@ -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--
<?xml version="1.0"?>
@ -25,3 +32,4 @@ print $dom->ownerDocument->saveXML();
<author>John Steinbeck</author>
</book>
</books>
DOM\import_simplexml(): Argument #1 ($node) must not be already imported as a DOMNode

View file

@ -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"]=>

View file

@ -4,6 +4,7 @@ GH-11830 (ParentNode methods should perform their checks upfront) - document var
dom
--FILE--
<?php
function test($method) {
$otherDoc = new DOMDocument;
$otherDoc->loadXML(<<<XML
<?xml version="1.0"?>
@ -24,48 +25,59 @@ XML);
$testElement = $doc->documentElement->firstElementChild->nextElementSibling->firstElementChild;
try {
$doc->documentElement->firstElementChild->prepend($testElement, $otherElement);
} catch (\DOMException $e) {
echo $e->getMessage(), "\n";
}
try {
$doc->documentElement->firstElementChild->append($testElement, $otherElement);
} catch (\DOMException $e) {
echo $e->getMessage(), "\n";
}
try {
$doc->documentElement->firstElementChild->before($testElement, $otherElement);
} catch (\DOMException $e) {
echo $e->getMessage(), "\n";
}
try {
$doc->documentElement->firstElementChild->after($testElement, $otherElement);
} catch (\DOMException $e) {
echo $e->getMessage(), "\n";
}
try {
$doc->documentElement->firstElementChild->replaceWith($testElement, $otherElement);
$doc->documentElement->firstElementChild->$method($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
<?xml version="1.0"?>
<other/>
<?xml version="1.0"?>
<container>
<alone/>
<child><testelement/></child>
<child/>
</container>
Wrong Document Error
<?xml version="1.0"?>
<other/>
<?xml version="1.0"?>
<container>
<alone/>
<child/>
</container>
Wrong Document Error
<?xml version="1.0"?>
<other/>
<?xml version="1.0"?>
<container>
<alone/>
<child/>
</container>
Wrong Document Error
<?xml version="1.0"?>
<other/>
<?xml version="1.0"?>
<container>
<alone/>
<child/>
</container>
Wrong Document Error
<?xml version="1.0"?>
<other/>
<?xml version="1.0"?>
<container>
<alone/>
<child/>
</container>

View file

@ -4,6 +4,7 @@ GH-11830 (ParentNode methods should perform their checks upfront) - hierarchy va
dom
--FILE--
<?php
function test($method) {
$doc = new DOMDocument;
$doc->loadXML(<<<XML
<?xml version="1.0"?>
@ -18,45 +19,28 @@ $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";
}
try {
$testElement->before($alone, $container);
} catch (\DOMException $e) {
echo $e->getMessage(), "\n";
}
try {
$testElement->after($alone, $container);
} catch (\DOMException $e) {
echo $e->getMessage(), "\n";
}
try {
$testElement->replaceWith($alone, $container);
$testElement->$method($alone, $container);
} catch (\DOMException $e) {
echo $e->getMessage(), "\n";
}
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
<?xml version="1.0"?>
Hierarchy Request Error
<?xml version="1.0"?>
Hierarchy Request Error
<?xml version="1.0"?>
Hierarchy Request Error
<?xml version="1.0"?>
Hierarchy Request Error
<?xml version="1.0"?>
<container>
<alone/>
<child><testelement/></child>
</container>

View file

@ -6,7 +6,7 @@ dom
<?php
$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/gb18030.html");
var_dump($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";
?>

View file

@ -6,7 +6,7 @@ dom
<?php
$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/shift_jis.html");
var_dump($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";
?>

View file

@ -6,7 +6,7 @@ dom
<?php
$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/utf16be_bom.html");
var_dump($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";
?>

View file

@ -6,7 +6,7 @@ dom
<?php
$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/utf16le_bom.html");
var_dump($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";
?>

View file

@ -6,7 +6,7 @@ dom
<?php
$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/utf8_bom.html");
var_dump($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";
?>

View file

@ -6,7 +6,7 @@ dom
<?php
$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/windows1251.html");
var_dump($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";
?>

View file

@ -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);
}

View file

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

View file

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

View file

@ -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("<container>")) . "\xf0\x90\x8d\x88AA");
var_dump($dom->saveHTML());
?>
--EXPECT--
string(4100) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA𐍈AA"
string(4112) "<container>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA𐍈AA</container>"

View file

@ -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("<container>")) . "\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𐍈AA"
string(4112) "<container>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA𐍈AA</container>"
bool(true)
bool(true)
bool(true)

View file

@ -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("<container>")) . "\xff\xff\xff");
var_dump($dom->saveHTML());
?>
--EXPECT--
string(4106) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<EFBFBD><EFBFBD><EFBFBD>"
string(4118) "<container>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<EFBFBD><EFBFBD><EFBFBD></container>"

View file

@ -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("<container>")) . "\xff\xff\xff");
var_dump($dom->saveHTML());
?>
--EXPECT--
string(4103) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<EFBFBD><EFBFBD><EFBFBD>"
string(4115) "<container>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<EFBFBD><EFBFBD><EFBFBD></container>"

View file

@ -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());
?>

View file

@ -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("<container>")) . "\xf0\x90");
var_dump($dom->saveHTML());
?>
--EXPECT--
string(4101) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<EFBFBD><EFBFBD>"
string(4110) "<container>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<EFBFBD></container>"

View file

@ -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());
?>

View file

@ -6,23 +6,23 @@ dom
<?php
$dom = DOM\HTMLDocument::createEmpty();
var_dump($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 {

View file

@ -6,7 +6,7 @@ dom
<?php
$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/fallback_encoding.html");
var_dump($dom->encoding);
var_dump($dom->inputEncoding);
echo $dom->saveHTML();
?>

View file

@ -1,30 +0,0 @@
--TEST--
DOM\HTMLDocument adopts a DOMDocument
--EXTENSIONS--
dom
--FILE--
<?php
$dom = new DOMDocument();
$dom->loadHTML(<<<HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
</head>
<body>
</body>
</html>
HTML);
$dom2 = DOM\HTMLDocument::createEmpty();
$dom2->appendChild($dom2->adoptNode($dom->documentElement));
echo $dom2->saveHTML();
?>
--EXPECT--
<html>
<head>
</head>
<body>
</body>
</html>

View file

@ -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"

View file

@ -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"

View file

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

View file

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

View file

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

View file

@ -0,0 +1,25 @@
--TEST--
DOM\HTMLDocument::registerNodeClass 03
--EXTENSIONS--
dom
--FILE--
<?php
class Custom extends DOM\Element {
public int $test = 1;
public function reverseTagName(): string {
var_dump($this->test);
return strrev($this->tagName);
}
}
$dom = DOM\HTMLDocument::createFromString("<div>foo</div>", LIBXML_NOERROR);
$dom->registerNodeClass("DOM\\Element", "Custom");
var_dump($dom->getElementsByTagName('div')[0]->reverseTagName());
?>
--EXPECT--
int(1)
string(3) "VID"

View file

@ -5,45 +5,57 @@ dom
--FILE--
<?php
class MyElement extends DOM\Element {}
$dom = DOM\HTMLDocument::createFromString("<p>foo</p>", 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)
["nodeName"]=>
string(9) "#document"
["nodeValue"]=>
NULL
["nodeType"]=>
int(13)
["nodeName"]=>
string(9) "#document"
["baseURI"]=>
string(11) "about:blank"
["isConnected"]=>
bool(true)
["ownerDocument"]=>
NULL
["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"

View file

@ -5,9 +5,11 @@ dom
--FILE--
<?php
class MyElement extends DOM\Element {}
$dom = DOM\HTMLDocument::createFromString("<p>foo</p>", 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)
["nodeName"]=>
string(9) "#document"
["nodeValue"]=>
NULL
int(1)
["nodeType"]=>
int(13)
["nodeName"]=>
string(9) "#document"
["baseURI"]=>
string(11) "about:blank"
["isConnected"]=>
bool(true)
["ownerDocument"]=>
NULL
["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"

View file

@ -9,27 +9,29 @@ echo "--- Parsing ---\n";
$dom = DOM\HTMLDocument::createFromString("<!doctype html><html><body><noscript><p>hi</p></noscript></body></html>", 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 = "<p>bye</p>";
echo $dom->saveHTML(), "\n";
echo $dom->saveXML();
echo $dom->saveXML(), "\n";
echo "--- Modifying the text content: trick ---\n";
$noscript->textContent = "<!-- </noscript> -->";
echo $dom->saveHTML(), "\n";
echo $dom->saveXML();
echo $dom->saveXML(), "\n";
?>
--EXPECT--
--- Parsing ---
string(2) "hi"
NULL
<!DOCTYPE html><html><head></head><body><noscript><p>hi</p></noscript></body></html>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE html>

View file

@ -10,11 +10,13 @@ $trailer = "</body></html>";
$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"
<!DOCTYPE html><html><head></head><body>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</body></html>
string(4052) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

View file

@ -0,0 +1,49 @@
--TEST--
HTMLDocument::createFromString() with namespaced attributes
--EXTENSIONS--
dom
--FILE--
<?php
$dom = DOM\HTMLDocument::createFromString(<<<HTML
<!DOCTYPE html>
<html>
<svg width="1" xmlns:xlink='http://www.w3.org/1999/xlink'>
<use xlink:href='#test' foo="bar"></use>
</svg>
<math>
<mo accent="true"></mo>
</math>
</html>
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

View file

@ -6,32 +6,32 @@ dom
<?php
$dom = DOM\HTMLDocument::createFromFile(__DIR__ . "/paragraph.html", LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR);
$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"));
$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)
}

View file

@ -6,32 +6,32 @@ dom
<?php
$dom = DOM\HTMLDocument::createFromString(file_get_contents(__DIR__ . "/paragraph.html"), LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR);
$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"));
$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)
}

View file

@ -23,7 +23,7 @@ $dom = DOM\HTMLDocument::createFromString(<<<HTML
</html>
HTML, LIBXML_COMPACT);
$xpath = new DOMXPath($dom);
$xpath = new DOM\XPath($dom);
foreach ($xpath->query("//*[name()='p']") as $p) {
echo $p->textContent, "\n";
}

View file

@ -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("<p>foo</p>", 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("<p>foo</p><strong>bar</strong>", 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 ---
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
--- Single element ---
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<p xmlns="http://www.w3.org/1999/xhtml">foo</p>
string(28) "http://www.w3.org/1999/xhtml"
string(0) ""
NULL
--- Multiple elements ---
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<p xmlns="http://www.w3.org/1999/xhtml">foo</p>
<strong xmlns="http://www.w3.org/1999/xhtml">bar</strong>
<p xmlns="http://www.w3.org/1999/xhtml">foo</p><strong xmlns="http://www.w3.org/1999/xhtml">bar</strong>
string(28) "http://www.w3.org/1999/xhtml"
string(0) ""
NULL
string(28) "http://www.w3.org/1999/xhtml"
string(0) ""
NULL

View file

@ -22,7 +22,7 @@ $dom = DOM\HTMLDocument::createFromString(<<<HTML
</html>
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

View file

@ -28,7 +28,7 @@ $dom = DOM\HTMLDocument::createFromString(<<<HTML
HTML);
echo "--- Namespaces ---\n";
$xpath = new DOMXPath($dom);
$xpath = new DOM\XPath($dom);
foreach ($xpath->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)

View file

@ -18,7 +18,7 @@ $dom = DOM\HTMLDocument::createFromString(<<<HTML
HTML);
echo "--- Namespaces ---\n";
$xpath = new DOMXPath($dom);
$xpath = new DOM\XPath($dom);
foreach ($xpath->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 ---
<!DOCTYPE html><html><head>
@ -52,6 +52,6 @@ prefix: ""
<title>Test</title>
</head>
<body xmlns:foo="urn:hi">
<foo:bar/>
<foo:bar></foo:bar>
</body></html>

View file

@ -0,0 +1,22 @@
--TEST--
Serialize entity reference
--EXTENSIONS--
dom
--FILE--
<?php
$dom1 = new DOMDocument();
$root = $dom1->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--
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>&nbsp;</root>
<root>&nbsp;</root>

View file

@ -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--
<root x:foo="" xml:id="" xmlns:f="" xlink:f=""></root>
<root x:foo="" xml:id="" xmlns="" xmlns:f="" xlink:f=""></root>

View file

@ -5,10 +5,15 @@ dom
--FILE--
<?php
// Note: can't create CData in an HTML document.
$dom = DOM\XMLDocument::createEmpty();
$cdata = $dom->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é"&lt;&gt;-&amp;
<container>foobaré"&lt;&gt;-&amp;</container>

View file

@ -16,10 +16,10 @@ $dom = DOM\HTMLDocument::createFromString(<<<HTML
HTML, LIBXML_NOERROR);
echo "--- XML encoding ---\n";
echo $dom->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--

View file

@ -8,7 +8,7 @@ dom
$xml = DOM\XMLDocument::createFromString('<?xml version="1.0"?><container xmlns="some:ns" xmlns:bar="another:ns"/>');
$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('<p>foo</p>', 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";
<container xmlns="some:ns" xmlns:bar="another:ns" xmlns:foo="http://foo/" foo:bar="value"><child xmlns="some:ns2"/></container>
--- After import into HTML ---
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<html xmlns="http://www.w3.org/1999/xhtml"><head/><body><p>foo<container xmlns="some:ns" xmlns:bar="another:ns" xmlns:foo="http://foo/" foo:bar="value"><child xmlns="some:ns2"/></container></p></body></html>
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><p>foo<container xmlns="some:ns" xmlns:bar="another:ns" xmlns:foo="http://foo/" foo:bar="value"><child xmlns="some:ns2"/></container></p></body></html>
<html><head></head><body><p>foo<container xmlns="some:ns" xmlns:bar="another:ns" foo:bar="value"><child></child></container></p></body></html>

View file

@ -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('<p>foo</p>', 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";
<child xmlns="some:ns2"/></container>
--- After import into HTML ---
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<html xmlns="http://www.w3.org/1999/xhtml"><head/><body><p>foo<container xmlns="some:ns" xmlns:bar="another:ns">
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><p>foo<container xmlns="some:ns" xmlns:bar="another:ns">
<x>
<subcontainer>
<test xmlns="x:y"/>

View file

@ -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('<p>foo</p>', 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";
<child xmlns="some:ns2"/></container>
--- After import into HTML ---
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<html xmlns="http://www.w3.org/1999/xhtml"><head/><body><p>foo<container xmlns="some:ns" xmlns:bar="another:ns"/></p></body></html>
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><p>foo<container xmlns="some:ns" xmlns:bar="another:ns"/></p></body></html>
<html><head></head><body><p>foo<container xmlns="some:ns" xmlns:bar="another:ns"></container></p></body></html>

Some files were not shown because too many files have changed in this diff Show more