From: Benjamin Eberlei Date: Fri, 28 Feb 2020 15:13:39 +0000 (+0100) Subject: [RFC] Implement new DOM Living Standard APIs in ext/dom X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5acd86df8e39fe65f98e4cc2c5fc1c59ab90f68d;p=php [RFC] Implement new DOM Living Standard APIs in ext/dom --- diff --git a/ext/dom/characterdata.c b/ext/dom/characterdata.c index b3e91e769c..cff745a522 100644 --- a/ext/dom/characterdata.c +++ b/ext/dom/characterdata.c @@ -37,6 +37,10 @@ const zend_function_entry php_dom_characterdata_class_functions[] = { PHP_ME(domcharacterdata, insertData, arginfo_class_DOMCharacterData_insertData, ZEND_ACC_PUBLIC) PHP_ME(domcharacterdata, deleteData, arginfo_class_DOMCharacterData_deleteData, ZEND_ACC_PUBLIC) PHP_ME(domcharacterdata, replaceData, arginfo_class_DOMCharacterData_replaceData, ZEND_ACC_PUBLIC) + PHP_ME(domcharacterdata, remove, arginfo_class_DOMChildNode_remove, ZEND_ACC_PUBLIC) + PHP_ME(domcharacterdata, after, arginfo_class_DOMChildNode_after, ZEND_ACC_PUBLIC) + PHP_ME(domcharacterdata, before, arginfo_class_DOMChildNode_before, ZEND_ACC_PUBLIC) + PHP_ME(domcharacterdata, replaceWith, arginfo_class_DOMChildNode_replaceWith, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -361,4 +365,104 @@ PHP_METHOD(domcharacterdata, replaceData) } /* }}} end dom_characterdata_replace_data */ +PHP_METHOD(domcharacterdata, remove) +{ + zval *id = ZEND_THIS; + xmlNodePtr children, child; + dom_object *intern; + int stricterror; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + 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_THROWS(); + } + + 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_THROWS(); + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_before(intern, args, argc); +} + +PHP_METHOD(domcharacterdata, replaceWith) +{ + int argc; + zval *args, *id; + dom_object *intern; + xmlNode *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + RETURN_THROWS(); + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_after(intern, args, argc); + dom_child_node_remove(intern); +} + #endif diff --git a/ext/dom/config.m4 b/ext/dom/config.m4 index de3b54da4c..6a83d10c8e 100644 --- a/ext/dom/config.m4 +++ b/ext/dom/config.m4 @@ -13,7 +13,7 @@ if test "$PHP_DOM" != "no"; then PHP_SETUP_LIBXML(DOM_SHARED_LIBADD, [ AC_DEFINE(HAVE_DOM,1,[ ]) PHP_NEW_EXTENSION(dom, [php_dom.c attr.c document.c \ - domexception.c \ + domexception.c parentnode.c \ processinginstruction.c cdatasection.c \ documentfragment.c domimplementation.c \ element.c node.c characterdata.c \ diff --git a/ext/dom/config.w32 b/ext/dom/config.w32 index cd5f5a6cb8..7795445019 100644 --- a/ext/dom/config.w32 +++ b/ext/dom/config.w32 @@ -8,7 +8,7 @@ if (PHP_DOM == "yes") { CHECK_HEADER_ADD_INCLUDE("libxml/parser.h", "CFLAGS_DOM", PHP_PHP_BUILD + "\\include\\libxml2") ) { EXTENSION("dom", "php_dom.c attr.c document.c \ - domexception.c processinginstruction.c \ + domexception.c parentnode.c processinginstruction.c \ cdatasection.c documentfragment.c domimplementation.c element.c \ node.c characterdata.c documenttype.c \ entity.c nodelist.c text.c comment.c \ diff --git a/ext/dom/document.c b/ext/dom/document.c index 2b66fccf80..5cb114d45c 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -82,6 +82,8 @@ const zend_function_entry php_dom_document_class_functions[] = { /* {{{ */ PHP_ME(domdocument, relaxNGValidateSource, arginfo_class_DOMDocument_relaxNGValidateSource, ZEND_ACC_PUBLIC) #endif PHP_ME(domdocument, registerNodeClass, arginfo_class_DOMDocument_registerNodeClass, ZEND_ACC_PUBLIC) + PHP_ME(domdocument, append, arginfo_class_DOMParentNode_append, ZEND_ACC_PUBLIC) + PHP_ME(domdocument, prepend, arginfo_class_DOMParentNode_prepend, ZEND_ACC_PUBLIC) PHP_FE_END }; /* }}} */ @@ -2128,4 +2130,48 @@ 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_THROWS(); + } + + 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_THROWS(); + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + 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 d0f44a7d8f..d6e1060d34 100644 --- a/ext/dom/documentfragment.c +++ b/ext/dom/documentfragment.c @@ -34,6 +34,8 @@ const zend_function_entry php_dom_documentfragment_class_functions[] = { PHP_ME(domdocumentfragment, __construct, arginfo_class_DOMDocumentFragment___construct, ZEND_ACC_PUBLIC) PHP_ME(domdocumentfragment, appendXML, arginfo_class_DOMDocumentFragment_appendXML, ZEND_ACC_PUBLIC) + PHP_ME(domdocumentfragment, append, arginfo_class_DOMParentNode_append, ZEND_ACC_PUBLIC) + PHP_ME(domdocumentfragment, prepend, arginfo_class_DOMParentNode_prepend, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -137,4 +139,48 @@ 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_THROWS(); + } + + 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_THROWS(); + } + + id = ZEND_THIS; + DOM_GET_OBJ(context, id, xmlNodePtr, intern); + + dom_parent_node_prepend(intern, args, argc); +} +/* }}} */ + #endif diff --git a/ext/dom/dom.stub.php b/ext/dom/dom.stub.php index d88fb7f4c4..e87c2917c9 100644 --- a/ext/dom/dom.stub.php +++ b/ext/dom/dom.stub.php @@ -1,5 +1,28 @@ 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_class_DOMChildNode_remove) + PHP_ABSTRACT_ME(DOMChildNode, after, arginfo_class_DOMChildNode_after) + PHP_ABSTRACT_ME(DOMChildNode, before, arginfo_class_DOMChildNode_before) + PHP_FE_END +}; /* }}} */ /* {{{ nodeName string @@ -333,7 +309,6 @@ int dom_node_child_nodes_read(dom_object *obj, zval *retval) return SUCCESS; } - /* }}} */ /* {{{ firstChild DomNode @@ -454,6 +429,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 0000000000..109b74d162 --- /dev/null +++ b/ext/dom/parentnode.c @@ -0,0 +1,408 @@ +/* + +----------------------------------------------------------------------+ + | 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" +#include "dom_arginfo.h" + + +/* {{{ DOMParentNode methods */ +const zend_function_entry php_dom_parent_node_class_functions[] = { /* {{{ */ + PHP_ABSTRACT_ME(DOMParentNode, append, arginfo_class_DOMParentNode_append) + PHP_ABSTRACT_ME(DOMParentNode, prepend, arginfo_class_DOMParentNode_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; + int stricterror; + + 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; + } + + stricterror = dom_get_strict_error(document); + + 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->doc != documentNode) { + php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror); + return NULL; + } + + if (newNode->parent != NULL) { + xmlUnlinkNode(newNode); + } + + newNodeObj->document = document; + xmlSetTreeDoc(newNode, documentNode); + + if (!xmlAddChild(fragment, newNode)) { + xmlFree(fragment); + + php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror); + return NULL; + } + + continue; + } else { + xmlFree(fragment); + + zend_type_error("Invalid argument type must be either DOMNode or string"); + return NULL; + } + } else if (Z_TYPE(nodes[i]) == IS_STRING) { + newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[i])); + + xmlSetTreeDoc(newNode, documentNode); + + if (!xmlAddChild(fragment, newNode)) { + xmlFree(fragment); + + return NULL; + } + } else { + xmlFree(fragment); + + zend_type_error("Invalid argument type must be either DOMNode or string"); + + return NULL; + } + } + + return fragment; +} + +static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fragment) +{ + xmlNodePtr node = fragment->children; + + while (node != NULL) { + node->parent = parentNode; + + if (node == fragment->last) { + break; + } + node = node->next; + } + + fragment->children = NULL; + fragment->last = NULL; +} + +void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) +{ + xmlNode *parentNode = dom_object_get_node(context); + xmlNodePtr newchild, prevsib; + xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); + + if (fragment == NULL) { + return; + } + + newchild = fragment->children; + prevsib = parentNode->last; + + if (newchild) { + if (prevsib != NULL) { + prevsib->next = newchild; + } else { + parentNode->children = newchild; + } + + parentNode->last = fragment->last; + + newchild->prev = prevsib; + + dom_fragment_assign_parent_node(parentNode, fragment); + + dom_reconcile_ns(parentNode->doc, newchild); + } + + xmlFree(fragment); +} + +void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc) +{ + xmlNode *parentNode = dom_object_get_node(context); + + if (parentNode->children == NULL) { + dom_parent_node_append(context, nodes, nodesc); + return; + } + + xmlNodePtr newchild, nextsib; + xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); + + if (fragment == NULL) { + return; + } + + newchild = fragment->children; + nextsib = parentNode->children; + + if (newchild) { + parentNode->children = newchild; + fragment->last->next = nextsib; + nextsib->prev = fragment->last; + + dom_fragment_assign_parent_node(parentNode, fragment); + + dom_reconcile_ns(parentNode->doc, newchild); + } + + xmlFree(fragment); +} + +void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc) +{ + xmlNode *prevsib = dom_object_get_node(context); + xmlNodePtr newchild, parentNode; + xmlNode *fragment; + + int stricterror = dom_get_strict_error(context->document); + + if (!prevsib->parent) { + php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror); + return; + } + + parentNode = prevsib->parent; + fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); + + if (fragment == NULL) { + return; + } + + newchild = fragment->children; + + if (newchild) { + fragment->last->next = prevsib->next; + prevsib->next = newchild; + + newchild->prev = prevsib; + + dom_fragment_assign_parent_node(parentNode, fragment); + dom_reconcile_ns(prevsib->doc, newchild); + } + + xmlFree(fragment); +} + +void dom_parent_node_before(dom_object *context, zval *nodes, int nodesc) +{ + xmlNode *nextsib = dom_object_get_node(context); + xmlNodePtr newchild, prevsib, parentNode; + xmlNode *fragment; + + prevsib = nextsib->prev; + parentNode = nextsib->parent; + fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); + + if (fragment == NULL) { + return; + } + + newchild = fragment->children; + + if (newchild) { + if (parentNode->children == nextsib) { + parentNode->children = newchild; + } else { + prevsib->next = newchild; + } + fragment->last->next = nextsib; + nextsib->prev = fragment->last; + + newchild->prev = prevsib; + + dom_fragment_assign_parent_node(parentNode, fragment); + + dom_reconcile_ns(nextsib->doc, newchild); + } + + xmlFree(fragment); +} + +void dom_child_node_remove(dom_object *context) +{ + xmlNode *child = dom_object_get_node(context); + xmlNodePtr children; + int stricterror; + + if (dom_node_children_valid(child) == FAILURE) { + return; + } + + stricterror = dom_get_strict_error(context->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; + } + + if (!child->parent) { + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + return; + } + + children = child->parent->children; + if (!children) { + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + return; + } + + while (children) { + if (children == child) { + xmlUnlinkNode(child); + return; + } + children = children->next; + } + + php_dom_throw_error(NOT_FOUND_ERR, stricterror); +} + +#endif diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 98c4e703c5..dae52faeeb 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -35,6 +35,8 @@ /* {{{ class entries */ 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_parentnode_class_entry; +PHP_DOM_EXPORT zend_class_entry *dom_childnode_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_documentfragment_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_document_class_entry; @@ -66,6 +68,7 @@ zend_object_handlers dom_xpath_object_handlers; static HashTable classes; /* {{{ prop handler tables */ 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; @@ -585,6 +588,12 @@ PHP_MINIT_FUNCTION(dom) dom_domexception_class_entry->ce_flags |= ZEND_ACC_FINAL; zend_declare_property_long(dom_domexception_class_entry, "code", sizeof("code")-1, 0, ZEND_ACC_PUBLIC); + 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, "DOMImplementation", NULL, php_dom_domimplementation_class_functions, dom_domimplementation_class_entry); REGISTER_DOM_CLASS(ce, "DOMNode", NULL, php_dom_node_class_functions, dom_node_class_entry); @@ -622,7 +631,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); @@ -646,8 +663,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; @@ -674,9 +696,13 @@ PHP_MINIT_FUNCTION(dom) zend_hash_init(&dom_characterdata_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1); dom_register_prop_handler(&dom_characterdata_prop_handlers, "data", sizeof("data")-1, dom_characterdata_data_read, dom_characterdata_data_write); dom_register_prop_handler(&dom_characterdata_prop_handlers, "length", sizeof("length")-1, dom_characterdata_length_read, NULL); + dom_register_prop_handler(&dom_characterdata_prop_handlers, "previousElementSibling", sizeof("previousElementSibling")-1, dom_node_previous_element_sibling_read, NULL); + dom_register_prop_handler(&dom_characterdata_prop_handlers, "nextElementSibling", sizeof("nextElementSibling")-1, dom_node_next_element_sibling_read, NULL); 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); @@ -693,9 +719,16 @@ 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); + dom_register_prop_handler(&dom_element_prop_handlers, "previousElementSibling", sizeof("previousElementSibling")-1, dom_node_previous_element_sibling_read, NULL); + dom_register_prop_handler(&dom_element_prop_handlers, "nextElementSibling", sizeof("nextElementSibling")-1, dom_node_next_element_sibling_read, 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); @@ -851,6 +884,7 @@ PHP_MINFO_FUNCTION(dom) PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */ { 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); @@ -1344,6 +1378,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 6d9eaef46d..e1058e849b 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -109,6 +109,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); @@ -125,6 +126,12 @@ xmlNode *php_dom_libxml_notation_iter(xmlHashTable *ht, int index); zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref); void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce); +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); +void dom_child_node_remove(dom_object *context); + #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_ChildNode_wrong_document.phpt b/ext/dom/tests/DOM4_ChildNode_wrong_document.phpt new file mode 100644 index 0000000000..dc16cc158f --- /dev/null +++ b/ext/dom/tests/DOM4_ChildNode_wrong_document.phpt @@ -0,0 +1,41 @@ +--TEST-- +DOMChildNode::after(), before, replaceWith with DOMNode from wrong document throws exception +--SKIPIF-- + +--FILE-- +loadXML(''); + +$dom2 = new DOMDocument; +$dom2->loadXML(''); + +$element = $dom1->documentElement; + +try { + $element->after($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(); +} +?> +--EXPECT-- +Wrong Document Error +Wrong Document Error +Wrong Document Error diff --git a/ext/dom/tests/DOM4_DOMNode_ElementSiblings.phpt b/ext/dom/tests/DOM4_DOMNode_ElementSiblings.phpt new file mode 100644 index 0000000000..8a27837cf6 --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_ElementSiblings.phpt @@ -0,0 +1,25 @@ +--TEST-- +DOMNode: Element Siblings +--SKIPIF-- + +--FILE-- +loadXML('fooFirstElementLastElementbar'); + +$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 0000000000..c53fdba972 --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_after.phpt @@ -0,0 +1,35 @@ +--TEST-- +DOMNode::after() +--SKIPIF-- + +--FILE-- +loadXML('firstsecond'); + +$element = $dom->documentElement->firstElementChild; +$secondMark = $dom->documentElement->lastElementChild; + +$element->after( + 'text inserted after', + $dom->createElement('inserted-after', 'content') +); + +$secondMark->after('text inserted after second'); + +print_node_list_compact($dom->documentElement->childNodes); +?> +--EXPECT-- + + first + +text inserted after + + content + + + second + +text inserted after second 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 0000000000..0b28846a56 --- /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 0000000000..936addb1fa --- /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 0000000000..b3806aff2e --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_before.phpt @@ -0,0 +1,35 @@ +--TEST-- +DOMNode::before() +--SKIPIF-- + +--FILE-- +loadXML('firstsecond'); + +$element = $dom->documentElement->firstElementChild; +$secondMark = $dom->documentElement->lastElementChild; + +$element->before( + $dom->createElement('inserted-before', 'content'), + 'text inserted before' +); + +$secondMark->before('text inserted before second'); + +print_node_list_compact($dom->documentElement->childNodes); +?> +--EXPECT-- + + content + +text inserted before + + first + +text inserted before second + + second + 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 0000000000..6ff2ca7729 --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_before_ns.phpt @@ -0,0 +1,29 @@ +--TEST-- +DOMNode::before() 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 0000000000..c31cb6fa77 --- /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 0000000000..8eb1609531 --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_remove.phpt @@ -0,0 +1,30 @@ +--TEST-- +DOMNode::remove() +--SKIPIF-- + +--FILE-- +loadXML('firstsecond'); + +$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_removeDanglingElement.phpt b/ext/dom/tests/DOM4_DOMNode_removeDanglingElement.phpt new file mode 100644 index 0000000000..ceedac4084 --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_removeDanglingElement.phpt @@ -0,0 +1,18 @@ +--TEST-- +DOMNode::remove() dangling element +--SKIPIF-- + +--FILE-- +createElement('test'); + +try { + $element->remove(); +} catch (DOMException $e) { + echo $e->getMessage(); +} +--EXPECT-- +Not Found Error diff --git a/ext/dom/tests/DOM4_DOMNode_replaceWith.phpt b/ext/dom/tests/DOM4_DOMNode_replaceWith.phpt new file mode 100644 index 0000000000..ceaf72678a --- /dev/null +++ b/ext/dom/tests/DOM4_DOMNode_replaceWith.phpt @@ -0,0 +1,27 @@ +--TEST-- +DOMNode::replaceWith() +--SKIPIF-- + +--FILE-- +loadXML('firstsecond'); + +$element = $dom->documentElement->firstChild; +$element->replaceWith( + $dom->createElement('element', 'content'), + 'content' +); + +print_node_list_compact($dom->documentElement->childNodes); +?> +--EXPECT-- + + content + +content + + second + diff --git a/ext/dom/tests/DOM4_ParentNode.phpt b/ext/dom/tests/DOM4_ParentNode.phpt new file mode 100644 index 0000000000..f5bc5dab90 --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode.phpt @@ -0,0 +1,51 @@ +--TEST-- +DOMParentNode: Child Element Handling +--SKIPIF-- + +--FILE-- +loadXML('fooFirstElementLastElementbar'); + +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 0000000000..4f7fee3f41 --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_Fragment.phpt @@ -0,0 +1,41 @@ +--TEST-- +DOMParentNode: Child Element Handling +--SKIPIF-- + +--FILE-- +loadXML(''); + +$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 0000000000..2bab2a4bd6 --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_append.phpt @@ -0,0 +1,41 @@ +--TEST-- +DOMParentNode::append() +--SKIPIF-- + +--FILE-- +loadXML(''); + +$element = $dom->documentElement; +$element->append( + $dom->createElement('element', 'content'), + 'content' +); + +var_dump($dom->documentElement->childElementCount); +print_node_list_compact($element->childNodes); + +$element->append( + $dom->createElement('element', 'content'), + 'content' +); +var_dump($dom->documentElement->childElementCount); +?> +--EXPECT-- +int(4) + + + + + + + + content + +content +int(5) diff --git a/ext/dom/tests/DOM4_ParentNode_append_invalidtypes.phpt b/ext/dom/tests/DOM4_ParentNode_append_invalidtypes.phpt new file mode 100644 index 0000000000..ab715caaa0 --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_append_invalidtypes.phpt @@ -0,0 +1,18 @@ +--TEST-- +DOMParentNode::append() exception on invalid argument +--SKIPIF-- + +--FILE-- +loadXML(''); + +try { + $dom->documentElement->append(array()); +} catch(TypeError $e) { + echo "OK! {$e->getMessage()}"; +} +--EXPECT-- +OK! Invalid argument type must be either DOMNode or string 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 0000000000..3fb266729a --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_append_with_attributes.phpt @@ -0,0 +1,26 @@ +--TEST-- +DOMParentNode::append() with attributes +--SKIPIF-- + +--FILE-- +loadXML(''); + +$replacement = $dom->createAttribute('attr-one'); +$replacement->value ='42'; +$addition = $dom->createAttribute('attr-two'); +$addition->value = '23'; + +$element = $dom->documentElement; + +try { + $element->append($replacement, $addition); +} catch (DOMException $e) { + echo $e->getMessage(); +} +?> +--EXPECT-- +Hierarchy Request Error diff --git a/ext/dom/tests/DOM4_ParentNode_append_wrong_document.phpt b/ext/dom/tests/DOM4_ParentNode_append_wrong_document.phpt new file mode 100644 index 0000000000..8d5be4373e --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_append_wrong_document.phpt @@ -0,0 +1,33 @@ +--TEST-- +DOMParentNode::append() with DOMNode from wrong document throws exception +--SKIPIF-- + +--FILE-- +loadXML(''); + +$dom2 = new DOMDocument; +$dom2->loadXML(''); + +$element = $dom1->documentElement; + +try { + $element->append($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(); +} +?> +--EXPECT-- +Wrong Document Error +Wrong Document Error diff --git a/ext/dom/tests/DOM4_ParentNode_prepend.phpt b/ext/dom/tests/DOM4_ParentNode_prepend.phpt new file mode 100644 index 0000000000..6b6ad2f538 --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_prepend.phpt @@ -0,0 +1,49 @@ +--TEST-- +DOMParentNode::prepend() +--SKIPIF-- + +--FILE-- +loadXML(''); + +$element = $dom->documentElement; +$firstMark = $element->childNodes[0]; +$element->prepend( + $dom->createElement('element', 'content'), + 'content' +); + +var_dump($element->childElementCount); +print_node_list_compact($element->childNodes); + +$element = $dom->documentElement; +$element->prepend( + $dom->createElement('element', 'content'), + 'content' +); +var_dump($element->childElementCount); + +$firstMark->prepend('content'); +print_node_list_compact($firstMark->childNodes); +?> +--EXPECT-- +int(4) + + content + +content + + + + + + + + +int(5) +content diff --git a/ext/dom/tests/bug69846.phpt b/ext/dom/tests/bug69846.phpt index 74db47c571..a47d30a057 100644 --- a/ext/dom/tests/bug69846.phpt +++ b/ext/dom/tests/bug69846.phpt @@ -30,7 +30,7 @@ foreach ($dataNodes AS $node) { ?> --EXPECTF-- int(3) -object(DOMText)#%d (19) { +object(DOMText)#%d (21) { ["wholeText"]=> string(3) " " @@ -39,6 +39,10 @@ object(DOMText)#%d (19) { " ["length"]=> int(3) + ["previousElementSibling"]=> + NULL + ["nextElementSibling"]=> + NULL ["nodeName"]=> string(5) "#text" ["nodeValue"]=> @@ -74,11 +78,21 @@ 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) + ["previousElementSibling"]=> + NULL + ["nextElementSibling"]=> + NULL ["nodeName"]=> string(5) "form1" ["nodeValue"]=> @@ -120,7 +134,7 @@ object(DOMElement)#%d (18) { Value C " } -object(DOMText)#%d (19) { +object(DOMText)#%d (21) { ["wholeText"]=> string(1) " " @@ -129,6 +143,10 @@ object(DOMText)#%d (19) { " ["length"]=> int(1) + ["previousElementSibling"]=> + NULL + ["nextElementSibling"]=> + NULL ["nodeName"]=> string(5) "#text" ["nodeValue"]=> diff --git a/ext/dom/tests/dom_test.inc b/ext/dom/tests/dom_test.inc index 93264ea2aa..04bfa04d53 100644 --- a/ext/dom/tests/dom_test.inc +++ b/ext/dom/tests/dom_test.inc @@ -48,4 +48,22 @@ function print_node_list($nodelist) } } +function print_node_compact($node, $spaces) +{ + if ($node->nodeType == 3) { + print str_repeat(" ", $spaces) . trim($node->nodeValue) . "\n"; + } else { + print str_repeat(" ", $spaces) . "<" . $node->nodeName . ">\n"; + print_node_list_compact($node->childNodes, $spaces + 2); + print str_repeat(" ", $spaces) . "nodeName . ">\n"; + } +} + +function print_node_list_compact($nodelist, $spaces = 0) +{ + foreach ($nodelist as $node) { + print_node_compact($node, $spaces); + } +} + ?> diff --git a/ext/dom/tests/domobject_debug_handler.phpt b/ext/dom/tests/domobject_debug_handler.phpt index d2834e815c..3545b78dd4 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