diff --git a/ext/dom/characterdata.c b/ext/dom/characterdata.c index f47324049894b..192ca4ce3fc31 100644 --- a/ext/dom/characterdata.c +++ b/ext/dom/characterdata.c @@ -51,6 +51,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_characterdata_replace_data, 0, 0, 3) ZEND_ARG_INFO(0, count) ZEND_ARG_INFO(0, arg) ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_characterdata_remove, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_characterdata_after, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_characterdata_before, 0, 0, 0) +ZEND_END_ARG_INFO(); /* }}} */ /* @@ -66,6 +75,9 @@ const zend_function_entry php_dom_characterdata_class_functions[] = { PHP_FALIAS(insertData, dom_characterdata_insert_data, arginfo_dom_characterdata_insert_data) PHP_FALIAS(deleteData, dom_characterdata_delete_data, arginfo_dom_characterdata_delete_data) PHP_FALIAS(replaceData, dom_characterdata_replace_data, arginfo_dom_characterdata_replace_data) + PHP_ME(domcharacterdata, remove, arginfo_dom_characterdata_remove, ZEND_ACC_PUBLIC) + PHP_ME(domcharacterdata, after, arginfo_dom_characterdata_after, ZEND_ACC_PUBLIC) + PHP_ME(domcharacterdata, before, arginfo_dom_characterdata_before, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -387,4 +399,87 @@ PHP_FUNCTION(dom_characterdata_replace_data) } /* }}} end dom_characterdata_replace_data */ +PHP_METHOD(domcharacterdata, remove) +{ + zval *id; + xmlNodePtr children, child; + dom_object *intern; + int stricterror; + + id = ZEND_THIS; + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + DOM_GET_OBJ(child, id, xmlNodePtr, intern); + + if (dom_node_children_valid(child) == FAILURE) { + RETURN_NULL(); + } + + stricterror = dom_get_strict_error(intern->document); + + if (dom_node_is_read_only(child) == SUCCESS || + (child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) { + php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror); + RETURN_NULL(); + } + + if (!child->parent) { + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_NULL(); + } + + children = child->parent->children; + if (!children) { + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_NULL(); + } + + while (children) { + if (children == child) { + xmlUnlinkNode(child); + RETURN_NULL(); + } + children = children->next; + } + + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_NULL(); +} + +PHP_METHOD(domcharacterdata, after) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_after(intern, args, argc); +} + +PHP_METHOD(domcharacterdata, before) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_before(intern, args, argc); +} + #endif diff --git a/ext/dom/config.m4 b/ext/dom/config.m4 index 5d96e7bc0f783..acc10279a8ec3 100644 --- a/ext/dom/config.m4 +++ b/ext/dom/config.m4 @@ -18,6 +18,7 @@ if test "$PHP_DOM" != "no"; then AC_DEFINE(HAVE_DOM,1,[ ]) PHP_NEW_EXTENSION(dom, [php_dom.c attr.c document.c domerrorhandler.c \ domstringlist.c domexception.c namelist.c \ + parentnode.c \ processinginstruction.c cdatasection.c \ documentfragment.c domimplementation.c \ element.c node.c string_extend.c characterdata.c \ diff --git a/ext/dom/config.w32 b/ext/dom/config.w32 index 1ac3c614a55d0..0e43de8c638d5 100644 --- a/ext/dom/config.w32 +++ b/ext/dom/config.w32 @@ -9,6 +9,7 @@ if (PHP_DOM == "yes") { ) { EXTENSION("dom", "php_dom.c attr.c document.c domerrorhandler.c \ domstringlist.c domexception.c namelist.c processinginstruction.c \ + parentnode.c \ cdatasection.c documentfragment.c domimplementation.c element.c \ node.c string_extend.c characterdata.c documenttype.c \ domimplementationlist.c entity.c nodelist.c text.c comment.c \ diff --git a/ext/dom/document.c b/ext/dom/document.c index 95b077b6483a1..12a125acfd52c 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -183,6 +183,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_document_registernodeclass, 0, 0, 2) ZEND_ARG_INFO(0, baseClass) ZEND_ARG_INFO(0, extendedClass) ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_document_append, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_document_prepend, 0, 0, 0) +ZEND_END_ARG_INFO(); /* }}} */ /* @@ -230,6 +236,8 @@ const zend_function_entry php_dom_document_class_functions[] = { /* {{{ */ PHP_FALIAS(relaxNGValidateSource, dom_document_relaxNG_validate_xml, arginfo_dom_document_relaxNG_validate_xml) #endif PHP_ME(domdocument, registerNodeClass, arginfo_dom_document_registernodeclass, ZEND_ACC_PUBLIC) + PHP_ME(domdocument, append, arginfo_dom_document_append, ZEND_ACC_PUBLIC) + PHP_ME(domdocument, prepend, arginfo_dom_document_prepend, ZEND_ACC_PUBLIC) PHP_FE_END }; /* }}} */ @@ -2272,4 +2280,52 @@ PHP_METHOD(domdocument, registerNodeClass) } /* }}} */ +/* {{{ proto void domdocument::append(string|DOMNode ...$nodes) +URL: https://dom.spec.whatwg.org/#dom-parentnode-append +Since: DOM Living Standard (DOM4) +*/ +PHP_METHOD(domdocument, append) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_append(intern, args, argc); +} +/* }}} */ + +/* {{{ proto void domdocument::prepend(string|DOMNode ...$nodes) +URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend +Since: DOM Living Standard (DOM4) +*/ +PHP_METHOD(domdocument, prepend) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + if (context->children == NULL) { + dom_parent_node_append(intern, args, argc); + } else { + dom_parent_node_prepend(intern, args, argc); + } +} +/* }}} */ + #endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/ext/dom/documentfragment.c b/ext/dom/documentfragment.c index 9b222586ac537..965e65e6a79db 100644 --- a/ext/dom/documentfragment.c +++ b/ext/dom/documentfragment.c @@ -32,6 +32,12 @@ ZEND_END_ARG_INFO(); ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_documentfragement_appendXML, 0, 0, 1) ZEND_ARG_INFO(0, data) ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_documentfragment_append, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_documentfragment_prepend, 0, 0, 0) +ZEND_END_ARG_INFO(); /* }}} */ /* @@ -44,6 +50,8 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_documentfragment_class_functions[] = { PHP_ME(domdocumentfragment, __construct, arginfo_dom_documentfragement_construct, ZEND_ACC_PUBLIC) PHP_ME(domdocumentfragment, appendXML, arginfo_dom_documentfragement_appendXML, ZEND_ACC_PUBLIC) + PHP_ME(domdocumentfragment, append, arginfo_dom_documentfragment_append, ZEND_ACC_PUBLIC) + PHP_ME(domdocumentfragment, prepend, arginfo_dom_documentfragment_prepend, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -147,4 +155,52 @@ PHP_METHOD(domdocumentfragment, appendXML) { } /* }}} */ +/* {{{ proto void domdocumentfragment::append(string|DOMNode ...$nodes) +URL: https://dom.spec.whatwg.org/#dom-parentnode-append +Since: DOM Living Standard (DOM4) +*/ +PHP_METHOD(domdocumentfragment, append) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_append(intern, args, argc); +} +/* }}} */ + +/* {{{ proto void domdocumentfragment::prepend(string|DOMNode ...$nodes) +URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend +Since: DOM Living Standard (DOM4) +*/ +PHP_METHOD(domdocumentfragment, prepend) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + if (context->children == NULL) { + dom_parent_node_append(intern, args, argc); + } else { + dom_parent_node_prepend(intern, args, argc); + } +} +/* }}} */ + #endif diff --git a/ext/dom/dom_fe.h b/ext/dom/dom_fe.h index b8c1ff2266ee9..c15f8922ece58 100644 --- a/ext/dom/dom_fe.h +++ b/ext/dom/dom_fe.h @@ -23,6 +23,8 @@ extern const zend_function_entry php_dom_domexception_class_functions[]; extern const zend_function_entry php_dom_domstringlist_class_functions[]; extern const zend_function_entry php_dom_namelist_class_functions[]; +extern const zend_function_entry php_dom_parent_node_class_functions[]; +extern const zend_function_entry php_dom_child_node_class_functions[]; extern const zend_function_entry php_dom_domimplementationlist_class_functions[]; extern const zend_function_entry php_dom_domimplementationsource_class_functions[]; extern const zend_function_entry php_dom_domimplementation_class_functions[]; @@ -102,6 +104,8 @@ PHP_METHOD(domimplementation, getFeature); /* domdocumentfragment methods */ PHP_METHOD(domdocumentfragment, __construct); PHP_METHOD(domdocumentfragment, appendXML); +PHP_METHOD(domdocumentfragment, append); +PHP_METHOD(domdocumentfragment, prepend); /* domdocument methods */ PHP_FUNCTION(dom_document_create_element); @@ -130,6 +134,8 @@ PHP_FUNCTION(dom_document_savexml); PHP_FUNCTION(dom_document_validate); PHP_FUNCTION(dom_document_xinclude); PHP_METHOD(domdocument, registerNodeClass); +PHP_METHOD(domdocument, append); +PHP_METHOD(domdocument, prepend); #if defined(LIBXML_HTML_ENABLED) PHP_METHOD(domdocument, loadHTML); @@ -189,6 +195,9 @@ PHP_FUNCTION(dom_characterdata_append_data); PHP_FUNCTION(dom_characterdata_insert_data); PHP_FUNCTION(dom_characterdata_delete_data); PHP_FUNCTION(dom_characterdata_replace_data); +PHP_METHOD(domcharacterdata, remove); +PHP_METHOD(domcharacterdata, after); +PHP_METHOD(domcharacterdata, before); /* domattr methods */ PHP_FUNCTION(dom_attr_is_id); @@ -214,6 +223,11 @@ PHP_FUNCTION(dom_element_set_id_attribute); PHP_FUNCTION(dom_element_set_id_attribute_ns); PHP_FUNCTION(dom_element_set_id_attribute_node); PHP_METHOD(domelement, __construct); +PHP_METHOD(domelement, remove); +PHP_METHOD(domelement, after); +PHP_METHOD(domelement, before); +PHP_METHOD(domelement, append); +PHP_METHOD(domelement, prepend); /* domtext methods */ PHP_FUNCTION(dom_text_split_text); diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h index 73d404429e004..4d2fdefdd1ae8 100644 --- a/ext/dom/dom_properties.h +++ b/ext/dom/dom_properties.h @@ -113,6 +113,11 @@ int dom_namednodemap_length_read(dom_object *obj, zval *retval); /* namelist properties */ int dom_namelist_length_read(dom_object *obj, zval *retval); +/* parent node properties */ +int dom_parent_node_first_element_child_read(dom_object *obj, zval *retval); +int dom_parent_node_last_element_child_read(dom_object *obj, zval *retval); +int dom_parent_node_child_element_count(dom_object *obj, zval *retval); + /* node properties */ int dom_node_node_name_read(dom_object *obj, zval *retval); int dom_node_node_value_read(dom_object *obj, zval *retval); @@ -124,6 +129,8 @@ int dom_node_first_child_read(dom_object *obj, zval *retval); int dom_node_last_child_read(dom_object *obj, zval *retval); int dom_node_previous_sibling_read(dom_object *obj, zval *retval); int dom_node_next_sibling_read(dom_object *obj, zval *retval); +int dom_node_previous_element_sibling_read(dom_object *obj, zval *retval); +int dom_node_next_element_sibling_read(dom_object *obj, zval *retval); int dom_node_attributes_read(dom_object *obj, zval *retval); int dom_node_owner_document_read(dom_object *obj, zval *retval); int dom_node_namespace_uri_read(dom_object *obj, zval *retval); diff --git a/ext/dom/element.c b/ext/dom/element.c index 51aba0e155175..467646478d633 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -115,6 +115,21 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_element_construct, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, uri) ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_element_remove, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_element_after, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_element_before, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_element_append, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_element_prepend, 0, 0, 0) +ZEND_END_ARG_INFO(); /* }}} */ /* @@ -144,6 +159,11 @@ const zend_function_entry php_dom_element_class_functions[] = { /* {{{ */ PHP_FALIAS(setIdAttributeNS, dom_element_set_id_attribute_ns, arginfo_dom_element_set_id_attribute_ns) PHP_FALIAS(setIdAttributeNode, dom_element_set_id_attribute_node, arginfo_dom_element_set_id_attribute_node) PHP_ME(domelement, __construct, arginfo_dom_element_construct, ZEND_ACC_PUBLIC) + PHP_ME(domelement, remove, arginfo_dom_element_remove, ZEND_ACC_PUBLIC) + PHP_ME(domelement, after, arginfo_dom_element_after, ZEND_ACC_PUBLIC) + PHP_ME(domelement, before, arginfo_dom_element_before, ZEND_ACC_PUBLIC) + PHP_ME(domelement, append, arginfo_dom_element_append, ZEND_ACC_PUBLIC) + PHP_ME(domelement, prepend, arginfo_dom_element_prepend, ZEND_ACC_PUBLIC) PHP_FE_END }; /* }}} */ @@ -1276,4 +1296,140 @@ PHP_FUNCTION(dom_element_set_id_attribute_node) } /* }}} end dom_element_set_id_attribute_node */ +/* {{{ proto void DOMElement::remove(); +URL: +Since: +*/ +PHP_METHOD(domelement, remove) +{ + zval *id; + xmlNodePtr children, child; + dom_object *intern; + int stricterror; + + id = ZEND_THIS; + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + DOM_GET_OBJ(child, id, xmlNodePtr, intern); + + if (dom_node_children_valid(child) == FAILURE) { + RETURN_NULL(); + } + + stricterror = dom_get_strict_error(intern->document); + + if (dom_node_is_read_only(child) == SUCCESS || + (child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) { + php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror); + RETURN_NULL(); + } + + if (!child->parent) { + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_NULL(); + } + + children = child->parent->children; + if (!children) { + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_NULL(); + } + + while (children) { + if (children == child) { + xmlUnlinkNode(child); + RETURN_NULL(); + } + children = children->next; + } + + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_NULL(); +} +/* }}} end DOMElement::remove */ + +PHP_METHOD(domelement, after) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_after(intern, args, argc); +} + +PHP_METHOD(domelement, before) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_before(intern, args, argc); +} + +/* {{{ proto void domelement::append(string|DOMNode ...$nodes) +URL: https://dom.spec.whatwg.org/#dom-parentnode-append +Since: DOM Living Standard (DOM4) +*/ +PHP_METHOD(domelement, append) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_append(intern, args, argc); +} +/* }}} end DOMElement::append */ + +/* {{{ proto void domelement::prepend(string|DOMNode ...$nodes) +URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend +Since: DOM Living Standard (DOM4) +*/ +PHP_METHOD(domelement, prepend) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + if (context->children == NULL) { + dom_parent_node_append(intern, args, argc); + } else { + dom_parent_node_prepend(intern, args, argc); + } +} +/* }}} end DOMElement::prepend */ + #endif diff --git a/ext/dom/node.c b/ext/dom/node.c index 03e61efa674a3..1e11a80510629 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -121,6 +121,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_node_C14NFile, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, xpath, 1) ZEND_ARG_ARRAY_INFO(0, ns_prefixes, 1) ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_child_node_remove, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_child_node_after, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_child_node_before, 0, 0, 0) +ZEND_END_ARG_INFO(); /* }}} */ /* @@ -157,36 +166,12 @@ const zend_function_entry php_dom_node_class_functions[] = { /* {{{ */ }; /* }}} */ -static void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */ -{ - xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL; - - if (nodep->type == XML_ELEMENT_NODE) { - /* Following if block primarily used for inserting nodes created via createElementNS */ - if (nodep->nsDef != NULL) { - curns = nodep->nsDef; - while (curns) { - nsdftptr = curns->next; - if (curns->href != NULL) { - if((nsptr = xmlSearchNsByHref(doc, nodep->parent, curns->href)) && - (curns->prefix == NULL || xmlStrEqual(nsptr->prefix, curns->prefix))) { - curns->next = NULL; - if (prevns == NULL) { - nodep->nsDef = nsdftptr; - } else { - prevns->next = nsdftptr; - } - dom_set_old_ns(doc, curns); - curns = prevns; - } - } - prevns = curns; - curns = nsdftptr; - } - } - xmlReconciliateNs(doc, nodep); - } -} +const zend_function_entry php_dom_child_node_class_functions[] = { /* {{{ */ + PHP_ABSTRACT_ME(DOMChildNode, remove, arginfo_dom_child_node_remove) + PHP_ABSTRACT_ME(DOMChildNode, after, arginfo_dom_child_node_after) + PHP_ABSTRACT_ME(DOMChildNode, before, arginfo_dom_child_node_before) + PHP_FE_END +}; /* }}} */ /* {{{ nodeName string @@ -438,7 +423,6 @@ int dom_node_child_nodes_read(dom_object *obj, zval *retval) return SUCCESS; } - /* }}} */ /* {{{ firstChild DomNode @@ -559,6 +543,72 @@ int dom_node_next_sibling_read(dom_object *obj, zval *retval) /* }}} */ +/* {{{ previousElementSibling DomNode +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-640FB3C8 +Since: +*/ +int dom_node_previous_element_sibling_read(dom_object *obj, zval *retval) +{ + xmlNode *nodep, *prevsib; + + nodep = dom_object_get_node(obj); + + if (nodep == NULL) { + php_dom_throw_error(INVALID_STATE_ERR, 0); + return FAILURE; + } + + prevsib = nodep->prev; + + while (prevsib && prevsib->type != XML_ELEMENT_NODE) { + prevsib = prevsib->prev; + } + + if (!prevsib) { + ZVAL_NULL(retval); + return SUCCESS; + } + + php_dom_create_object(prevsib, retval, obj); + return SUCCESS; +} + +/* }}} */ + +/* {{{ nextElementSibling DomNode +readonly=yes +URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6AC54C2F +Since: +*/ +int dom_node_next_element_sibling_read(dom_object *obj, zval *retval) +{ + xmlNode *nodep, *nextsib; + + nodep = dom_object_get_node(obj); + + if (nodep == NULL) { + php_dom_throw_error(INVALID_STATE_ERR, 0); + return FAILURE; + } + + nextsib = nodep->next; + + while (nextsib != NULL && nextsib->type != XML_ELEMENT_NODE) { + nextsib = nextsib->next; + } + + if (!nextsib) { + ZVAL_NULL(retval); + return SUCCESS; + } + + php_dom_create_object(nextsib, retval, obj); + return SUCCESS; +} + +/* }}} */ + /* {{{ attributes DomNamedNodeMap readonly=yes URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-84CF096 diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c new file mode 100644 index 0000000000000..8829c282fefc0 --- /dev/null +++ b/ext/dom/parentnode.c @@ -0,0 +1,344 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | 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: | + | http://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: Benjamin Eberlei | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#if HAVE_LIBXML && HAVE_DOM +#include "php_dom.h" + + +/* {{{ DOMParentNode methods */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_parentnode_append, 0, 0, 0) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_parentnode_prepend, 0, 0, 0) +ZEND_END_ARG_INFO(); + +const zend_function_entry php_dom_parent_node_class_functions[] = { /* {{{ */ + PHP_ABSTRACT_ME(DOMParentNode, append, arginfo_dom_parentnode_append) + PHP_ABSTRACT_ME(DOMParentNode, prepend, arginfo_dom_parentnode_prepend) + PHP_FE_END +}; +/* }}} */ + +/* {{{ firstElementChild DomParentNode +readonly=yes +URL: https://www.w3.org/TR/dom/#dom-parentnode-firstelementchild +*/ +int dom_parent_node_first_element_child_read(dom_object *obj, zval *retval) +{ + xmlNode *nodep, *first = NULL; + + nodep = dom_object_get_node(obj); + + if (nodep == NULL) { + php_dom_throw_error(INVALID_STATE_ERR, 0); + return FAILURE; + } + + if (dom_node_children_valid(nodep) == SUCCESS) { + first = nodep->children; + + while (first && first->type != XML_ELEMENT_NODE) { + first = first->next; + } + } + + if (!first) { + ZVAL_NULL(retval); + return SUCCESS; + } + + php_dom_create_object(first, retval, obj); + return SUCCESS; +} +/* }}} */ + +/* {{{ lastElementChild DomParentNode +readonly=yes +URL: https://www.w3.org/TR/dom/#dom-parentnode-lastelementchild +*/ +int dom_parent_node_last_element_child_read(dom_object *obj, zval *retval) +{ + xmlNode *nodep, *last = NULL; + + nodep = dom_object_get_node(obj); + + if (nodep == NULL) { + php_dom_throw_error(INVALID_STATE_ERR, 0); + return FAILURE; + } + + if (dom_node_children_valid(nodep) == SUCCESS) { + last = nodep->last; + + while (last && last->type != XML_ELEMENT_NODE) { + last = last->prev; + } + } + + if (!last) { + ZVAL_NULL(retval); + return SUCCESS; + } + + php_dom_create_object(last, retval, obj); + return SUCCESS; +} +/* }}} */ + +/* {{{ childElementCount DomParentNode +readonly=yes +https://www.w3.org/TR/dom/#dom-parentnode-childelementcount +*/ +int dom_parent_node_child_element_count(dom_object *obj, zval *retval) +{ + xmlNode *nodep, *first = NULL; + zend_long count = 0; + + nodep = dom_object_get_node(obj); + + if (nodep == NULL) { + php_dom_throw_error(INVALID_STATE_ERR, 0); + return FAILURE; + } + + if (dom_node_children_valid(nodep) == SUCCESS) { + first = nodep->children; + + while (first != NULL) { + if (first->type == XML_ELEMENT_NODE) { + count++; + } + + first = first->next; + } + } + + ZVAL_LONG(retval, count); + + return SUCCESS; +} +/* }}} */ + +xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNode, zval *nodes, int nodesc) +{ + int i; + xmlDoc *documentNode; + xmlNode *fragment; + xmlNode *newNode; + zend_class_entry *ce; + dom_object *newNodeObj; + + if (contextNode->type == XML_DOCUMENT_NODE || contextNode->type == XML_HTML_DOCUMENT_NODE) { + documentNode = (xmlDoc *) contextNode; + } else { + documentNode = contextNode->doc; + } + + fragment = xmlNewDocFragment(documentNode); + + if (!fragment) { + return NULL; + } + + for (i = 0; i < nodesc; i++) { + if (Z_TYPE(nodes[i]) == IS_OBJECT) { + ce = Z_OBJCE(nodes[i]); + + if (instanceof_function(ce, dom_node_class_entry)) { + newNodeObj = Z_DOMOBJ_P(&nodes[i]); + newNode = dom_object_get_node(newNodeObj); + + if (newNode->parent != NULL) { + xmlUnlinkNode(newNode); + } + + newNodeObj->document = document; + xmlSetTreeDoc(newNode, documentNode); + + if (!xmlAddChild(fragment, newNode)) { + return NULL; + } + + continue; + } + } + + if (Z_TYPE(nodes[i]) != IS_STRING) { + convert_to_string_ex(&nodes[i]); + } + + newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[i])); + + xmlSetTreeDoc(newNode, documentNode); + + if (!xmlAddChild(fragment, newNode)) { + return NULL; + } + } + + return fragment; +} + +void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) +{ + xmlNode *contextNode = dom_object_get_node(context); + xmlNodePtr newchild, node, prevsib; + xmlNode *fragment = dom_zvals_to_fragment(context->document, contextNode, nodes, nodesc); + + newchild = fragment->children; + prevsib = contextNode->last; + + if (newchild) { + if (prevsib != NULL) { + prevsib->next = newchild; + } else { + contextNode->children = newchild; + } + + newchild->prev = prevsib; + contextNode->last = fragment->last; + + node = newchild; + while (node != NULL) { + node->parent = contextNode; + + if (node == fragment->last) { + break; + } + node = node->next; + } + + fragment->children = NULL; + fragment->last = NULL; + + dom_reconcile_ns(contextNode->doc, newchild); + } +} + +void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc) +{ + xmlNode *contextNode = dom_object_get_node(context); + xmlNodePtr newchild, node, prevsib, nextsib; + xmlNode *fragment = dom_zvals_to_fragment(context->document, contextNode, nodes, nodesc); + + newchild = fragment->children; + prevsib = NULL; + nextsib = contextNode->children; + + if (newchild) { + contextNode->children = newchild; + newchild->prev = prevsib; + fragment->last->next = nextsib; + nextsib->prev = fragment->last; + + node = newchild; + while (node != NULL) { + node->parent = contextNode; + + if (node == fragment->last) { + break; + } + node = node->next; + } + + fragment->children = NULL; + fragment->last = NULL; + + dom_reconcile_ns(contextNode->doc, newchild); + } +} + +void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc) +{ + xmlNode *prevsib = dom_object_get_node(context); + xmlNodePtr newchild, node, contextNode; + xmlNode *fragment; + + int stricterror = dom_get_strict_error(context->document); + + if (!prevsib->parent) { + php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror); + return; + } + + contextNode = prevsib->parent; + fragment = dom_zvals_to_fragment(context->document, contextNode, nodes, nodesc); + newchild = fragment->children; + + if (newchild) { + prevsib->next = newchild; + newchild->prev = prevsib; + contextNode->last = fragment->last; + + node = newchild; + while (node != NULL) { + node->parent = contextNode; + + if (node == fragment->last) { + break; + } + node = node->next; + } + + fragment->children = NULL; + fragment->last = NULL; + + dom_reconcile_ns(prevsib->doc, newchild); + } +} + +void dom_parent_node_before(dom_object *context, zval *nodes, int nodesc) +{ + xmlNode *nextsib = dom_object_get_node(context); + xmlNodePtr newchild, node, prevsib, contextNode; + xmlNode *fragment; + + prevsib = NULL; + contextNode = nextsib->parent; + fragment = dom_zvals_to_fragment(context->document, contextNode, nodes, nodesc); + newchild = fragment->children; + + if (newchild) { + contextNode->children = newchild; + newchild->prev = prevsib; + fragment->last->next = nextsib; + nextsib->prev = fragment->last; + + node = newchild; + while (node != NULL) { + node->parent = contextNode; + + if (node == fragment->last) { + break; + } + node = node->next; + } + + fragment->children = NULL; + fragment->last = NULL; + + dom_reconcile_ns(nextsib->doc, newchild); + } +} + +#endif diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 726b74b93452d..f7c40379aa29f 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -38,6 +38,8 @@ PHP_DOM_EXPORT zend_class_entry *dom_node_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domexception_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domstringlist_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_namelist_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_parentnode_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_childnode_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domimplementationlist_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domimplementationsource_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry; @@ -81,6 +83,7 @@ static HashTable dom_domstringlist_prop_handlers; static HashTable dom_namelist_prop_handlers; static HashTable dom_domimplementationlist_prop_handlers; static HashTable dom_document_prop_handlers; +static HashTable dom_documentfragment_prop_handlers; static HashTable dom_node_prop_handlers; static HashTable dom_nodelist_prop_handlers; static HashTable dom_namednodemap_prop_handlers; @@ -629,6 +632,12 @@ PHP_MINIT_FUNCTION(dom) dom_register_prop_handler(&dom_namelist_prop_handlers, "length", sizeof("length")-1, dom_namelist_length_read, NULL); zend_hash_add_ptr(&classes, ce.name, &dom_namelist_prop_handlers); + INIT_CLASS_ENTRY(ce, "DOMParentNode", php_dom_parent_node_class_functions); + dom_parentnode_class_entry = zend_register_internal_interface(&ce); + + INIT_CLASS_ENTRY(ce, "DOMChildNode", php_dom_child_node_class_functions); + dom_childnode_class_entry = zend_register_internal_interface(&ce); + REGISTER_DOM_CLASS(ce, "DOMImplementationList", NULL, php_dom_domimplementationlist_class_functions, dom_domimplementationlist_class_entry); zend_hash_init(&dom_domimplementationlist_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1); @@ -650,6 +659,8 @@ PHP_MINIT_FUNCTION(dom) dom_register_prop_handler(&dom_node_prop_handlers, "lastChild", sizeof("lastChild")-1, dom_node_last_child_read, NULL); dom_register_prop_handler(&dom_node_prop_handlers, "previousSibling", sizeof("previousSibling")-1, dom_node_previous_sibling_read, NULL); dom_register_prop_handler(&dom_node_prop_handlers, "nextSibling", sizeof("nextSibling")-1, dom_node_next_sibling_read, NULL); + dom_register_prop_handler(&dom_node_prop_handlers, "previousElementSibling", sizeof("previousElementSibling")-1, dom_node_previous_element_sibling_read, NULL); + dom_register_prop_handler(&dom_node_prop_handlers, "nextElementSibling", sizeof("nextElementSibling")-1, dom_node_next_element_sibling_read, NULL); dom_register_prop_handler(&dom_node_prop_handlers, "attributes", sizeof("attributes")-1, dom_node_attributes_read, NULL); dom_register_prop_handler(&dom_node_prop_handlers, "ownerDocument", sizeof("ownerDocument")-1, dom_node_owner_document_read, NULL); dom_register_prop_handler(&dom_node_prop_handlers, "namespaceURI", sizeof("namespaceURI")-1, dom_node_namespace_uri_read, NULL); @@ -673,7 +684,15 @@ PHP_MINIT_FUNCTION(dom) zend_hash_add_ptr(&classes, ce.name, &dom_namespace_node_prop_handlers); REGISTER_DOM_CLASS(ce, "DOMDocumentFragment", dom_node_class_entry, php_dom_documentfragment_class_functions, dom_documentfragment_class_entry); - zend_hash_add_ptr(&classes, ce.name, &dom_node_prop_handlers); + zend_hash_init(&dom_documentfragment_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1); + + dom_register_prop_handler(&dom_documentfragment_prop_handlers, "firstElementChild", sizeof("firstElementChild")-1, dom_parent_node_first_element_child_read, NULL); + dom_register_prop_handler(&dom_documentfragment_prop_handlers, "lastElementChild", sizeof("lastElementChild")-1, dom_parent_node_last_element_child_read, NULL); + dom_register_prop_handler(&dom_documentfragment_prop_handlers, "childElementCount", sizeof("childElementCount")-1, dom_parent_node_child_element_count, NULL); + + zend_hash_merge(&dom_documentfragment_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0); + zend_hash_add_ptr(&classes, ce.name, &dom_documentfragment_prop_handlers); + zend_class_implements(dom_documentfragment_class_entry, 1, dom_parentnode_class_entry); REGISTER_DOM_CLASS(ce, "DOMDocument", dom_node_class_entry, php_dom_document_class_functions, dom_document_class_entry); zend_hash_init(&dom_document_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1); @@ -697,8 +716,13 @@ PHP_MINIT_FUNCTION(dom) dom_register_prop_handler(&dom_document_prop_handlers, "recover", sizeof("recover")-1, dom_document_recover_read, dom_document_recover_write); dom_register_prop_handler(&dom_document_prop_handlers, "substituteEntities", sizeof("substituteEntities")-1, dom_document_substitue_entities_read, dom_document_substitue_entities_write); + dom_register_prop_handler(&dom_document_prop_handlers, "firstElementChild", sizeof("firstElementChild")-1, dom_parent_node_first_element_child_read, NULL); + dom_register_prop_handler(&dom_document_prop_handlers, "lastElementChild", sizeof("lastElementChild")-1, dom_parent_node_last_element_child_read, NULL); + dom_register_prop_handler(&dom_document_prop_handlers, "childElementCount", sizeof("childElementCount")-1, dom_parent_node_child_element_count, NULL); + zend_hash_merge(&dom_document_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0); zend_hash_add_ptr(&classes, ce.name, &dom_document_prop_handlers); + zend_class_implements(dom_document_class_entry, 1, dom_parentnode_class_entry); INIT_CLASS_ENTRY(ce, "DOMNodeList", php_dom_nodelist_class_functions); ce.create_object = dom_nnodemap_objects_new; @@ -728,6 +752,8 @@ PHP_MINIT_FUNCTION(dom) zend_hash_merge(&dom_characterdata_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0); zend_hash_add_ptr(&classes, ce.name, &dom_characterdata_prop_handlers); + zend_class_implements(dom_characterdata_class_entry, 1, dom_childnode_class_entry); + REGISTER_DOM_CLASS(ce, "DOMAttr", dom_node_class_entry, php_dom_attr_class_functions, dom_attr_class_entry); zend_hash_init(&dom_attr_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1); @@ -744,9 +770,14 @@ PHP_MINIT_FUNCTION(dom) zend_hash_init(&dom_element_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1); dom_register_prop_handler(&dom_element_prop_handlers, "tagName", sizeof("tagName")-1, dom_element_tag_name_read, NULL); dom_register_prop_handler(&dom_element_prop_handlers, "schemaTypeInfo", sizeof("schemaTypeInfo")-1, dom_element_schema_type_info_read, NULL); + dom_register_prop_handler(&dom_element_prop_handlers, "firstElementChild", sizeof("firstElementChild")-1, dom_parent_node_first_element_child_read, NULL); + dom_register_prop_handler(&dom_element_prop_handlers, "lastElementChild", sizeof("lastElementChild")-1, dom_parent_node_last_element_child_read, NULL); + dom_register_prop_handler(&dom_element_prop_handlers, "childElementCount", sizeof("childElementCount")-1, dom_parent_node_child_element_count, NULL); zend_hash_merge(&dom_element_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0); zend_hash_add_ptr(&classes, ce.name, &dom_element_prop_handlers); + zend_class_implements(dom_element_class_entry, 2, dom_parentnode_class_entry, dom_childnode_class_entry); + REGISTER_DOM_CLASS(ce, "DOMText", dom_characterdata_class_entry, php_dom_text_class_functions, dom_text_class_entry); zend_hash_init(&dom_text_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1); @@ -937,6 +968,7 @@ PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */ zend_hash_destroy(&dom_namelist_prop_handlers); zend_hash_destroy(&dom_domimplementationlist_prop_handlers); zend_hash_destroy(&dom_document_prop_handlers); + zend_hash_destroy(&dom_documentfragment_prop_handlers); zend_hash_destroy(&dom_node_prop_handlers); zend_hash_destroy(&dom_namespace_node_prop_handlers); zend_hash_destroy(&dom_nodelist_prop_handlers); @@ -1432,6 +1464,38 @@ void dom_set_old_ns(xmlDoc *doc, xmlNs *ns) { } /* }}} end dom_set_old_ns */ +void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */ +{ + xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL; + + if (nodep->type == XML_ELEMENT_NODE) { + /* Following if block primarily used for inserting nodes created via createElementNS */ + if (nodep->nsDef != NULL) { + curns = nodep->nsDef; + while (curns) { + nsdftptr = curns->next; + if (curns->href != NULL) { + if((nsptr = xmlSearchNsByHref(doc, nodep->parent, curns->href)) && + (curns->prefix == NULL || xmlStrEqual(nsptr->prefix, curns->prefix))) { + curns->next = NULL; + if (prevns == NULL) { + nodep->nsDef = nsdftptr; + } else { + prevns->next = nsdftptr; + } + dom_set_old_ns(doc, curns); + curns = prevns; + } + } + prevns = curns; + curns = nsdftptr; + } + } + xmlReconciliateNs(doc, nodep); + } +} +/* }}} */ + /* http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index ce377b5ce72e0..392f3984ed8aa 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -110,6 +110,7 @@ void node_list_unlink(xmlNodePtr node); int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len); xmlNsPtr dom_get_ns(xmlNodePtr node, char *uri, int *errorcode, char *prefix); void dom_set_old_ns(xmlDoc *doc, xmlNs *ns); +void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep); xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName); void dom_normalize (xmlNodePtr nodep); xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr nodep, char *ns, char *local, int *cur, int index); @@ -128,6 +129,11 @@ void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece zval *dom_nodelist_read_dimension(zval *object, zval *offset, int type, zval *rv); int dom_nodelist_has_dimension(zval *object, zval *member, int check_empty); +void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc); +void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc); +void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc); +void dom_parent_node_before(dom_object *context, zval *nodes, int nodesc); + #define REGISTER_DOM_CLASS(ce, name, parent_ce, funcs, entry) \ INIT_CLASS_ENTRY(ce, name, funcs); \ ce.create_object = dom_objects_new; \ diff --git a/ext/dom/tests/DOM4_DOMDocument_adoptNode.phpt b/ext/dom/tests/DOM4_DOMDocument_adoptNode.phpt new file mode 100644 index 0000000000000..e17e1cf624c3e --- /dev/null +++ b/ext/dom/tests/DOM4_DOMDocument_adoptNode.phpt @@ -0,0 +1,50 @@ +--TEST-- +DOMDocumentNode::adoptNode() +--SKIPIF-- + +--FILE-- +loadXML('content'); +if(!$source) { + echo "Error while parsing the document\n"; + exit; +} +$target = new DOMDocument; +$target->loadXML('first'); +if(!$target) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $source->documentElement->firstChild; +$adoptedElement = $target->adoptNode($element); + +if($adoptedElement !== $element || $element->ownerDocument !== $target) { + echo "Node was not adopted into target document\n"; + exit; +} + +$target->documentElement->appendChild($adoptedElement); + +print_node($target->documentElement->childNodes[0]); +print_node($target->documentElement->childNodes[1]); +print_node($source->documentElement); + +--EXPECT-- +Node Name: mark +Node Type: 1 +Num Children: 1 +Node Content: first + +Node Name: foo +Node Type: 1 +Num Children: 1 +Node Content: content + +Node Name: test +Node Type: 1 +Num Children: 0 +Node Content: diff --git a/ext/dom/tests/DOM4_DOMNode_ElementSiblings.phpt b/ext/dom/tests/DOM4_DOMNode_ElementSiblings.phpt new file mode 100644 index 0000000000000..c986972eb2faa --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_ElementSiblings.phpt @@ -0,0 +1,28 @@ +--TEST-- +DOMNode: Element Siblings +--SKIPIF-- + +--FILE-- +loadXML('fooFirstElementLastElementbar'); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $dom->documentElement; +print_node($element->firstElementChild->nextElementSibling); +print_node($element->lastElementChild->previousElementSibling); +--EXPECT-- +Node Name: bar +Node Type: 1 +Num Children: 1 +Node Content: LastElement + +Node Name: bar +Node Type: 1 +Num Children: 1 +Node Content: FirstElement diff --git a/ext/dom/tests/DOM4_DOMNode_after.phpt b/ext/dom/tests/DOM4_DOMNode_after.phpt new file mode 100644 index 0000000000000..dd697a3a6aff2 --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_after.phpt @@ -0,0 +1,38 @@ +--TEST-- +DOMNode::after() +--SKIPIF-- + +--FILE-- +loadXML('first'); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $dom->documentElement->firstChild; +print_node($element); +$element->after( + 'text inserted after', + $dom->createElement('inserted-after', 'content') +); +print_node($dom->documentElement->childNodes[1]); +print_node($dom->documentElement->childNodes[2]); +--EXPECT-- +Node Name: mark +Node Type: 1 +Num Children: 1 +Node Content: first + +Node Name: #text +Node Type: 3 +Num Children: 0 +Node Content: text inserted after + +Node Name: inserted-after +Node Type: 1 +Num Children: 1 +Node Content: content diff --git a/ext/dom/tests/DOM4_DOMNode_after_adopts_node.phpt b/ext/dom/tests/DOM4_DOMNode_after_adopts_node.phpt new file mode 100644 index 0000000000000..24898492ec25c --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_after_adopts_node.phpt @@ -0,0 +1,49 @@ +--TEST-- +DOMNode::after() adopts node from other document +--SKIPIF-- + +--FILE-- +loadXML('content'); +if(!$source) { + echo "Error while parsing the document\n"; + exit; +} +$target = new DOMDocument; +$target->loadXML('first'); +if(!$target) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $source->documentElement->firstChild; +$markElement = $target->documentElement->firstChild; +$markElement->after($element); + +if($element->ownerDocument !== $target || $element !== $target->documentElement->childNodes[1]) { + echo "Node was not adopted into target document\n"; + exit; +} + +print_node($target->documentElement->childNodes[0]); +print_node($target->documentElement->childNodes[1]); +print_node($source->documentElement); + +--EXPECT-- +Node Name: mark +Node Type: 1 +Num Children: 1 +Node Content: first + +Node Name: foo +Node Type: 1 +Num Children: 1 +Node Content: content + +Node Name: test +Node Type: 1 +Num Children: 0 +Node Content: diff --git a/ext/dom/tests/DOM4_DOMNode_after_ns.phpt b/ext/dom/tests/DOM4_DOMNode_after_ns.phpt new file mode 100644 index 0000000000000..f15245a611ffb --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_after_ns.phpt @@ -0,0 +1,29 @@ +--TEST-- +DOMNode::after() with namespace +--SKIPIF-- + +--FILE-- +formatOutput = true; + +$root = $doc->createElementNS('http://www.w3.org/2005/Atom', 'element'); +$doc->appendChild($root); +$root->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:g', 'http://base.google.com/ns/1.0'); + +$item = $doc->createElementNS('http://base.google.com/ns/1.0', 'g:item_type', 'house'); +$root->append($item); + +$item2 = $doc->createElementNS('http://base.google.com/ns/1.0', 'g:item_type', 'street'); +$item->after($item2); + +echo $doc->saveXML(), "\n"; + +--EXPECT-- + + + house + street + diff --git a/ext/dom/tests/DOM4_DOMNode_append_ns.phpt b/ext/dom/tests/DOM4_DOMNode_append_ns.phpt new file mode 100644 index 0000000000000..6e3c34d50e672 --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_append_ns.phpt @@ -0,0 +1,25 @@ +--TEST-- +DOMNode::append() with namespace +--SKIPIF-- + +--FILE-- +formatOutput = true; + +$root = $doc->createElementNS('http://www.w3.org/2005/Atom', 'element'); +$doc->appendChild($root); +$root->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:g', 'http://base.google.com/ns/1.0'); + +$item = $doc->createElementNS('http://base.google.com/ns/1.0', 'g:item_type', 'house'); +$root->append($item); + +echo $doc->saveXML(), "\n"; + +--EXPECT-- + + + house + diff --git a/ext/dom/tests/DOM4_DOMNode_before.phpt b/ext/dom/tests/DOM4_DOMNode_before.phpt new file mode 100644 index 0000000000000..5b172ace683cf --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_before.phpt @@ -0,0 +1,38 @@ +--TEST-- +DOMNode::before() +--SKIPIF-- + +--FILE-- +loadXML('first'); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $dom->documentElement->firstChild; +print_node($element); +$element->before( + $dom->createElement('inserted-before', 'content'), + 'text inserted before' +); +print_node($dom->documentElement->childNodes[0]); +print_node($dom->documentElement->childNodes[1]); +--EXPECT-- +Node Name: mark +Node Type: 1 +Num Children: 1 +Node Content: first + +Node Name: inserted-before +Node Type: 1 +Num Children: 1 +Node Content: content + +Node Name: #text +Node Type: 3 +Num Children: 0 +Node Content: text inserted before diff --git a/ext/dom/tests/DOM4_DOMNode_before_ns.phpt b/ext/dom/tests/DOM4_DOMNode_before_ns.phpt new file mode 100644 index 0000000000000..3d32c2ce9301a --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_before_ns.phpt @@ -0,0 +1,29 @@ +--TEST-- +DOMNode::after() with namespace +--SKIPIF-- + +--FILE-- +formatOutput = true; + +$root = $doc->createElementNS('http://www.w3.org/2005/Atom', 'element'); +$doc->appendChild($root); +$root->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:g', 'http://base.google.com/ns/1.0'); + +$item = $doc->createElementNS('http://base.google.com/ns/1.0', 'g:item_type', 'house'); +$root->append($item); + +$item2 = $doc->createElementNS('http://base.google.com/ns/1.0', 'g:item_type', 'street'); +$item->before($item2); + +echo $doc->saveXML(), "\n"; + +--EXPECT-- + + + street + house + diff --git a/ext/dom/tests/DOM4_DOMNode_prepend_ns.phpt b/ext/dom/tests/DOM4_DOMNode_prepend_ns.phpt new file mode 100644 index 0000000000000..b3f8f356d397d --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_prepend_ns.phpt @@ -0,0 +1,25 @@ +--TEST-- +DOMNode::prepend() with namespace +--SKIPIF-- + +--FILE-- +formatOutput = true; + +$root = $doc->createElementNS('http://www.w3.org/2005/Atom', 'element'); +$doc->appendChild($root); +$root->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:g', 'http://base.google.com/ns/1.0'); + +$item = $doc->createElementNS('http://base.google.com/ns/1.0', 'g:item_type', 'house'); +$root->prepend($item); + +echo $doc->saveXML(), "\n"; + +--EXPECT-- + + + house + diff --git a/ext/dom/tests/DOM4_DOMNode_remove.phpt b/ext/dom/tests/DOM4_DOMNode_remove.phpt new file mode 100644 index 0000000000000..c49277290e4d8 --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_remove.phpt @@ -0,0 +1,33 @@ +--TEST-- +DOMNode::remove() +--SKIPIF-- + +--FILE-- +loadXML('firstsecond'); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $dom->documentElement; +print_node($element->firstChild); +$returnValue = $element->firstChild->remove(); +print_node($element->firstChild); +var_dump($returnValue); +--EXPECT-- +Node Name: one +Node Type: 1 +Num Children: 1 +Node Content: first + +Node Name: two +Node Type: 1 +Num Children: 1 +Node Content: second + +NULL + diff --git a/ext/dom/tests/DOM4_DOMNode_replaceWith.phpt b/ext/dom/tests/DOM4_DOMNode_replaceWith.phpt new file mode 100644 index 0000000000000..72c5053f1e421 --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_replaceWith.phpt @@ -0,0 +1,44 @@ +--TEST-- +DOMNode::replaceWith() +--SKIPIF-- + +--FILE-- +loadXML('first'); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $dom->documentElement->firstChild; +print_node($dom->documentElement); +$element->replaceWith( + $dom->createElement('element', 'content'), + 'content' +); + +print_node($dom->documentElement); +print_node($dom->documentElement->childNodes[0]); +print_node($dom->documentElement->childNodes[1]); +--EXPECT-- +Node Name: test +Node Type: 1 +Num Children: 1 +Node Content: first + +Node Name: test +Node Type: 1 +Num Children: 2 + +Node Name: element +Node Type: 1 +Num Children: 1 +Node Content: content + +Node Name: #text +Node Type: 3 +Num Children: 0 +Node Content: content diff --git a/ext/dom/tests/DOM4_ParentNode.phpt b/ext/dom/tests/DOM4_ParentNode.phpt new file mode 100644 index 0000000000000..fcb164d6db84d --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode.phpt @@ -0,0 +1,54 @@ +--TEST-- +DOMParentNode: Child Element Handling +--SKIPIF-- + +--FILE-- +loadXML('fooFirstElementLastElementbar'); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +var_dump($dom instanceof DOMParentNode); +print_node($dom->firstElementChild); +print_node($dom->lastElementChild); +var_dump($dom->childElementCount); + +$element = $dom->documentElement; +var_dump($element instanceof DOMParentNode); +print_node($element->firstElementChild); +print_node($element->lastElementChild); +var_dump($element->childElementCount); +var_dump($element->lastElementChild->firstElementChild); +var_dump($element->lastElementChild->lastElementChild); +var_dump($element->lastElementChild->childElementCount); +--EXPECT-- +bool(true) +Node Name: test +Node Type: 1 +Num Children: 4 + +Node Name: test +Node Type: 1 +Num Children: 4 + +int(1) +bool(true) +Node Name: bar +Node Type: 1 +Num Children: 1 +Node Content: FirstElement + +Node Name: bar +Node Type: 1 +Num Children: 1 +Node Content: LastElement + +int(2) +NULL +NULL +int(0) diff --git a/ext/dom/tests/DOM4_ParentNode_Fragment.phpt b/ext/dom/tests/DOM4_ParentNode_Fragment.phpt new file mode 100644 index 0000000000000..82ab729efca4a --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_Fragment.phpt @@ -0,0 +1,44 @@ +--TEST-- +DOMParentNode: Child Element Handling +--SKIPIF-- + +--FILE-- +loadXML(''); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$fragment = $dom->createDocumentFragment(); +$fragment->appendChild($dom->createTextNode('foo')); +$fragment->appendChild($dom->createElement('bar', 'FirstElement')); +$fragment->appendChild($dom->createElement('bar', 'LastElement')); +$fragment->appendChild($dom->createTextNode('bar')); + +var_dump($fragment instanceof DOMParentNode); +print_node($fragment->firstElementChild); +print_node($fragment->lastElementChild); +var_dump($fragment->childElementCount); +var_dump($fragment->lastElementChild->firstElementChild); +var_dump($fragment->lastElementChild->lastElementChild); +var_dump($fragment->lastElementChild->childElementCount); +--EXPECT-- +bool(true) +Node Name: bar +Node Type: 1 +Num Children: 1 +Node Content: FirstElement + +Node Name: bar +Node Type: 1 +Num Children: 1 +Node Content: LastElement + +int(2) +NULL +NULL +int(0) diff --git a/ext/dom/tests/DOM4_ParentNode_append.phpt b/ext/dom/tests/DOM4_ParentNode_append.phpt new file mode 100644 index 0000000000000..f67e7c98abb43 --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_append.phpt @@ -0,0 +1,41 @@ +--TEST-- +DOMParentNode::append() +--SKIPIF-- + +--FILE-- +loadXML(''); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $dom->documentElement; +$element->append( + $dom->createElement('element', 'content'), + 'content' +); + +var_dump($dom->documentElement->childElementCount); +print_node($element->childNodes[0]); +print_node($element->childNodes[1]); +print_node($element->childNodes[2]); +--EXPECT-- +int(2) +Node Name: mark +Node Type: 1 +Num Children: 0 +Node Content: + +Node Name: element +Node Type: 1 +Num Children: 1 +Node Content: content + +Node Name: #text +Node Type: 3 +Num Children: 0 +Node Content: content diff --git a/ext/dom/tests/DOM4_ParentNode_append_adopts_node.phpt b/ext/dom/tests/DOM4_ParentNode_append_adopts_node.phpt new file mode 100644 index 0000000000000..187b35dc73839 --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_append_adopts_node.phpt @@ -0,0 +1,48 @@ +--TEST-- +DOMParentNode::append() adopts node from other document +--SKIPIF-- + +--FILE-- +loadXML('content'); +if(!$source) { + echo "Error while parsing the document\n"; + exit; +} +$target = new DOMDocument; +$target->loadXML('first'); +if(!$target) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $source->documentElement->firstChild; +$target->documentElement->append($element); + +if($element->ownerDocument !== $target || $element !== $target->documentElement->childNodes[1]) { + echo "Node was not adopted into target document\n"; + exit; +} + +print_node($target->documentElement->childNodes[0]); +print_node($target->documentElement->childNodes[1]); +print_node($source->documentElement); + +--EXPECT-- +Node Name: mark +Node Type: 1 +Num Children: 1 +Node Content: first + +Node Name: foo +Node Type: 1 +Num Children: 1 +Node Content: content + +Node Name: test +Node Type: 1 +Num Children: 0 +Node Content: diff --git a/ext/dom/tests/DOM4_ParentNode_append_with_attributes.phpt b/ext/dom/tests/DOM4_ParentNode_append_with_attributes.phpt new file mode 100644 index 0000000000000..dd72f72d4fb4d --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_append_with_attributes.phpt @@ -0,0 +1,27 @@ +--TEST-- +DOMParentNode::append() with attributes +--SKIPIF-- + +--FILE-- +loadXML(''); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$replacement = $dom->createAttribute('attr-one'); +$replacement->value ='42'; +$addition = $dom->createAttribute('attr-two'); +$addition->value = '23'; + +$element = $dom->documentElement; +$element->append( + $replacement, $addition +); +echo $dom->saveXML($dom->documentElement); +--EXPECT-- + diff --git a/ext/dom/tests/DOM4_ParentNode_prepend.phpt b/ext/dom/tests/DOM4_ParentNode_prepend.phpt new file mode 100644 index 0000000000000..1879186d92459 --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_prepend.phpt @@ -0,0 +1,39 @@ +--TEST-- +DOMParentNode::prepend() +--SKIPIF-- + +--FILE-- +loadXML(''); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$element = $dom->documentElement; +$element->prepend( + $dom->createElement('element', 'content'), + 'content' +); + +print_node($element->childNodes[0]); +print_node($element->childNodes[1]); +print_node($element->childNodes[2]); +--EXPECT-- +Node Name: element +Node Type: 1 +Num Children: 1 +Node Content: content + +Node Name: #text +Node Type: 3 +Num Children: 0 +Node Content: content + +Node Name: mark +Node Type: 1 +Num Children: 0 +Node Content: diff --git a/ext/dom/tests/bug69846.phpt b/ext/dom/tests/bug69846.phpt index fcca4c06d6514..00d04e82c424d 100644 --- a/ext/dom/tests/bug69846.phpt +++ b/ext/dom/tests/bug69846.phpt @@ -31,7 +31,7 @@ foreach ($dataNodes AS $node) { ===DONE=== --EXPECTF-- int(3) -object(DOMText)#%d (19) { +object(DOMText)#%d (21) { ["wholeText"]=> string(3) " " @@ -59,6 +59,10 @@ object(DOMText)#%d (19) { NULL ["nextSibling"]=> NULL + ["previousElementSibling"]=> + NULL + ["nextElementSibling"]=> + NULL ["attributes"]=> NULL ["ownerDocument"]=> @@ -75,11 +79,17 @@ object(DOMText)#%d (19) { string(3) " " } -object(DOMElement)#%d (18) { +object(DOMElement)#%d (23) { ["tagName"]=> string(5) "form1" ["schemaTypeInfo"]=> NULL + ["firstElementChild"]=> + string(22) "(object value omitted)" + ["lastElementChild"]=> + string(22) "(object value omitted)" + ["childElementCount"]=> + int(3) ["nodeName"]=> string(5) "form1" ["nodeValue"]=> @@ -102,6 +112,10 @@ object(DOMElement)#%d (18) { NULL ["nextSibling"]=> NULL + ["previousElementSibling"]=> + NULL + ["nextElementSibling"]=> + NULL ["attributes"]=> string(22) "(object value omitted)" ["ownerDocument"]=> @@ -121,7 +135,7 @@ object(DOMElement)#%d (18) { Value C " } -object(DOMText)#%d (19) { +object(DOMText)#%d (21) { ["wholeText"]=> string(1) " " @@ -149,6 +163,10 @@ object(DOMText)#%d (19) { NULL ["nextSibling"]=> NULL + ["previousElementSibling"]=> + NULL + ["nextElementSibling"]=> + NULL ["attributes"]=> NULL ["ownerDocument"]=> diff --git a/ext/dom/tests/domobject_debug_handler.phpt b/ext/dom/tests/domobject_debug_handler.phpt index 57d4a66ebb49d..d4df423a40ba3 100644 --- a/ext/dom/tests/domobject_debug_handler.phpt +++ b/ext/dom/tests/domobject_debug_handler.phpt @@ -39,6 +39,9 @@ DOMDocument Object [preserveWhiteSpace] => 1 [recover] => [substituteEntities] => + [firstElementChild] => (object value omitted) + [lastElementChild] => (object value omitted) + [childElementCount] => 1 [nodeName] => #document [nodeValue] => [nodeType] => 9 @@ -48,6 +51,8 @@ DOMDocument Object [lastChild] => (object value omitted) [previousSibling] => [nextSibling] => + [previousElementSibling] => + [nextElementSibling] => [attributes] => [ownerDocument] => [namespaceURI] =>