1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 08:12:21 +01:00
Files
archived-php-src/ext/dom/document.c
Niels Dossche 080fd14458 Fix GH-19612: Mitigate libxml2 tree dictionary bug
This code is very similar to code on PHP 8.4 and higher, but the
mitigation is extended to entity references and to attribute children.

Closes GH-19620.
2025-09-03 21:52:48 +02:00

2274 lines
58 KiB
C

/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Christian Stocker <chregu@php.net> |
| Rob Richards <rrichards@php.net> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "php_dom.h"
#include <libxml/xmlsave.h>
#ifdef LIBXML_SCHEMAS_ENABLED
#include <libxml/relaxng.h>
#include <libxml/xmlschemas.h>
#endif
typedef struct _idsIterator idsIterator;
struct _idsIterator {
xmlChar *elementId;
xmlNode *element;
};
#define DOM_LOAD_STRING 0
#define DOM_LOAD_FILE 1
/*
* class DOMDocument extends DOMNode
*
* URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-i-Document
* Since:
*/
/* {{{ docType DOMDocumentType
readonly=yes
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-B63ED1A31
Since:
*/
int dom_document_doctype_read(dom_object *obj, zval *retval)
{
xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
xmlDtdPtr dtdptr;
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
dtdptr = xmlGetIntSubset(docp);
if (!dtdptr) {
ZVAL_NULL(retval);
return SUCCESS;
}
php_dom_create_object((xmlNodePtr) dtdptr, retval, obj);
return SUCCESS;
}
/* }}} */
/* {{{ implementation DOMImplementation
readonly=yes
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1B793EBA
Since:
*/
int dom_document_implementation_read(dom_object *obj, zval *retval)
{
php_dom_create_implementation(retval);
return SUCCESS;
}
/* }}} */
/* {{{ documentElement DOMElement
readonly=yes
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-87CD092
Since:
*/
int dom_document_document_element_read(dom_object *obj, zval *retval)
{
xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
xmlNode *root;
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
root = xmlDocGetRootElement(docp);
if (!root) {
ZVAL_NULL(retval);
return SUCCESS;
}
php_dom_create_object(root, retval, obj);
return SUCCESS;
}
/* }}} */
/* {{{ encoding string
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-encoding
Since: DOM Level 3
*/
int dom_document_encoding_read(dom_object *obj, zval *retval)
{
xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
char *encoding;
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
encoding = (char *) docp->encoding;
if (encoding != NULL) {
ZVAL_STRING(retval, encoding);
} else {
ZVAL_NULL(retval);
}
return SUCCESS;
}
zend_result dom_document_encoding_write(dom_object *obj, zval *newval)
{
xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
xmlCharEncodingHandlerPtr handler;
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
/* Typed property, can only be IS_STRING or IS_NULL. */
ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING || Z_TYPE_P(newval) == IS_NULL);
if (Z_TYPE_P(newval) == IS_NULL) {
goto invalid_encoding;
}
zend_string *str = Z_STR_P(newval);
handler = xmlFindCharEncodingHandler(ZSTR_VAL(str));
if (handler != NULL) {
xmlCharEncCloseFunc(handler);
if (docp->encoding != NULL) {
xmlFree((xmlChar *)docp->encoding);
}
docp->encoding = xmlStrdup((const xmlChar *) ZSTR_VAL(str));
} else {
goto invalid_encoding;
}
return SUCCESS;
invalid_encoding:
zend_value_error("Invalid document encoding");
return FAILURE;
}
/* }}} */
/* {{{ standalone boolean
readonly=no
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-standalone
Since: DOM Level 3
*/
int dom_document_standalone_read(dom_object *obj, zval *retval)
{
xmlDoc *docp;
docp = (xmlDocPtr) dom_object_get_node(obj);
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
ZVAL_BOOL(retval, docp->standalone > 0);
return SUCCESS;
}
int dom_document_standalone_write(dom_object *obj, zval *newval)
{
xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
zend_long standalone;
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
standalone = zval_get_long(newval);
docp->standalone = ZEND_NORMALIZE_BOOL(standalone);
return SUCCESS;
}
/* }}} */
/* {{{ version string
readonly=no
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-version
Since: DOM Level 3
*/
int dom_document_version_read(dom_object *obj, zval *retval)
{
xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
char *version;
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
version = (char *) docp->version;
if (version != NULL) {
ZVAL_STRING(retval, version);
} else {
ZVAL_NULL(retval);
}
return SUCCESS;
}
int dom_document_version_write(dom_object *obj, zval *newval)
{
xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
zend_string *str;
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
str = zval_try_get_string(newval);
if (UNEXPECTED(!str)) {
return FAILURE;
}
if (docp->version != NULL) {
xmlFree((xmlChar *) docp->version );
}
docp->version = xmlStrdup((const xmlChar *) ZSTR_VAL(str));
zend_string_release_ex(str, 0);
return SUCCESS;
}
/* }}} */
/* {{{ strictErrorChecking boolean
readonly=no
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-strictErrorChecking
Since: DOM Level 3
*/
int dom_document_strict_error_checking_read(dom_object *obj, zval *retval)
{
libxml_doc_props const* doc_prop = dom_get_doc_props_read_only(obj->document);
ZVAL_BOOL(retval, doc_prop->stricterror);
return SUCCESS;
}
int dom_document_strict_error_checking_write(dom_object *obj, zval *newval)
{
if (obj->document) {
dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
doc_prop->stricterror = zend_is_true(newval);
}
return SUCCESS;
}
/* }}} */
/* {{{ formatOutput boolean
readonly=no
*/
int dom_document_format_output_read(dom_object *obj, zval *retval)
{
libxml_doc_props const* doc_prop = dom_get_doc_props_read_only(obj->document);
ZVAL_BOOL(retval, doc_prop->formatoutput);
return SUCCESS;
}
int dom_document_format_output_write(dom_object *obj, zval *newval)
{
if (obj->document) {
dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
doc_prop->formatoutput = zend_is_true(newval);
}
return SUCCESS;
}
/* }}} */
/* {{{ validateOnParse boolean
readonly=no
*/
int dom_document_validate_on_parse_read(dom_object *obj, zval *retval)
{
libxml_doc_props const* doc_prop = dom_get_doc_props_read_only(obj->document);
ZVAL_BOOL(retval, doc_prop->validateonparse);
return SUCCESS;
}
int dom_document_validate_on_parse_write(dom_object *obj, zval *newval)
{
if (obj->document) {
dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
doc_prop->validateonparse = zend_is_true(newval);
}
return SUCCESS;
}
/* }}} */
/* {{{ resolveExternals boolean
readonly=no
*/
int dom_document_resolve_externals_read(dom_object *obj, zval *retval)
{
libxml_doc_props const* doc_prop = dom_get_doc_props_read_only(obj->document);
ZVAL_BOOL(retval, doc_prop->resolveexternals);
return SUCCESS;
}
int dom_document_resolve_externals_write(dom_object *obj, zval *newval)
{
if (obj->document) {
dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
doc_prop->resolveexternals = zend_is_true(newval);
}
return SUCCESS;
}
/* }}} */
/* {{{ preserveWhiteSpace boolean
readonly=no
*/
int dom_document_preserve_whitespace_read(dom_object *obj, zval *retval)
{
libxml_doc_props const* doc_prop = dom_get_doc_props_read_only(obj->document);
ZVAL_BOOL(retval, doc_prop->preservewhitespace);
return SUCCESS;
}
int dom_document_preserve_whitespace_write(dom_object *obj, zval *newval)
{
if (obj->document) {
dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
doc_prop->preservewhitespace = zend_is_true(newval);
}
return SUCCESS;
}
/* }}} */
/* {{{ recover boolean
readonly=no
*/
int dom_document_recover_read(dom_object *obj, zval *retval)
{
libxml_doc_props const* doc_prop = dom_get_doc_props_read_only(obj->document);
ZVAL_BOOL(retval, doc_prop->recover);
return SUCCESS;
}
int dom_document_recover_write(dom_object *obj, zval *newval)
{
if (obj->document) {
dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
doc_prop->recover = zend_is_true(newval);
}
return SUCCESS;
}
/* }}} */
/* {{{ substituteEntities boolean
readonly=no
*/
int dom_document_substitue_entities_read(dom_object *obj, zval *retval)
{
libxml_doc_props const* doc_prop = dom_get_doc_props_read_only(obj->document);
ZVAL_BOOL(retval, doc_prop->substituteentities);
return SUCCESS;
}
int dom_document_substitue_entities_write(dom_object *obj, zval *newval)
{
if (obj->document) {
dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
doc_prop->substituteentities = zend_is_true(newval);
}
return SUCCESS;
}
/* }}} */
/* {{{ documentURI string
readonly=no
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-documentURI
Since: DOM Level 3
*/
int dom_document_document_uri_read(dom_object *obj, zval *retval)
{
xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
char *url;
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
url = (char *) docp->URL;
if (url != NULL) {
ZVAL_STRING(retval, url);
} else {
ZVAL_NULL(retval);
}
return SUCCESS;
}
int dom_document_document_uri_write(dom_object *obj, zval *newval)
{
xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
zend_string *str;
if (docp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
str = zval_try_get_string(newval);
if (UNEXPECTED(!str)) {
return FAILURE;
}
if (docp->URL != NULL) {
xmlFree((xmlChar *) docp->URL);
}
docp->URL = xmlStrdup((const xmlChar *) ZSTR_VAL(str));
zend_string_release_ex(str, 0);
return SUCCESS;
}
/* }}} */
/* {{{ config DOMConfiguration
readonly=yes
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-config
Since: DOM Level 3
*/
int dom_document_config_read(dom_object *obj, zval *retval)
{
ZVAL_NULL(retval);
return SUCCESS;
}
/* }}} */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-2141741547
Since:
*/
PHP_METHOD(DOMDocument, createElement)
{
zval *id;
xmlNode *node;
xmlDocPtr docp;
dom_object *intern;
int ret;
size_t name_len, value_len;
char *name, *value = NULL;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &name, &name_len, &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
if (xmlValidateName((xmlChar *) name, 0) != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
node = xmlNewDocNode(docp, NULL, (xmlChar *) name, (xmlChar *) value);
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
RETURN_THROWS();
}
DOM_RET_OBJ(node, &ret, intern);
}
/* }}} end dom_document_create_element */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-35CB04B5
Since:
*/
PHP_METHOD(DOMDocument, createDocumentFragment)
{
zval *id;
xmlNode *node;
xmlDocPtr docp;
dom_object *intern;
int ret;
id = ZEND_THIS;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
node = xmlNewDocFragment(docp);
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
RETURN_THROWS();
}
DOM_RET_OBJ(node, &ret, intern);
}
/* }}} end dom_document_create_document_fragment */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1975348127
Since:
*/
PHP_METHOD(DOMDocument, createTextNode)
{
zval *id;
xmlNode *node;
xmlDocPtr docp;
int ret;
size_t value_len;
dom_object *intern;
char *value;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
node = xmlNewDocText(docp, (xmlChar *) value);
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
RETURN_THROWS();
}
DOM_RET_OBJ(node, &ret, intern);
}
/* }}} end dom_document_create_text_node */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1334481328
Since:
*/
PHP_METHOD(DOMDocument, createComment)
{
zval *id;
xmlNode *node;
xmlDocPtr docp;
int ret;
size_t value_len;
dom_object *intern;
char *value;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
node = xmlNewDocComment(docp, (xmlChar *) value);
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
RETURN_THROWS();
}
DOM_RET_OBJ(node, &ret, intern);
}
/* }}} end dom_document_create_comment */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D26C0AF8
Since:
*/
PHP_METHOD(DOMDocument, createCDATASection)
{
zval *id;
xmlNode *node;
xmlDocPtr docp;
int ret;
size_t value_len;
dom_object *intern;
char *value;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
node = xmlNewCDataBlock(docp, (xmlChar *) value, value_len);
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
RETURN_THROWS();
}
DOM_RET_OBJ(node, &ret, intern);
}
/* }}} end dom_document_create_cdatasection */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-135944439
Since:
*/
PHP_METHOD(DOMDocument, createProcessingInstruction)
{
zval *id;
xmlNode *node;
xmlDocPtr docp;
int ret;
size_t value_len, name_len = 0;
dom_object *intern;
char *name, *value = NULL;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &name, &name_len, &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
if (xmlValidateName((xmlChar *) name, 0) != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
node = xmlNewPI((xmlChar *) name, (xmlChar *) value);
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
RETURN_THROWS();
}
node->doc = docp;
DOM_RET_OBJ(node, &ret, intern);
}
/* }}} end dom_document_create_processing_instruction */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1084891198
Since:
*/
PHP_METHOD(DOMDocument, createAttribute)
{
zval *id;
xmlAttrPtr node;
xmlDocPtr docp;
int ret;
size_t name_len;
dom_object *intern;
char *name;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
if (xmlValidateName((xmlChar *) name, 0) != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
node = xmlNewDocProp(docp, (xmlChar *) name, NULL);
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
RETURN_THROWS();
}
DOM_RET_OBJ((xmlNodePtr) node, &ret, intern);
}
/* }}} end dom_document_create_attribute */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-392B75AE
Since:
*/
PHP_METHOD(DOMDocument, createEntityReference)
{
zval *id;
xmlNode *node;
xmlDocPtr docp = NULL;
dom_object *intern;
int ret;
size_t name_len;
char *name;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
if (xmlValidateName((xmlChar *) name, 0) != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
node = xmlNewReference(docp, (xmlChar *) name);
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
RETURN_THROWS();
}
DOM_RET_OBJ((xmlNodePtr) node, &ret, intern);
}
/* }}} end dom_document_create_entity_reference */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C9094
Since:
*/
PHP_METHOD(DOMDocument, getElementsByTagName)
{
size_t name_len;
dom_object *intern, *namednode;
char *name;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
php_dom_create_iterator(return_value, DOM_NODELIST);
namednode = Z_DOMOBJ_P(return_value);
dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, NULL, 0);
}
/* }}} end dom_document_get_elements_by_tag_name */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Core-Document-importNode
Since: DOM Level 2
*/
PHP_METHOD(DOMDocument, importNode)
{
zval *node;
xmlDocPtr docp;
xmlNodePtr nodep, retnodep;
dom_object *intern, *nodeobj;
int ret;
bool recursive = 0;
/* See http://www.xmlsoft.org/html/libxml-tree.html#xmlDocCopyNode for meaning of values */
int extended_recursive;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &node, dom_node_class_entry, &recursive) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
DOM_GET_OBJ(nodep, node, xmlNodePtr, nodeobj);
if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE
|| nodep->type == XML_DOCUMENT_TYPE_NODE) {
php_error_docref(NULL, E_WARNING, "Cannot import: Node Type Not Supported");
RETURN_FALSE;
}
if (nodep->doc == docp) {
retnodep = nodep;
} else {
extended_recursive = recursive;
if ((recursive == 0) && (nodep->type == XML_ELEMENT_NODE)) {
extended_recursive = 2;
}
retnodep = xmlDocCopyNode(nodep, docp, extended_recursive);
if (!retnodep) {
RETURN_FALSE;
}
if ((retnodep->type == XML_ATTRIBUTE_NODE) && (nodep->ns != NULL)) {
xmlNsPtr nsptr = NULL;
xmlNodePtr root = xmlDocGetRootElement(docp);
nsptr = xmlSearchNsByHref (docp, root, nodep->ns->href);
if (nsptr == NULL || nsptr->prefix == NULL) {
int errorcode;
nsptr = dom_get_ns(root, (char *) nodep->ns->href, &errorcode, (char *) nodep->ns->prefix);
/* If there is no root, the namespace cannot be attached to it, so we have to attach it to the old list. */
if (nsptr != NULL && root == NULL) {
php_libxml_set_old_ns(docp, nsptr);
}
}
retnodep->ns = nsptr;
}
}
php_libxml_invalidate_node_list_cache(intern->document);
DOM_RET_OBJ((xmlNodePtr) retnodep, &ret, intern);
}
/* }}} end dom_document_import_node */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS
Since: DOM Level 2
*/
PHP_METHOD(DOMDocument, createElementNS)
{
zval *id;
xmlDocPtr docp;
xmlNodePtr nodep = NULL;
int ret;
size_t uri_len = 0, name_len = 0, value_len = 0;
char *uri, *name, *value = NULL;
char *localname = NULL, *prefix = NULL;
int errorcode;
dom_object *intern;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s|s", &uri, &uri_len, &name, &name_len, &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
if (errorcode == 0) {
if (xmlValidateName((xmlChar *) localname, 0) == 0) {
nodep = xmlNewDocNode(docp, NULL, (xmlChar *) localname, (xmlChar *) value);
if (nodep != NULL && uri != NULL) {
xmlNsPtr nsptr = xmlSearchNsByHref(nodep->doc, nodep, (xmlChar *) uri);
if (nsptr == NULL) {
nsptr = dom_get_ns(nodep, uri, &errorcode, prefix);
}
xmlSetNs(nodep, nsptr);
}
} else {
errorcode = INVALID_CHARACTER_ERR;
}
}
xmlFree(localname);
if (prefix != NULL) {
xmlFree(prefix);
}
if (errorcode != 0) {
if (nodep != NULL) {
xmlFreeNode(nodep);
}
php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
if (nodep == NULL) {
RETURN_FALSE;
}
DOM_RET_OBJ(nodep, &ret, intern);
}
/* }}} end dom_document_create_element_ns */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNS
Since: DOM Level 2
*/
PHP_METHOD(DOMDocument, createAttributeNS)
{
zval *id;
xmlDocPtr docp;
xmlNodePtr nodep = NULL, root;
xmlNsPtr nsptr;
int ret;
zend_string *name, *uri;
char *localname = NULL, *prefix = NULL;
dom_object *intern;
int errorcode;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!S", &uri, &name) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
if (UNEXPECTED(uri == NULL)) {
uri = zend_empty_string;
}
size_t uri_len = ZSTR_LEN(uri);
root = xmlDocGetRootElement(docp);
if (root != NULL) {
errorcode = dom_check_qname(ZSTR_VAL(name), &localname, &prefix, uri_len, ZSTR_LEN(name));
/* TODO: switch to early goto-out style error-checking */
if (errorcode == 0) {
if (xmlValidateName((xmlChar *) localname, 0) == 0) {
/* If prefix is "xml" and namespace is not the XML namespace, then throw a "NamespaceError" DOMException. */
if (UNEXPECTED(!zend_string_equals_literal(uri, "http://www.w3.org/XML/1998/namespace") && xmlStrEqual(BAD_CAST prefix, BAD_CAST "xml"))) {
errorcode = NAMESPACE_ERR;
goto error;
}
/* If either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace, then throw a "NamespaceError" DOMException. */
if (UNEXPECTED((zend_string_equals_literal(name, "xmlns") || xmlStrEqual(BAD_CAST prefix, BAD_CAST "xmlns")) && !zend_string_equals_literal(uri, "http://www.w3.org/2000/xmlns/"))) {
errorcode = NAMESPACE_ERR;
goto error;
}
/* If namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns", then throw a "NamespaceError" DOMException. */
if (UNEXPECTED(zend_string_equals_literal(uri, "http://www.w3.org/2000/xmlns/") && !zend_string_equals_literal(name, "xmlns") && !xmlStrEqual(BAD_CAST prefix, BAD_CAST "xmlns"))) {
errorcode = NAMESPACE_ERR;
goto error;
}
nodep = (xmlNodePtr) xmlNewDocProp(docp, (xmlChar *) localname, NULL);
if (nodep != NULL && uri_len > 0) {
nsptr = xmlSearchNsByHref(docp, root, BAD_CAST ZSTR_VAL(uri));
if (zend_string_equals_literal(name, "xmlns") || xmlStrEqual(BAD_CAST prefix, BAD_CAST "xml")) {
if (nsptr == NULL) {
nsptr = xmlNewNs(NULL, BAD_CAST ZSTR_VAL(uri), BAD_CAST prefix);
php_libxml_set_old_ns(docp, nsptr);
}
} else {
if (nsptr == NULL || nsptr->prefix == NULL) {
nsptr = dom_get_ns_unchecked(root, ZSTR_VAL(uri), prefix ? prefix : "default");
if (UNEXPECTED(nsptr == NULL)) {
errorcode = NAMESPACE_ERR;
}
}
}
xmlSetNs(nodep, nsptr);
}
} else {
errorcode = INVALID_CHARACTER_ERR;
}
}
} else {
php_error_docref(NULL, E_WARNING, "Document Missing Root Element");
RETURN_FALSE;
}
error:
xmlFree(localname);
if (prefix != NULL) {
xmlFree(prefix);
}
if (errorcode != 0) {
if (nodep != NULL) {
xmlFreeProp((xmlAttrPtr) nodep);
}
php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
if (nodep == NULL) {
RETURN_FALSE;
}
DOM_RET_OBJ(nodep, &ret, intern);
}
/* }}} end dom_document_create_attribute_ns */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBTNNS
Since: DOM Level 2
*/
PHP_METHOD(DOMDocument, getElementsByTagNameNS)
{
size_t uri_len, name_len;
dom_object *intern, *namednode;
char *uri, *name;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
php_dom_create_iterator(return_value, DOM_NODELIST);
namednode = Z_DOMOBJ_P(return_value);
dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, uri ? uri : "", uri_len);
}
/* }}} end dom_document_get_elements_by_tag_name_ns */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBId
Since: DOM Level 2
*/
PHP_METHOD(DOMDocument, getElementById)
{
zval *id;
xmlDocPtr docp;
xmlAttrPtr attrp;
int ret;
size_t idname_len;
dom_object *intern;
char *idname;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &idname, &idname_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
attrp = xmlGetID(docp, (xmlChar *) idname);
/* From the moment an ID is created, libxml2's behaviour is to cache that element, even
* if that element is not yet attached to the document. Similarly, only upon destruction of
* the element the ID is actually removed by libxml2. Since libxml2 has such behaviour deeply
* ingrained in the library, and uses the cache for various purposes, it seems like a bad
* idea and lost cause to fight it. Instead, we'll simply walk the tree upwards to check
* if the node is attached to the document. */
if (attrp && attrp->parent && php_dom_is_node_connected(attrp->parent)) {
DOM_RET_OBJ((xmlNodePtr) attrp->parent, &ret, intern);
} else {
RETVAL_NULL();
}
}
/* }}} end dom_document_get_element_by_id */
static zend_always_inline void php_dom_transfer_document_ref_single_node(xmlNodePtr node, php_libxml_ref_obj *new_document)
{
php_libxml_node_ptr *iteration_object_ptr = node->_private;
if (iteration_object_ptr) {
php_libxml_node_object *iteration_object = iteration_object_ptr->_private;
ZEND_ASSERT(iteration_object != NULL);
/* Must increase refcount first because we could be the last reference holder, and the document may be equal. */
new_document->refcount++;
php_libxml_decrement_doc_ref(iteration_object);
iteration_object->document = new_document;
}
}
static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *new_document)
{
if (node->children) {
php_dom_transfer_document_ref(node->children, new_document);
}
while (node) {
if (node->type == XML_ELEMENT_NODE) {
for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
php_dom_transfer_document_ref_single_node((xmlNodePtr) attr, new_document);
}
}
php_dom_transfer_document_ref_single_node(node, new_document);
node = node->next;
}
}
/* Workaround for bug that was fixed in https://github.com/GNOME/libxml2/commit/4bc3ebf3eaba352fbbce2ef70ad00a3c7752478a
* and https://github.com/GNOME/libxml2/commit/bc7ab5a2e61e4b36accf6803c5b0e245c11154b1 */
#if LIBXML_VERSION < 21300
static xmlChar *libxml_copy_dicted_string(xmlDictPtr src_dict, xmlDictPtr dst_dict, xmlChar *str)
{
if (str == NULL) {
return NULL;
}
if (xmlDictOwns(src_dict, str) == 1) {
if (dst_dict == NULL) {
return xmlStrdup(str);
}
return BAD_CAST xmlDictLookup(dst_dict, str, -1);
}
return str;
}
static void libxml_fixup_name_and_content(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
{
if (node->type == XML_ENTITY_REF_NODE) {
node->children = NULL; /* Break link with original document. */
}
if (src_doc != NULL && src_doc->dict != NULL) {
ZEND_ASSERT(dst_doc != src_doc);
node->name = libxml_copy_dicted_string(src_doc->dict, dst_doc->dict, BAD_CAST node->name);
node->content = libxml_copy_dicted_string(src_doc->dict, NULL, node->content);
}
}
static void libxml_fixup_name_and_content_outer(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
{
libxml_fixup_name_and_content(src_doc, dst_doc, node);
if (node->type == XML_ELEMENT_NODE) {
for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
libxml_fixup_name_and_content(src_doc, dst_doc, (xmlNodePtr) attr);
for (xmlNodePtr attr_child = attr->children; attr_child != NULL; attr_child = attr_child->next) {
libxml_fixup_name_and_content(src_doc, dst_doc, attr_child);
}
}
}
if (node->type == XML_ELEMENT_NODE || node->type == XML_ATTRIBUTE_NODE) {
for (xmlNodePtr child = node->children; child != NULL; child = child->next) {
libxml_fixup_name_and_content_outer(src_doc, dst_doc, child);
}
}
}
#endif
bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document)
{
xmlDocPtr old_doc = nodep->doc;
php_libxml_invalidate_node_list_cache_from_doc(old_doc);
if (old_doc != new_document) {
php_libxml_invalidate_node_list_cache(dom_object_new_document->document);
/* Note for ATTRIBUTE_NODE: specified is always true in ext/dom,
* and since this unlink it; the owner element will be unset (i.e. parentNode). */
int ret = xmlDOMWrapAdoptNode(NULL, nodep->doc, nodep, new_document, NULL, /* options, unused */ 0);
if (UNEXPECTED(ret != 0)) {
return false;
}
#if LIBXML_VERSION < 21300
/* Must be first before transferring the ref to ensure the old document dictionary stays alive. */
libxml_fixup_name_and_content_outer(old_doc, new_document, nodep);
#endif
php_dom_transfer_document_ref(nodep, dom_object_new_document->document);
} else {
xmlUnlinkNode(nodep);
}
return true;
}
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNode
Since: DOM Level 3
Modern spec URL: https://dom.spec.whatwg.org/#dom-document-adoptnode
*/
PHP_METHOD(DOMDocument, adoptNode)
{
zval *node_zval;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, dom_node_class_entry) == FAILURE) {
RETURN_THROWS();
}
xmlNodePtr nodep;
dom_object *dom_object_nodep;
DOM_GET_OBJ(nodep, node_zval, xmlNodePtr, dom_object_nodep);
if (UNEXPECTED(nodep->type == XML_DOCUMENT_NODE
|| nodep->type == XML_HTML_DOCUMENT_NODE
|| nodep->type == XML_DOCUMENT_TYPE_NODE
|| nodep->type == XML_DTD_NODE
|| nodep->type == XML_ENTITY_NODE
|| nodep->type == XML_NOTATION_NODE)) {
php_dom_throw_error(NOT_SUPPORTED_ERR, dom_get_strict_error(dom_object_nodep->document));
RETURN_FALSE;
}
xmlDocPtr new_document;
dom_object *dom_object_new_document;
zval *new_document_zval = ZEND_THIS;
DOM_GET_OBJ(new_document, new_document_zval, xmlDocPtr, dom_object_new_document);
if (!php_dom_adopt_node(nodep, dom_object_new_document, new_document)) {
RETURN_FALSE;
}
RETURN_OBJ_COPY(&dom_object_nodep->std);
}
/* }}} end dom_document_adopt_node */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocument
Since: DOM Level 3
*/
PHP_METHOD(DOMDocument, normalizeDocument)
{
zval *id;
xmlDocPtr docp;
dom_object *intern;
id = ZEND_THIS;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
php_libxml_invalidate_node_list_cache(intern->document);
dom_normalize((xmlNodePtr) docp);
}
/* }}} end dom_document_normalize_document */
/* {{{ */
PHP_METHOD(DOMDocument, __construct)
{
xmlDoc *docp = NULL, *olddoc;
dom_object *intern;
char *encoding, *version = NULL;
size_t encoding_len = 0, version_len = 0;
int refcount;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &version, &version_len, &encoding, &encoding_len) == FAILURE) {
RETURN_THROWS();
}
docp = xmlNewDoc((xmlChar *) version);
if (!docp) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return;
}
if (encoding_len > 0) {
docp->encoding = (const xmlChar *) xmlStrdup((xmlChar *) encoding);
}
intern = Z_DOMOBJ_P(ZEND_THIS);
olddoc = (xmlDocPtr) dom_object_get_node(intern);
if (olddoc != NULL) {
php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
if (refcount != 0) {
olddoc->_private = NULL;
}
}
intern->document = NULL;
if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp) == -1) {
/* docp is always non-null so php_libxml_increment_doc_ref() never returns -1 */
ZEND_UNREACHABLE();
}
php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)docp, (void *)intern);
}
/* }}} end DOMDocument::__construct */
char *_dom_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len ) /* {{{ */
{
xmlURI *uri;
xmlChar *escsource;
char *file_dest;
int isFileUri = 0;
uri = xmlCreateURI();
if (uri == NULL) {
return NULL;
}
escsource = xmlURIEscapeStr((xmlChar *) source, (xmlChar *) ":");
xmlParseURIReference(uri, (char *) escsource);
xmlFree(escsource);
if (uri->scheme != NULL) {
/* absolute file uris - libxml only supports localhost or empty host */
#ifdef PHP_WIN32
if (strncasecmp(source, "file://",7) == 0 && ':' == source[8]) {
isFileUri = 1;
source += 7;
} else
#endif
if (strncasecmp(source, "file:///",8) == 0) {
isFileUri = 1;
#ifdef PHP_WIN32
source += 8;
#else
source += 7;
#endif
} else if (strncasecmp(source, "file://localhost/",17) == 0) {
isFileUri = 1;
#ifdef PHP_WIN32
source += 17;
#else
source += 16;
#endif
}
}
file_dest = source;
if ((uri->scheme == NULL || isFileUri)) {
/* XXX possible buffer overflow if VCWD_REALPATH does not know size of resolved_path */
if (!VCWD_REALPATH(source, resolved_path) && !expand_filepath(source, resolved_path)) {
xmlFreeURI(uri);
return NULL;
}
file_dest = resolved_path;
}
xmlFreeURI(uri);
return file_dest;
}
/* }}} */
static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t source_len, size_t options) /* {{{ */
{
xmlDocPtr ret;
xmlParserCtxtPtr ctxt = NULL;
int validate, recover, resolve_externals, keep_blanks, substitute_ent;
int resolved_path_len;
int old_error_reporting = 0;
char *directory=NULL, resolved_path[MAXPATHLEN + 1];
dom_object *intern = Z_DOMOBJ_P(id);
php_libxml_ref_obj *document = intern->document;
libxml_doc_props const* doc_props = dom_get_doc_props_read_only(document);
validate = doc_props->validateonparse;
resolve_externals = doc_props->resolveexternals;
keep_blanks = doc_props->preservewhitespace;
substitute_ent = doc_props->substituteentities;
recover = doc_props->recover;
xmlInitParser();
if (mode == DOM_LOAD_FILE) {
char *file_dest;
if (CHECK_NULL_PATH(source, source_len)) {
zend_value_error("Path to document must not contain any null bytes");
return NULL;
}
file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
if (file_dest) {
ctxt = xmlCreateFileParserCtxt(file_dest);
}
} else {
ctxt = xmlCreateMemoryParserCtxt(source, source_len);
}
if (ctxt == NULL) {
return(NULL);
}
/* If loading from memory, we need to set the base directory for the document */
if (mode != DOM_LOAD_FILE) {
#ifdef HAVE_GETCWD
directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
#elif defined(HAVE_GETWD)
directory = VCWD_GETWD(resolved_path);
#endif
if (directory) {
if(ctxt->directory != NULL) {
xmlFree((char *) ctxt->directory);
}
resolved_path_len = strlen(resolved_path);
if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) {
resolved_path[resolved_path_len] = DEFAULT_SLASH;
resolved_path[++resolved_path_len] = '\0';
}
ctxt->directory = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
}
}
ctxt->vctxt.error = php_libxml_ctx_error;
ctxt->vctxt.warning = php_libxml_ctx_warning;
if (ctxt->sax != NULL) {
ctxt->sax->error = php_libxml_ctx_error;
ctxt->sax->warning = php_libxml_ctx_warning;
}
if (validate && ! (options & XML_PARSE_DTDVALID)) {
options |= XML_PARSE_DTDVALID;
}
if (resolve_externals && ! (options & XML_PARSE_DTDATTR)) {
options |= XML_PARSE_DTDATTR;
}
if (substitute_ent && ! (options & XML_PARSE_NOENT)) {
options |= XML_PARSE_NOENT;
}
if (keep_blanks == 0 && ! (options & XML_PARSE_NOBLANKS)) {
options |= XML_PARSE_NOBLANKS;
}
if (recover) {
options |= XML_PARSE_RECOVER;
}
php_libxml_sanitize_parse_ctxt_options(ctxt);
xmlCtxtUseOptions(ctxt, options);
if (recover) {
old_error_reporting = EG(error_reporting);
EG(error_reporting) = old_error_reporting | E_WARNING;
}
xmlParseDocument(ctxt);
if (ctxt->wellFormed || recover) {
ret = ctxt->myDoc;
if (recover) {
EG(error_reporting) = old_error_reporting;
}
/* If loading from memory, set the base reference uri for the document */
if (ret && ret->URL == NULL && ctxt->directory != NULL) {
ret->URL = xmlStrdup((xmlChar *) ctxt->directory);
}
} else {
ret = NULL;
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = NULL;
}
xmlFreeParserCtxt(ctxt);
return(ret);
}
/* }}} */
static void dom_finish_loading_document(zval *this, zval *return_value, xmlDocPtr newdoc)
{
if (!newdoc)
RETURN_FALSE;
dom_object *intern = Z_DOMOBJ_P(this);
size_t old_modification_nr = 0;
if (intern != NULL) {
xmlDocPtr docp = (xmlDocPtr) dom_object_get_node(intern);
dom_doc_propsptr doc_prop = NULL;
if (docp != NULL) {
const php_libxml_ref_obj *doc_ptr = intern->document;
ZEND_ASSERT(doc_ptr != NULL); /* Must exist, we have a document */
old_modification_nr = doc_ptr->cache_tag.modification_nr;
php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
doc_prop = intern->document->doc_props;
intern->document->doc_props = NULL;
int refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
if (refcount != 0) {
docp->_private = NULL;
}
}
intern->document = NULL;
if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc) == -1) {
RETURN_FALSE;
}
intern->document->doc_props = doc_prop;
}
php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
/* Since iterators should invalidate, we need to start the modification number from the old counter */
if (old_modification_nr != 0) {
intern->document->cache_tag.modification_nr = old_modification_nr;
php_libxml_invalidate_node_list_cache(intern->document);
}
RETURN_TRUE;
}
/* {{{ static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) */
static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) {
char *source;
size_t source_len;
zend_long options = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
RETURN_THROWS();
}
if (!source_len) {
zend_argument_value_error(1, "must not be empty");
RETURN_THROWS();
}
if (ZEND_SIZE_T_INT_OVFL(source_len)) {
php_error_docref(NULL, E_WARNING, "Input string is too long");
RETURN_FALSE;
}
if (ZEND_LONG_EXCEEDS_INT(options)) {
php_error_docref(NULL, E_WARNING, "Invalid options");
RETURN_FALSE;
}
xmlDocPtr newdoc = dom_document_parser(ZEND_THIS, mode, source, source_len, options);
dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
}
/* }}} end dom_parser_document */
/* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-load
Since: DOM Level 3
*/
PHP_METHOD(DOMDocument, load)
{
dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
}
/* }}} end dom_document_load */
/* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadXML
Since: DOM Level 3
*/
PHP_METHOD(DOMDocument, loadXML)
{
dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
}
/* }}} end dom_document_loadxml */
/* {{{ Convenience method to save to file */
PHP_METHOD(DOMDocument, save)
{
zval *id;
xmlDoc *docp;
size_t file_len = 0;
int bytes, format, saveempty = 0;
dom_object *intern;
char *file;
zend_long options = 0;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &file, &file_len, &options) == FAILURE) {
RETURN_THROWS();
}
if (file_len == 0) {
zend_argument_value_error(1, "must not be empty");
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
/* encoding handled by property on doc */
libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
format = doc_props->formatoutput;
if (options & LIBXML_SAVE_NOEMPTYTAG) {
saveempty = xmlSaveNoEmptyTags;
xmlSaveNoEmptyTags = 1;
}
bytes = xmlSaveFormatFileEnc(file, docp, NULL, format);
if (options & LIBXML_SAVE_NOEMPTYTAG) {
xmlSaveNoEmptyTags = saveempty;
}
if (bytes == -1) {
RETURN_FALSE;
}
RETURN_LONG(bytes);
}
/* }}} end dom_document_save */
/* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML
Since: DOM Level 3
*/
PHP_METHOD(DOMDocument, saveXML)
{
zval *id, *nodep = NULL;
xmlDoc *docp;
xmlNode *node;
xmlBufferPtr buf;
const xmlChar *mem;
dom_object *intern, *nodeobj;
int size, format, old_xml_save_no_empty_tags;
zend_long options = 0;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!l", &nodep, dom_node_class_entry, &options) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
format = doc_props->formatoutput;
if (nodep != NULL) {
/* Dump contents of Node */
DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
if (node->doc != docp) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
buf = xmlBufferCreate();
if (!buf) {
php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
RETURN_FALSE;
}
/* Save libxml2 global, override its vaule, and restore after saving. */
old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
xmlNodeDump(buf, docp, node, 0, format);
xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
} else {
buf = xmlBufferCreate();
if (!buf) {
php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
RETURN_FALSE;
}
int converted_options = XML_SAVE_AS_XML;
if (options & XML_SAVE_NO_DECL) {
converted_options |= XML_SAVE_NO_DECL;
}
if (format) {
converted_options |= XML_SAVE_FORMAT;
}
/* Save libxml2 global, override its vaule, and restore after saving. */
old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
/* Encoding is handled from the encoding property set on the document */
xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(buf, (const char *) docp->encoding, converted_options);
xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
if (UNEXPECTED(!ctxt)) {
xmlBufferFree(buf);
php_error_docref(NULL, E_WARNING, "Could not create save context");
RETURN_FALSE;
}
if (UNEXPECTED(xmlSaveDoc(ctxt, docp) < 0)) {
(void) xmlSaveClose(ctxt);
xmlBufferFree(buf);
php_error_docref(NULL, E_WARNING, "Could not save document");
RETURN_FALSE;
}
(void) xmlSaveFlush(ctxt);
(void) xmlSaveClose(ctxt);
}
mem = xmlBufferContent(buf);
if (!mem) {
xmlBufferFree(buf);
RETURN_FALSE;
}
size = xmlBufferLength(buf);
RETVAL_STRINGL((const char *) mem, size);
xmlBufferFree(buf);
}
/* }}} end dom_document_savexml */
/* Backported from master branch xml_common.h */
static zend_always_inline xmlNodePtr php_dom_next_in_tree_order(const xmlNode *nodep, const xmlNode *basep)
{
if (nodep->type == XML_ELEMENT_NODE && nodep->children) {
return nodep->children;
}
if (nodep->next) {
return nodep->next;
} else {
/* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
do {
nodep = nodep->parent;
if (nodep == basep) {
return NULL;
}
} while (nodep->next == NULL);
return nodep->next;
}
}
static void dom_xinclude_strip_references_for_attributes(xmlNodePtr basep)
{
for (xmlAttrPtr prop = basep->properties; prop; prop = prop->next) {
php_libxml_node_free_resource((xmlNodePtr) prop);
for (xmlNodePtr child = prop->children; child; child = child->next) {
php_libxml_node_free_resource(child);
}
}
}
static void dom_xinclude_strip_references(xmlNodePtr basep)
{
php_libxml_node_free_resource(basep);
dom_xinclude_strip_references_for_attributes(basep);
xmlNodePtr current = basep->children;
while (current) {
php_libxml_node_free_resource(current);
if (current->type == XML_ELEMENT_NODE) {
dom_xinclude_strip_references_for_attributes(current);
}
current = php_dom_next_in_tree_order(current, basep);
}
}
/* See GH-14702 and GH-17847.
* We have to remove userland references to xinclude nodes because libxml2 will make clones of these
* and remove the original nodes. If the originals are removed while there are still userland references
* this will cause memory corruption. */
static void dom_xinclude_strip_fallback_references(const xmlNode *basep)
{
xmlNodePtr current = basep->children;
/* TODO: try to improve loop search performance */
while (current) {
if (current->type == XML_ELEMENT_NODE
&& current->ns != NULL
&& xmlStrEqual(current->name, XINCLUDE_NODE)
&& (xmlStrEqual(current->ns->href, XINCLUDE_NS) || xmlStrEqual(current->ns->href, XINCLUDE_OLD_NS))) {
dom_xinclude_strip_references(current);
}
current = php_dom_next_in_tree_order(current, basep);
}
}
/* {{{ Substitutues xincludes in a DomDocument */
PHP_METHOD(DOMDocument, xinclude)
{
zval *id;
xmlDoc *docp;
zend_long flags = 0;
int err;
dom_object *intern;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
RETURN_THROWS();
}
if (ZEND_LONG_EXCEEDS_INT(flags)) {
php_error_docref(NULL, E_WARNING, "Invalid flags");
RETURN_FALSE;
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
dom_xinclude_strip_fallback_references((const xmlNode *) docp);
flags |= XML_PARSE_NOXINCNODE;
PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
err = xmlXIncludeProcessFlags(docp, (int)flags);
PHP_LIBXML_RESTORE_GLOBALS(xinclude);
php_libxml_invalidate_node_list_cache(intern->document);
if (err) {
RETVAL_LONG(err);
} else {
RETVAL_FALSE;
}
}
/* }}} */
/* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument, validate)
{
zval *id;
xmlDoc *docp;
dom_object *intern;
xmlValidCtxt *cvp;
id = ZEND_THIS;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
PHP_LIBXML_SANITIZE_GLOBALS(validate);
cvp = xmlNewValidCtxt();
cvp->userData = NULL;
cvp->error = (xmlValidityErrorFunc) php_libxml_error_handler;
cvp->warning = (xmlValidityErrorFunc) php_libxml_error_handler;
if (xmlValidateDocument(cvp, docp)) {
RETVAL_TRUE;
} else {
RETVAL_FALSE;
}
PHP_LIBXML_RESTORE_GLOBALS(validate);
xmlFreeValidCtxt(cvp);
}
/* }}} */
#ifdef LIBXML_SCHEMAS_ENABLED
static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
{
zval *id;
xmlDoc *docp;
dom_object *intern;
char *source = NULL, *valid_file = NULL;
size_t source_len = 0;
int valid_opts = 0;
zend_long flags = 0;
xmlSchemaParserCtxtPtr parser;
xmlSchemaPtr sptr;
xmlSchemaValidCtxtPtr vptr;
int is_valid;
char resolved_path[MAXPATHLEN + 1];
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &flags) == FAILURE) {
RETURN_THROWS();
}
if (!source_len) {
zend_argument_value_error(1, "must not be empty");
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
PHP_LIBXML_SANITIZE_GLOBALS(new_parser_ctxt);
switch (type) {
case DOM_LOAD_FILE:
if (CHECK_NULL_PATH(source, source_len)) {
PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
zend_argument_value_error(1, "must not contain any null bytes");
RETURN_THROWS();
}
valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
if (!valid_file) {
PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
php_error_docref(NULL, E_WARNING, "Invalid Schema file source");
RETURN_FALSE;
}
parser = xmlSchemaNewParserCtxt(valid_file);
break;
case DOM_LOAD_STRING:
parser = xmlSchemaNewMemParserCtxt(source, source_len);
/* If loading from memory, we need to set the base directory for the document
but it is not apparent how to do that for schema's */
break;
default:
return;
}
xmlSchemaSetParserErrors(parser,
(xmlSchemaValidityErrorFunc) php_libxml_error_handler,
(xmlSchemaValidityWarningFunc) php_libxml_error_handler,
parser);
sptr = xmlSchemaParse(parser);
xmlSchemaFreeParserCtxt(parser);
PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
if (!sptr) {
if (!EG(exception)) {
php_error_docref(NULL, E_WARNING, "Invalid Schema");
}
RETURN_FALSE;
}
docp = (xmlDocPtr) dom_object_get_node(intern);
vptr = xmlSchemaNewValidCtxt(sptr);
if (!vptr) {
xmlSchemaFree(sptr);
zend_throw_error(NULL, "Invalid Schema Validation Context");
RETURN_THROWS();
}
if (flags & XML_SCHEMA_VAL_VC_I_CREATE) {
valid_opts |= XML_SCHEMA_VAL_VC_I_CREATE;
}
PHP_LIBXML_SANITIZE_GLOBALS(validate);
xmlSchemaSetValidOptions(vptr, valid_opts);
xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
is_valid = xmlSchemaValidateDoc(vptr, docp);
xmlSchemaFree(sptr);
xmlSchemaFreeValidCtxt(vptr);
PHP_LIBXML_RESTORE_GLOBALS(validate);
if (is_valid == 0) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
}
/* }}} */
/* {{{ */
PHP_METHOD(DOMDocument, schemaValidate)
{
_dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
}
/* }}} end dom_document_schema_validate_file */
/* {{{ */
PHP_METHOD(DOMDocument, schemaValidateSource)
{
_dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
}
/* }}} end dom_document_schema_validate */
static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
{
zval *id;
xmlDoc *docp;
dom_object *intern;
char *source = NULL, *valid_file = NULL;
size_t source_len = 0;
xmlRelaxNGParserCtxtPtr parser;
xmlRelaxNGPtr sptr;
xmlRelaxNGValidCtxtPtr vptr;
int is_valid;
char resolved_path[MAXPATHLEN + 1];
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &source, &source_len) == FAILURE) {
RETURN_THROWS();
}
if (!source_len) {
zend_argument_value_error(1, "must not be empty");
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
switch (type) {
case DOM_LOAD_FILE:
if (CHECK_NULL_PATH(source, source_len)) {
zend_argument_value_error(1, "must not contain any null bytes");
RETURN_THROWS();
}
valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
if (!valid_file) {
php_error_docref(NULL, E_WARNING, "Invalid RelaxNG file source");
RETURN_FALSE;
}
parser = xmlRelaxNGNewParserCtxt(valid_file);
break;
case DOM_LOAD_STRING:
parser = xmlRelaxNGNewMemParserCtxt(source, source_len);
/* If loading from memory, we need to set the base directory for the document
but it is not apparent how to do that for schema's */
break;
default:
return;
}
PHP_LIBXML_SANITIZE_GLOBALS(parse);
xmlRelaxNGSetParserErrors(parser,
(xmlRelaxNGValidityErrorFunc) php_libxml_error_handler,
(xmlRelaxNGValidityWarningFunc) php_libxml_error_handler,
parser);
sptr = xmlRelaxNGParse(parser);
xmlRelaxNGFreeParserCtxt(parser);
PHP_LIBXML_RESTORE_GLOBALS(parse);
if (!sptr) {
php_error_docref(NULL, E_WARNING, "Invalid RelaxNG");
RETURN_FALSE;
}
docp = (xmlDocPtr) dom_object_get_node(intern);
vptr = xmlRelaxNGNewValidCtxt(sptr);
if (!vptr) {
xmlRelaxNGFree(sptr);
zend_throw_error(NULL, "Invalid RelaxNG Validation Context");
RETURN_THROWS();
}
xmlRelaxNGSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
is_valid = xmlRelaxNGValidateDoc(vptr, docp);
xmlRelaxNGFree(sptr);
xmlRelaxNGFreeValidCtxt(vptr);
if (is_valid == 0) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
}
/* }}} */
/* {{{ */
PHP_METHOD(DOMDocument, relaxNGValidate)
{
_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
}
/* }}} end dom_document_relaxNG_validate_file */
/* {{{ */
PHP_METHOD(DOMDocument, relaxNGValidateSource)
{
_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
}
/* }}} end dom_document_relaxNG_validate_xml */
#endif
#ifdef LIBXML_HTML_ENABLED
static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
{
char *source;
size_t source_len;
zend_long options = 0;
htmlParserCtxtPtr ctxt;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
RETURN_THROWS();
}
if (!source_len) {
zend_argument_value_error(1, "must not be empty");
RETURN_THROWS();
}
if (ZEND_LONG_EXCEEDS_INT(options)) {
php_error_docref(NULL, E_WARNING, "Invalid options");
RETURN_FALSE;
}
if (mode == DOM_LOAD_FILE) {
if (CHECK_NULL_PATH(source, source_len)) {
zend_argument_value_error(1, "must not contain any null bytes");
RETURN_THROWS();
}
ctxt = htmlCreateFileParserCtxt(source, NULL);
} else {
if (ZEND_SIZE_T_INT_OVFL(source_len)) {
php_error_docref(NULL, E_WARNING, "Input string is too long");
RETURN_FALSE;
}
ctxt = htmlCreateMemoryParserCtxt(source, (int)source_len);
}
if (!ctxt) {
RETURN_FALSE;
}
ctxt->vctxt.error = php_libxml_ctx_error;
ctxt->vctxt.warning = php_libxml_ctx_warning;
if (ctxt->sax != NULL) {
ctxt->sax->error = php_libxml_ctx_error;
ctxt->sax->warning = php_libxml_ctx_warning;
}
php_libxml_sanitize_parse_ctxt_options(ctxt);
if (options) {
htmlCtxtUseOptions(ctxt, (int)options);
}
htmlParseDocument(ctxt);
xmlDocPtr newdoc = ctxt->myDoc;
htmlFreeParserCtxt(ctxt);
dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
}
/* }}} */
/* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument, loadHTMLFile)
{
dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
}
/* }}} end dom_document_load_html_file */
/* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument, loadHTML)
{
dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
}
/* }}} end dom_document_load_html */
/* {{{ Convenience method to save to file as html */
PHP_METHOD(DOMDocument, saveHTMLFile)
{
zval *id;
xmlDoc *docp;
size_t file_len;
int bytes, format;
dom_object *intern;
char *file;
const char *encoding;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &file, &file_len) == FAILURE) {
RETURN_THROWS();
}
if (file_len == 0) {
zend_argument_value_error(1, "must not be empty");
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
encoding = (const char *) htmlGetMetaEncoding(docp);
libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
format = doc_props->formatoutput;
bytes = htmlSaveFileFormat(file, docp, encoding, format);
if (bytes == -1) {
RETURN_FALSE;
}
RETURN_LONG(bytes);
}
/* }}} end dom_document_save_html_file */
/* {{{ Convenience method to output as html */
PHP_METHOD(DOMDocument, saveHTML)
{
zval *id, *nodep = NULL;
xmlDoc *docp;
xmlNode *node;
xmlOutputBufferPtr outBuf;
xmlBufferPtr buf;
dom_object *intern, *nodeobj;
xmlChar *mem = NULL;
int format;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(),
"|O!", &nodep, dom_node_class_entry)
== FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
libxml_doc_props const* doc_props = dom_get_doc_props(intern->document);
format = doc_props->formatoutput;
if (nodep != NULL) {
/* Dump contents of Node */
DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
if (node->doc != docp) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
buf = xmlBufferCreate();
if (!buf) {
php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
RETURN_FALSE;
}
outBuf = xmlOutputBufferCreateBuffer(buf, NULL);
if (!outBuf) {
xmlBufferFree(buf);
php_error_docref(NULL, E_WARNING, "Could not fetch output buffer");
RETURN_FALSE;
}
if (node->type == XML_DOCUMENT_FRAG_NODE) {
for (node = node->children; node; node = node->next) {
htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
if (outBuf->error) {
break;
}
}
} else {
htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
}
if (!outBuf->error) {
xmlOutputBufferFlush(outBuf);
mem = (xmlChar*) xmlBufferContent(buf);
if (!mem) {
RETVAL_FALSE;
} else {
int size = xmlBufferLength(buf);
RETVAL_STRINGL((const char*) mem, size);
}
} else {
php_error_docref(NULL, E_WARNING, "Error dumping HTML node");
RETVAL_FALSE;
}
xmlOutputBufferClose(outBuf);
xmlBufferFree(buf);
} else {
int size = 0;
htmlDocDumpMemoryFormat(docp, &mem, &size, format);
if (!size || !mem) {
RETVAL_FALSE;
} else {
RETVAL_STRINGL((const char*) mem, size);
}
if (mem)
xmlFree(mem);
}
}
/* }}} end dom_document_save_html */
#endif /* defined(LIBXML_HTML_ENABLED) */
/* {{{ Register extended class used to create base node type */
PHP_METHOD(DOMDocument, registerNodeClass)
{
zend_class_entry *basece = dom_node_class_entry, *ce = NULL;
dom_object *intern;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "CC!", &basece, &ce) == FAILURE) {
RETURN_THROWS();
}
if (ce == NULL || instanceof_function(ce, basece)) {
if (UNEXPECTED(ce != NULL && (ce->ce_flags & ZEND_ACC_ABSTRACT))) {
zend_argument_value_error(2, "must not be an abstract class");
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
dom_set_doc_classmap(intern->document, basece, ce);
RETURN_TRUE;
}
zend_argument_error(NULL, 2, "must be a class name derived from %s or null, %s given", ZSTR_VAL(basece->name), ZSTR_VAL(ce->name));
}
/* }}} */
/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-append
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(DOMDocument, append)
{
uint32_t argc = 0;
zval *args;
dom_object *intern;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
dom_parent_node_append(intern, args, argc);
}
/* }}} */
/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(DOMDocument, prepend)
{
uint32_t argc = 0;
zval *args;
dom_object *intern;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
dom_parent_node_prepend(intern, args, argc);
}
/* }}} */
/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
Since:
*/
PHP_METHOD(DOMDocument, replaceChildren)
{
uint32_t argc = 0;
zval *args;
dom_object *intern;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
dom_parent_node_replace_children(intern, args, argc);
}
/* }}} */
#endif /* HAVE_LIBXML && HAVE_DOM */