php-src/ext/dom/php_dom.c
2024-09-18 17:23:24 +02:00

2766 lines
111 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
+----------------------------------------------------------------------+
| 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> |
| Marcus Borger <helly@php.net> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "php.h"
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "zend_enum.h"
#include "php_dom.h"
#include "nodelist.h"
#include "html_collection.h"
#include "namespace_compat.h"
#include "private_data.h"
#include "internal_helpers.h"
#include "php_dom_arginfo.h"
#include "dom_properties.h"
#include "token_list.h"
#include "zend_interfaces.h"
#include "lexbor/lexbor/core/types.h"
#include "lexbor/lexbor/core/lexbor.h"
#include "ext/standard/info.h"
/* {{{ class entries */
PHP_DOM_EXPORT zend_class_entry *dom_node_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_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_modern_parentnode_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_childnode_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_childnode_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_domimplementation_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_documentfragment_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_documentfragment_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_document_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_html_document_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_xml_document_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_nodelist_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_nodelist_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_namednodemap_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_namednodemap_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_dtd_namednodemap_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_html_collection_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_characterdata_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_characterdata_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_attr_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_attr_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_element_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_element_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_html_element_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_text_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_text_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_comment_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_comment_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_cdatasection_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_cdatasection_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_documenttype_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_documenttype_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_notation_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_notation_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_entity_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_entity_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_entityreference_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_entityreference_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_processinginstruction_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_processinginstruction_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_abstract_base_document_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_token_list_class_entry;
#ifdef LIBXML_XPATH_ENABLED
PHP_DOM_EXPORT zend_class_entry *dom_xpath_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_modern_xpath_class_entry;
#endif
PHP_DOM_EXPORT zend_class_entry *dom_namespace_node_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_adjacent_position_class_entry;
PHP_DOM_EXPORT zend_class_entry *dom_namespace_info_class_entry;
/* }}} */
static zend_object_handlers dom_object_handlers;
static zend_object_handlers dom_nnodemap_object_handlers;
static zend_object_handlers dom_nodelist_object_handlers;
static zend_object_handlers dom_modern_nnodemap_object_handlers;
static zend_object_handlers dom_modern_nodelist_object_handlers;
static zend_object_handlers dom_html_collection_object_handlers;
static zend_object_handlers dom_object_namespace_node_handlers;
static zend_object_handlers dom_modern_domimplementation_object_handlers;
static zend_object_handlers dom_token_list_object_handlers;
#ifdef LIBXML_XPATH_ENABLED
zend_object_handlers dom_xpath_object_handlers;
#endif
static HashTable classes;
/* {{{ prop handler tables */
static HashTable dom_document_prop_handlers;
static HashTable dom_xml_document_prop_handlers;
static HashTable dom_abstract_base_document_prop_handlers;
static HashTable dom_documentfragment_prop_handlers;
static HashTable dom_modern_documentfragment_prop_handlers;
static HashTable dom_node_prop_handlers;
static HashTable dom_modern_node_prop_handlers;
static HashTable dom_entity_reference_prop_handlers;
static HashTable dom_modern_entity_reference_prop_handlers;
static HashTable dom_nodelist_prop_handlers;
static HashTable dom_namednodemap_prop_handlers;
static HashTable dom_characterdata_prop_handlers;
static HashTable dom_modern_characterdata_prop_handlers;
static HashTable dom_attr_prop_handlers;
static HashTable dom_modern_attr_prop_handlers;
static HashTable dom_element_prop_handlers;
static HashTable dom_modern_element_prop_handlers;
static HashTable dom_modern_element_prop_handlers;
static HashTable dom_text_prop_handlers;
static HashTable dom_modern_text_prop_handlers;
static HashTable dom_documenttype_prop_handlers;
static HashTable dom_modern_documenttype_prop_handlers;
static HashTable dom_notation_prop_handlers;
static HashTable dom_modern_notation_prop_handlers;
static HashTable dom_entity_prop_handlers;
static HashTable dom_modern_entity_prop_handlers;
static HashTable dom_processinginstruction_prop_handlers;
static HashTable dom_modern_processinginstruction_prop_handlers;
static HashTable dom_namespace_node_prop_handlers;
static HashTable dom_token_list_prop_handlers;
#ifdef LIBXML_XPATH_ENABLED
static HashTable dom_xpath_prop_handlers;
#endif
/* }}} */
static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type);
static void dom_object_namespace_node_free_storage(zend_object *object);
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original);
typedef zend_result (*dom_read_t)(dom_object *obj, zval *retval);
typedef zend_result (*dom_write_t)(dom_object *obj, zval *newval);
typedef struct dom_prop_handler {
dom_read_t read_func;
dom_write_t write_func;
} dom_prop_handler;
int dom_node_is_read_only(const xmlNode *node) {
switch (node->type) {
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_NAMESPACE_DECL:
return SUCCESS;
break;
default:
if (node->doc == NULL) {
return SUCCESS;
} else {
return FAILURE;
}
}
}
bool dom_node_children_valid(const xmlNode *node) {
switch (node->type) {
case XML_DOCUMENT_TYPE_NODE:
case XML_DTD_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_NOTATION_NODE:
return false;
default:
return true;
}
}
static const libxml_doc_props default_doc_props = {
.formatoutput = false,
.validateonparse = false,
.resolveexternals = false,
.preservewhitespace = true,
.substituteentities = false,
.stricterror = true,
.recover = false,
.classmap = NULL,
};
ZEND_DECLARE_MODULE_GLOBALS(dom)
static PHP_GINIT_FUNCTION(dom)
{
#if defined(COMPILE_DL_DOM) && defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
dom_globals->suppress_warnings = false;
}
/* {{{ dom_get_doc_props() */
dom_doc_propsptr dom_get_doc_props(php_libxml_ref_obj *document)
{
dom_doc_propsptr doc_props;
if (document && document->doc_props) {
return document->doc_props;
} else {
doc_props = emalloc(sizeof(libxml_doc_props));
memcpy(doc_props, &default_doc_props, sizeof(libxml_doc_props));
if (document) {
document->doc_props = doc_props;
}
return doc_props;
}
}
/* }}} */
libxml_doc_props const* dom_get_doc_props_read_only(const php_libxml_ref_obj *document)
{
if (document && document->doc_props) {
return document->doc_props;
} else {
return &default_doc_props;
}
}
static void dom_copy_document_ref(php_libxml_ref_obj *source_doc, php_libxml_ref_obj *dest_doc)
{
dom_doc_propsptr dest;
if (source_doc && dest_doc) {
libxml_doc_props const* source = dom_get_doc_props_read_only(source_doc);
dest = dom_get_doc_props(dest_doc);
dest->formatoutput = source->formatoutput;
dest->validateonparse = source->validateonparse;
dest->resolveexternals = source->resolveexternals;
dest->preservewhitespace = source->preservewhitespace;
dest->substituteentities = source->substituteentities;
dest->stricterror = source->stricterror;
dest->recover = source->recover;
if (source->classmap) {
ALLOC_HASHTABLE(dest->classmap);
zend_hash_init(dest->classmap, 0, NULL, NULL, false);
zend_hash_copy(dest->classmap, source->classmap, NULL);
}
dest_doc->class_type = source_doc->class_type;
dest_doc->handlers = source_doc->handlers;
}
}
void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce)
{
dom_doc_propsptr doc_props;
if (document) {
doc_props = dom_get_doc_props(document);
if (doc_props->classmap == NULL) {
if (ce == NULL) {
return;
}
ALLOC_HASHTABLE(doc_props->classmap);
zend_hash_init(doc_props->classmap, 0, NULL, NULL, false);
}
if (ce) {
zend_hash_update_ptr(doc_props->classmap, basece->name, ce);
} else {
zend_hash_del(doc_props->classmap, basece->name);
}
}
}
zend_class_entry *dom_get_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece)
{
if (document) {
libxml_doc_props const* doc_props = dom_get_doc_props_read_only(document);
if (doc_props->classmap) {
zend_class_entry *ce = zend_hash_find_ptr(doc_props->classmap, basece->name);
if (ce) {
return ce;
}
}
}
return basece;
}
/* }}} */
/* {{{ dom_get_strict_error() */
bool dom_get_strict_error(php_libxml_ref_obj *document) {
return dom_get_doc_props_read_only(document)->stricterror;
}
/* }}} */
/* {{{ xmlNodePtr dom_object_get_node(dom_object *obj) */
PHP_DOM_EXPORT xmlNodePtr dom_object_get_node(dom_object *obj)
{
if (obj && obj->ptr != NULL) {
return ((php_libxml_node_ptr *)obj->ptr)->node;
} else {
return NULL;
}
}
/* }}} end dom_object_get_node */
/* {{{ dom_object *php_dom_object_get_data(xmlNodePtr obj) */
PHP_DOM_EXPORT dom_object *php_dom_object_get_data(xmlNodePtr obj)
{
if (obj && obj->_private != NULL) {
return (dom_object *) ((php_libxml_node_ptr *) obj->_private)->_private;
} else {
return NULL;
}
}
/* }}} end php_dom_object_get_data */
static void dom_register_prop_handler(HashTable *prop_handler, const char *name, size_t name_len, const dom_prop_handler *hnd)
{
zend_string *str = zend_string_init_interned(name, name_len, true);
zend_hash_add_new_ptr(prop_handler, str, (void *) hnd);
zend_string_release_ex(str, true);
}
static void dom_overwrite_prop_handler(HashTable *prop_handler, const char *name, size_t name_len, const dom_prop_handler *hnd)
{
zend_hash_str_update_ptr(prop_handler, name, name_len, (void *) hnd);
}
#define DOM_REGISTER_PROP_HANDLER(prop_handler, name, prop_read_func, prop_write_func) do { \
static const dom_prop_handler hnd = {.read_func = prop_read_func, .write_func = prop_write_func}; \
dom_register_prop_handler(prop_handler, "" name, sizeof("" name) - 1, &hnd); \
} while (0)
#define DOM_OVERWRITE_PROP_HANDLER(prop_handler, name, prop_read_func, prop_write_func) do { \
static const dom_prop_handler hnd = {.read_func = prop_read_func, .write_func = prop_write_func}; \
dom_overwrite_prop_handler(prop_handler, "" name, sizeof("" name) - 1, &hnd); \
} while (0)
static zval *dom_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
{
dom_object *obj = php_dom_obj_from_obj(object);
if (!obj->prop_handler || !zend_hash_exists(obj->prop_handler, name)) {
return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
}
return NULL;
}
static zend_always_inline const dom_prop_handler *dom_get_prop_handler(const dom_object *obj, zend_string *name, void **cache_slot)
{
const dom_prop_handler *hnd = NULL;
if (obj->prop_handler != NULL) {
if (cache_slot && *cache_slot == obj->prop_handler) {
hnd = *(cache_slot + 1);
}
if (!hnd) {
hnd = zend_hash_find_ptr(obj->prop_handler, name);
if (cache_slot) {
*cache_slot = obj->prop_handler;
*(cache_slot + 1) = (void *) hnd;
}
}
}
return hnd;
}
/* {{{ dom_read_property */
zval *dom_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
{
dom_object *obj = php_dom_obj_from_obj(object);
zval *retval;
const dom_prop_handler *hnd = dom_get_prop_handler(obj, name, cache_slot);
if (hnd) {
int ret = hnd->read_func(obj, rv);
if (ret == SUCCESS) {
retval = rv;
} else {
retval = &EG(uninitialized_zval);
}
} else {
retval = zend_std_read_property(object, name, type, cache_slot, rv);
}
return retval;
}
/* }}} */
zval *dom_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
{
dom_object *obj = php_dom_obj_from_obj(object);
const dom_prop_handler *hnd = dom_get_prop_handler(obj, name, cache_slot);
if (hnd) {
if (UNEXPECTED(!hnd->write_func)) {
zend_readonly_property_modification_error_ex(ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
return &EG(error_zval);
}
zend_property_info *prop = NULL;
if (cache_slot) {
ZEND_ASSERT(*cache_slot == obj->prop_handler);
prop = *(cache_slot + 2);
}
if (!prop) {
prop = zend_get_property_info(object->ce, name, /* silent */ true);
if (cache_slot) {
*(cache_slot + 2) = prop;
}
}
ZEND_ASSERT(prop && ZEND_TYPE_IS_SET(prop->type));
zval tmp;
ZVAL_COPY(&tmp, value);
if (!zend_verify_property_type(prop, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) {
zval_ptr_dtor(&tmp);
return &EG(error_zval);
}
hnd->write_func(obj, &tmp);
zval_ptr_dtor(&tmp);
return value;
}
return zend_std_write_property(object, name, value, cache_slot);
}
// todo: make dom_property_exists return bool as well
/* {{{ dom_property_exists */
static int dom_property_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
{
dom_object *obj = php_dom_obj_from_obj(object);
bool retval = false;
const dom_prop_handler *hnd = dom_get_prop_handler(obj, name, cache_slot);
if (hnd) {
zval tmp;
if (check_empty == 2) {
retval = true;
} else if (hnd->read_func(obj, &tmp) == SUCCESS) {
if (check_empty == 1) {
retval = zend_is_true(&tmp);
} else if (check_empty == 0) {
retval = (Z_TYPE(tmp) != IS_NULL);
}
zval_ptr_dtor(&tmp);
}
} else {
retval = zend_std_has_property(object, name, check_empty, cache_slot);
}
return retval;
}
/* }}} */
static void dom_unset_property(zend_object *object, zend_string *member, void **cache_slot)
{
dom_object *obj = php_dom_obj_from_obj(object);
if (obj->prop_handler != NULL && zend_hash_exists(obj->prop_handler, member)) {
zend_throw_error(NULL, "Cannot unset %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(member));
return;
}
zend_std_unset_property(object, member, cache_slot);
}
static HashTable* dom_get_debug_info_helper(zend_object *object, int *is_temp) /* {{{ */
{
dom_object *obj = php_dom_obj_from_obj(object);
HashTable *debug_info,
*prop_handlers = obj->prop_handler,
*std_props;
zend_string *string_key;
dom_prop_handler *entry;
zend_string *object_str;
*is_temp = 1;
std_props = zend_std_get_properties(object);
debug_info = zend_array_dup(std_props);
if (!prop_handlers) {
return debug_info;
}
DOM_G(suppress_warnings) = true;
object_str = ZSTR_INIT_LITERAL("(object value omitted)", false);
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(prop_handlers, string_key, entry) {
zval value;
ZEND_ASSERT(string_key != NULL);
if (entry->read_func(obj, &value) == FAILURE) {
continue;
}
if (Z_TYPE(value) == IS_OBJECT) {
zval_ptr_dtor(&value);
ZVAL_NEW_STR(&value, object_str);
zend_string_addref(object_str);
}
zend_hash_update(debug_info, string_key, &value);
} ZEND_HASH_FOREACH_END();
zend_string_release_ex(object_str, false);
DOM_G(suppress_warnings) = false;
return debug_info;
}
/* }}} */
static HashTable* dom_get_debug_info(zend_object *object, int *is_temp) /* {{{ */
{
return dom_get_debug_info_helper(object, is_temp);
}
/* }}} */
void *php_dom_export_node(zval *object) /* {{{ */
{
php_libxml_node_object *intern;
xmlNodePtr nodep = NULL;
intern = (php_libxml_node_object *) Z_DOMOBJ_P(object);
if (intern->node) {
nodep = intern->node->node;
}
return nodep;
}
/* }}} */
/* {{{ Get a simplexml_element object from dom to allow for processing */
static void dom_import_simplexml_common(INTERNAL_FUNCTION_PARAMETERS, php_libxml_class_type new_class)
{
zval *node;
xmlNodePtr nodep = NULL;
php_libxml_node_object *nodeobj;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &node) == FAILURE) {
RETURN_THROWS();
}
nodeobj = (php_libxml_node_object *) ((char *) Z_OBJ_P(node) - Z_OBJ_HT_P(node)->offset);
nodep = php_libxml_import_node(node);
if (nodep && nodeobj && nodeobj->document && (nodep->type == XML_ELEMENT_NODE || nodep->type == XML_ATTRIBUTE_NODE)) {
php_libxml_class_type old_class_type = nodeobj->document->class_type;
if (old_class_type != PHP_LIBXML_CLASS_UNSET && old_class_type != new_class) {
if (new_class == PHP_LIBXML_CLASS_MODERN) {
zend_argument_type_error(1, "must not be already imported as a DOMNode");
} else {
zend_argument_type_error(1, "must not be already imported as a Dom\\Node");
}
RETURN_THROWS();
}
/* Lock the node class type to prevent creating multiple representations of the same node. */
nodeobj->document->class_type = new_class;
if (old_class_type != PHP_LIBXML_CLASS_MODERN && new_class == PHP_LIBXML_CLASS_MODERN && nodep->doc != NULL) {
dom_document_convert_to_modern(nodeobj->document, nodep->doc);
}
DOM_RET_OBJ((xmlNodePtr) nodep, (dom_object *)nodeobj);
} else {
zend_argument_type_error(1, "is not a valid node type");
RETURN_THROWS();
}
}
PHP_FUNCTION(dom_import_simplexml)
{
dom_import_simplexml_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LIBXML_CLASS_LEGACY);
}
PHP_FUNCTION(Dom_import_simplexml)
{
dom_import_simplexml_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LIBXML_CLASS_MODERN);
}
/* }}} */
static dom_object* dom_objects_set_class(zend_class_entry *class_type);
void php_dom_update_document_after_clone(dom_object *original, xmlNodePtr original_node, dom_object *clone, xmlNodePtr cloned_node)
{
dom_copy_document_ref(original->document, clone->document);
/* Workaround libxml2 bug, see https://gitlab.gnome.org/GNOME/libxml2/-/commit/07920b4381873187c02df53fa9b5d44aff3a7041 */
#if LIBXML_VERSION < 20911
if (original_node->type == XML_HTML_DOCUMENT_NODE) {
cloned_node->type = XML_HTML_DOCUMENT_NODE;
}
#endif
}
static void dom_update_refcount_after_clone(dom_object *original, xmlNodePtr original_node, dom_object *clone, xmlNodePtr cloned_node)
{
/* If we cloned a document then we must create new doc proxy */
if (cloned_node->doc == original_node->doc) {
clone->document = original->document;
}
php_libxml_increment_doc_ref((php_libxml_node_object *)clone, cloned_node->doc);
php_libxml_increment_node_ptr((php_libxml_node_object *)clone, cloned_node, (void *)clone);
if (original->document != clone->document) {
php_dom_update_document_after_clone(original, original_node, clone, cloned_node);
}
}
static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */
{
dom_object *intern = php_dom_obj_from_obj(zobject);
dom_object *clone = dom_objects_set_class(intern->std.ce);
if (instanceof_function(intern->std.ce, dom_node_class_entry) || instanceof_function(intern->std.ce, dom_modern_node_class_entry)) {
xmlNodePtr node = (xmlNodePtr)dom_object_get_node(intern);
if (node != NULL) {
php_dom_private_data *private_data = NULL;
if (php_dom_follow_spec_intern(intern)) {
if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) {
private_data = php_dom_private_data_create();
} else {
private_data = php_dom_get_private_data(intern);
}
}
xmlNodePtr cloned_node = dom_clone_node(php_dom_ns_mapper_from_private(private_data), node, node->doc, true);
if (cloned_node != NULL) {
dom_update_refcount_after_clone(intern, node, clone, cloned_node);
}
if (private_data != NULL) {
clone->document->private_data = php_dom_libxml_private_data_header(private_data);
}
}
}
zend_objects_clone_members(&clone->std, &intern->std);
return &clone->std;
}
/* }}} */
static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject)
{
dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject);
zend_object *clone = dom_objects_namespace_node_new(intern->dom.std.ce);
dom_object_namespace_node *clone_intern = php_dom_namespace_node_obj_from_obj(clone);
xmlNodePtr original_node = dom_object_get_node(&intern->dom);
ZEND_ASSERT(original_node->type == XML_NAMESPACE_DECL);
xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(original_node->parent, original_node->ns);
if (intern->parent_intern) {
clone_intern->parent_intern = intern->parent_intern;
GC_ADDREF(&clone_intern->parent_intern->std);
}
dom_update_refcount_after_clone(&intern->dom, original_node, &clone_intern->dom, cloned_node);
zend_objects_clone_members(clone, &intern->dom.std);
return clone;
}
static zend_object *dom_token_list_new(zend_class_entry *class_type)
{
dom_token_list_object *intern = zend_object_alloc(sizeof(*intern), class_type);
intern->dom.prop_handler = &dom_token_list_prop_handlers;
zend_object_std_init(&intern->dom.std, class_type);
object_properties_init(&intern->dom.std, class_type);
return &intern->dom.std;
}
static const zend_module_dep dom_deps[] = {
ZEND_MOD_REQUIRED("libxml")
ZEND_MOD_CONFLICTS("domxml")
ZEND_MOD_END
};
zend_module_entry dom_module_entry = { /* {{{ */
STANDARD_MODULE_HEADER_EX, NULL,
dom_deps,
"dom",
ext_functions,
PHP_MINIT(dom),
PHP_MSHUTDOWN(dom),
NULL,
NULL,
PHP_MINFO(dom),
DOM_API_VERSION, /* Extension versionnumber */
PHP_MODULE_GLOBALS(dom),
PHP_GINIT(dom),
NULL,
NULL,
STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */
#ifdef COMPILE_DL_DOM
ZEND_GET_MODULE(dom)
#endif
void dom_nnodemap_objects_free_storage(zend_object *object);
static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv);
static int dom_nodelist_has_dimension(zend_object *object, zval *member, int check_empty);
static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv);
static int dom_nodemap_has_dimension(zend_object *object, zval *member, int check_empty);
static zval *dom_modern_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv);
static int dom_modern_nodemap_has_dimension(zend_object *object, zval *member, int check_empty);
#ifdef LIBXML_XPATH_ENABLED
void dom_xpath_objects_free_storage(zend_object *object);
HashTable *dom_xpath_get_gc(zend_object *object, zval **table, int *n);
#endif
static void *dom_malloc(size_t size) {
return emalloc(size);
}
static void *dom_realloc(void *dst, size_t size) {
return erealloc(dst, size);
}
static void *dom_calloc(size_t num, size_t size) {
return ecalloc(num, size);
}
static void dom_free(void *ptr) {
efree(ptr);
}
/* {{{ PHP_MINIT_FUNCTION(dom) */
PHP_MINIT_FUNCTION(dom)
{
memcpy(&dom_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
dom_object_handlers.offset = XtOffsetOf(dom_object, std);
dom_object_handlers.free_obj = dom_objects_free_storage;
dom_object_handlers.read_property = dom_read_property;
dom_object_handlers.write_property = dom_write_property;
dom_object_handlers.get_property_ptr_ptr = dom_get_property_ptr_ptr;
dom_object_handlers.unset_property = dom_unset_property;
dom_object_handlers.clone_obj = dom_objects_store_clone_obj;
dom_object_handlers.has_property = dom_property_exists;
dom_object_handlers.get_debug_info = dom_get_debug_info;
memcpy(&dom_modern_domimplementation_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
/* The IDL has the [SameObject] constraint, which is incompatible with cloning because it imposes that there is only
* one instance per parent object. */
dom_modern_domimplementation_object_handlers.clone_obj = NULL;
memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage;
dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension;
dom_nnodemap_object_handlers.has_dimension = dom_nodemap_has_dimension;
memcpy(&dom_nodelist_object_handlers, &dom_nnodemap_object_handlers, sizeof(zend_object_handlers));
dom_nodelist_object_handlers.read_dimension = dom_nodelist_read_dimension;
dom_nodelist_object_handlers.has_dimension = dom_nodelist_has_dimension;
memcpy(&dom_modern_nnodemap_object_handlers, &dom_nnodemap_object_handlers, sizeof(zend_object_handlers));
dom_modern_nnodemap_object_handlers.read_dimension = dom_modern_nodemap_read_dimension;
dom_modern_nnodemap_object_handlers.has_dimension = dom_modern_nodemap_has_dimension;
memcpy(&dom_modern_nodelist_object_handlers, &dom_nodelist_object_handlers, sizeof(zend_object_handlers));
dom_modern_nodelist_object_handlers.read_dimension = dom_modern_nodelist_read_dimension;
dom_modern_nodelist_object_handlers.has_dimension = dom_modern_nodelist_has_dimension;
memcpy(&dom_html_collection_object_handlers, &dom_modern_nodelist_object_handlers, sizeof(zend_object_handlers));
dom_html_collection_object_handlers.read_dimension = dom_html_collection_read_dimension;
dom_html_collection_object_handlers.has_dimension = dom_html_collection_has_dimension;
memcpy(&dom_object_namespace_node_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
dom_object_namespace_node_handlers.offset = XtOffsetOf(dom_object_namespace_node, dom.std);
dom_object_namespace_node_handlers.free_obj = dom_object_namespace_node_free_storage;
dom_object_namespace_node_handlers.clone_obj = dom_object_namespace_node_clone_obj;
memcpy(&dom_token_list_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
dom_token_list_object_handlers.offset = XtOffsetOf(dom_token_list_object, dom.std);
dom_token_list_object_handlers.free_obj = dom_token_list_free_obj;
/* The Web IDL (Web Interface Description Language - https://webidl.spec.whatwg.org) has the [SameObject] constraint
* for this object, which is incompatible with cloning because it imposes that there is only one instance
* per parent object. */
dom_token_list_object_handlers.clone_obj = NULL;
dom_token_list_object_handlers.read_dimension = dom_token_list_read_dimension;
dom_token_list_object_handlers.has_dimension = dom_token_list_has_dimension;
zend_hash_init(&classes, 0, NULL, NULL, true);
dom_adjacent_position_class_entry = register_class_Dom_AdjacentPosition();
dom_domexception_class_entry = register_class_DOMException(zend_ce_exception);
dom_parentnode_class_entry = register_class_DOMParentNode();
dom_modern_parentnode_class_entry = register_class_Dom_ParentNode();
dom_childnode_class_entry = register_class_DOMChildNode();
dom_modern_childnode_class_entry = register_class_Dom_ChildNode();
dom_domimplementation_class_entry = register_class_DOMImplementation();
dom_domimplementation_class_entry->create_object = dom_objects_new;
dom_domimplementation_class_entry->default_object_handlers = &dom_object_handlers;
dom_modern_domimplementation_class_entry = register_class_Dom_Implementation();
dom_modern_domimplementation_class_entry->create_object = dom_objects_new;
dom_modern_domimplementation_class_entry->default_object_handlers = &dom_modern_domimplementation_object_handlers;
dom_node_class_entry = register_class_DOMNode();
dom_node_class_entry->create_object = dom_objects_new;
dom_node_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_node_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeValue", dom_node_node_value_read, dom_node_node_value_write);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "childNodes", dom_node_child_nodes_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "firstChild", dom_node_first_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "lastChild", dom_node_last_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "previousSibling", dom_node_previous_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nextSibling", dom_node_next_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "attributes", dom_node_attributes_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "prefix", dom_node_prefix_read, dom_node_prefix_write);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "localName", dom_node_local_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "baseURI", dom_node_base_uri_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
zend_hash_add_new_ptr(&classes, dom_node_class_entry->name, &dom_node_prop_handlers);
dom_modern_node_class_entry = register_class_Dom_Node();
dom_modern_node_class_entry->create_object = dom_objects_new;
dom_modern_node_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_node_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "baseURI", dom_node_base_uri_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "childNodes", dom_node_child_nodes_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "firstChild", dom_node_first_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "lastChild", dom_node_last_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "previousSibling", dom_node_previous_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nextSibling", dom_node_next_sibling_read, NULL);
/* We will set-up the setter for the derived classes afterwards on a class-by-class basis. */
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeValue", dom_node_node_value_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "textContent", dom_node_text_content_read, NULL);
zend_hash_add_new_ptr(&classes, dom_modern_node_class_entry->name, &dom_modern_node_prop_handlers);
dom_namespace_node_class_entry = register_class_DOMNameSpaceNode();
dom_namespace_node_class_entry->create_object = dom_objects_namespace_node_new;
dom_namespace_node_class_entry->default_object_handlers = &dom_object_namespace_node_handlers;
zend_hash_init(&dom_namespace_node_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeValue", dom_node_node_value_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "prefix", dom_node_prefix_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "localName", dom_node_local_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL);
zend_hash_add_new_ptr(&classes, dom_namespace_node_class_entry->name, &dom_namespace_node_prop_handlers);
dom_namespace_info_class_entry = register_class_Dom_NamespaceInfo();
dom_documentfragment_class_entry = register_class_DOMDocumentFragment(dom_node_class_entry, dom_parentnode_class_entry);
dom_documentfragment_class_entry->create_object = dom_objects_new;
dom_documentfragment_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_documentfragment_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
zend_hash_merge(&dom_documentfragment_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_documentfragment_class_entry->name, &dom_documentfragment_prop_handlers);
dom_modern_documentfragment_class_entry = register_class_Dom_DocumentFragment(dom_modern_node_class_entry, dom_modern_parentnode_class_entry);
dom_modern_documentfragment_class_entry->create_object = dom_objects_new;
dom_modern_documentfragment_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_documentfragment_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
zend_hash_merge(&dom_modern_documentfragment_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
zend_hash_add_new_ptr(&classes, dom_modern_documentfragment_class_entry->name, &dom_modern_documentfragment_prop_handlers);
dom_abstract_base_document_class_entry = register_class_Dom_Document(dom_modern_node_class_entry, dom_modern_parentnode_class_entry);
dom_abstract_base_document_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_abstract_base_document_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "implementation", dom_modern_document_implementation_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "URL", dom_document_document_uri_read, dom_document_document_uri_write);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentURI", dom_document_document_uri_read, dom_document_document_uri_write);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "characterSet", dom_document_encoding_read, dom_html_document_encoding_write);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "charset", dom_document_encoding_read, dom_html_document_encoding_write);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "inputEncoding", dom_document_encoding_read, dom_html_document_encoding_write);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "doctype", dom_document_doctype_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentElement", dom_document_document_element_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "body", dom_html_document_body_read, dom_html_document_body_write);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "head", dom_html_document_head_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "title", dom_html_document_title_read, dom_html_document_title_write);
zend_hash_merge(&dom_abstract_base_document_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
/* No need to register in &classes because this is an abstract class handler. */
dom_document_class_entry = register_class_DOMDocument(dom_node_class_entry, dom_parentnode_class_entry);
dom_document_class_entry->create_object = dom_objects_new;
dom_document_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_document_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "doctype", dom_document_doctype_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "implementation", dom_document_implementation_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "documentElement", dom_document_document_element_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "actualEncoding", dom_document_actual_encoding_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "encoding", dom_document_encoding_read, dom_document_encoding_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "xmlEncoding", dom_document_encoding_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "standalone", dom_document_standalone_read, dom_document_standalone_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "xmlStandalone", dom_document_standalone_read, dom_document_standalone_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "version", dom_document_version_read, dom_document_version_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "xmlVersion", dom_document_version_read, dom_document_version_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "strictErrorChecking", dom_document_strict_error_checking_read, dom_document_strict_error_checking_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "documentURI", dom_document_document_uri_read, dom_document_document_uri_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "config", dom_document_config_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "formatOutput", dom_document_format_output_read, dom_document_format_output_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "validateOnParse", dom_document_validate_on_parse_read, dom_document_validate_on_parse_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "resolveExternals", dom_document_resolve_externals_read, dom_document_resolve_externals_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "preserveWhiteSpace", dom_document_preserve_whitespace_read, dom_document_preserve_whitespace_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "recover", dom_document_recover_read, dom_document_recover_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "substituteEntities", dom_document_substitue_entities_read, dom_document_substitue_entities_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
zend_hash_merge(&dom_document_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_document_class_entry->name, &dom_document_prop_handlers);
dom_html_document_class_entry = register_class_Dom_HTMLDocument(dom_abstract_base_document_class_entry);
dom_html_document_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_add_new_ptr(&classes, dom_html_document_class_entry->name, &dom_abstract_base_document_prop_handlers);
dom_xml_document_class_entry = register_class_Dom_XMLDocument(dom_abstract_base_document_class_entry);
dom_xml_document_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_xml_document_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "xmlEncoding", dom_document_encoding_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "xmlStandalone", dom_document_standalone_read, dom_document_standalone_write);
DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "xmlVersion", dom_document_version_read, dom_document_version_write);
DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "formatOutput", dom_document_format_output_read, dom_document_format_output_write);
zend_hash_merge(&dom_xml_document_prop_handlers, &dom_abstract_base_document_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_xml_document_class_entry->name, &dom_xml_document_prop_handlers);
dom_nodelist_class_entry = register_class_DOMNodeList(zend_ce_aggregate, zend_ce_countable);
dom_nodelist_class_entry->create_object = dom_nnodemap_objects_new;
dom_nodelist_class_entry->default_object_handlers = &dom_nodelist_object_handlers;
dom_nodelist_class_entry->get_iterator = php_dom_get_iterator;
zend_hash_init(&dom_nodelist_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_nodelist_prop_handlers, "length", dom_nodelist_length_read, NULL);
zend_hash_add_new_ptr(&classes, dom_nodelist_class_entry->name, &dom_nodelist_prop_handlers);
dom_modern_nodelist_class_entry = register_class_Dom_NodeList(zend_ce_aggregate, zend_ce_countable);
dom_modern_nodelist_class_entry->create_object = dom_nnodemap_objects_new;
dom_modern_nodelist_class_entry->default_object_handlers = &dom_modern_nodelist_object_handlers;
dom_modern_nodelist_class_entry->get_iterator = php_dom_get_iterator;
zend_hash_add_new_ptr(&classes, dom_modern_nodelist_class_entry->name, &dom_nodelist_prop_handlers);
dom_namednodemap_class_entry = register_class_DOMNamedNodeMap(zend_ce_aggregate, zend_ce_countable);
dom_namednodemap_class_entry->create_object = dom_nnodemap_objects_new;
dom_namednodemap_class_entry->default_object_handlers = &dom_nnodemap_object_handlers;
dom_namednodemap_class_entry->get_iterator = php_dom_get_iterator;
zend_hash_init(&dom_namednodemap_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_namednodemap_prop_handlers, "length", dom_namednodemap_length_read, NULL);
zend_hash_add_new_ptr(&classes, dom_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers);
dom_modern_namednodemap_class_entry = register_class_Dom_NamedNodeMap(zend_ce_aggregate, zend_ce_countable);
dom_modern_namednodemap_class_entry->create_object = dom_nnodemap_objects_new;
dom_modern_namednodemap_class_entry->default_object_handlers = &dom_modern_nnodemap_object_handlers;
dom_modern_namednodemap_class_entry->get_iterator = php_dom_get_iterator;
zend_hash_add_new_ptr(&classes, dom_modern_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers);
dom_modern_dtd_namednodemap_class_entry = register_class_Dom_DtdNamedNodeMap(zend_ce_aggregate, zend_ce_countable);
dom_modern_dtd_namednodemap_class_entry->create_object = dom_nnodemap_objects_new;
dom_modern_dtd_namednodemap_class_entry->default_object_handlers = &dom_modern_nnodemap_object_handlers;
dom_modern_dtd_namednodemap_class_entry->get_iterator = php_dom_get_iterator;
zend_hash_add_new_ptr(&classes, dom_modern_dtd_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers);
dom_html_collection_class_entry = register_class_Dom_HTMLCollection(zend_ce_aggregate, zend_ce_countable);
dom_html_collection_class_entry->create_object = dom_nnodemap_objects_new;
dom_html_collection_class_entry->default_object_handlers = &dom_html_collection_object_handlers;
dom_html_collection_class_entry->get_iterator = php_dom_get_iterator;
zend_hash_add_new_ptr(&classes, dom_html_collection_class_entry->name, &dom_nodelist_prop_handlers);
dom_characterdata_class_entry = register_class_DOMCharacterData(dom_node_class_entry, dom_childnode_class_entry);
dom_characterdata_class_entry->create_object = dom_objects_new;
dom_characterdata_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_characterdata_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "data", dom_characterdata_data_read, dom_characterdata_data_write);
DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "length", dom_characterdata_length_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
zend_hash_merge(&dom_characterdata_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_characterdata_class_entry->name, &dom_characterdata_prop_handlers);
dom_modern_characterdata_class_entry = register_class_Dom_CharacterData(dom_modern_node_class_entry, dom_modern_childnode_class_entry);
dom_modern_characterdata_class_entry->create_object = dom_objects_new;
dom_modern_characterdata_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_characterdata_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "data", dom_characterdata_data_read, dom_characterdata_data_write);
DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "length", dom_characterdata_length_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
zend_hash_merge(&dom_modern_characterdata_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "nodeValue", dom_node_node_value_read, dom_node_node_value_write);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
zend_hash_add_new_ptr(&classes, dom_modern_characterdata_class_entry->name, &dom_modern_characterdata_prop_handlers);
dom_attr_class_entry = register_class_DOMAttr(dom_node_class_entry);
dom_attr_class_entry->create_object = dom_objects_new;
dom_attr_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_attr_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "name", dom_attr_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "specified", dom_attr_specified_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "value", dom_attr_value_read, dom_attr_value_write);
DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "ownerElement", dom_attr_owner_element_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "schemaTypeInfo", dom_attr_schema_type_info_read, NULL);
zend_hash_merge(&dom_attr_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_attr_class_entry->name, &dom_attr_prop_handlers);
dom_modern_attr_class_entry = register_class_Dom_Attr(dom_modern_node_class_entry);
dom_modern_attr_class_entry->create_object = dom_objects_new;
dom_modern_attr_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_attr_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "prefix", dom_modern_node_prefix_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "localName", dom_node_local_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "name", dom_attr_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "value", dom_attr_value_read, dom_attr_value_write);
DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "ownerElement", dom_attr_owner_element_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "specified", dom_attr_specified_read, NULL);
zend_hash_merge(&dom_modern_attr_prop_handlers, &dom_node_prop_handlers, NULL, false);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_attr_prop_handlers, "nodeValue", dom_node_node_value_read, dom_node_node_value_write);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_attr_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
zend_hash_add_new_ptr(&classes, dom_modern_attr_class_entry->name, &dom_modern_attr_prop_handlers);
dom_element_class_entry = register_class_DOMElement(dom_node_class_entry, dom_parentnode_class_entry, dom_childnode_class_entry);
dom_element_class_entry->create_object = dom_objects_new;
dom_element_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_element_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "tagName", dom_element_tag_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "className", dom_element_class_name_read, dom_element_class_name_write);
DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "id", dom_element_id_read, dom_element_id_write);
DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "schemaTypeInfo", dom_element_schema_type_info_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
zend_hash_merge(&dom_element_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_element_class_entry->name, &dom_element_prop_handlers);
dom_modern_element_class_entry = register_class_Dom_Element(dom_modern_node_class_entry, dom_modern_parentnode_class_entry, dom_modern_childnode_class_entry);
dom_modern_element_class_entry->create_object = dom_objects_new;
dom_modern_element_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_element_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "prefix", dom_modern_node_prefix_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "localName", dom_node_local_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "tagName", dom_element_tag_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "id", dom_element_id_read, dom_element_id_write);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "className", dom_element_class_name_read, dom_element_class_name_write);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "classList", dom_element_class_list_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "attributes", dom_node_attributes_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "innerHTML", dom_element_inner_html_read, dom_element_inner_html_write);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "substitutedNodeValue", dom_modern_element_substituted_node_value_read, dom_modern_element_substituted_node_value_write);
zend_hash_merge(&dom_modern_element_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_element_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
zend_hash_add_new_ptr(&classes, dom_modern_element_class_entry->name, &dom_modern_element_prop_handlers);
dom_html_element_class_entry = register_class_Dom_HTMLElement(dom_modern_element_class_entry);
dom_html_element_class_entry->create_object = dom_objects_new;
dom_html_element_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_add_new_ptr(&classes, dom_html_element_class_entry->name, &dom_modern_element_prop_handlers);
dom_text_class_entry = register_class_DOMText(dom_characterdata_class_entry);
dom_text_class_entry->create_object = dom_objects_new;
dom_text_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_text_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_text_prop_handlers, "wholeText", dom_text_whole_text_read, NULL);
zend_hash_merge(&dom_text_prop_handlers, &dom_characterdata_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_text_class_entry->name, &dom_text_prop_handlers);
dom_modern_text_class_entry = register_class_Dom_Text(dom_modern_characterdata_class_entry);
dom_modern_text_class_entry->create_object = dom_objects_new;
dom_modern_text_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_text_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_text_prop_handlers, "wholeText", dom_text_whole_text_read, NULL);
zend_hash_merge(&dom_modern_text_prop_handlers, &dom_modern_characterdata_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_modern_text_class_entry->name, &dom_modern_text_prop_handlers);
dom_comment_class_entry = register_class_DOMComment(dom_characterdata_class_entry);
dom_comment_class_entry->create_object = dom_objects_new;
dom_comment_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_add_new_ptr(&classes, dom_comment_class_entry->name, &dom_characterdata_prop_handlers);
dom_modern_comment_class_entry = register_class_Dom_Comment(dom_modern_characterdata_class_entry);
dom_modern_comment_class_entry->create_object = dom_objects_new;
dom_modern_comment_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_add_new_ptr(&classes, dom_modern_comment_class_entry->name, &dom_modern_characterdata_prop_handlers);
dom_cdatasection_class_entry = register_class_DOMCdataSection(dom_text_class_entry);
dom_cdatasection_class_entry->create_object = dom_objects_new;
dom_cdatasection_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_add_new_ptr(&classes, dom_cdatasection_class_entry->name, &dom_text_prop_handlers);
dom_modern_cdatasection_class_entry = register_class_Dom_CDATASection(dom_modern_text_class_entry);
dom_modern_cdatasection_class_entry->create_object = dom_objects_new;
dom_modern_cdatasection_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_add_new_ptr(&classes, dom_modern_cdatasection_class_entry->name, &dom_modern_text_prop_handlers);
dom_documenttype_class_entry = register_class_DOMDocumentType(dom_node_class_entry);
dom_documenttype_class_entry->create_object = dom_objects_new;
dom_documenttype_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_documenttype_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "name", dom_documenttype_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "entities", dom_documenttype_entities_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "notations", dom_documenttype_notations_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "publicId", dom_documenttype_public_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "systemId", dom_documenttype_system_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "internalSubset", dom_documenttype_internal_subset_read, NULL);
zend_hash_merge(&dom_documenttype_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_documenttype_class_entry->name, &dom_documenttype_prop_handlers);
dom_modern_documenttype_class_entry = register_class_Dom_DocumentType(dom_modern_node_class_entry, dom_modern_childnode_class_entry);
dom_modern_documenttype_class_entry->create_object = dom_objects_new;
dom_modern_documenttype_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_documenttype_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "name", dom_documenttype_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "entities", dom_documenttype_entities_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "notations", dom_documenttype_notations_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "publicId", dom_documenttype_public_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "systemId", dom_documenttype_system_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "internalSubset", dom_documenttype_internal_subset_read, NULL);
zend_hash_merge(&dom_modern_documenttype_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_modern_documenttype_class_entry->name, &dom_modern_documenttype_prop_handlers);
dom_notation_class_entry = register_class_DOMNotation(dom_node_class_entry);
dom_notation_class_entry->create_object = dom_objects_new;
dom_notation_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_notation_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_notation_prop_handlers, "publicId", dom_notation_public_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_notation_prop_handlers, "systemId", dom_notation_system_id_read, NULL);
zend_hash_merge(&dom_notation_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_notation_class_entry->name, &dom_notation_prop_handlers);
dom_modern_notation_class_entry = register_class_Dom_Notation(dom_modern_node_class_entry);
dom_modern_notation_class_entry->create_object = dom_objects_new;
dom_modern_notation_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_notation_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_notation_prop_handlers, "publicId", dom_notation_public_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_notation_prop_handlers, "systemId", dom_notation_system_id_read, NULL);
zend_hash_merge(&dom_modern_notation_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_modern_notation_class_entry->name, &dom_modern_node_prop_handlers);
dom_entity_class_entry = register_class_DOMEntity(dom_node_class_entry);
dom_entity_class_entry->create_object = dom_objects_new;
dom_entity_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_entity_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "publicId", dom_entity_public_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "systemId", dom_entity_system_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "notationName", dom_entity_notation_name_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "actualEncoding", dom_entity_actual_encoding_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "encoding", dom_entity_encoding_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "version", dom_entity_version_read, NULL);
zend_hash_merge(&dom_entity_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_entity_class_entry->name, &dom_entity_prop_handlers);
dom_modern_entity_class_entry = register_class_Dom_Entity(dom_modern_node_class_entry);
dom_modern_entity_class_entry->create_object = dom_objects_new;
dom_modern_entity_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_entity_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_entity_prop_handlers, "publicId", dom_entity_public_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_entity_prop_handlers, "systemId", dom_entity_system_id_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_entity_prop_handlers, "notationName", dom_entity_notation_name_read, NULL);
zend_hash_merge(&dom_modern_entity_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_modern_entity_class_entry->name, &dom_modern_entity_prop_handlers);
dom_entityreference_class_entry = register_class_DOMEntityReference(dom_node_class_entry);
dom_entityreference_class_entry->create_object = dom_objects_new;
dom_entityreference_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_entity_reference_prop_handlers, 0, NULL, NULL, true);
zend_hash_merge(&dom_entity_reference_prop_handlers, &dom_node_prop_handlers, NULL, false);
DOM_OVERWRITE_PROP_HANDLER(&dom_entity_reference_prop_handlers, "firstChild", dom_entity_reference_child_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_entity_reference_prop_handlers, "lastChild", dom_entity_reference_child_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_entity_reference_prop_handlers, "textContent", dom_entity_reference_text_content_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_entity_reference_prop_handlers, "childNodes", dom_entity_reference_child_nodes_read, NULL);
zend_hash_add_new_ptr(&classes, dom_entityreference_class_entry->name, &dom_entity_reference_prop_handlers);
dom_modern_entityreference_class_entry = register_class_Dom_EntityReference(dom_modern_node_class_entry);
dom_modern_entityreference_class_entry->create_object = dom_objects_new;
dom_modern_entityreference_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_entity_reference_prop_handlers, 0, NULL, NULL, true);
zend_hash_merge(&dom_modern_entity_reference_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "firstChild", dom_entity_reference_child_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "lastChild", dom_entity_reference_child_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "textContent", dom_entity_reference_text_content_read, NULL);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "childNodes", dom_entity_reference_child_nodes_read, NULL);
zend_hash_add_new_ptr(&classes, dom_modern_entityreference_class_entry->name, &dom_modern_entity_reference_prop_handlers);
dom_processinginstruction_class_entry = register_class_DOMProcessingInstruction(dom_node_class_entry);
dom_processinginstruction_class_entry->create_object = dom_objects_new;
dom_processinginstruction_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_processinginstruction_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_processinginstruction_prop_handlers, "target", dom_processinginstruction_target_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_processinginstruction_prop_handlers, "data", dom_processinginstruction_data_read, dom_processinginstruction_data_write);
zend_hash_merge(&dom_processinginstruction_prop_handlers, &dom_node_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_processinginstruction_class_entry->name, &dom_processinginstruction_prop_handlers);
dom_modern_processinginstruction_class_entry = register_class_Dom_ProcessingInstruction(dom_modern_characterdata_class_entry);
dom_modern_processinginstruction_class_entry->create_object = dom_objects_new;
dom_modern_processinginstruction_class_entry->default_object_handlers = &dom_object_handlers;
zend_hash_init(&dom_modern_processinginstruction_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_modern_processinginstruction_prop_handlers, "target", dom_processinginstruction_target_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_processinginstruction_prop_handlers, "data", dom_processinginstruction_data_read, dom_processinginstruction_data_write);
zend_hash_merge(&dom_modern_processinginstruction_prop_handlers, &dom_modern_characterdata_prop_handlers, NULL, false);
zend_hash_add_new_ptr(&classes, dom_modern_processinginstruction_class_entry->name, &dom_modern_processinginstruction_prop_handlers);
#ifdef LIBXML_XPATH_ENABLED
memcpy(&dom_xpath_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
dom_xpath_object_handlers.offset = XtOffsetOf(dom_xpath_object, dom) + XtOffsetOf(dom_object, std);
dom_xpath_object_handlers.free_obj = dom_xpath_objects_free_storage;
dom_xpath_object_handlers.get_gc = dom_xpath_get_gc;
dom_xpath_object_handlers.clone_obj = NULL;
dom_xpath_class_entry = register_class_DOMXPath();
dom_xpath_class_entry->create_object = dom_xpath_objects_new;
dom_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers;
zend_hash_init(&dom_xpath_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "document", dom_xpath_document_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "registerNodeNamespaces", dom_xpath_register_node_ns_read, dom_xpath_register_node_ns_write);
zend_hash_add_new_ptr(&classes, dom_xpath_class_entry->name, &dom_xpath_prop_handlers);
dom_modern_xpath_class_entry = register_class_Dom_XPath();
dom_modern_xpath_class_entry->create_object = dom_xpath_objects_new;
dom_modern_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers;
zend_hash_add_new_ptr(&classes, dom_modern_xpath_class_entry->name, &dom_xpath_prop_handlers);
#endif
dom_token_list_class_entry = register_class_Dom_TokenList(zend_ce_aggregate, zend_ce_countable);
dom_token_list_class_entry->create_object = dom_token_list_new;
dom_token_list_class_entry->default_object_handlers = &dom_token_list_object_handlers;
dom_token_list_class_entry->get_iterator = dom_token_list_get_iterator;
zend_hash_init(&dom_token_list_prop_handlers, 0, NULL, NULL, true);
DOM_REGISTER_PROP_HANDLER(&dom_token_list_prop_handlers, "length", dom_token_list_length_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_token_list_prop_handlers, "value", dom_token_list_value_read, dom_token_list_value_write);
zend_hash_add_new_ptr(&classes, dom_token_list_class_entry->name, &dom_token_list_prop_handlers);
register_php_dom_symbols(module_number);
php_libxml_register_export(dom_node_class_entry, php_dom_export_node);
php_libxml_register_export(dom_modern_node_class_entry, php_dom_export_node);
lexbor_memory_setup(dom_malloc, dom_realloc, dom_calloc, dom_free);
return SUCCESS;
}
/* }}} */
/* {{{ */
PHP_MINFO_FUNCTION(dom)
{
php_info_print_table_start();
php_info_print_table_row(2, "DOM/XML", "enabled");
php_info_print_table_row(2, "DOM/XML API Version", DOM_API_VERSION);
php_info_print_table_row(2, "libxml Version", LIBXML_DOTTED_VERSION);
#ifdef LIBXML_HTML_ENABLED
php_info_print_table_row(2, "HTML Support", "enabled");
#endif
#ifdef LIBXML_XPATH_ENABLED
php_info_print_table_row(2, "XPath Support", "enabled");
#endif
#ifdef LIBXML_XPTR_ENABLED
php_info_print_table_row(2, "XPointer Support", "enabled");
#endif
#ifdef LIBXML_SCHEMAS_ENABLED
php_info_print_table_row(2, "Schema Support", "enabled");
php_info_print_table_row(2, "RelaxNG Support", "enabled");
#endif
php_info_print_table_end();
}
/* }}} */
PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */
{
zend_hash_destroy(&dom_document_prop_handlers);
zend_hash_destroy(&dom_abstract_base_document_prop_handlers);
zend_hash_destroy(&dom_xml_document_prop_handlers);
zend_hash_destroy(&dom_documentfragment_prop_handlers);
zend_hash_destroy(&dom_modern_documentfragment_prop_handlers);
zend_hash_destroy(&dom_node_prop_handlers);
zend_hash_destroy(&dom_modern_node_prop_handlers);
zend_hash_destroy(&dom_entity_reference_prop_handlers);
zend_hash_destroy(&dom_modern_entity_reference_prop_handlers);
zend_hash_destroy(&dom_namespace_node_prop_handlers);
zend_hash_destroy(&dom_nodelist_prop_handlers);
zend_hash_destroy(&dom_namednodemap_prop_handlers);
zend_hash_destroy(&dom_characterdata_prop_handlers);
zend_hash_destroy(&dom_modern_characterdata_prop_handlers);
zend_hash_destroy(&dom_attr_prop_handlers);
zend_hash_destroy(&dom_modern_attr_prop_handlers);
zend_hash_destroy(&dom_element_prop_handlers);
zend_hash_destroy(&dom_modern_element_prop_handlers);
zend_hash_destroy(&dom_text_prop_handlers);
zend_hash_destroy(&dom_modern_text_prop_handlers);
zend_hash_destroy(&dom_documenttype_prop_handlers);
zend_hash_destroy(&dom_modern_documenttype_prop_handlers);
zend_hash_destroy(&dom_notation_prop_handlers);
zend_hash_destroy(&dom_modern_notation_prop_handlers);
zend_hash_destroy(&dom_entity_prop_handlers);
zend_hash_destroy(&dom_modern_entity_prop_handlers);
zend_hash_destroy(&dom_processinginstruction_prop_handlers);
zend_hash_destroy(&dom_modern_processinginstruction_prop_handlers);
zend_hash_destroy(&dom_token_list_prop_handlers);
#ifdef LIBXML_XPATH_ENABLED
zend_hash_destroy(&dom_xpath_prop_handlers);
#endif
zend_hash_destroy(&classes);
/* If you want do find memleaks in this module, compile libxml2 with --with-mem-debug and
uncomment the following line, this will tell you the amount of not freed memory
and the total used memory into apaches error_log */
/* xmlMemoryDump();*/
return SUCCESS;
}
/* }}} */
/* {{{ node_list_unlink */
void node_list_unlink(xmlNodePtr node)
{
dom_object *wrapper;
while (node != NULL) {
wrapper = php_dom_object_get_data(node);
if (wrapper != NULL ) {
xmlUnlinkNode(node);
} else {
if (node->type == XML_ENTITY_REF_NODE)
break;
node_list_unlink(node->children);
switch (node->type) {
case XML_ATTRIBUTE_DECL:
case XML_DTD_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_ENTITY_DECL:
case XML_ATTRIBUTE_NODE:
case XML_TEXT_NODE:
break;
default:
node_list_unlink((xmlNodePtr) node->properties);
}
}
node = node->next;
}
}
/* }}} end node_list_unlink */
/* {{{ dom_objects_free_storage */
void dom_objects_free_storage(zend_object *object)
{
dom_object *intern = php_dom_obj_from_obj(object);
zend_object_std_dtor(&intern->std);
php_libxml_node_ptr *ptr = intern->ptr;
if (ptr != NULL && ptr->node != NULL) {
xmlNodePtr node = ptr->node;
if (node->type != XML_DOCUMENT_NODE && node->type != XML_HTML_DOCUMENT_NODE) {
php_libxml_node_decrement_resource((php_libxml_node_object *) intern);
} else {
php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
php_libxml_decrement_doc_ref((php_libxml_node_object *) intern);
}
intern->ptr = NULL;
}
}
/* }}} */
void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xmlHashTablePtr ht, const char *local, size_t local_len, const char *ns, size_t ns_len) /* {{{ */
{
dom_nnodemap_object *mapptr = (dom_nnodemap_object *) intern->ptr;
ZEND_ASSERT(basenode != NULL);
ZVAL_OBJ_COPY(&mapptr->baseobj_zv, &basenode->std);
xmlDocPtr doc = basenode->document ? basenode->document->ptr : NULL;
mapptr->baseobj = basenode;
mapptr->nodetype = ntype;
mapptr->ht = ht;
const xmlChar* tmp;
if (local) {
int len = local_len > INT_MAX ? -1 : (int) local_len;
if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)local, len)) != NULL) {
mapptr->local = BAD_CAST tmp;
} else {
mapptr->local = xmlCharStrndup(local, len);
mapptr->free_local = true;
}
mapptr->local_lower = BAD_CAST estrdup(local);
if (len < 0) {
zend_str_tolower((char *) mapptr->local_lower, strlen((const char *) mapptr->local_lower));
} else {
zend_str_tolower((char *) mapptr->local_lower, len);
}
}
if (ns) {
int len = ns_len > INT_MAX ? -1 : (int) ns_len;
if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ns, len)) != NULL) {
mapptr->ns = BAD_CAST tmp;
} else {
mapptr->ns = xmlCharStrndup(ns, len);
mapptr->free_ns = true;
}
}
}
/* }}} */
static void dom_objects_set_class_ex(zend_class_entry *class_type, dom_object *intern)
{
zend_class_entry *base_class = class_type;
while ((base_class->type != ZEND_INTERNAL_CLASS || base_class->info.internal.module->module_number != dom_module_entry.module_number) && base_class->parent != NULL) {
base_class = base_class->parent;
}
intern->prop_handler = zend_hash_find_ptr(&classes, base_class->name);
zend_object_std_init(&intern->std, class_type);
object_properties_init(&intern->std, class_type);
}
static dom_object* dom_objects_set_class(zend_class_entry *class_type)
{
dom_object *intern = zend_object_alloc(sizeof(dom_object), class_type);
dom_objects_set_class_ex(class_type, intern);
return intern;
}
/* {{{ dom_objects_new */
zend_object *dom_objects_new(zend_class_entry *class_type)
{
dom_object *intern = dom_objects_set_class(class_type);
return &intern->std;
}
/* }}} */
static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type)
{
dom_object_namespace_node *intern = zend_object_alloc(sizeof(dom_object_namespace_node), class_type);
dom_objects_set_class_ex(class_type, &intern->dom);
return &intern->dom.std;
}
static void dom_object_namespace_node_free_storage(zend_object *object)
{
dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(object);
if (intern->parent_intern != NULL) {
OBJ_RELEASE(&intern->parent_intern->std);
}
dom_objects_free_storage(object);
}
#ifdef LIBXML_XPATH_ENABLED
/* {{{ zend_object dom_xpath_objects_new(zend_class_entry *class_type) */
zend_object *dom_xpath_objects_new(zend_class_entry *class_type)
{
dom_xpath_object *intern = zend_object_alloc(sizeof(dom_xpath_object), class_type);
php_dom_xpath_callbacks_ctor(&intern->xpath_callbacks);
intern->register_node_ns = true;
intern->dom.prop_handler = &dom_xpath_prop_handlers;
zend_object_std_init(&intern->dom.std, class_type);
object_properties_init(&intern->dom.std, class_type);
return &intern->dom.std;
}
/* }}} */
#endif
void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */
{
dom_object *intern = php_dom_obj_from_obj(object);
dom_nnodemap_object *objmap = (dom_nnodemap_object *)intern->ptr;
if (objmap) {
if (objmap->cached_obj && GC_DELREF(&objmap->cached_obj->std) == 0) {
zend_objects_store_del(&objmap->cached_obj->std);
}
if (objmap->free_local) {
xmlFree(objmap->local);
}
if (objmap->free_ns) {
xmlFree(objmap->ns);
}
if (objmap->local_lower) {
efree(objmap->local_lower);
}
if (!Z_ISUNDEF(objmap->baseobj_zv)) {
zval_ptr_dtor(&objmap->baseobj_zv);
}
efree(objmap);
intern->ptr = NULL;
}
php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
zend_object_std_dtor(&intern->std);
}
/* }}} */
zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type)
{
dom_object *intern;
dom_nnodemap_object *objmap;
intern = dom_objects_set_class(class_type);
intern->ptr = emalloc(sizeof(dom_nnodemap_object));
objmap = (dom_nnodemap_object *)intern->ptr;
ZVAL_UNDEF(&objmap->baseobj_zv);
objmap->baseobj = NULL;
objmap->nodetype = 0;
objmap->ht = NULL;
objmap->local = NULL;
objmap->local_lower = NULL;
objmap->free_local = false;
objmap->ns = NULL;
objmap->free_ns = false;
objmap->cache_tag.modification_nr = 0;
objmap->cached_length = -1;
objmap->cached_obj = NULL;
objmap->cached_obj_index = 0;
return &intern->std;
}
void php_dom_create_iterator(zval *return_value, dom_iterator_type iterator_type, bool modern) /* {{{ */
{
zend_class_entry *ce;
if (iterator_type == DOM_NAMEDNODEMAP) {
ce = dom_get_namednodemap_ce(modern);
} else if (iterator_type == DOM_HTMLCOLLECTION) {
/* This only exists in modern DOM. */
ZEND_ASSERT(modern);
ce = dom_html_collection_class_entry;
} else if (iterator_type == DOM_DTD_NAMEDNODEMAP) {
ce = dom_get_dtd_namednodemap_ce(modern);
} else {
ZEND_ASSERT(iterator_type == DOM_NODELIST);
ce = dom_get_nodelist_ce(modern);
}
object_init_ex(return_value, ce);
}
/* }}} */
static zend_always_inline zend_class_entry *dom_get_element_ce(const xmlNode *node, bool modern)
{
if (modern) {
if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token)) {
return dom_html_element_class_entry;
} else {
return dom_modern_element_class_entry;
}
} else {
return dom_element_class_entry;
}
}
bool php_dom_create_nullable_object(xmlNodePtr obj, zval *return_value, dom_object *domobj)
{
if (!obj) {
ZVAL_NULL(return_value);
return false;
}
return php_dom_create_object(obj, return_value, domobj);
}
/* {{{ php_dom_create_object */
PHP_DOM_EXPORT bool php_dom_create_object(xmlNodePtr obj, zval *return_value, dom_object *domobj)
{
dom_object *intern = php_dom_object_get_data(obj);
if (intern) {
ZVAL_OBJ_COPY(return_value, &intern->std);
return true;
}
bool modern = domobj && php_dom_follow_spec_intern(domobj);
zend_class_entry *ce;
switch (obj->type) {
case XML_DOCUMENT_NODE:
{
ce = dom_get_xml_document_ce(modern);
break;
}
case XML_HTML_DOCUMENT_NODE:
{
ce = dom_get_html_document_ce(modern);
break;
}
case XML_DTD_NODE:
case XML_DOCUMENT_TYPE_NODE:
{
ce = dom_get_documenttype_ce(modern);
break;
}
case XML_ELEMENT_NODE:
{
ce = dom_get_element_ce(obj, modern);
break;
}
case XML_ATTRIBUTE_NODE:
{
ce = dom_get_attr_ce(modern);
break;
}
case XML_TEXT_NODE:
{
ce = dom_get_text_ce(modern);
break;
}
case XML_COMMENT_NODE:
{
ce = dom_get_comment_ce(modern);
break;
}
case XML_PI_NODE:
{
ce = dom_get_processinginstruction_ce(modern);
break;
}
case XML_ENTITY_REF_NODE:
{
ce = dom_get_entityreference_ce(modern);
break;
}
case XML_ENTITY_DECL:
case XML_ELEMENT_DECL:
{
ce = dom_get_entity_ce(modern);
break;
}
case XML_CDATA_SECTION_NODE:
{
ce = dom_get_cdatasection_ce(modern);
break;
}
case XML_DOCUMENT_FRAG_NODE:
{
ce = dom_get_documentfragment_ce(modern);
break;
}
case XML_NOTATION_NODE:
{
ce = dom_get_notation_ce(modern);
break;
}
case XML_NAMESPACE_DECL:
{
/* This has no modern equivalent */
ce = dom_namespace_node_class_entry;
break;
}
default:
/* TODO you can actually hit this with fixed attributes in the DTD for example... */
zend_throw_error(NULL, "Unsupported node type: %d", obj->type);
ZVAL_NULL(return_value);
return false;
}
if (domobj && domobj->document) {
ce = dom_get_doc_classmap(domobj->document, ce);
}
php_dom_instantiate_object_helper(return_value, ce, obj, domobj);
return false;
}
/* }}} end php_domobject_new */
dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_entry *ce, xmlNodePtr obj, dom_object *parent)
{
object_init_ex(return_value, ce);
dom_object *intern = Z_DOMOBJ_P(return_value);
if (obj->doc != NULL) {
if (parent != NULL) {
intern->document = parent->document;
}
php_libxml_increment_doc_ref((php_libxml_node_object *)intern, obj->doc);
}
php_libxml_increment_node_ptr((php_libxml_node_object *)intern, obj, (void *)intern);
return intern;
}
void php_dom_create_implementation(zval *retval, bool modern) {
object_init_ex(retval, dom_get_domimplementation_ce(modern));
}
/* {{{ int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child) */
int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child)
{
xmlNodePtr nodep;
if (parent == NULL || child == NULL || child->doc != parent->doc) {
return SUCCESS;
}
if (child->type == XML_DOCUMENT_NODE) {
return FAILURE;
}
nodep = parent;
while (nodep) {
if (nodep == child) {
return FAILURE;
}
nodep = nodep->parent;
}
return SUCCESS;
}
/* }}} end dom_hierarchy */
/* {{{ */
bool dom_has_feature(zend_string *feature, zend_string *version)
{
if (zend_string_equals_literal(version, "1.0")
|| zend_string_equals_literal(version, "2.0")
|| zend_string_equals_literal(version, "")
) {
if (zend_string_equals_literal_ci(feature, "XML")
|| (zend_string_equals_literal_ci(feature, "Core") && zend_string_equals_literal(version, "1.0"))
) {
return true;
}
}
return false;
}
/* }}} end dom_has_feature */
bool dom_match_qualified_name_according_to_spec(const xmlChar *qname, const xmlNode *nodep)
{
const xmlChar *node_local = nodep->name;
/* The qualified name must be matched, which means either:
* - The local parts are equal and there is no ns prefix for this element (i.e. the fqn is in the local name).
* - There is a prefix, the prefixes are equal and the local parts are equal. */
if (nodep->ns != NULL && nodep->ns->prefix != NULL) {
const char *prefix = (const char *) nodep->ns->prefix;
/* 1. match prefix up to |prefix| characters.
* This won't overflow as it'll stop at the '\0' if the lengths don't match. */
size_t prefix_len = strlen(prefix);
if (strncmp((const char *) qname, prefix, prefix_len) != 0) {
return false;
}
/* 2. match ':' */
if (qname[prefix_len] != ':') {
return false;
}
/* 3. match local name */
return xmlStrEqual(qname + prefix_len + 1, node_local);
} else {
return xmlStrEqual(node_local, qname);
}
}
static bool dom_match_qualified_name_for_tag_name_equality(const xmlChar *local, const xmlChar *local_lower, const xmlNode *nodep, bool match_qname)
{
if (!match_qname) {
return xmlStrEqual(nodep->name, local);
}
const xmlChar *local_to_use = nodep->doc->type == XML_HTML_DOCUMENT_NODE && php_dom_ns_is_fast(nodep, php_dom_ns_is_html_magic_token) ? local_lower : local;
return dom_match_qualified_name_according_to_spec(local_to_use, nodep);
}
xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, xmlChar *ns, xmlChar *local, xmlChar *local_lower, zend_long *cur, zend_long index) /* {{{ */
{
/* Can happen with detached document */
if (UNEXPECTED(nodep == NULL)) {
return NULL;
}
xmlNodePtr ret = NULL;
bool local_match_any = local[0] == '*' && local[1] == '\0';
/* Note: The spec says that ns == '' must be transformed to ns == NULL. In other words, they are equivalent.
* PHP however does not do this and internally uses the empty string everywhere when the user provides ns == NULL.
* This is because for PHP ns == NULL has another meaning: "match every namespace" instead of "match the empty namespace". */
bool ns_match_any = ns == NULL || (ns[0] == '*' && ns[1] == '\0');
bool match_qname = ns == NULL && php_dom_follow_spec_node(basep);
while (*cur <= index) {
if (nodep->type == XML_ELEMENT_NODE) {
if (local_match_any || dom_match_qualified_name_for_tag_name_equality(local, local_lower, nodep, match_qname)) {
if (ns_match_any || (ns[0] == '\0' && nodep->ns == NULL) || (nodep->ns != NULL && xmlStrEqual(nodep->ns->href, ns))) {
if (*cur == index) {
ret = nodep;
break;
}
(*cur)++;
}
}
}
nodep = php_dom_next_in_tree_order(nodep, basep);
if (!nodep) {
return NULL;
}
}
return ret;
}
/* }}} end dom_element_get_elements_by_tag_name_ns_raw */
static inline bool is_empty_node(xmlNodePtr nodep)
{
return nodep->content == NULL || *nodep->content == '\0';
}
static inline void free_node(xmlNodePtr node)
{
if (node->_private == NULL) {
xmlFreeNode(node);
}
}
static void dom_merge_adjacent_exclusive_text_nodes(xmlNodePtr node)
{
xmlNodePtr nextp = node->next;
while (nextp != NULL && nextp->type == XML_TEXT_NODE) {
xmlNodePtr newnextp = nextp->next;
xmlChar *strContent = nextp->content;
if (strContent != NULL) {
xmlNodeAddContent(node, strContent);
}
xmlUnlinkNode(nextp);
free_node(nextp);
nextp = newnextp;
}
}
/* {{{ void php_dom_normalize_legacy(xmlNodePtr nodep) */
void php_dom_normalize_legacy(xmlNodePtr nodep)
{
xmlNodePtr child = nodep->children;
while(child != NULL) {
switch (child->type) {
case XML_TEXT_NODE:
dom_merge_adjacent_exclusive_text_nodes(child);
if (is_empty_node(child)) {
xmlNodePtr nextp = child->next;
xmlUnlinkNode(child);
free_node(child);
child = nextp;
continue;
}
break;
case XML_ELEMENT_NODE:
php_dom_normalize_legacy(child);
xmlAttrPtr attr = child->properties;
while (attr != NULL) {
php_dom_normalize_legacy((xmlNodePtr) attr);
attr = attr->next;
}
break;
default:
break;
}
child = child->next;
}
}
/* }}} end php_dom_normalize_legacy */
/* https://dom.spec.whatwg.org/#dom-node-normalize */
void php_dom_normalize_modern(xmlNodePtr this)
{
/* for each descendant exclusive Text node node of this: */
xmlNodePtr node = this->children;
while (node != NULL) {
if (node->type == XML_TEXT_NODE) {
/* 1. Let length be nodes length.
* We'll deviate a bit here: we'll just check if it's empty or not as we don't want to compute the length. */
bool is_empty = is_empty_node(node);
/* 2. If length is zero, then remove node and continue with the next exclusive Text node, if any. */
if (is_empty) {
xmlNodePtr next = node->next;
xmlUnlinkNode(node);
free_node(node);
node = next;
continue;
}
/* 3. Let data be the concatenation of the data of nodes contiguous exclusive Text nodes (excluding itself), in tree order.
* 4. Replace data with node node, offset length, count 0, and data data.
* 7. Remove nodes contiguous exclusive Text nodes (excluding itself), in tree order.
* => In other words: Concat every contiguous text node into node and delete the merged nodes. */
dom_merge_adjacent_exclusive_text_nodes(node);
/* Steps 5-6 deal with mutation records, we don't do that here. */
} else if (node->type == XML_ELEMENT_NODE) {
php_dom_normalize_modern(node);
}
node = node->next;
}
}
static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr search_parent)
{
xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL;
/* 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, search_parent, curns->href)) &&
(curns->prefix == NULL || xmlStrEqual(nsptr->prefix, curns->prefix))) {
curns->next = NULL;
if (prevns == NULL) {
nodep->nsDef = nsdftptr;
} else {
prevns->next = nsdftptr;
}
/* Note: we can't get here if the ns is already on the oldNs list.
* This is because in that case the definition won't be on the node, and
* therefore won't be in the nodep->nsDef list. */
php_libxml_set_old_ns(doc, curns);
curns = prevns;
}
}
prevns = curns;
curns = nsdftptr;
}
}
}
void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */
{
ZEND_ASSERT(nodep->type != XML_ATTRIBUTE_NODE);
/* Although the node type will be checked by the libxml2 API,
* we still want to do the internal reconciliation conditionally. */
if (nodep->type == XML_ELEMENT_NODE) {
dom_reconcile_ns_internal(doc, nodep, nodep->parent);
xmlReconciliateNs(doc, nodep);
}
}
/* }}} */
static void dom_reconcile_ns_list_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last, xmlNodePtr search_parent)
{
ZEND_ASSERT(nodep != NULL);
while (true) {
if (nodep->type == XML_ELEMENT_NODE) {
dom_reconcile_ns_internal(doc, nodep, search_parent);
if (nodep->children) {
dom_reconcile_ns_list_internal(doc, nodep->children, nodep->last /* process the whole children list */, search_parent);
}
}
if (nodep == last) {
break;
}
nodep = nodep->next;
}
}
void dom_reconcile_ns_list(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last)
{
dom_reconcile_ns_list_internal(doc, nodep, last, nodep->parent);
/* The loop is outside of the recursion in the above call because
* dom_libxml_reconcile_ensure_namespaces_are_declared() performs its own recursion. */
while (true) {
/* The internal libxml2 call will already check the node type, no need for us to do it here. */
xmlReconciliateNs(doc, nodep);
if (nodep == last) {
break;
}
nodep = nodep->next;
}
}
/* https://dom.spec.whatwg.org/#validate-and-extract */
int dom_validate_and_extract(const zend_string *namespace, const zend_string *qname, xmlChar **localName, xmlChar **prefix)
{
/* 1. If namespace is the empty string, then set it to null.
* However, we're going to cheat and do the opposite to make
* implementation of the below steps with existing zend_ helpers easier. */
if (namespace == NULL) {
namespace = zend_empty_string;
}
/* 2. Validate qualifiedName. */
if (xmlValidateQName(BAD_CAST ZSTR_VAL(qname), /* allow spaces */ 0) != 0) {
return INVALID_CHARACTER_ERR;
}
/* Steps 3-5 */
*localName = xmlSplitQName2(BAD_CAST ZSTR_VAL(qname), prefix);
/* 6. If prefix is non-null and namespace is null, then throw a "NamespaceError" DOMException.
* Note that null namespace means empty string here becaue of step 1. */
if (*prefix != NULL && ZSTR_VAL(namespace)[0] == '\0') {
return NAMESPACE_ERR;
}
/* 7. If prefix is "xml" and namespace is not the XML namespace, then throw a "NamespaceError" DOMException. */
if (UNEXPECTED(!zend_string_equals_literal(namespace, "http://www.w3.org/XML/1998/namespace") && xmlStrEqual(*prefix, BAD_CAST "xml"))) {
return NAMESPACE_ERR;
}
/* 8. 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(qname, "xmlns") || xmlStrEqual(*prefix, BAD_CAST "xmlns")) && !zend_string_equals_literal(namespace, "http://www.w3.org/2000/xmlns/"))) {
return NAMESPACE_ERR;
}
/* 9. If namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns", then throw a "NamespaceError" DOMException. */
if (UNEXPECTED(zend_string_equals_literal(namespace, "http://www.w3.org/2000/xmlns/") && !zend_string_equals_literal(qname, "xmlns") && !xmlStrEqual(*prefix, BAD_CAST "xmlns"))) {
return NAMESPACE_ERR;
}
if (*localName == NULL) {
*localName = xmlStrdup(BAD_CAST ZSTR_VAL(qname));
}
return 0;
}
/*
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS
NAMESPACE_ERR: Raised if
1. the qualifiedName is a malformed qualified name
2. the qualifiedName has a prefix and the namespaceURI is null
*/
/* {{{ int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len) */
int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len) {
if (name_len == 0) {
return NAMESPACE_ERR;
}
*localname = (char *)xmlSplitQName2(BAD_CAST qname, (xmlChar **) prefix);
if (*localname == NULL) {
*localname = (char *)xmlStrdup(BAD_CAST qname);
if (*prefix == NULL && uri_len == 0) {
return 0;
}
}
/* 1 */
if (xmlValidateQName(BAD_CAST qname, 0) != 0) {
return NAMESPACE_ERR;
}
/* 2 */
if (*prefix != NULL && uri_len == 0) {
return NAMESPACE_ERR;
}
return 0;
}
/* }}} */
/* Creates a new namespace declaration with a random prefix with the given uri on the tree.
* This is used to resolve a namespace prefix conflict in cases where spec does not want a
* namespace error in case of conflicts, but demands a resolution. */
xmlNsPtr dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree, const char *uri)
{
ZEND_ASSERT(tree != NULL);
xmlDocPtr doc = tree->doc;
if (UNEXPECTED(doc == NULL)) {
return NULL;
}
/* Code adapted from libxml2 (2.10.4) */
char prefix[50];
int counter = 1;
snprintf(prefix, sizeof(prefix), "default");
xmlNsPtr nsptr = xmlSearchNs(doc, tree, (const xmlChar *) prefix);
while (nsptr != NULL) {
if (counter > 1000) {
return NULL;
}
snprintf(prefix, sizeof(prefix), "default%d", counter++);
nsptr = xmlSearchNs(doc, tree, (const xmlChar *) prefix);
}
/* Search yielded no conflict */
return xmlNewNs(tree, (const xmlChar *) uri, (const xmlChar *) prefix);
}
/*
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS
NAMESPACE_ERR: Raised if
3. the qualifiedName has a prefix that is "xml" and the namespaceURI is different from "http://www.w3.org/XML/1998/namespace" [XML Namespaces]
4. the qualifiedName or its prefix is "xmlns" and the namespaceURI is different from "http://www.w3.org/2000/xmlns/"
5. the namespaceURI is "http://www.w3.org/2000/xmlns/" and neither the qualifiedName nor its prefix is "xmlns".
*/
xmlNsPtr dom_get_ns_unchecked(xmlNodePtr nodep, char *uri, char *prefix)
{
xmlNsPtr nsptr = xmlNewNs(nodep, BAD_CAST uri, BAD_CAST prefix);
if (UNEXPECTED(nsptr == NULL)) {
/* Either memory allocation failure, or it's because of a prefix conflict.
* We'll assume a conflict and try again. If it was a memory allocation failure we'll just fail again, whatever.
* This isn't needed for every caller (such as createElementNS & DOMElement::__construct), but isn't harmful and simplifies the mental model "when do I use which function?".
* This branch will also be taken unlikely anyway as in those cases it'll be for allocation failure. */
return dom_get_ns_resolve_prefix_conflict(nodep, uri);
}
return nsptr;
}
/* {{{ xmlNsPtr dom_get_ns(xmlNodePtr nodep, char *uri, int *errorcode, char *prefix) */
xmlNsPtr dom_get_ns(xmlNodePtr nodep, char *uri, int *errorcode, char *prefix) {
xmlNsPtr nsptr;
if (! ((prefix && !strcmp (prefix, "xml") && strcmp(uri, (char *)XML_XML_NAMESPACE)) ||
(prefix && !strcmp (prefix, "xmlns") && strcmp(uri, DOM_XMLNS_NS_URI)) ||
(prefix && !strcmp(uri, DOM_XMLNS_NS_URI) && strcmp (prefix, "xmlns")))) {
nsptr = dom_get_ns_unchecked(nodep, uri, prefix);
if (UNEXPECTED(nsptr == NULL)) {
goto err;
}
} else {
goto err;
}
*errorcode = 0;
return nsptr;
err:
*errorcode = NAMESPACE_ERR;
return NULL;
}
/* }}} end dom_get_ns */
/* {{{ xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) */
xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
xmlNsPtr cur;
xmlNs *ret = NULL;
if (node == NULL)
return NULL;
if (localName == NULL || localName[0] == '\0') {
cur = node->nsDef;
while (cur != NULL) {
if (cur->prefix == NULL && cur->href != NULL) {
ret = cur;
break;
}
cur = cur->next;
}
} else {
cur = node->nsDef;
while (cur != NULL) {
if (cur->prefix != NULL && xmlStrEqual(localName, cur->prefix)) {
ret = cur;
break;
}
cur = cur->next;
}
}
return ret;
}
/* }}} end dom_get_nsdecl */
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original)
{
xmlNodePtr attrp;
xmlNsPtr curns = xmlNewNs(NULL, original->href, NULL);
if (original->prefix) {
curns->prefix = xmlStrdup(original->prefix);
attrp = xmlNewDocNode(nodep->doc, NULL, BAD_CAST original->prefix, original->href);
} else {
attrp = xmlNewDocNode(nodep->doc, NULL, BAD_CAST "xmlns", original->href);
}
attrp->type = XML_NAMESPACE_DECL;
attrp->parent = nodep;
attrp->ns = curns;
return attrp;
}
/* Note: Assumes the additional lifetime was already added in the caller. */
xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern)
{
xmlNodePtr attrp = php_dom_create_fake_namespace_decl_node_ptr(nodep, original);
php_dom_create_object(attrp, return_value, parent_intern);
/* This object must exist, because we just created an object for it via php_dom_create_object(). */
php_dom_namespace_node_obj_from_obj(Z_OBJ_P(return_value))->parent_intern = parent_intern;
return attrp;
}
static bool dom_nodemap_or_nodelist_process_offset_as_named(zval *offset, zend_long *lval)
{
if (Z_TYPE_P(offset) == IS_STRING) {
/* See zval_get_long_func() */
double dval;
zend_uchar is_numeric_string_type;
if (0 == (is_numeric_string_type = is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), lval, &dval, true))) {
return true;
} else if (is_numeric_string_type == IS_DOUBLE) {
*lval = zend_dval_to_lval_cap(dval);
}
} else {
*lval = zval_get_long(offset);
}
return false;
}
static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
{
if (UNEXPECTED(!offset)) {
zend_throw_error(NULL, "Cannot access %s without offset", ZSTR_VAL(object->ce->name));
return NULL;
}
ZVAL_DEREF(offset);
zend_long lval;
if (dom_nodemap_or_nodelist_process_offset_as_named(offset, &lval)) {
/* does not support named lookup */
ZVAL_NULL(rv);
return rv;
}
php_dom_nodelist_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv);
return rv;
}
static int dom_nodelist_has_dimension(zend_object *object, zval *member, int check_empty)
{
ZVAL_DEREF(member);
/* If it exists, it cannot be empty because nodes aren't empty. */
ZEND_IGNORE_VALUE(check_empty);
zend_long offset;
if (dom_nodemap_or_nodelist_process_offset_as_named(member, &offset)) {
/* does not support named lookup */
return 0;
}
return offset >= 0 && offset < php_dom_get_nodelist_length(php_dom_obj_from_obj(object));
}
void dom_remove_all_children(xmlNodePtr nodep)
{
if (nodep->children) {
node_list_unlink(nodep->children);
php_libxml_node_free_list((xmlNodePtr) nodep->children);
nodep->children = NULL;
nodep->last = NULL;
}
}
void php_dom_get_content_into_zval(const xmlNode *nodep, zval *return_value, bool null_on_failure)
{
ZEND_ASSERT(nodep != NULL);
switch (nodep->type) {
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE: {
char *str = (char * ) nodep->content;
if (str != NULL) {
RETURN_STRING(str);
}
break;
}
case XML_ATTRIBUTE_NODE: {
bool free;
xmlChar *value = php_libxml_attr_value((const xmlAttr *) nodep, &free);
RETURN_STRING_FAST((const char *) value);
if (free) {
xmlFree(value);
}
return;
}
default: {
char *str = (char *) xmlNodeGetContent(nodep);
if (str != NULL) {
RETVAL_STRING(str);
xmlFree(str);
return;
}
break;
}
}
if (null_on_failure) {
RETURN_NULL();
} else {
RETURN_EMPTY_STRING();
}
}
static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
{
if (UNEXPECTED(!offset)) {
zend_throw_error(NULL, "Cannot access %s without offset", ZSTR_VAL(object->ce->name));
return NULL;
}
ZVAL_DEREF(offset);
zend_long lval;
if (dom_nodemap_or_nodelist_process_offset_as_named(offset, &lval)) {
/* exceptional case, switch to named lookup */
php_dom_named_node_map_get_named_item_into_zval(php_dom_obj_from_obj(object)->ptr, Z_STR_P(offset), rv);
return rv;
}
/* see PHP_METHOD(DOMNamedNodeMap, item) */
if (UNEXPECTED(lval < 0 || ZEND_LONG_INT_OVFL(lval))) {
zend_value_error("must be between 0 and %d", INT_MAX);
return NULL;
}
php_dom_named_node_map_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv);
return rv;
}
static int dom_nodemap_has_dimension(zend_object *object, zval *member, int check_empty)
{
ZVAL_DEREF(member);
/* If it exists, it cannot be empty because nodes aren't empty. */
ZEND_IGNORE_VALUE(check_empty);
zend_long offset;
if (dom_nodemap_or_nodelist_process_offset_as_named(member, &offset)) {
/* exceptional case, switch to named lookup */
return php_dom_named_node_map_get_named_item(php_dom_obj_from_obj(object)->ptr, Z_STR_P(member), false) != NULL;
}
return offset >= 0 && offset < php_dom_get_namednodemap_length(php_dom_obj_from_obj(object));
}
static zval *dom_modern_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
{
if (UNEXPECTED(!offset)) {
zend_throw_error(NULL, "Cannot append to %s", ZSTR_VAL(object->ce->name));
return NULL;
}
dom_nnodemap_object *map = php_dom_obj_from_obj(object)->ptr;
ZVAL_DEREF(offset);
if (Z_TYPE_P(offset) == IS_STRING) {
zend_ulong lval;
if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), lval)) {
php_dom_named_node_map_get_item_into_zval(map, (zend_long) lval, rv);
} else {
php_dom_named_node_map_get_named_item_into_zval(map, Z_STR_P(offset), rv);
}
} else if (Z_TYPE_P(offset) == IS_LONG) {
php_dom_named_node_map_get_item_into_zval(map, Z_LVAL_P(offset), rv);
} else if (Z_TYPE_P(offset) == IS_DOUBLE) {
php_dom_named_node_map_get_item_into_zval(map, zend_dval_to_lval_safe(Z_DVAL_P(offset)), rv);
} else {
zend_illegal_container_offset(object->ce->name, offset, type);
return NULL;
}
return rv;
}
static int dom_modern_nodemap_has_dimension(zend_object *object, zval *member, int check_empty)
{
/* If it exists, it cannot be empty because nodes aren't empty. */
ZEND_IGNORE_VALUE(check_empty);
dom_object *obj = php_dom_obj_from_obj(object);
dom_nnodemap_object *map = obj->ptr;
ZVAL_DEREF(member);
if (Z_TYPE_P(member) == IS_STRING) {
zend_ulong lval;
if (ZEND_HANDLE_NUMERIC(Z_STR_P(member), lval)) {
return (zend_long) lval >= 0 && (zend_long) lval < php_dom_get_namednodemap_length(obj);
} else {
return php_dom_named_node_map_get_named_item(map, Z_STR_P(member), false) != NULL;
}
} else if (Z_TYPE_P(member) == IS_LONG) {
zend_long offset = Z_LVAL_P(member);
return offset >= 0 && offset < php_dom_get_namednodemap_length(obj);
} else if (Z_TYPE_P(member) == IS_DOUBLE) {
zend_long offset = zend_dval_to_lval_safe(Z_DVAL_P(member));
return offset >= 0 && offset < php_dom_get_namednodemap_length(obj);
} else {
zend_illegal_container_offset(object->ce->name, member, BP_VAR_IS);
return 0;
}
}
static xmlNodePtr dom_clone_container_helper(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr src_node, xmlDocPtr dst_doc)
{
xmlNodePtr clone = xmlDocCopyNode(src_node, dst_doc, 0);
if (EXPECTED(clone != NULL)) {
/* Set namespace to the original, reconciliation will fix this up. */
clone->ns = src_node->ns;
if (src_node->type == XML_ELEMENT_NODE) {
xmlAttrPtr last_added_attr = NULL;
if (src_node->nsDef != NULL) {
xmlNsPtr current_ns = src_node->nsDef;
do {
php_dom_ns_compat_mark_attribute(ns_mapper, clone, current_ns);
} while ((current_ns = current_ns->next) != NULL);
last_added_attr = clone->properties;
while (last_added_attr->next != NULL) {
last_added_attr = last_added_attr->next;
}
}
/* Attribute cloning logic. */
for (xmlAttrPtr attr = src_node->properties; attr != NULL; attr = attr->next) {
xmlAttrPtr new_attr = (xmlAttrPtr) xmlDocCopyNode((xmlNodePtr) attr, dst_doc, 0);
if (UNEXPECTED(new_attr == NULL)) {
xmlFreeNode(clone);
return NULL;
}
if (last_added_attr == NULL) {
clone->properties = new_attr;
} else {
new_attr->prev = last_added_attr;
last_added_attr->next = new_attr;
}
new_attr->parent = clone;
last_added_attr = new_attr;
/* Set namespace to the original, reconciliation will fix this up. */
new_attr->ns = attr->ns;
}
}
}
return clone;
}
static xmlNodePtr dom_clone_helper(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr src_node, xmlDocPtr dst_doc, bool recursive)
{
xmlNodePtr outer_clone = dom_clone_container_helper(ns_mapper, src_node, dst_doc);
if (!recursive || (src_node->type != XML_ELEMENT_NODE && src_node->type != XML_DOCUMENT_FRAG_NODE && src_node->type != XML_DOCUMENT_NODE && src_node->type != XML_HTML_DOCUMENT_NODE)) {
return outer_clone;
}
/* Handle dtd separately, because it is linked twice and requires a special copy function. */
if (src_node->type == XML_DOCUMENT_NODE || src_node->type == XML_HTML_DOCUMENT_NODE) {
dst_doc = (xmlDocPtr) outer_clone;
xmlDtdPtr original_subset = ((xmlDocPtr) src_node)->intSubset;
if (original_subset != NULL) {
dst_doc->intSubset = xmlCopyDtd(((xmlDocPtr) src_node)->intSubset);
if (UNEXPECTED(dst_doc->intSubset == NULL)) {
xmlFreeNode(outer_clone);
return NULL;
}
dst_doc->intSubset->parent = dst_doc;
xmlSetTreeDoc((xmlNodePtr) dst_doc->intSubset, dst_doc);
dst_doc->children = dst_doc->last = (xmlNodePtr) dst_doc->intSubset;
}
}
xmlNodePtr cloned_parent = outer_clone;
xmlNodePtr base = src_node;
src_node = src_node->children;
while (src_node != NULL) {
ZEND_ASSERT(src_node != base);
xmlNodePtr cloned;
if (src_node->type == XML_ELEMENT_NODE) {
cloned = dom_clone_container_helper(ns_mapper, src_node, dst_doc);
} else if (src_node->type == XML_DTD_NODE) {
/* Already handled. */
cloned = NULL;
} else {
cloned = xmlDocCopyNode(src_node, dst_doc, 1);
}
if (EXPECTED(cloned != NULL)) {
if (cloned_parent->children == NULL) {
cloned_parent->children = cloned;
} else {
cloned->prev = cloned_parent->last;
cloned_parent->last->next = cloned;
}
cloned->parent = cloned_parent;
cloned_parent->last = cloned;
}
if (src_node->type == XML_ELEMENT_NODE && src_node->children) {
cloned_parent = cloned;
src_node = src_node->children;
} else if (src_node->next) {
src_node = src_node->next;
} else {
/* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
do {
src_node = src_node->parent;
if (src_node == base) {
return outer_clone;
}
cloned_parent = cloned_parent->parent;
} while (src_node->next == NULL);
src_node = src_node->next;
}
}
return outer_clone;
}
xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive)
{
if (node->type == XML_DTD_NODE) {
/* The behaviour w.r.t. the internal subset is implementation-defined according to DOM 3.
* This follows what e.g. Java and C# do: copy it regardless of the recursiveness.
* Makes sense as the subset is not exactly a child in the normal sense. */
xmlDtdPtr dtd = xmlCopyDtd((xmlDtdPtr) node);
xmlSetTreeDoc((xmlNodePtr) dtd, doc);
return (xmlNodePtr) dtd;
}
if (ns_mapper != NULL) {
xmlNodePtr clone = dom_clone_helper(ns_mapper, node, doc, recursive);
/* Defensively set doc to NULL because we should not be using it after this point.
* When cloning a document the new document will be clone->doc, not doc. */
doc = NULL;
if (EXPECTED(clone != NULL) && clone->doc != node->doc) {
/* We only need to reconcile the namespace when the document changes because the namespaces have to be
* put into their respective namespace mapper. */
if (clone->type == XML_DOCUMENT_NODE || clone->type == XML_HTML_DOCUMENT_NODE || clone->type == XML_DOCUMENT_FRAG_NODE) {
for (xmlNodePtr child = clone->children; child != NULL; child = child->next) {
php_dom_libxml_reconcile_modern(ns_mapper, child);
}
} else {
php_dom_libxml_reconcile_modern(ns_mapper, clone);
}
}
return clone;
} else {
/* See http://www.xmlsoft.org/html/libxml-tree.html#xmlDocCopyNode for meaning of values */
int extended_recursive = recursive;
if (!recursive && node->type == XML_ELEMENT_NODE) {
extended_recursive = 2;
}
return xmlDocCopyNode(node, doc, extended_recursive);
}
}
bool php_dom_has_child_of_type(xmlNodePtr node, xmlElementType type)
{
xmlNodePtr child = node->children;
while (child != NULL) {
if (child->type == type) {
return true;
}
child = child->next;
}
return false;
}
bool php_dom_has_sibling_following_node(xmlNodePtr node, xmlElementType type)
{
xmlNodePtr next = node->next;
while (next != NULL) {
if (next->type == type) {
return true;
}
next = next->next;
}
return false;
}
bool php_dom_has_sibling_preceding_node(xmlNodePtr node, xmlElementType type)
{
xmlNodePtr prev = node->prev;
while (prev != NULL) {
if (prev->type == type) {
return true;
}
prev = prev->prev;
}
return false;
}
xmlAttrPtr php_dom_get_attribute_node(xmlNodePtr elem, const xmlChar *name, size_t name_len)
{
xmlChar *name_processed = BAD_CAST name;
if (php_dom_ns_is_html_and_document_is_html(elem)) {
char *lowercase_copy = zend_str_tolower_dup_ex((char *) name, name_len);
if (lowercase_copy != NULL) {
name_processed = BAD_CAST lowercase_copy;
}
}
xmlAttrPtr ret = NULL;
for (xmlAttrPtr attr = elem->properties; attr != NULL; attr = attr->next) {
if (dom_match_qualified_name_according_to_spec(name_processed, (xmlNodePtr) attr)) {
ret = attr;
break;
}
}
if (name_processed != name) {
efree(name_processed);
}
return ret;
}
/* Workaround for a libxml2 bug on Windows: https://gitlab.gnome.org/GNOME/libxml2/-/issues/611. */
xmlChar *php_dom_libxml_fix_file_path(xmlChar *path)
{
if (strncmp((char *) path, "file:/", sizeof("file:/") - 1) == 0) {
if (path[6] != '/' && path[6] != '\0' && path[7] != '/' && path[7] != '\0') {
/* The path is file:/xx... where xx != "//", which is broken */
xmlChar *new_path = xmlStrdup(BAD_CAST "file:///");
if (UNEXPECTED(new_path == NULL)) {
return path;
}
new_path = xmlStrcat(new_path, path + 6);
xmlFree(path);
return new_path;
}
}
return path;
}
xmlDocPtr php_dom_create_html_doc(void)
{
#ifdef LIBXML_HTML_ENABLED
xmlDocPtr lxml_doc = htmlNewDocNoDtD(NULL, NULL);
if (EXPECTED(lxml_doc)) {
lxml_doc->dict = xmlDictCreate();
}
#else
/* If HTML support is not enabled, then htmlNewDocNoDtD() is not available.
* This code mimics the behaviour. */
xmlDocPtr lxml_doc = xmlNewDoc((const xmlChar *) "1.0");
if (EXPECTED(lxml_doc)) {
lxml_doc->type = XML_HTML_DOCUMENT_NODE;
lxml_doc->dict = xmlDictCreate();
}
#endif
return lxml_doc;
}
#endif /* HAVE_DOM */