[RFC] Implement new DOM Living Standard APIs in ext/dom

This commit is contained in:
Benjamin Eberlei 2020-02-28 16:13:39 +01:00
parent 87bc99439d
commit 5acd86df8e
35 changed files with 1565 additions and 41 deletions

View file

@ -37,6 +37,10 @@ const zend_function_entry php_dom_characterdata_class_functions[] = {
PHP_ME(domcharacterdata, insertData, arginfo_class_DOMCharacterData_insertData, ZEND_ACC_PUBLIC)
PHP_ME(domcharacterdata, deleteData, arginfo_class_DOMCharacterData_deleteData, ZEND_ACC_PUBLIC)
PHP_ME(domcharacterdata, replaceData, arginfo_class_DOMCharacterData_replaceData, ZEND_ACC_PUBLIC)
PHP_ME(domcharacterdata, remove, arginfo_class_DOMChildNode_remove, ZEND_ACC_PUBLIC)
PHP_ME(domcharacterdata, after, arginfo_class_DOMChildNode_after, ZEND_ACC_PUBLIC)
PHP_ME(domcharacterdata, before, arginfo_class_DOMChildNode_before, ZEND_ACC_PUBLIC)
PHP_ME(domcharacterdata, replaceWith, arginfo_class_DOMChildNode_replaceWith, ZEND_ACC_PUBLIC)
PHP_FE_END
};
@ -361,4 +365,104 @@ PHP_METHOD(domcharacterdata, replaceData)
}
/* }}} end dom_characterdata_replace_data */
PHP_METHOD(domcharacterdata, remove)
{
zval *id = ZEND_THIS;
xmlNodePtr children, child;
dom_object *intern;
int stricterror;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(child, id, xmlNodePtr, intern);
if (dom_node_children_valid(child) == FAILURE) {
RETURN_NULL();
}
stricterror = dom_get_strict_error(intern->document);
if (dom_node_is_read_only(child) == SUCCESS ||
(child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
RETURN_NULL();
}
if (!child->parent) {
php_dom_throw_error(NOT_FOUND_ERR, stricterror);
RETURN_NULL();
}
children = child->parent->children;
if (!children) {
php_dom_throw_error(NOT_FOUND_ERR, stricterror);
RETURN_NULL();
}
while (children) {
if (children == child) {
xmlUnlinkNode(child);
RETURN_NULL();
}
children = children->next;
}
php_dom_throw_error(NOT_FOUND_ERR, stricterror);
RETURN_NULL();
}
PHP_METHOD(domcharacterdata, after)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_after(intern, args, argc);
}
PHP_METHOD(domcharacterdata, before)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_before(intern, args, argc);
}
PHP_METHOD(domcharacterdata, replaceWith)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_after(intern, args, argc);
dom_child_node_remove(intern);
}
#endif

View file

@ -13,7 +13,7 @@ if test "$PHP_DOM" != "no"; then
PHP_SETUP_LIBXML(DOM_SHARED_LIBADD, [
AC_DEFINE(HAVE_DOM,1,[ ])
PHP_NEW_EXTENSION(dom, [php_dom.c attr.c document.c \
domexception.c \
domexception.c parentnode.c \
processinginstruction.c cdatasection.c \
documentfragment.c domimplementation.c \
element.c node.c characterdata.c \

View file

@ -8,7 +8,7 @@ if (PHP_DOM == "yes") {
CHECK_HEADER_ADD_INCLUDE("libxml/parser.h", "CFLAGS_DOM", PHP_PHP_BUILD + "\\include\\libxml2")
) {
EXTENSION("dom", "php_dom.c attr.c document.c \
domexception.c processinginstruction.c \
domexception.c parentnode.c processinginstruction.c \
cdatasection.c documentfragment.c domimplementation.c element.c \
node.c characterdata.c documenttype.c \
entity.c nodelist.c text.c comment.c \

View file

@ -82,6 +82,8 @@ const zend_function_entry php_dom_document_class_functions[] = { /* {{{ */
PHP_ME(domdocument, relaxNGValidateSource, arginfo_class_DOMDocument_relaxNGValidateSource, ZEND_ACC_PUBLIC)
#endif
PHP_ME(domdocument, registerNodeClass, arginfo_class_DOMDocument_registerNodeClass, ZEND_ACC_PUBLIC)
PHP_ME(domdocument, append, arginfo_class_DOMParentNode_append, ZEND_ACC_PUBLIC)
PHP_ME(domdocument, prepend, arginfo_class_DOMParentNode_prepend, ZEND_ACC_PUBLIC)
PHP_FE_END
};
/* }}} */
@ -2128,4 +2130,48 @@ PHP_METHOD(domdocument, registerNodeClass)
}
/* }}} */
/* {{{ proto void domdocument::append(string|DOMNode ...$nodes)
URL: https://dom.spec.whatwg.org/#dom-parentnode-append
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(domdocument, append)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_append(intern, args, argc);
}
/* }}} */
/* {{{ proto void domdocument::prepend(string|DOMNode ...$nodes)
URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(domdocument, prepend)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_prepend(intern, args, argc);
}
/* }}} */
#endif /* HAVE_LIBXML && HAVE_DOM */

View file

@ -34,6 +34,8 @@
const zend_function_entry php_dom_documentfragment_class_functions[] = {
PHP_ME(domdocumentfragment, __construct, arginfo_class_DOMDocumentFragment___construct, ZEND_ACC_PUBLIC)
PHP_ME(domdocumentfragment, appendXML, arginfo_class_DOMDocumentFragment_appendXML, ZEND_ACC_PUBLIC)
PHP_ME(domdocumentfragment, append, arginfo_class_DOMParentNode_append, ZEND_ACC_PUBLIC)
PHP_ME(domdocumentfragment, prepend, arginfo_class_DOMParentNode_prepend, ZEND_ACC_PUBLIC)
PHP_FE_END
};
@ -137,4 +139,48 @@ PHP_METHOD(domdocumentfragment, appendXML) {
}
/* }}} */
/* {{{ proto void domdocumentfragment::append(string|DOMNode ...$nodes)
URL: https://dom.spec.whatwg.org/#dom-parentnode-append
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(domdocumentfragment, append)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_append(intern, args, argc);
}
/* }}} */
/* {{{ proto void domdocumentfragment::prepend(string|DOMNode ...$nodes)
URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(domdocumentfragment, prepend)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_prepend(intern, args, argc);
}
/* }}} */
#endif

View file

@ -1,5 +1,28 @@
<?php
interface DOMChildNode
{
public function remove(): void;
/** @var ...DOMNode|string $nodes */
public function before(... $nodes): void;
/** @var ...DOMNode|string $nodes */
public function after(...$nodes): void;
/** @var ...DOMNode|string $nodes */
public function replaceWith(...$nodes): void;
}
interface DOMParentNode
{
/** @var ...DOMNode|string $nodes */
public function append(...$nodes): void;
/** @var ...DOMNode|string $nodes */
public function prepend(...$nodes): void;
}
class DOMNode {
/** @return DOMNode|false */
public function appendChild(DOMNode $newChild) {}
@ -63,7 +86,7 @@ class DOMCdataSection {
public function __construct(string $value) {}
}
class DOMCharacterData {
class DOMCharacterData implements DOMChildNode {
/** @return bool */
public function appendData(string $data) {}
@ -84,7 +107,7 @@ class DOMComment {
public function __construct(string $value = "") {}
}
class DOMDocument {
class DOMDocument implements DOMParentNode {
public function __construct(string $version = "1.0", string $encoding = UNKNOWN) {}
/** @return DOMAttr|false */
@ -185,14 +208,14 @@ class DOMDocument {
public function adoptNode(DOMNode $source) {}
}
class DOMDocumentFragment {
class DOMDocumentFragment implements DOMParentNode {
public function __construct() {}
/** @return bool */
public function appendXML(string $data) {}
}
class DOMElement {
class DOMElement implements DOMParentNode, DOMChildNode {
public function __construct(string $name, ?string $value = null, string $uri = "") {}
/** @return string */

View file

@ -1,5 +1,20 @@
/* This is a generated file, edit the .stub.php file instead. */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOMChildNode_remove, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOMChildNode_before, 0, 0, IS_VOID, 0)
ZEND_ARG_VARIADIC_INFO(0, nodes)
ZEND_END_ARG_INFO()
#define arginfo_class_DOMChildNode_after arginfo_class_DOMChildNode_before
#define arginfo_class_DOMChildNode_replaceWith arginfo_class_DOMChildNode_before
#define arginfo_class_DOMParentNode_append arginfo_class_DOMChildNode_before
#define arginfo_class_DOMParentNode_prepend arginfo_class_DOMChildNode_before
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMNode_appendChild, 0, 0, 1)
ZEND_ARG_OBJ_INFO(0, newChild, DOMNode, 0)
ZEND_END_ARG_INFO()

View file

@ -19,6 +19,8 @@
#define DOM_FE_H
extern const zend_function_entry php_dom_domexception_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_domimplementation_class_functions[];
extern const zend_function_entry php_dom_documentfragment_class_functions[];
extern const zend_function_entry php_dom_document_class_functions[];
@ -75,6 +77,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_METHOD(domdocument, createElement);
@ -102,6 +106,8 @@ PHP_METHOD(domdocument, saveXML);
PHP_METHOD(domdocument, validate);
PHP_METHOD(domdocument, xinclude);
PHP_METHOD(domdocument, registerNodeClass);
PHP_METHOD(domdocument, append);
PHP_METHOD(domdocument, prepend);
#if defined(LIBXML_HTML_ENABLED)
PHP_METHOD(domdocument, loadHTML);
@ -152,6 +158,10 @@ PHP_METHOD(domcharacterdata, appendData);
PHP_METHOD(domcharacterdata, insertData);
PHP_METHOD(domcharacterdata, deleteData);
PHP_METHOD(domcharacterdata, replaceData);
PHP_METHOD(domcharacterdata, remove);
PHP_METHOD(domcharacterdata, after);
PHP_METHOD(domcharacterdata, before);
PHP_METHOD(domcharacterdata, replaceWith);
/* domattr methods */
PHP_METHOD(domattr, isId);
@ -177,6 +187,12 @@ PHP_METHOD(domelement, setIdAttribute);
PHP_METHOD(domelement, setIdAttributeNS);
PHP_METHOD(domelement, setIdAttributeNode);
PHP_METHOD(domelement, __construct);
PHP_METHOD(domelement, remove);
PHP_METHOD(domelement, after);
PHP_METHOD(domelement, before);
PHP_METHOD(domelement, append);
PHP_METHOD(domelement, prepend);
PHP_METHOD(domelement, replaceWith);
/* domtext methods */
PHP_METHOD(domtext, splitText);

View file

@ -87,6 +87,11 @@ int dom_entity_version_write(dom_object *obj, zval *newval);
/* namednodemap properties */
int dom_namednodemap_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);
@ -98,6 +103,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);

View file

@ -51,6 +51,12 @@ const zend_function_entry php_dom_element_class_functions[] = { /* {{{ */
PHP_ME(domelement, setIdAttributeNS, arginfo_class_DOMElement_setIdAttributeNS, ZEND_ACC_PUBLIC)
PHP_ME(domelement, setIdAttributeNode, arginfo_class_DOMElement_setIdAttributeNode, ZEND_ACC_PUBLIC)
PHP_ME(domelement, __construct, arginfo_class_DOMElement___construct, ZEND_ACC_PUBLIC)
PHP_ME(domelement, remove, arginfo_class_DOMChildNode_remove, ZEND_ACC_PUBLIC)
PHP_ME(domelement, after, arginfo_class_DOMChildNode_after, ZEND_ACC_PUBLIC)
PHP_ME(domelement, before, arginfo_class_DOMChildNode_before, ZEND_ACC_PUBLIC)
PHP_ME(domelement, replaceWith, arginfo_class_DOMChildNode_replaceWith, ZEND_ACC_PUBLIC)
PHP_ME(domelement, append, arginfo_class_DOMParentNode_append, ZEND_ACC_PUBLIC)
PHP_ME(domelement, prepend, arginfo_class_DOMParentNode_prepend, ZEND_ACC_PUBLIC)
PHP_FE_END
};
/* }}} */
@ -1183,4 +1189,126 @@ PHP_METHOD(domelement, setIdAttributeNode)
}
/* }}} end dom_element_set_id_attribute_node */
/* {{{ proto void DOMElement::remove();
URL:
Since:
*/
PHP_METHOD(domelement, remove)
{
zval *id;
xmlNodePtr child;
dom_object *intern;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(child, id, xmlNodePtr, intern);
dom_child_node_remove(intern);
}
/* }}} end DOMElement::remove */
PHP_METHOD(domelement, after)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
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_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_before(intern, args, argc);
}
/* {{{ proto void domelement::append(string|DOMNode ...$nodes)
URL: https://dom.spec.whatwg.org/#dom-parentnode-append
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(domelement, append)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_append(intern, args, argc);
}
/* }}} end DOMElement::append */
/* {{{ proto void domelement::prepend(string|DOMNode ...$nodes)
URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(domelement, prepend)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_prepend(intern, args, argc);
}
/* }}} end DOMElement::prepend */
/* {{{ proto void domelement::replaceWith(string|DOMNode ...$nodes)
URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(domelement, replaceWith)
{
int argc;
zval *args, *id;
dom_object *intern;
xmlNode *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
id = ZEND_THIS;
DOM_GET_OBJ(context, id, xmlNodePtr, intern);
dom_parent_node_after(intern, args, argc);
dom_child_node_remove(intern);
}
/* }}} end DOMElement::prepend */
#endif

View file

@ -53,36 +53,12 @@ const zend_function_entry php_dom_node_class_functions[] = { /* {{{ */
};
/* }}} */
static void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */
{
xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL;
if (nodep->type == XML_ELEMENT_NODE) {
/* Following if block primarily used for inserting nodes created via createElementNS */
if (nodep->nsDef != NULL) {
curns = nodep->nsDef;
while (curns) {
nsdftptr = curns->next;
if (curns->href != NULL) {
if((nsptr = xmlSearchNsByHref(doc, nodep->parent, curns->href)) &&
(curns->prefix == NULL || xmlStrEqual(nsptr->prefix, curns->prefix))) {
curns->next = NULL;
if (prevns == NULL) {
nodep->nsDef = nsdftptr;
} else {
prevns->next = nsdftptr;
}
dom_set_old_ns(doc, curns);
curns = prevns;
}
}
prevns = curns;
curns = nsdftptr;
}
}
xmlReconciliateNs(doc, nodep);
}
}
const zend_function_entry php_dom_child_node_class_functions[] = { /* {{{ */
PHP_ABSTRACT_ME(DOMChildNode, remove, arginfo_class_DOMChildNode_remove)
PHP_ABSTRACT_ME(DOMChildNode, after, arginfo_class_DOMChildNode_after)
PHP_ABSTRACT_ME(DOMChildNode, before, arginfo_class_DOMChildNode_before)
PHP_FE_END
};
/* }}} */
/* {{{ nodeName string
@ -333,7 +309,6 @@ int dom_node_child_nodes_read(dom_object *obj, zval *retval)
return SUCCESS;
}
/* }}} */
/* {{{ firstChild DomNode
@ -454,6 +429,72 @@ int dom_node_next_sibling_read(dom_object *obj, zval *retval)
/* }}} */
/* {{{ previousElementSibling DomNode
readonly=yes
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-640FB3C8
Since:
*/
int dom_node_previous_element_sibling_read(dom_object *obj, zval *retval)
{
xmlNode *nodep, *prevsib;
nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return FAILURE;
}
prevsib = nodep->prev;
while (prevsib && prevsib->type != XML_ELEMENT_NODE) {
prevsib = prevsib->prev;
}
if (!prevsib) {
ZVAL_NULL(retval);
return SUCCESS;
}
php_dom_create_object(prevsib, retval, obj);
return SUCCESS;
}
/* }}} */
/* {{{ nextElementSibling DomNode
readonly=yes
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6AC54C2F
Since:
*/
int dom_node_next_element_sibling_read(dom_object *obj, zval *retval)
{
xmlNode *nodep, *nextsib;
nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return FAILURE;
}
nextsib = nodep->next;
while (nextsib != NULL && nextsib->type != XML_ELEMENT_NODE) {
nextsib = nextsib->next;
}
if (!nextsib) {
ZVAL_NULL(retval);
return SUCCESS;
}
php_dom_create_object(nextsib, retval, obj);
return SUCCESS;
}
/* }}} */
/* {{{ attributes DomNamedNodeMap
readonly=yes
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-84CF096

408
ext/dom/parentnode.c Normal file
View file

@ -0,0 +1,408 @@
/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Benjamin Eberlei <beberlei@php.net> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#if HAVE_LIBXML && HAVE_DOM
#include "php_dom.h"
#include "dom_arginfo.h"
/* {{{ DOMParentNode methods */
const zend_function_entry php_dom_parent_node_class_functions[] = { /* {{{ */
PHP_ABSTRACT_ME(DOMParentNode, append, arginfo_class_DOMParentNode_append)
PHP_ABSTRACT_ME(DOMParentNode, prepend, arginfo_class_DOMParentNode_prepend)
PHP_FE_END
};
/* }}} */
/* {{{ firstElementChild DomParentNode
readonly=yes
URL: https://www.w3.org/TR/dom/#dom-parentnode-firstelementchild
*/
int dom_parent_node_first_element_child_read(dom_object *obj, zval *retval)
{
xmlNode *nodep, *first = NULL;
nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return FAILURE;
}
if (dom_node_children_valid(nodep) == SUCCESS) {
first = nodep->children;
while (first && first->type != XML_ELEMENT_NODE) {
first = first->next;
}
}
if (!first) {
ZVAL_NULL(retval);
return SUCCESS;
}
php_dom_create_object(first, retval, obj);
return SUCCESS;
}
/* }}} */
/* {{{ lastElementChild DomParentNode
readonly=yes
URL: https://www.w3.org/TR/dom/#dom-parentnode-lastelementchild
*/
int dom_parent_node_last_element_child_read(dom_object *obj, zval *retval)
{
xmlNode *nodep, *last = NULL;
nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return FAILURE;
}
if (dom_node_children_valid(nodep) == SUCCESS) {
last = nodep->last;
while (last && last->type != XML_ELEMENT_NODE) {
last = last->prev;
}
}
if (!last) {
ZVAL_NULL(retval);
return SUCCESS;
}
php_dom_create_object(last, retval, obj);
return SUCCESS;
}
/* }}} */
/* {{{ childElementCount DomParentNode
readonly=yes
https://www.w3.org/TR/dom/#dom-parentnode-childelementcount
*/
int dom_parent_node_child_element_count(dom_object *obj, zval *retval)
{
xmlNode *nodep, *first = NULL;
zend_long count = 0;
nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return FAILURE;
}
if (dom_node_children_valid(nodep) == SUCCESS) {
first = nodep->children;
while (first != NULL) {
if (first->type == XML_ELEMENT_NODE) {
count++;
}
first = first->next;
}
}
ZVAL_LONG(retval, count);
return SUCCESS;
}
/* }}} */
xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNode, zval *nodes, int nodesc)
{
int i;
xmlDoc *documentNode;
xmlNode *fragment;
xmlNode *newNode;
zend_class_entry *ce;
dom_object *newNodeObj;
int stricterror;
if (contextNode->type == XML_DOCUMENT_NODE || contextNode->type == XML_HTML_DOCUMENT_NODE) {
documentNode = (xmlDoc *) contextNode;
} else {
documentNode = contextNode->doc;
}
fragment = xmlNewDocFragment(documentNode);
if (!fragment) {
return NULL;
}
stricterror = dom_get_strict_error(document);
for (i = 0; i < nodesc; i++) {
if (Z_TYPE(nodes[i]) == IS_OBJECT) {
ce = Z_OBJCE(nodes[i]);
if (instanceof_function(ce, dom_node_class_entry)) {
newNodeObj = Z_DOMOBJ_P(&nodes[i]);
newNode = dom_object_get_node(newNodeObj);
if (newNode->doc != documentNode) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror);
return NULL;
}
if (newNode->parent != NULL) {
xmlUnlinkNode(newNode);
}
newNodeObj->document = document;
xmlSetTreeDoc(newNode, documentNode);
if (!xmlAddChild(fragment, newNode)) {
xmlFree(fragment);
php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
return NULL;
}
continue;
} else {
xmlFree(fragment);
zend_type_error("Invalid argument type must be either DOMNode or string");
return NULL;
}
} else if (Z_TYPE(nodes[i]) == IS_STRING) {
newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[i]));
xmlSetTreeDoc(newNode, documentNode);
if (!xmlAddChild(fragment, newNode)) {
xmlFree(fragment);
return NULL;
}
} else {
xmlFree(fragment);
zend_type_error("Invalid argument type must be either DOMNode or string");
return NULL;
}
}
return fragment;
}
static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fragment)
{
xmlNodePtr node = fragment->children;
while (node != NULL) {
node->parent = parentNode;
if (node == fragment->last) {
break;
}
node = node->next;
}
fragment->children = NULL;
fragment->last = NULL;
}
void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc)
{
xmlNode *parentNode = dom_object_get_node(context);
xmlNodePtr newchild, prevsib;
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
if (fragment == NULL) {
return;
}
newchild = fragment->children;
prevsib = parentNode->last;
if (newchild) {
if (prevsib != NULL) {
prevsib->next = newchild;
} else {
parentNode->children = newchild;
}
parentNode->last = fragment->last;
newchild->prev = prevsib;
dom_fragment_assign_parent_node(parentNode, fragment);
dom_reconcile_ns(parentNode->doc, newchild);
}
xmlFree(fragment);
}
void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc)
{
xmlNode *parentNode = dom_object_get_node(context);
if (parentNode->children == NULL) {
dom_parent_node_append(context, nodes, nodesc);
return;
}
xmlNodePtr newchild, nextsib;
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
if (fragment == NULL) {
return;
}
newchild = fragment->children;
nextsib = parentNode->children;
if (newchild) {
parentNode->children = newchild;
fragment->last->next = nextsib;
nextsib->prev = fragment->last;
dom_fragment_assign_parent_node(parentNode, fragment);
dom_reconcile_ns(parentNode->doc, newchild);
}
xmlFree(fragment);
}
void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc)
{
xmlNode *prevsib = dom_object_get_node(context);
xmlNodePtr newchild, parentNode;
xmlNode *fragment;
int stricterror = dom_get_strict_error(context->document);
if (!prevsib->parent) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return;
}
parentNode = prevsib->parent;
fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
if (fragment == NULL) {
return;
}
newchild = fragment->children;
if (newchild) {
fragment->last->next = prevsib->next;
prevsib->next = newchild;
newchild->prev = prevsib;
dom_fragment_assign_parent_node(parentNode, fragment);
dom_reconcile_ns(prevsib->doc, newchild);
}
xmlFree(fragment);
}
void dom_parent_node_before(dom_object *context, zval *nodes, int nodesc)
{
xmlNode *nextsib = dom_object_get_node(context);
xmlNodePtr newchild, prevsib, parentNode;
xmlNode *fragment;
prevsib = nextsib->prev;
parentNode = nextsib->parent;
fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
if (fragment == NULL) {
return;
}
newchild = fragment->children;
if (newchild) {
if (parentNode->children == nextsib) {
parentNode->children = newchild;
} else {
prevsib->next = newchild;
}
fragment->last->next = nextsib;
nextsib->prev = fragment->last;
newchild->prev = prevsib;
dom_fragment_assign_parent_node(parentNode, fragment);
dom_reconcile_ns(nextsib->doc, newchild);
}
xmlFree(fragment);
}
void dom_child_node_remove(dom_object *context)
{
xmlNode *child = dom_object_get_node(context);
xmlNodePtr children;
int stricterror;
if (dom_node_children_valid(child) == FAILURE) {
return;
}
stricterror = dom_get_strict_error(context->document);
if (dom_node_is_read_only(child) == SUCCESS ||
(child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return;
}
if (!child->parent) {
php_dom_throw_error(NOT_FOUND_ERR, stricterror);
return;
}
children = child->parent->children;
if (!children) {
php_dom_throw_error(NOT_FOUND_ERR, stricterror);
return;
}
while (children) {
if (children == child) {
xmlUnlinkNode(child);
return;
}
children = children->next;
}
php_dom_throw_error(NOT_FOUND_ERR, stricterror);
}
#endif

View file

@ -35,6 +35,8 @@
/* {{{ class entries */
PHP_DOM_EXPORT zend_class_entry *dom_node_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_domexception_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_parentnode_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_childnode_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_documentfragment_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_document_class_entry;
@ -66,6 +68,7 @@ zend_object_handlers dom_xpath_object_handlers;
static HashTable classes;
/* {{{ prop handler tables */
static HashTable dom_document_prop_handlers;
static HashTable dom_documentfragment_prop_handlers;
static HashTable dom_node_prop_handlers;
static HashTable dom_nodelist_prop_handlers;
static HashTable dom_namednodemap_prop_handlers;
@ -585,6 +588,12 @@ PHP_MINIT_FUNCTION(dom)
dom_domexception_class_entry->ce_flags |= ZEND_ACC_FINAL;
zend_declare_property_long(dom_domexception_class_entry, "code", sizeof("code")-1, 0, ZEND_ACC_PUBLIC);
INIT_CLASS_ENTRY(ce, "DOMParentNode", php_dom_parent_node_class_functions);
dom_parentnode_class_entry = zend_register_internal_interface(&ce);
INIT_CLASS_ENTRY(ce, "DOMChildNode", php_dom_child_node_class_functions);
dom_childnode_class_entry = zend_register_internal_interface(&ce);
REGISTER_DOM_CLASS(ce, "DOMImplementation", NULL, php_dom_domimplementation_class_functions, dom_domimplementation_class_entry);
REGISTER_DOM_CLASS(ce, "DOMNode", NULL, php_dom_node_class_functions, dom_node_class_entry);
@ -622,7 +631,15 @@ PHP_MINIT_FUNCTION(dom)
zend_hash_add_ptr(&classes, ce.name, &dom_namespace_node_prop_handlers);
REGISTER_DOM_CLASS(ce, "DOMDocumentFragment", dom_node_class_entry, php_dom_documentfragment_class_functions, dom_documentfragment_class_entry);
zend_hash_add_ptr(&classes, ce.name, &dom_node_prop_handlers);
zend_hash_init(&dom_documentfragment_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
dom_register_prop_handler(&dom_documentfragment_prop_handlers, "firstElementChild", sizeof("firstElementChild")-1, dom_parent_node_first_element_child_read, NULL);
dom_register_prop_handler(&dom_documentfragment_prop_handlers, "lastElementChild", sizeof("lastElementChild")-1, dom_parent_node_last_element_child_read, NULL);
dom_register_prop_handler(&dom_documentfragment_prop_handlers, "childElementCount", sizeof("childElementCount")-1, dom_parent_node_child_element_count, NULL);
zend_hash_merge(&dom_documentfragment_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0);
zend_hash_add_ptr(&classes, ce.name, &dom_documentfragment_prop_handlers);
zend_class_implements(dom_documentfragment_class_entry, 1, dom_parentnode_class_entry);
REGISTER_DOM_CLASS(ce, "DOMDocument", dom_node_class_entry, php_dom_document_class_functions, dom_document_class_entry);
zend_hash_init(&dom_document_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
@ -646,8 +663,13 @@ PHP_MINIT_FUNCTION(dom)
dom_register_prop_handler(&dom_document_prop_handlers, "recover", sizeof("recover")-1, dom_document_recover_read, dom_document_recover_write);
dom_register_prop_handler(&dom_document_prop_handlers, "substituteEntities", sizeof("substituteEntities")-1, dom_document_substitue_entities_read, dom_document_substitue_entities_write);
dom_register_prop_handler(&dom_document_prop_handlers, "firstElementChild", sizeof("firstElementChild")-1, dom_parent_node_first_element_child_read, NULL);
dom_register_prop_handler(&dom_document_prop_handlers, "lastElementChild", sizeof("lastElementChild")-1, dom_parent_node_last_element_child_read, NULL);
dom_register_prop_handler(&dom_document_prop_handlers, "childElementCount", sizeof("childElementCount")-1, dom_parent_node_child_element_count, NULL);
zend_hash_merge(&dom_document_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0);
zend_hash_add_ptr(&classes, ce.name, &dom_document_prop_handlers);
zend_class_implements(dom_document_class_entry, 1, dom_parentnode_class_entry);
INIT_CLASS_ENTRY(ce, "DOMNodeList", php_dom_nodelist_class_functions);
ce.create_object = dom_nnodemap_objects_new;
@ -674,9 +696,13 @@ PHP_MINIT_FUNCTION(dom)
zend_hash_init(&dom_characterdata_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
dom_register_prop_handler(&dom_characterdata_prop_handlers, "data", sizeof("data")-1, dom_characterdata_data_read, dom_characterdata_data_write);
dom_register_prop_handler(&dom_characterdata_prop_handlers, "length", sizeof("length")-1, dom_characterdata_length_read, NULL);
dom_register_prop_handler(&dom_characterdata_prop_handlers, "previousElementSibling", sizeof("previousElementSibling")-1, dom_node_previous_element_sibling_read, NULL);
dom_register_prop_handler(&dom_characterdata_prop_handlers, "nextElementSibling", sizeof("nextElementSibling")-1, dom_node_next_element_sibling_read, NULL);
zend_hash_merge(&dom_characterdata_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0);
zend_hash_add_ptr(&classes, ce.name, &dom_characterdata_prop_handlers);
zend_class_implements(dom_characterdata_class_entry, 1, dom_childnode_class_entry);
REGISTER_DOM_CLASS(ce, "DOMAttr", dom_node_class_entry, php_dom_attr_class_functions, dom_attr_class_entry);
zend_hash_init(&dom_attr_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
@ -693,9 +719,16 @@ PHP_MINIT_FUNCTION(dom)
zend_hash_init(&dom_element_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
dom_register_prop_handler(&dom_element_prop_handlers, "tagName", sizeof("tagName")-1, dom_element_tag_name_read, NULL);
dom_register_prop_handler(&dom_element_prop_handlers, "schemaTypeInfo", sizeof("schemaTypeInfo")-1, dom_element_schema_type_info_read, NULL);
dom_register_prop_handler(&dom_element_prop_handlers, "firstElementChild", sizeof("firstElementChild")-1, dom_parent_node_first_element_child_read, NULL);
dom_register_prop_handler(&dom_element_prop_handlers, "lastElementChild", sizeof("lastElementChild")-1, dom_parent_node_last_element_child_read, NULL);
dom_register_prop_handler(&dom_element_prop_handlers, "childElementCount", sizeof("childElementCount")-1, dom_parent_node_child_element_count, NULL);
dom_register_prop_handler(&dom_element_prop_handlers, "previousElementSibling", sizeof("previousElementSibling")-1, dom_node_previous_element_sibling_read, NULL);
dom_register_prop_handler(&dom_element_prop_handlers, "nextElementSibling", sizeof("nextElementSibling")-1, dom_node_next_element_sibling_read, NULL);
zend_hash_merge(&dom_element_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, 0);
zend_hash_add_ptr(&classes, ce.name, &dom_element_prop_handlers);
zend_class_implements(dom_element_class_entry, 2, dom_parentnode_class_entry, dom_childnode_class_entry);
REGISTER_DOM_CLASS(ce, "DOMText", dom_characterdata_class_entry, php_dom_text_class_functions, dom_text_class_entry);
zend_hash_init(&dom_text_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
@ -851,6 +884,7 @@ PHP_MINFO_FUNCTION(dom)
PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */
{
zend_hash_destroy(&dom_document_prop_handlers);
zend_hash_destroy(&dom_documentfragment_prop_handlers);
zend_hash_destroy(&dom_node_prop_handlers);
zend_hash_destroy(&dom_namespace_node_prop_handlers);
zend_hash_destroy(&dom_nodelist_prop_handlers);
@ -1344,6 +1378,38 @@ void dom_set_old_ns(xmlDoc *doc, xmlNs *ns) {
}
/* }}} end dom_set_old_ns */
void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */
{
xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL;
if (nodep->type == XML_ELEMENT_NODE) {
/* Following if block primarily used for inserting nodes created via createElementNS */
if (nodep->nsDef != NULL) {
curns = nodep->nsDef;
while (curns) {
nsdftptr = curns->next;
if (curns->href != NULL) {
if((nsptr = xmlSearchNsByHref(doc, nodep->parent, curns->href)) &&
(curns->prefix == NULL || xmlStrEqual(nsptr->prefix, curns->prefix))) {
curns->next = NULL;
if (prevns == NULL) {
nodep->nsDef = nsdftptr;
} else {
prevns->next = nsdftptr;
}
dom_set_old_ns(doc, curns);
curns = prevns;
}
}
prevns = curns;
curns = nsdftptr;
}
}
xmlReconciliateNs(doc, nodep);
}
}
/* }}} */
/*
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS

View file

@ -109,6 +109,7 @@ void node_list_unlink(xmlNodePtr node);
int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len);
xmlNsPtr dom_get_ns(xmlNodePtr node, char *uri, int *errorcode, char *prefix);
void dom_set_old_ns(xmlDoc *doc, xmlNs *ns);
void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep);
xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName);
void dom_normalize (xmlNodePtr nodep);
xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr nodep, char *ns, char *local, int *cur, int index);
@ -125,6 +126,12 @@ xmlNode *php_dom_libxml_notation_iter(xmlHashTable *ht, int index);
zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref);
void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce);
void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc);
void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc);
void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc);
void dom_parent_node_before(dom_object *context, zval *nodes, int nodesc);
void dom_child_node_remove(dom_object *context);
#define REGISTER_DOM_CLASS(ce, name, parent_ce, funcs, entry) \
INIT_CLASS_ENTRY(ce, name, funcs); \
ce.create_object = dom_objects_new; \

View file

@ -0,0 +1,41 @@
--TEST--
DOMChildNode::after(), before, replaceWith with DOMNode from wrong document throws exception
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom1 = new DOMDocument;
$dom1->loadXML('<test/>');
$dom2 = new DOMDocument;
$dom2->loadXML('<test><foo /></test>');
$element = $dom1->documentElement;
try {
$element->after($dom2->documentElement->firstChild);
echo "FAIL";
} catch (DOMException $e) {
echo $e->getMessage() . "\n";
}
try {
$element->before($dom2->documentElement->firstChild);
echo "FAIL";
} catch (DOMException $e) {
echo $e->getMessage() . "\n";
}
try {
$element->replaceWith($dom2->documentElement->firstChild);
echo "FAIL";
} catch (DOMException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Wrong Document Error
Wrong Document Error
Wrong Document Error

View file

@ -0,0 +1,25 @@
--TEST--
DOMNode: Element Siblings
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test>foo<bar>FirstElement</bar><bar>LastElement</bar>bar</test>');
$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

View file

@ -0,0 +1,35 @@
--TEST--
DOMNode::after()
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test><mark>first</mark><mark>second</mark></test>');
$element = $dom->documentElement->firstElementChild;
$secondMark = $dom->documentElement->lastElementChild;
$element->after(
'text inserted after',
$dom->createElement('inserted-after', 'content')
);
$secondMark->after('text inserted after second');
print_node_list_compact($dom->documentElement->childNodes);
?>
--EXPECT--
<mark>
first
</mark>
text inserted after
<inserted-after>
content
</inserted-after>
<mark>
second
</mark>
text inserted after second

View file

@ -0,0 +1,29 @@
--TEST--
DOMNode::after() with namespace
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$doc = new DOMDocument('1.0', 'utf-8');
$doc->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--
<?xml version="1.0" encoding="utf-8"?>
<element xmlns="http://www.w3.org/2005/Atom" xmlns:g="http://base.google.com/ns/1.0">
<g:item_type>house</g:item_type>
<g:item_type>street</g:item_type>
</element>

View file

@ -0,0 +1,25 @@
--TEST--
DOMNode::append() with namespace
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$doc = new DOMDocument('1.0', 'utf-8');
$doc->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--
<?xml version="1.0" encoding="utf-8"?>
<element xmlns="http://www.w3.org/2005/Atom" xmlns:g="http://base.google.com/ns/1.0">
<g:item_type>house</g:item_type>
</element>

View file

@ -0,0 +1,35 @@
--TEST--
DOMNode::before()
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test><mark>first</mark><mark>second</mark></test>');
$element = $dom->documentElement->firstElementChild;
$secondMark = $dom->documentElement->lastElementChild;
$element->before(
$dom->createElement('inserted-before', 'content'),
'text inserted before'
);
$secondMark->before('text inserted before second');
print_node_list_compact($dom->documentElement->childNodes);
?>
--EXPECT--
<inserted-before>
content
</inserted-before>
text inserted before
<mark>
first
</mark>
text inserted before second
<mark>
second
</mark>

View file

@ -0,0 +1,29 @@
--TEST--
DOMNode::before() with namespace
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$doc = new DOMDocument('1.0', 'utf-8');
$doc->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--
<?xml version="1.0" encoding="utf-8"?>
<element xmlns="http://www.w3.org/2005/Atom" xmlns:g="http://base.google.com/ns/1.0">
<g:item_type>street</g:item_type>
<g:item_type>house</g:item_type>
</element>

View file

@ -0,0 +1,25 @@
--TEST--
DOMNode::prepend() with namespace
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$doc = new DOMDocument('1.0', 'utf-8');
$doc->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--
<?xml version="1.0" encoding="utf-8"?>
<element xmlns="http://www.w3.org/2005/Atom" xmlns:g="http://base.google.com/ns/1.0">
<g:item_type>house</g:item_type>
</element>

View file

@ -0,0 +1,30 @@
--TEST--
DOMNode::remove()
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test><one>first</one><two>second</two></test>');
$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

View file

@ -0,0 +1,18 @@
--TEST--
DOMNode::remove() dangling element
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
$dom = new DOMDocument();
$element = $dom->createElement('test');
try {
$element->remove();
} catch (DOMException $e) {
echo $e->getMessage();
}
--EXPECT--
Not Found Error

View file

@ -0,0 +1,27 @@
--TEST--
DOMNode::replaceWith()
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test><mark>first</mark><mark>second</mark></test>');
$element = $dom->documentElement->firstChild;
$element->replaceWith(
$dom->createElement('element', 'content'),
'content'
);
print_node_list_compact($dom->documentElement->childNodes);
?>
--EXPECT--
<element>
content
</element>
content
<mark>
second
</mark>

View file

@ -0,0 +1,51 @@
--TEST--
DOMParentNode: Child Element Handling
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test>foo<bar>FirstElement</bar><bar>LastElement</bar>bar</test>');
var_dump($dom instanceof DOMParentNode);
print_node($dom->firstElementChild);
print_node($dom->lastElementChild);
var_dump($dom->childElementCount);
$element = $dom->documentElement;
var_dump($element instanceof DOMParentNode);
print_node($element->firstElementChild);
print_node($element->lastElementChild);
var_dump($element->childElementCount);
var_dump($element->lastElementChild->firstElementChild);
var_dump($element->lastElementChild->lastElementChild);
var_dump($element->lastElementChild->childElementCount);
?>
--EXPECT--
bool(true)
Node Name: test
Node Type: 1
Num Children: 4
Node Name: test
Node Type: 1
Num Children: 4
int(1)
bool(true)
Node Name: bar
Node Type: 1
Num Children: 1
Node Content: FirstElement
Node Name: bar
Node Type: 1
Num Children: 1
Node Content: LastElement
int(2)
NULL
NULL
int(0)

View file

@ -0,0 +1,41 @@
--TEST--
DOMParentNode: Child Element Handling
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test></test>');
$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)

View file

@ -0,0 +1,41 @@
--TEST--
DOMParentNode::append()
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
declare(strict_types=1);
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test><mark/><mark /><mark /></test>');
$element = $dom->documentElement;
$element->append(
$dom->createElement('element', 'content'),
'content'
);
var_dump($dom->documentElement->childElementCount);
print_node_list_compact($element->childNodes);
$element->append(
$dom->createElement('element', 'content'),
'content'
);
var_dump($dom->documentElement->childElementCount);
?>
--EXPECT--
int(4)
<mark>
</mark>
<mark>
</mark>
<mark>
</mark>
<element>
content
</element>
content
int(5)

View file

@ -0,0 +1,18 @@
--TEST--
DOMParentNode::append() exception on invalid argument
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test />');
try {
$dom->documentElement->append(array());
} catch(TypeError $e) {
echo "OK! {$e->getMessage()}";
}
--EXPECT--
OK! Invalid argument type must be either DOMNode or string

View file

@ -0,0 +1,26 @@
--TEST--
DOMParentNode::append() with attributes
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test attr-one="21"/>');
$replacement = $dom->createAttribute('attr-one');
$replacement->value ='42';
$addition = $dom->createAttribute('attr-two');
$addition->value = '23';
$element = $dom->documentElement;
try {
$element->append($replacement, $addition);
} catch (DOMException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Hierarchy Request Error

View file

@ -0,0 +1,33 @@
--TEST--
DOMParentNode::append() with DOMNode from wrong document throws exception
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
require_once("dom_test.inc");
$dom1 = new DOMDocument;
$dom1->loadXML('<test/>');
$dom2 = new DOMDocument;
$dom2->loadXML('<test><foo /></test>');
$element = $dom1->documentElement;
try {
$element->append($dom2->documentElement->firstChild);
echo "FAIL";
} catch (DOMException $e) {
echo $e->getMessage() . "\n";
}
try {
$element->prepend($dom2->documentElement->firstChild);
echo "FAIL";
} catch (DOMException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Wrong Document Error
Wrong Document Error

View file

@ -0,0 +1,49 @@
--TEST--
DOMParentNode::prepend()
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
declare(strict_types=1);
require_once("dom_test.inc");
$dom = new DOMDocument;
$dom->loadXML('<test><mark/><mark><nested /></mark><mark/></test>');
$element = $dom->documentElement;
$firstMark = $element->childNodes[0];
$element->prepend(
$dom->createElement('element', 'content'),
'content'
);
var_dump($element->childElementCount);
print_node_list_compact($element->childNodes);
$element = $dom->documentElement;
$element->prepend(
$dom->createElement('element', 'content'),
'content'
);
var_dump($element->childElementCount);
$firstMark->prepend('content');
print_node_list_compact($firstMark->childNodes);
?>
--EXPECT--
int(4)
<element>
content
</element>
content
<mark>
</mark>
<mark>
<nested>
</nested>
</mark>
<mark>
</mark>
int(5)
content

View file

@ -30,7 +30,7 @@ foreach ($dataNodes AS $node) {
?>
--EXPECTF--
int(3)
object(DOMText)#%d (19) {
object(DOMText)#%d (21) {
["wholeText"]=>
string(3) "
"
@ -39,6 +39,10 @@ object(DOMText)#%d (19) {
"
["length"]=>
int(3)
["previousElementSibling"]=>
NULL
["nextElementSibling"]=>
NULL
["nodeName"]=>
string(5) "#text"
["nodeValue"]=>
@ -74,11 +78,21 @@ object(DOMText)#%d (19) {
string(3) "
"
}
object(DOMElement)#%d (18) {
object(DOMElement)#%d (23) {
["tagName"]=>
string(5) "form1"
["schemaTypeInfo"]=>
NULL
["firstElementChild"]=>
string(22) "(object value omitted)"
["lastElementChild"]=>
string(22) "(object value omitted)"
["childElementCount"]=>
int(3)
["previousElementSibling"]=>
NULL
["nextElementSibling"]=>
NULL
["nodeName"]=>
string(5) "form1"
["nodeValue"]=>
@ -120,7 +134,7 @@ object(DOMElement)#%d (18) {
Value C
"
}
object(DOMText)#%d (19) {
object(DOMText)#%d (21) {
["wholeText"]=>
string(1) "
"
@ -129,6 +143,10 @@ object(DOMText)#%d (19) {
"
["length"]=>
int(1)
["previousElementSibling"]=>
NULL
["nextElementSibling"]=>
NULL
["nodeName"]=>
string(5) "#text"
["nodeValue"]=>

View file

@ -48,4 +48,22 @@ function print_node_list($nodelist)
}
}
function print_node_compact($node, $spaces)
{
if ($node->nodeType == 3) {
print str_repeat(" ", $spaces) . trim($node->nodeValue) . "\n";
} else {
print str_repeat(" ", $spaces) . "<" . $node->nodeName . ">\n";
print_node_list_compact($node->childNodes, $spaces + 2);
print str_repeat(" ", $spaces) . "</" . $node->nodeName . ">\n";
}
}
function print_node_list_compact($nodelist, $spaces = 0)
{
foreach ($nodelist as $node) {
print_node_compact($node, $spaces);
}
}
?>

View file

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