From 0425f8e42d0ccd74e11cd191c409d01e837948e9 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 3 Mar 2019 21:14:51 +0100 Subject: [PATCH 01/26] dom: Let DOMElement implement DOM Level 4 DOMParentNode interface --- ext/dom/config.m4 | 1 + ext/dom/config.w32 | 1 + ext/dom/dom_fe.h | 1 + ext/dom/dom_properties.h | 5 ++ ext/dom/node.c | 1 - ext/dom/parentnode.c | 131 +++++++++++++++++++++++++++++ ext/dom/php_dom.c | 9 ++ ext/dom/tests/DOM4_ParentNode.phpt | 39 +++++++++ 8 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 ext/dom/parentnode.c create mode 100644 ext/dom/tests/DOM4_ParentNode.phpt 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/dom_fe.h b/ext/dom/dom_fe.h index b8c1ff2266ee9..4d07dcec64f66 100644 --- a/ext/dom/dom_fe.h +++ b/ext/dom/dom_fe.h @@ -23,6 +23,7 @@ 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_domimplementationlist_class_functions[]; extern const zend_function_entry php_dom_domimplementationsource_class_functions[]; extern const zend_function_entry php_dom_domimplementation_class_functions[]; diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h index 73d404429e004..954105005077f 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); diff --git a/ext/dom/node.c b/ext/dom/node.c index 03e61efa674a3..404f79f17cac8 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -438,7 +438,6 @@ int dom_node_child_nodes_read(dom_object *obj, zval *retval) return SUCCESS; } - /* }}} */ /* {{{ firstChild DomNode diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c new file mode 100644 index 0000000000000..420c45b28f266 --- /dev/null +++ b/ext/dom/parentnode.c @@ -0,0 +1,131 @@ +/* + +----------------------------------------------------------------------+ + | 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" + +const zend_function_entry php_dom_parent_node_class_functions[] = { /* {{{ */ + 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; +} +/* }}} */ + +#endif diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 726b74b93452d..94821a10d0d22 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -38,6 +38,7 @@ 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_domimplementationlist_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domimplementationsource_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry; @@ -629,6 +630,9 @@ 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); + 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); @@ -744,9 +748,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, 1, dom_parentnode_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); diff --git a/ext/dom/tests/DOM4_ParentNode.phpt b/ext/dom/tests/DOM4_ParentNode.phpt new file mode 100644 index 0000000000000..ac042f737c21b --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode.phpt @@ -0,0 +1,39 @@ +--TEST-- +DOMParentNode: Child Element Handling +--SKIPIF-- + +--FILE-- +loadXML('fooFirstElementLastElementbar'); +if(!$dom) { + echo "Error while parsing the document\n"; + exit; +} + +$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: 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) From aeade27c9672300d134c3c3eaf03372d68f2ff9f Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 3 Mar 2019 21:19:39 +0100 Subject: [PATCH 02/26] [dom] Have DOMDocument implement DOMParentNode. --- ext/dom/php_dom.c | 6 ++++++ ext/dom/tests/DOM4_ParentNode.phpt | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 94821a10d0d22..dd0ae10049c34 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -701,9 +701,15 @@ 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; dom_nodelist_class_entry = zend_register_internal_class_ex(&ce, NULL); diff --git a/ext/dom/tests/DOM4_ParentNode.phpt b/ext/dom/tests/DOM4_ParentNode.phpt index ac042f737c21b..fcb164d6db84d 100644 --- a/ext/dom/tests/DOM4_ParentNode.phpt +++ b/ext/dom/tests/DOM4_ParentNode.phpt @@ -13,6 +13,11 @@ if(!$dom) { 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); @@ -23,6 +28,16 @@ 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 From 5cb019ff680594b2a8a8626055e043895fbba638 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 3 Mar 2019 22:54:20 +0100 Subject: [PATCH 03/26] [dom] Add DOMParentNode implementation to DOMDocumentFragment. Fix test failures. --- ext/dom/php_dom.c | 13 +++++- ext/dom/tests/DOM4_ParentNode_Fragment.phpt | 44 +++++++++++++++++++++ ext/dom/tests/bug69846.phpt | 8 +++- ext/dom/tests/domobject_debug_handler.phpt | 3 ++ 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 ext/dom/tests/DOM4_ParentNode_Fragment.phpt diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index dd0ae10049c34..a9aa5cfa053c8 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -82,6 +82,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; @@ -677,7 +678,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); @@ -707,7 +716,6 @@ PHP_MINIT_FUNCTION(dom) 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); @@ -952,6 +960,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); 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/bug69846.phpt b/ext/dom/tests/bug69846.phpt index fcca4c06d6514..483e345b32464 100644 --- a/ext/dom/tests/bug69846.phpt +++ b/ext/dom/tests/bug69846.phpt @@ -75,11 +75,17 @@ object(DOMText)#%d (19) { string(3) " " } -object(DOMElement)#%d (18) { +object(DOMElement)#%d (21) { ["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"]=> diff --git a/ext/dom/tests/domobject_debug_handler.phpt b/ext/dom/tests/domobject_debug_handler.phpt index 57d4a66ebb49d..ff645a04d22e6 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 From f14238ecfccb92d1f9b346985ecf5cac2090b6dd Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 3 Mar 2019 23:31:55 +0100 Subject: [PATCH 04/26] [dom] Add DOMNode#previousElementSibling and DOMNode#nextElementSibling. --- ext/dom/dom_properties.h | 2 + ext/dom/node.c | 66 +++++++++++++++++++ ext/dom/php_dom.c | 2 + .../tests/DOM4_DOMNode_ElementSiblings.phpt | 28 ++++++++ ext/dom/tests/bug69846.phpt | 18 ++++- ext/dom/tests/domobject_debug_handler.phpt | 2 + 6 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 ext/dom/tests/DOM4_DOMNode_ElementSiblings.phpt diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h index 954105005077f..4d2fdefdd1ae8 100644 --- a/ext/dom/dom_properties.h +++ b/ext/dom/dom_properties.h @@ -129,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/node.c b/ext/dom/node.c index 404f79f17cac8..440ef0815e43c 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -558,6 +558,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/php_dom.c b/ext/dom/php_dom.c index a9aa5cfa053c8..cc5848dc9dbbd 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -655,6 +655,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); 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/bug69846.phpt b/ext/dom/tests/bug69846.phpt index 483e345b32464..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,7 +79,7 @@ object(DOMText)#%d (19) { string(3) " " } -object(DOMElement)#%d (21) { +object(DOMElement)#%d (23) { ["tagName"]=> string(5) "form1" ["schemaTypeInfo"]=> @@ -108,6 +112,10 @@ object(DOMElement)#%d (21) { NULL ["nextSibling"]=> NULL + ["previousElementSibling"]=> + NULL + ["nextElementSibling"]=> + NULL ["attributes"]=> string(22) "(object value omitted)" ["ownerDocument"]=> @@ -127,7 +135,7 @@ object(DOMElement)#%d (21) { Value C " } -object(DOMText)#%d (19) { +object(DOMText)#%d (21) { ["wholeText"]=> string(1) " " @@ -155,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 ff645a04d22e6..d4df423a40ba3 100644 --- a/ext/dom/tests/domobject_debug_handler.phpt +++ b/ext/dom/tests/domobject_debug_handler.phpt @@ -51,6 +51,8 @@ DOMDocument Object [lastChild] => (object value omitted) [previousSibling] => [nextSibling] => + [previousElementSibling] => + [nextElementSibling] => [attributes] => [ownerDocument] => [namespaceURI] => From f715a896ff37c9ac4c4f54a9593e8230645942f4 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 12 Mar 2019 10:14:59 +0100 Subject: [PATCH 05/26] [dom] DomChildNode#remove interface and DOMElement implementation First implementation of DOMElement#remove() --- ext/dom/dom_fe.h | 2 ++ ext/dom/element.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++ ext/dom/node.c | 9 ++++++++ ext/dom/php_dom.c | 5 ++++ 4 files changed, 75 insertions(+) diff --git a/ext/dom/dom_fe.h b/ext/dom/dom_fe.h index 4d07dcec64f66..b614bb57bab76 100644 --- a/ext/dom/dom_fe.h +++ b/ext/dom/dom_fe.h @@ -24,6 +24,7 @@ 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[]; @@ -215,6 +216,7 @@ 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); /* domtext methods */ PHP_FUNCTION(dom_text_split_text); diff --git a/ext/dom/element.c b/ext/dom/element.c index 51aba0e155175..1d4d1d0d7f32a 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -115,6 +115,9 @@ 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(); /* }}} */ /* @@ -144,6 +147,7 @@ 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_FE_END }; /* }}} */ @@ -1276,4 +1280,59 @@ PHP_FUNCTION(dom_element_set_id_attribute_node) } /* }}} end dom_element_set_id_attribute_node */ +/* {{{ proto DomChildNode DOMElement::remove(); +URL: +Since: +*/ +PHP_METHOD(domelement, remove) +{ + zval *id, *node; + xmlNodePtr children, child; + dom_object *intern, *childobj; + int ret, stricterror; + + id = ZEND_THIS; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) { + return; + } + + DOM_GET_OBJ(child, id, xmlNodePtr, intern); + + if (dom_node_children_valid(child) == FAILURE) { + RETURN_FALSE; + } + + 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_FALSE; + } + + if (!child->parent) { + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_FALSE; + } + + children = child->parent->children; + if (!children) { + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_FALSE; + } + + while (children) { + if (children == child) { + xmlUnlinkNode(child); + DOM_RET_OBJ(child, &ret, intern); + return; + } + children = children->next; + } + + php_dom_throw_error(NOT_FOUND_ERR, stricterror); + RETURN_FALSE +} +/* }}} end dom_node_remove_child */ + #endif diff --git a/ext/dom/node.c b/ext/dom/node.c index 440ef0815e43c..202f7ef22b96c 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -121,6 +121,9 @@ 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(); /* }}} */ /* @@ -157,6 +160,12 @@ const zend_function_entry php_dom_node_class_functions[] = { /* {{{ */ }; /* }}} */ +const zend_function_entry php_dom_child_node_class_functions[] = { /* {{{ */ + PHP_ABSTRACT_ME(DOMChildNode, remove, arginfo_dom_child_node_remove) + PHP_FE_END +}; +/* }}} */ + static void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */ { xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL; diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index cc5848dc9dbbd..87c0bc9c88810 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -39,6 +39,7 @@ 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; @@ -634,6 +635,9 @@ PHP_MINIT_FUNCTION(dom) 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); @@ -771,6 +775,7 @@ PHP_MINIT_FUNCTION(dom) zend_hash_add_ptr(&classes, ce.name, &dom_element_prop_handlers); zend_class_implements(dom_element_class_entry, 1, dom_parentnode_class_entry); + zend_class_implements(dom_element_class_entry, 1, dom_childnode_class_entry); REGISTER_DOM_CLASS(ce, "DOMText", dom_characterdata_class_entry, php_dom_text_class_functions, dom_text_class_entry); From d73dc244c9b6bed0dc755ff6acc271033b1d8398 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 12 Mar 2019 20:27:47 +0100 Subject: [PATCH 06/26] [dom] fix parameter parsing for DomElement#remove() --- ext/dom/element.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/dom/element.c b/ext/dom/element.c index 1d4d1d0d7f32a..c74d102779dd8 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -1292,7 +1292,7 @@ PHP_METHOD(domelement, remove) int ret, stricterror; id = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) { + if (zend_parse_parameters_none() == FAILURE) { return; } From eb714da52d7cbd15bda90ad9fcfc835f29a3a873 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 12 Mar 2019 21:01:59 +0100 Subject: [PATCH 07/26] RETURN_NULL as proto is always void. --- ext/dom/element.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/ext/dom/element.c b/ext/dom/element.c index c74d102779dd8..cd3dd587a46ee 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -1280,7 +1280,7 @@ PHP_FUNCTION(dom_element_set_id_attribute_node) } /* }}} end dom_element_set_id_attribute_node */ -/* {{{ proto DomChildNode DOMElement::remove(); +/* {{{ proto void DOMElement::remove(); URL: Since: */ @@ -1299,7 +1299,7 @@ PHP_METHOD(domelement, remove) DOM_GET_OBJ(child, id, xmlNodePtr, intern); if (dom_node_children_valid(child) == FAILURE) { - RETURN_FALSE; + RETURN_NULL(); } stricterror = dom_get_strict_error(intern->document); @@ -1307,31 +1307,30 @@ PHP_METHOD(domelement, remove) 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_FALSE; + RETURN_NULL(); } if (!child->parent) { php_dom_throw_error(NOT_FOUND_ERR, stricterror); - RETURN_FALSE; + RETURN_NULL(); } children = child->parent->children; if (!children) { php_dom_throw_error(NOT_FOUND_ERR, stricterror); - RETURN_FALSE; + RETURN_NULL(); } while (children) { if (children == child) { xmlUnlinkNode(child); - DOM_RET_OBJ(child, &ret, intern); - return; + RETURN_NULL(); } children = children->next; } php_dom_throw_error(NOT_FOUND_ERR, stricterror); - RETURN_FALSE + RETURN_NULL(); } /* }}} end dom_node_remove_child */ From ba7158ca92cf4e9054a51d8f5660419ba8e6f941 Mon Sep 17 00:00:00 2001 From: Thomas Weinert Date: Tue, 12 Mar 2019 21:11:37 +0100 Subject: [PATCH 08/26] Test: DOM Level4 DOMNode::remove() --- ext/dom/tests/DOM4_DOMNode_remove.phpt | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 ext/dom/tests/DOM4_DOMNode_remove.phpt 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 + From c6862c5734d0443abad4cc95822185c421c52616 Mon Sep 17 00:00:00 2001 From: Thomas Weinert Date: Sun, 17 Mar 2019 13:43:50 +0100 Subject: [PATCH 09/26] Test: DOM Level4 DOMNode::before() --- ext/dom/tests/DOM4_DOMNode_before.phpt | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 ext/dom/tests/DOM4_DOMNode_before.phpt 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 From be84c27e89b3c27e43e8c172bc48cbbd16a39620 Mon Sep 17 00:00:00 2001 From: Thomas Weinert Date: Sun, 17 Mar 2019 13:47:53 +0100 Subject: [PATCH 10/26] Test: DOM Level4 DOMNode::after() --- ext/dom/tests/DOM4_DOMNode_after.phpt | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 ext/dom/tests/DOM4_DOMNode_after.phpt 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 From 73f90a40df640494c2e1bf469c9019cedff786f2 Mon Sep 17 00:00:00 2001 From: Thomas Weinert Date: Sun, 17 Mar 2019 13:55:08 +0100 Subject: [PATCH 11/26] Test: DOM Level4 DOMNode::replaceWith() --- ext/dom/tests/DOM4_DOMNode_replaceWith.phpt | 44 +++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 ext/dom/tests/DOM4_DOMNode_replaceWith.phpt 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 From f4f1be2a422c0cd294b3041dec165b311fe0afae Mon Sep 17 00:00:00 2001 From: Thomas Weinert Date: Sun, 17 Mar 2019 14:08:23 +0100 Subject: [PATCH 12/26] Test: DOM Level4 DOMParentNode::prepend() --- ext/dom/tests/DOM4_ParentNode_prepend.phpt | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 ext/dom/tests/DOM4_ParentNode_prepend.phpt 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: From 8fd82c37f7b6e46b0f847c3647219a4f34f1f104 Mon Sep 17 00:00:00 2001 From: Thomas Weinert Date: Sun, 17 Mar 2019 14:11:09 +0100 Subject: [PATCH 13/26] Test: DOM Level4 DOMParentNode::append() --- ext/dom/tests/DOM4_ParentNode_append.phpt | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 ext/dom/tests/DOM4_ParentNode_append.phpt diff --git a/ext/dom/tests/DOM4_ParentNode_append.phpt b/ext/dom/tests/DOM4_ParentNode_append.phpt new file mode 100644 index 0000000000000..4dcc710ca2303 --- /dev/null +++ b/ext/dom/tests/DOM4_ParentNode_append.phpt @@ -0,0 +1,39 @@ +--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' +); + +print_node($element->childNodes[0]); +print_node($element->childNodes[1]); +print_node($element->childNodes[2]); +--EXPECT-- +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 From 223132f3c03712ff1b48068ace2087b29e41b79e Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 20 Mar 2019 23:20:04 +0100 Subject: [PATCH 14/26] Add DOMParentNode::append() for DOMElement supporting strings as variadic arguments. --- ext/dom/dom_fe.h | 1 + ext/dom/element.c | 24 ++++++++ ext/dom/parentnode.c | 69 +++++++++++++++++++++++ ext/dom/tests/DOM4_ParentNode_append.phpt | 2 + 4 files changed, 96 insertions(+) diff --git a/ext/dom/dom_fe.h b/ext/dom/dom_fe.h index b614bb57bab76..16a58896a139b 100644 --- a/ext/dom/dom_fe.h +++ b/ext/dom/dom_fe.h @@ -217,6 +217,7 @@ 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, append); /* domtext methods */ PHP_FUNCTION(dom_text_split_text); diff --git a/ext/dom/element.c b/ext/dom/element.c index cd3dd587a46ee..106f753f8702f 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -118,6 +118,9 @@ 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_append, 0, 0, 0) +ZEND_END_ARG_INFO(); /* }}} */ /* @@ -148,6 +151,7 @@ const zend_function_entry php_dom_element_class_functions[] = { /* {{{ */ 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, append, arginfo_dom_element_append, ZEND_ACC_PUBLIC) PHP_FE_END }; /* }}} */ @@ -1334,4 +1338,24 @@ PHP_METHOD(domelement, remove) } /* }}} end dom_node_remove_child */ +/* {{{ proto void DOMElement::append(); +URL: +Since: +*/ +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); +} #endif diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index 420c45b28f266..0a932175d65d5 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -128,4 +128,73 @@ int dom_parent_node_child_element_count(dom_object *obj, zval *retval) } /* }}} */ +void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) +{ + int i; + xmlNode *documentNode; + xmlNode *fragment; + xmlNode *newNode; + xmlNode *contextNode = dom_object_get_node(context); + + if (contextNode->type == XML_DOCUMENT_NODE || contextNode->type == XML_HTML_DOCUMENT_NODE) { + documentNode = contextNode; + } else { + documentNode = contextNode->doc; + } + + fragment = xmlNewDocFragment(documentNode); + + if (!fragment) { + // err? + return; + } + + for (i = 0; i < nodesc; i++) { + switch (Z_TYPE(nodes[i])) { + case IS_STRING: + newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[i])); + + xmlSetTreeDoc(newNode, documentNode); + + newNode = xmlAddChild(fragment, newNode); + + if (!newNode) { + php_error_docref(NULL, E_WARNING, "Couldn't append node"); + return; + } + + break; + + case IS_OBJECT: + break; + } + } + + xmlNodePtr newchild, node, prevsib; + + newchild = fragment->children; + prevsib = contextNode->last; + + if (newchild) { + prevsib->next = newchild; + newchild->prev = prevsib; + contextNode->last = fragment->last; + + node = newchild; + while (node != NULL) { + node->parent = contextNode; + if (node->doc != documentNode) { + xmlSetTreeDoc(node, documentNode); + } + if (node == fragment->last) { + break; + } + node = node->next; + } + + fragment->children = NULL; + fragment->last = NULL; + } +} + #endif diff --git a/ext/dom/tests/DOM4_ParentNode_append.phpt b/ext/dom/tests/DOM4_ParentNode_append.phpt index 4dcc710ca2303..19e95ee39d56a 100644 --- a/ext/dom/tests/DOM4_ParentNode_append.phpt +++ b/ext/dom/tests/DOM4_ParentNode_append.phpt @@ -19,10 +19,12 @@ $element->append( 'content' ); +var_dump($dom->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 From 4819e639f11c1270bf82cd4e2904a5ce5d5c2f30 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 20 Mar 2019 23:57:35 +0100 Subject: [PATCH 15/26] Adopt DoMNode into fragment when passed an object. --- ext/dom/parentnode.c | 26 ++++++++++++++++++----- ext/dom/tests/DOM4_ParentNode_append.phpt | 4 ++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index 0a932175d65d5..bf2c8e035e10f 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -131,13 +131,15 @@ int dom_parent_node_child_element_count(dom_object *obj, zval *retval) void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) { int i; - xmlNode *documentNode; + xmlDoc *documentNode; xmlNode *fragment; xmlNode *newNode; xmlNode *contextNode = dom_object_get_node(context); + zend_class_entry *ce; + dom_object *newNodeObj; if (contextNode->type == XML_DOCUMENT_NODE || contextNode->type == XML_HTML_DOCUMENT_NODE) { - documentNode = contextNode; + documentNode = (xmlDoc *) contextNode; } else { documentNode = contextNode->doc; } @@ -166,6 +168,22 @@ void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) break; case 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 = context->document; + xmlSetTreeDoc(newNode, documentNode); + + newNode = xmlAddChild(fragment, newNode); + } + break; } } @@ -183,9 +201,7 @@ void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) node = newchild; while (node != NULL) { node->parent = contextNode; - if (node->doc != documentNode) { - xmlSetTreeDoc(node, documentNode); - } + if (node == fragment->last) { break; } diff --git a/ext/dom/tests/DOM4_ParentNode_append.phpt b/ext/dom/tests/DOM4_ParentNode_append.phpt index 19e95ee39d56a..f67e7c98abb43 100644 --- a/ext/dom/tests/DOM4_ParentNode_append.phpt +++ b/ext/dom/tests/DOM4_ParentNode_append.phpt @@ -19,7 +19,7 @@ $element->append( 'content' ); -var_dump($dom->childElementCount); +var_dump($dom->documentElement->childElementCount); print_node($element->childNodes[0]); print_node($element->childNodes[1]); print_node($element->childNodes[2]); @@ -28,7 +28,7 @@ int(2) Node Name: mark Node Type: 1 Num Children: 0 -Node Content: +Node Content: Node Name: element Node Type: 1 From 6bffe98f00cf9941ecf64ccc197d3b3a9c458eff Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 21 Mar 2019 00:06:27 +0100 Subject: [PATCH 16/26] Refactor converting zval array into dom fragment into function. --- ext/dom/parentnode.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index bf2c8e035e10f..2bf0e68f364cb 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -128,13 +128,12 @@ int dom_parent_node_child_element_count(dom_object *obj, zval *retval) } /* }}} */ -void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) +xmlNode* dom_zvals_to_fragment(dom_object *context, xmlNode *contextNode, zval *nodes, int nodesc) { int i; xmlDoc *documentNode; xmlNode *fragment; xmlNode *newNode; - xmlNode *contextNode = dom_object_get_node(context); zend_class_entry *ce; dom_object *newNodeObj; @@ -147,8 +146,7 @@ void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) fragment = xmlNewDocFragment(documentNode); if (!fragment) { - // err? - return; + return NULL; } for (i = 0; i < nodesc; i++) { @@ -158,10 +156,7 @@ void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) xmlSetTreeDoc(newNode, documentNode); - newNode = xmlAddChild(fragment, newNode); - - if (!newNode) { - php_error_docref(NULL, E_WARNING, "Couldn't append node"); + if (!xmlAddChild(fragment, newNode)) { return; } @@ -181,14 +176,23 @@ void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) newNodeObj->document = context->document; xmlSetTreeDoc(newNode, documentNode); - newNode = xmlAddChild(fragment, newNode); + if (!xmlAddChild(fragment, newNode)) { + return; + } } break; } } + 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, contextNode, nodes, nodesc); newchild = fragment->children; prevsib = contextNode->last; From 361eb7951c177207994b1975637875bb83d8758c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 21 Mar 2019 00:18:07 +0100 Subject: [PATCH 17/26] Add DOMElement::prepend. --- ext/dom/dom_fe.h | 1 + ext/dom/element.c | 34 +++++++++++++++++++++++++++++++++- ext/dom/parentnode.c | 35 +++++++++++++++++++++++++++++++++-- ext/dom/php_dom.h | 3 +++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/ext/dom/dom_fe.h b/ext/dom/dom_fe.h index 16a58896a139b..ac78a08c5f631 100644 --- a/ext/dom/dom_fe.h +++ b/ext/dom/dom_fe.h @@ -218,6 +218,7 @@ PHP_FUNCTION(dom_element_set_id_attribute_node); PHP_METHOD(domelement, __construct); PHP_METHOD(domelement, remove); PHP_METHOD(domelement, append); +PHP_METHOD(domelement, prepend); /* domtext methods */ PHP_FUNCTION(dom_text_split_text); diff --git a/ext/dom/element.c b/ext/dom/element.c index 106f753f8702f..b1acd613d3725 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -121,6 +121,9 @@ 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(); /* }}} */ /* @@ -152,6 +155,7 @@ const zend_function_entry php_dom_element_class_functions[] = { /* {{{ */ 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, append, arginfo_dom_element_append, ZEND_ACC_PUBLIC) + PHP_ME(domelement, prepend, arginfo_dom_element_prepend, ZEND_ACC_PUBLIC) PHP_FE_END }; /* }}} */ @@ -1336,7 +1340,7 @@ PHP_METHOD(domelement, remove) php_dom_throw_error(NOT_FOUND_ERR, stricterror); RETURN_NULL(); } -/* }}} end dom_node_remove_child */ +/* }}} end DOMElement::remove */ /* {{{ proto void DOMElement::append(); URL: @@ -1358,4 +1362,32 @@ PHP_METHOD(domelement, append) dom_parent_node_append(intern, args, argc); } +/* }}} end DOMElement::append */ + +/* {{{ proto void DOMElement::prepend(); +URL: +Since: +*/ +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/parentnode.c b/ext/dom/parentnode.c index 2bf0e68f364cb..45889ad1ec664 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -157,7 +157,7 @@ xmlNode* dom_zvals_to_fragment(dom_object *context, xmlNode *contextNode, zval * xmlSetTreeDoc(newNode, documentNode); if (!xmlAddChild(fragment, newNode)) { - return; + return NULL; } break; @@ -177,7 +177,7 @@ xmlNode* dom_zvals_to_fragment(dom_object *context, xmlNode *contextNode, zval * xmlSetTreeDoc(newNode, documentNode); if (!xmlAddChild(fragment, newNode)) { - return; + return NULL; } } @@ -217,4 +217,35 @@ void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) } } +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, 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; + } +} + #endif diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index ce377b5ce72e0..114a552468111 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -128,6 +128,9 @@ 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); + #define REGISTER_DOM_CLASS(ce, name, parent_ce, funcs, entry) \ INIT_CLASS_ENTRY(ce, name, funcs); \ ce.create_object = dom_objects_new; \ From b8f2b41f1e3de6249ab524e013fb7ced63651540 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 21 Mar 2019 00:38:14 +0100 Subject: [PATCH 18/26] Refactor dom_zvals_to_fragment to convert all non DOMNode zvals of any type to string and turn them to DOMTextNode --- ext/dom/parentnode.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index 45889ad1ec664..f31c24325e813 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -150,38 +150,38 @@ xmlNode* dom_zvals_to_fragment(dom_object *context, xmlNode *contextNode, zval * } for (i = 0; i < nodesc; i++) { - switch (Z_TYPE(nodes[i])) { - case IS_STRING: - newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[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 = context->document; xmlSetTreeDoc(newNode, documentNode); if (!xmlAddChild(fragment, newNode)) { return NULL; } - break; - - case 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); + continue; + } + } - if (newNode->parent != NULL) { - xmlUnlinkNode(newNode); - } + if (Z_TYPE(nodes[i]) != IS_STRING) { + convert_to_string_ex(&nodes[i]); + } - newNodeObj->document = context->document; - xmlSetTreeDoc(newNode, documentNode); + newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[i])); - if (!xmlAddChild(fragment, newNode)) { - return NULL; - } - } + xmlSetTreeDoc(newNode, documentNode); - break; + if (!xmlAddChild(fragment, newNode)) { + return NULL; } } From d5ad0f117fd56f5508f3ba2ade374c6867fa6a84 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 21 Mar 2019 23:35:24 +0100 Subject: [PATCH 19/26] Add prepend/append to DOMParentNode interface and add dummy implementations to DOMDocument+DOMDocumentFragment for now. --- ext/dom/document.c | 16 ++++++++++++++++ ext/dom/documentfragment.c | 15 +++++++++++++++ ext/dom/dom_fe.h | 4 ++++ ext/dom/parentnode.c | 11 +++++++++++ 4 files changed, 46 insertions(+) diff --git a/ext/dom/document.c b/ext/dom/document.c index 95b077b6483a1..3b8c67d9a7841 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,12 @@ PHP_METHOD(domdocument, registerNodeClass) } /* }}} */ +PHP_METHOD(domdocument, append) +{ +} + +PHP_METHOD(domdocument, prepend) +{ +} + #endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/ext/dom/documentfragment.c b/ext/dom/documentfragment.c index 9b222586ac537..885bcec27ed36 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,11 @@ PHP_METHOD(domdocumentfragment, appendXML) { } /* }}} */ +PHP_METHOD(domdocumentfragment, append) +{ +} + +PHP_METHOD(domdocumentfragment, prepend) +{ +} #endif diff --git a/ext/dom/dom_fe.h b/ext/dom/dom_fe.h index ac78a08c5f631..b6f3b2ad52465 100644 --- a/ext/dom/dom_fe.h +++ b/ext/dom/dom_fe.h @@ -104,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); @@ -132,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); diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index f31c24325e813..f7137e83ce9af 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -24,9 +24,20 @@ #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 From 79865f70e418744c75b37ed702688b795db89902 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 22 Mar 2019 13:55:51 +0100 Subject: [PATCH 20/26] Add docs to DOMParentNode methods. --- ext/dom/document.c | 9 +++++++++ ext/dom/documentfragment.c | 11 +++++++++++ ext/dom/element.c | 12 ++++++------ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/ext/dom/document.c b/ext/dom/document.c index 3b8c67d9a7841..b2aad0f36f875 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -2280,10 +2280,19 @@ 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) { } +/* }}} */ +/* {{{ 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) { } diff --git a/ext/dom/documentfragment.c b/ext/dom/documentfragment.c index 885bcec27ed36..80df950a7c70f 100644 --- a/ext/dom/documentfragment.c +++ b/ext/dom/documentfragment.c @@ -155,11 +155,22 @@ 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) { } +/* }}} */ +/* {{{ 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) { } +/* }}} */ + #endif diff --git a/ext/dom/element.c b/ext/dom/element.c index b1acd613d3725..7a32f72881dc5 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -1342,9 +1342,9 @@ PHP_METHOD(domelement, remove) } /* }}} end DOMElement::remove */ -/* {{{ proto void DOMElement::append(); -URL: -Since: +/* {{{ 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) { @@ -1364,9 +1364,9 @@ PHP_METHOD(domelement, append) } /* }}} end DOMElement::append */ -/* {{{ proto void DOMElement::prepend(); -URL: -Since: +/* {{{ 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) { From 6d7dbbf5d4b25e3b819453863829a6a734d6a149 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 22 Mar 2019 13:59:00 +0100 Subject: [PATCH 21/26] Add code for DOMDocument+DOMDocumentFragment implementations of append+prepend. --- ext/dom/document.c | 31 +++++++++++++++++++++++++++++++ ext/dom/documentfragment.c | 30 ++++++++++++++++++++++++++++++ ext/dom/element.c | 6 +++--- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/ext/dom/document.c b/ext/dom/document.c index b2aad0f36f875..12a125acfd52c 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -2286,6 +2286,19 @@ 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); } /* }}} */ @@ -2295,6 +2308,24 @@ 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 80df950a7c70f..965e65e6a79db 100644 --- a/ext/dom/documentfragment.c +++ b/ext/dom/documentfragment.c @@ -161,6 +161,19 @@ 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); } /* }}} */ @@ -170,6 +183,23 @@ 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); + } } /* }}} */ diff --git a/ext/dom/element.c b/ext/dom/element.c index 7a32f72881dc5..48a6dde687c06 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -1294,10 +1294,10 @@ PHP_FUNCTION(dom_element_set_id_attribute_node) */ PHP_METHOD(domelement, remove) { - zval *id, *node; + zval *id; xmlNodePtr children, child; - dom_object *intern, *childobj; - int ret, stricterror; + dom_object *intern; + int stricterror; id = ZEND_THIS; if (zend_parse_parameters_none() == FAILURE) { From 69c93b7def16cb96eea67932b2a7b2371115d7ef Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 22 Mar 2019 14:03:54 +0100 Subject: [PATCH 22/26] Add DOMElement::after() and ::before() stubs. --- ext/dom/dom_fe.h | 2 ++ ext/dom/element.c | 16 ++++++++++++++++ ext/dom/node.c | 8 ++++++++ 3 files changed, 26 insertions(+) diff --git a/ext/dom/dom_fe.h b/ext/dom/dom_fe.h index b6f3b2ad52465..69159f76832a6 100644 --- a/ext/dom/dom_fe.h +++ b/ext/dom/dom_fe.h @@ -221,6 +221,8 @@ 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); diff --git a/ext/dom/element.c b/ext/dom/element.c index 48a6dde687c06..6944e38eb0bc8 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -119,6 +119,12 @@ 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(); @@ -154,6 +160,8 @@ const zend_function_entry php_dom_element_class_functions[] = { /* {{{ */ 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 @@ -1342,6 +1350,14 @@ PHP_METHOD(domelement, remove) } /* }}} end DOMElement::remove */ +PHP_METHOD(domelement, after) +{ +} + +PHP_METHOD(domelement, before) +{ +} + /* {{{ proto void domelement::append(string|DOMNode ...$nodes) URL: https://dom.spec.whatwg.org/#dom-parentnode-append Since: DOM Living Standard (DOM4) diff --git a/ext/dom/node.c b/ext/dom/node.c index 202f7ef22b96c..5d068cdaf9aa5 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -124,6 +124,12 @@ 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(); /* }}} */ /* @@ -162,6 +168,8 @@ const zend_function_entry php_dom_node_class_functions[] = { /* {{{ */ 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 }; /* }}} */ From 63957fba75c816764ddfbaa694d822b1f2f4450f Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 22 Mar 2019 17:55:36 +0100 Subject: [PATCH 23/26] Implement DOMElement::after() and DOMElement::before() --- ext/dom/element.c | 26 +++++++++++++++ ext/dom/parentnode.c | 77 +++++++++++++++++++++++++++++++++++++++++--- ext/dom/php_dom.h | 2 ++ 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/ext/dom/element.c b/ext/dom/element.c index 6944e38eb0bc8..467646478d633 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -1352,10 +1352,36 @@ PHP_METHOD(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) diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index f7137e83ce9af..b79e5b030bdea 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -139,7 +139,7 @@ int dom_parent_node_child_element_count(dom_object *obj, zval *retval) } /* }}} */ -xmlNode* dom_zvals_to_fragment(dom_object *context, xmlNode *contextNode, zval *nodes, int nodesc) +xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNode, zval *nodes, int nodesc) { int i; xmlDoc *documentNode; @@ -172,7 +172,7 @@ xmlNode* dom_zvals_to_fragment(dom_object *context, xmlNode *contextNode, zval * xmlUnlinkNode(newNode); } - newNodeObj->document = context->document; + newNodeObj->document = document; xmlSetTreeDoc(newNode, documentNode); if (!xmlAddChild(fragment, newNode)) { @@ -203,7 +203,7 @@ 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, contextNode, nodes, nodesc); + xmlNode *fragment = dom_zvals_to_fragment(context->document, contextNode, nodes, nodesc); newchild = fragment->children; prevsib = contextNode->last; @@ -232,7 +232,7 @@ 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, contextNode, nodes, nodesc); + xmlNode *fragment = dom_zvals_to_fragment(context->document, contextNode, nodes, nodesc); newchild = fragment->children; prevsib = NULL; @@ -259,4 +259,73 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc) } } +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; + } +} + +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; + } +} + #endif diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 114a552468111..999cb11abc3f2 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -130,6 +130,8 @@ 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); \ From fddb4a4e6ad3dcd8c02cf9e49539ba879a463fab Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 22 Mar 2019 20:32:40 +0100 Subject: [PATCH 24/26] Make DOMCharacterData implements DOMChildNode --- ext/dom/characterdata.c | 95 +++++++++++++++++++++++++++++++++++++++++ ext/dom/dom_fe.h | 3 ++ ext/dom/php_dom.c | 5 ++- 3 files changed, 101 insertions(+), 2 deletions(-) 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/dom_fe.h b/ext/dom/dom_fe.h index 69159f76832a6..c15f8922ece58 100644 --- a/ext/dom/dom_fe.h +++ b/ext/dom/dom_fe.h @@ -195,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); diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 87c0bc9c88810..2d3270384def1 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -752,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); @@ -774,8 +776,7 @@ PHP_MINIT_FUNCTION(dom) 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, 1, dom_parentnode_class_entry); - zend_class_implements(dom_element_class_entry, 1, dom_childnode_class_entry); + 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); From acab4f48d6aaa9d579de993268962b06263541b1 Mon Sep 17 00:00:00 2001 From: Thomas Weinert Date: Sun, 31 Mar 2019 13:48:04 +0200 Subject: [PATCH 25/26] Test: DOM Level4 node adoption and append with attributes --- ext/dom/tests/DOM4_DOMDocument_adoptNode.phpt | 50 +++++++++++++++++++ .../tests/DOM4_DOMNode_after_adopts_node.phpt | 49 ++++++++++++++++++ .../DOM4_ParentNode_append_adopts_node.phpt | 48 ++++++++++++++++++ ...OM4_ParentNode_append_with_attributes.phpt | 27 ++++++++++ 4 files changed, 174 insertions(+) create mode 100644 ext/dom/tests/DOM4_DOMDocument_adoptNode.phpt create mode 100644 ext/dom/tests/DOM4_DOMNode_after_adopts_node.phpt create mode 100644 ext/dom/tests/DOM4_ParentNode_append_adopts_node.phpt create mode 100644 ext/dom/tests/DOM4_ParentNode_append_with_attributes.phpt 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_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_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-- + From 768aefceea9ce13b8dfd943a581523ebbdcd23da Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 7 Apr 2019 13:55:39 +0200 Subject: [PATCH 26/26] Add namespace reconcile support for append/prepend/after/before. --- ext/dom/node.c | 32 ---------------------- ext/dom/parentnode.c | 15 +++++++++- ext/dom/php_dom.c | 32 ++++++++++++++++++++++ ext/dom/php_dom.h | 1 + ext/dom/tests/DOM4_DOMNode_after_ns.phpt | 29 ++++++++++++++++++++ ext/dom/tests/DOM4_DOMNode_append_ns.phpt | 25 +++++++++++++++++ ext/dom/tests/DOM4_DOMNode_before_ns.phpt | 29 ++++++++++++++++++++ ext/dom/tests/DOM4_DOMNode_prepend_ns.phpt | 25 +++++++++++++++++ 8 files changed, 155 insertions(+), 33 deletions(-) create mode 100644 ext/dom/tests/DOM4_DOMNode_after_ns.phpt create mode 100644 ext/dom/tests/DOM4_DOMNode_append_ns.phpt create mode 100644 ext/dom/tests/DOM4_DOMNode_before_ns.phpt create mode 100644 ext/dom/tests/DOM4_DOMNode_prepend_ns.phpt diff --git a/ext/dom/node.c b/ext/dom/node.c index 5d068cdaf9aa5..1e11a80510629 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -174,38 +174,6 @@ const zend_function_entry php_dom_child_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); - } -} -/* }}} */ - /* {{{ nodeName string readonly=yes URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68D095 diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index b79e5b030bdea..8829c282fefc0 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -209,7 +209,12 @@ void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) prevsib = contextNode->last; if (newchild) { - prevsib->next = newchild; + if (prevsib != NULL) { + prevsib->next = newchild; + } else { + contextNode->children = newchild; + } + newchild->prev = prevsib; contextNode->last = fragment->last; @@ -225,6 +230,8 @@ void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) fragment->children = NULL; fragment->last = NULL; + + dom_reconcile_ns(contextNode->doc, newchild); } } @@ -256,6 +263,8 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc) fragment->children = NULL; fragment->last = NULL; + + dom_reconcile_ns(contextNode->doc, newchild); } } @@ -293,6 +302,8 @@ void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc) fragment->children = NULL; fragment->last = NULL; + + dom_reconcile_ns(prevsib->doc, newchild); } } @@ -325,6 +336,8 @@ void dom_parent_node_before(dom_object *context, zval *nodes, int nodesc) fragment->children = NULL; fragment->last = NULL; + + dom_reconcile_ns(nextsib->doc, newchild); } } diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 2d3270384def1..f7c40379aa29f 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1464,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 999cb11abc3f2..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); 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_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 +