1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Christian Stocker <chregu@php.net> |
14 | Rob Richards <rrichards@php.net> |
15 | Marcus Borger <helly@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "php.h"
24 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
25 #include "php_dom.h"
26 #include "nodelist.h"
27 #include "html_collection.h"
28 #include "namespace_compat.h"
29 #include "internal_helpers.h"
30 #include "php_dom_arginfo.h"
31 #include "dom_properties.h"
32 #include "zend_interfaces.h"
33 #include "lexbor/lexbor/core/types.h"
34 #include "lexbor/lexbor/core/lexbor.h"
35
36 #include "ext/standard/info.h"
37
38 /* {{{ class entries */
39 PHP_DOM_EXPORT zend_class_entry *dom_node_class_entry;
40 PHP_DOM_EXPORT zend_class_entry *dom_modern_node_class_entry;
41 PHP_DOM_EXPORT zend_class_entry *dom_domexception_class_entry;
42 PHP_DOM_EXPORT zend_class_entry *dom_parentnode_class_entry;
43 PHP_DOM_EXPORT zend_class_entry *dom_modern_parentnode_class_entry;
44 PHP_DOM_EXPORT zend_class_entry *dom_childnode_class_entry;
45 PHP_DOM_EXPORT zend_class_entry *dom_modern_childnode_class_entry;
46 PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry;
47 PHP_DOM_EXPORT zend_class_entry *dom_modern_domimplementation_class_entry;
48 PHP_DOM_EXPORT zend_class_entry *dom_documentfragment_class_entry;
49 PHP_DOM_EXPORT zend_class_entry *dom_modern_documentfragment_class_entry;
50 PHP_DOM_EXPORT zend_class_entry *dom_document_class_entry;
51 PHP_DOM_EXPORT zend_class_entry *dom_html_document_class_entry;
52 PHP_DOM_EXPORT zend_class_entry *dom_xml_document_class_entry;
53 PHP_DOM_EXPORT zend_class_entry *dom_nodelist_class_entry;
54 PHP_DOM_EXPORT zend_class_entry *dom_modern_nodelist_class_entry;
55 PHP_DOM_EXPORT zend_class_entry *dom_namednodemap_class_entry;
56 PHP_DOM_EXPORT zend_class_entry *dom_modern_namednodemap_class_entry;
57 PHP_DOM_EXPORT zend_class_entry *dom_modern_dtd_namednodemap_class_entry;
58 PHP_DOM_EXPORT zend_class_entry *dom_html_collection_class_entry;
59 PHP_DOM_EXPORT zend_class_entry *dom_characterdata_class_entry;
60 PHP_DOM_EXPORT zend_class_entry *dom_modern_characterdata_class_entry;
61 PHP_DOM_EXPORT zend_class_entry *dom_attr_class_entry;
62 PHP_DOM_EXPORT zend_class_entry *dom_modern_attr_class_entry;
63 PHP_DOM_EXPORT zend_class_entry *dom_element_class_entry;
64 PHP_DOM_EXPORT zend_class_entry *dom_modern_element_class_entry;
65 PHP_DOM_EXPORT zend_class_entry *dom_text_class_entry;
66 PHP_DOM_EXPORT zend_class_entry *dom_modern_text_class_entry;
67 PHP_DOM_EXPORT zend_class_entry *dom_comment_class_entry;
68 PHP_DOM_EXPORT zend_class_entry *dom_modern_comment_class_entry;
69 PHP_DOM_EXPORT zend_class_entry *dom_cdatasection_class_entry;
70 PHP_DOM_EXPORT zend_class_entry *dom_modern_cdatasection_class_entry;
71 PHP_DOM_EXPORT zend_class_entry *dom_documenttype_class_entry;
72 PHP_DOM_EXPORT zend_class_entry *dom_modern_documenttype_class_entry;
73 PHP_DOM_EXPORT zend_class_entry *dom_notation_class_entry;
74 PHP_DOM_EXPORT zend_class_entry *dom_modern_notation_class_entry;
75 PHP_DOM_EXPORT zend_class_entry *dom_entity_class_entry;
76 PHP_DOM_EXPORT zend_class_entry *dom_modern_entity_class_entry;
77 PHP_DOM_EXPORT zend_class_entry *dom_entityreference_class_entry;
78 PHP_DOM_EXPORT zend_class_entry *dom_modern_entityreference_class_entry;
79 PHP_DOM_EXPORT zend_class_entry *dom_processinginstruction_class_entry;
80 PHP_DOM_EXPORT zend_class_entry *dom_modern_processinginstruction_class_entry;
81 PHP_DOM_EXPORT zend_class_entry *dom_abstract_base_document_class_entry;
82 #ifdef LIBXML_XPATH_ENABLED
83 PHP_DOM_EXPORT zend_class_entry *dom_xpath_class_entry;
84 PHP_DOM_EXPORT zend_class_entry *dom_modern_xpath_class_entry;
85 #endif
86 PHP_DOM_EXPORT zend_class_entry *dom_namespace_node_class_entry;
87 /* }}} */
88
89 static zend_object_handlers dom_object_handlers;
90 static zend_object_handlers dom_nnodemap_object_handlers;
91 static zend_object_handlers dom_nodelist_object_handlers;
92 static zend_object_handlers dom_modern_nnodemap_object_handlers;
93 static zend_object_handlers dom_modern_nodelist_object_handlers;
94 static zend_object_handlers dom_html_collection_object_handlers;
95 static zend_object_handlers dom_object_namespace_node_handlers;
96 static zend_object_handlers dom_modern_domimplementation_object_handlers;
97 #ifdef LIBXML_XPATH_ENABLED
98 zend_object_handlers dom_xpath_object_handlers;
99 #endif
100
101 static HashTable classes;
102 /* {{{ prop handler tables */
103 static HashTable dom_document_prop_handlers;
104 static HashTable dom_xml_document_prop_handlers;
105 static HashTable dom_abstract_base_document_prop_handlers;
106 static HashTable dom_documentfragment_prop_handlers;
107 static HashTable dom_modern_documentfragment_prop_handlers;
108 static HashTable dom_node_prop_handlers;
109 static HashTable dom_modern_node_prop_handlers;
110 static HashTable dom_nodelist_prop_handlers;
111 static HashTable dom_namednodemap_prop_handlers;
112 static HashTable dom_characterdata_prop_handlers;
113 static HashTable dom_modern_characterdata_prop_handlers;
114 static HashTable dom_attr_prop_handlers;
115 static HashTable dom_modern_attr_prop_handlers;
116 static HashTable dom_element_prop_handlers;
117 static HashTable dom_modern_element_prop_handlers;
118 static HashTable dom_modern_element_prop_handlers;
119 static HashTable dom_text_prop_handlers;
120 static HashTable dom_modern_text_prop_handlers;
121 static HashTable dom_documenttype_prop_handlers;
122 static HashTable dom_modern_documenttype_prop_handlers;
123 static HashTable dom_notation_prop_handlers;
124 static HashTable dom_modern_notation_prop_handlers;
125 static HashTable dom_entity_prop_handlers;
126 static HashTable dom_modern_entity_prop_handlers;
127 static HashTable dom_processinginstruction_prop_handlers;
128 static HashTable dom_modern_processinginstruction_prop_handlers;
129 static HashTable dom_namespace_node_prop_handlers;
130 #ifdef LIBXML_XPATH_ENABLED
131 static HashTable dom_xpath_prop_handlers;
132 #endif
133 /* }}} */
134
135 static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type);
136 static void dom_object_namespace_node_free_storage(zend_object *object);
137 static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original);
138
139 typedef zend_result (*dom_read_t)(dom_object *obj, zval *retval);
140 typedef zend_result (*dom_write_t)(dom_object *obj, zval *newval);
141
142 typedef struct _dom_prop_handler {
143 dom_read_t read_func;
144 dom_write_t write_func;
145 } dom_prop_handler;
146
147 /* {{{ int dom_node_is_read_only(xmlNodePtr node) */
dom_node_is_read_only(xmlNodePtr node)148 int dom_node_is_read_only(xmlNodePtr node) {
149 switch (node->type) {
150 case XML_ENTITY_REF_NODE:
151 case XML_ENTITY_NODE:
152 case XML_DOCUMENT_TYPE_NODE:
153 case XML_NOTATION_NODE:
154 case XML_DTD_NODE:
155 case XML_ELEMENT_DECL:
156 case XML_ATTRIBUTE_DECL:
157 case XML_ENTITY_DECL:
158 case XML_NAMESPACE_DECL:
159 return SUCCESS;
160 break;
161 default:
162 if (node->doc == NULL) {
163 return SUCCESS;
164 } else {
165 return FAILURE;
166 }
167 }
168 }
169 /* }}} end dom_node_is_read_only */
170
dom_node_children_valid(xmlNodePtr node)171 bool dom_node_children_valid(xmlNodePtr node) {
172 switch (node->type) {
173 case XML_DOCUMENT_TYPE_NODE:
174 case XML_DTD_NODE:
175 case XML_PI_NODE:
176 case XML_COMMENT_NODE:
177 case XML_TEXT_NODE:
178 case XML_CDATA_SECTION_NODE:
179 case XML_NOTATION_NODE:
180 return false;
181 default:
182 return true;
183 }
184 }
185
186 static const libxml_doc_props default_doc_props = {
187 .formatoutput = false,
188 .validateonparse = false,
189 .resolveexternals = false,
190 .preservewhitespace = true,
191 .substituteentities = false,
192 .stricterror = true,
193 .recover = false,
194 .classmap = NULL,
195 };
196
197 /* {{{ dom_get_doc_props() */
dom_get_doc_props(php_libxml_ref_obj * document)198 dom_doc_propsptr dom_get_doc_props(php_libxml_ref_obj *document)
199 {
200 dom_doc_propsptr doc_props;
201
202 if (document && document->doc_props) {
203 return document->doc_props;
204 } else {
205 doc_props = emalloc(sizeof(libxml_doc_props));
206 memcpy(doc_props, &default_doc_props, sizeof(libxml_doc_props));
207 if (document) {
208 document->doc_props = doc_props;
209 }
210 return doc_props;
211 }
212 }
213 /* }}} */
214
dom_get_doc_props_read_only(const php_libxml_ref_obj * document)215 libxml_doc_props const* dom_get_doc_props_read_only(const php_libxml_ref_obj *document)
216 {
217 if (document && document->doc_props) {
218 return document->doc_props;
219 } else {
220 return &default_doc_props;
221 }
222 }
223
dom_copy_document_ref(php_libxml_ref_obj * source_doc,php_libxml_ref_obj * dest_doc)224 static void dom_copy_document_ref(php_libxml_ref_obj *source_doc, php_libxml_ref_obj *dest_doc)
225 {
226 dom_doc_propsptr dest;
227
228 if (source_doc && dest_doc) {
229
230 libxml_doc_props const* source = dom_get_doc_props_read_only(source_doc);
231 dest = dom_get_doc_props(dest_doc);
232
233 dest->formatoutput = source->formatoutput;
234 dest->validateonparse = source->validateonparse;
235 dest->resolveexternals = source->resolveexternals;
236 dest->preservewhitespace = source->preservewhitespace;
237 dest->substituteentities = source->substituteentities;
238 dest->stricterror = source->stricterror;
239 dest->recover = source->recover;
240 if (source->classmap) {
241 ALLOC_HASHTABLE(dest->classmap);
242 zend_hash_init(dest->classmap, 0, NULL, NULL, false);
243 zend_hash_copy(dest->classmap, source->classmap, NULL);
244 }
245
246 dest_doc->class_type = source_doc->class_type;
247 }
248 }
249
dom_set_doc_classmap(php_libxml_ref_obj * document,zend_class_entry * basece,zend_class_entry * ce)250 void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce)
251 {
252 dom_doc_propsptr doc_props;
253
254 if (document) {
255 doc_props = dom_get_doc_props(document);
256 if (doc_props->classmap == NULL) {
257 if (ce == NULL) {
258 return;
259 }
260 ALLOC_HASHTABLE(doc_props->classmap);
261 zend_hash_init(doc_props->classmap, 0, NULL, NULL, false);
262 }
263 if (ce) {
264 zend_hash_update_ptr(doc_props->classmap, basece->name, ce);
265 } else {
266 zend_hash_del(doc_props->classmap, basece->name);
267 }
268 }
269 }
270
dom_get_doc_classmap(php_libxml_ref_obj * document,zend_class_entry * basece)271 zend_class_entry *dom_get_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece)
272 {
273 if (document) {
274 libxml_doc_props const* doc_props = dom_get_doc_props_read_only(document);
275 if (doc_props->classmap) {
276 zend_class_entry *ce = zend_hash_find_ptr(doc_props->classmap, basece->name);
277 if (ce) {
278 return ce;
279 }
280 }
281 }
282
283 return basece;
284 }
285 /* }}} */
286
287 /* {{{ dom_get_strict_error() */
dom_get_strict_error(php_libxml_ref_obj * document)288 bool dom_get_strict_error(php_libxml_ref_obj *document) {
289 return dom_get_doc_props_read_only(document)->stricterror;
290 }
291 /* }}} */
292
293 /* {{{ xmlNodePtr dom_object_get_node(dom_object *obj) */
dom_object_get_node(dom_object * obj)294 PHP_DOM_EXPORT xmlNodePtr dom_object_get_node(dom_object *obj)
295 {
296 if (obj && obj->ptr != NULL) {
297 return ((php_libxml_node_ptr *)obj->ptr)->node;
298 } else {
299 return NULL;
300 }
301 }
302 /* }}} end dom_object_get_node */
303
304 /* {{{ dom_object *php_dom_object_get_data(xmlNodePtr obj) */
php_dom_object_get_data(xmlNodePtr obj)305 PHP_DOM_EXPORT dom_object *php_dom_object_get_data(xmlNodePtr obj)
306 {
307 if (obj && obj->_private != NULL) {
308 return (dom_object *) ((php_libxml_node_ptr *) obj->_private)->_private;
309 } else {
310 return NULL;
311 }
312 }
313 /* }}} end php_dom_object_get_data */
314
dom_register_prop_handler(HashTable * prop_handler,const char * name,size_t name_len,const dom_prop_handler * hnd)315 static void dom_register_prop_handler(HashTable *prop_handler, const char *name, size_t name_len, const dom_prop_handler *hnd)
316 {
317 zend_string *str = zend_string_init_interned(name, name_len, true);
318 zend_hash_add_new_ptr(prop_handler, str, (void *) hnd);
319 zend_string_release_ex(str, true);
320 }
321
dom_overwrite_prop_handler(HashTable * prop_handler,const char * name,size_t name_len,const dom_prop_handler * hnd)322 static void dom_overwrite_prop_handler(HashTable *prop_handler, const char *name, size_t name_len, const dom_prop_handler *hnd)
323 {
324 zend_hash_str_update_ptr(prop_handler, name, name_len, (void *) hnd);
325 }
326
327 #define DOM_REGISTER_PROP_HANDLER(prop_handler, name, prop_read_func, prop_write_func) do { \
328 static const dom_prop_handler hnd = {.read_func = prop_read_func, .write_func = prop_write_func}; \
329 dom_register_prop_handler(prop_handler, "" name, sizeof("" name) - 1, &hnd); \
330 } while (0)
331
332 #define DOM_OVERWRITE_PROP_HANDLER(prop_handler, name, prop_read_func, prop_write_func) do { \
333 static const dom_prop_handler hnd = {.read_func = prop_read_func, .write_func = prop_write_func}; \
334 dom_overwrite_prop_handler(prop_handler, "" name, sizeof("" name) - 1, &hnd); \
335 } while (0)
336
dom_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)337 static zval *dom_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
338 {
339 dom_object *obj = php_dom_obj_from_obj(object);
340
341 if (!obj->prop_handler || !zend_hash_exists(obj->prop_handler, name)) {
342 return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
343 }
344
345 return NULL;
346 }
347
348 /* {{{ dom_read_property */
dom_read_property(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)349 zval *dom_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
350 {
351 dom_object *obj = php_dom_obj_from_obj(object);
352 zval *retval;
353 dom_prop_handler *hnd = NULL;
354
355 if (obj->prop_handler != NULL) {
356 hnd = zend_hash_find_ptr(obj->prop_handler, name);
357 }
358
359 if (hnd) {
360 int ret = hnd->read_func(obj, rv);
361 if (ret == SUCCESS) {
362 retval = rv;
363 } else {
364 retval = &EG(uninitialized_zval);
365 }
366 } else {
367 retval = zend_std_read_property(object, name, type, cache_slot, rv);
368 }
369
370 return retval;
371 }
372 /* }}} */
373
dom_write_property(zend_object * object,zend_string * name,zval * value,void ** cache_slot)374 zval *dom_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
375 {
376 dom_object *obj = php_dom_obj_from_obj(object);
377 dom_prop_handler *hnd = NULL;
378
379 if (obj->prop_handler != NULL) {
380 hnd = zend_hash_find_ptr(obj->prop_handler, name);
381 }
382
383 if (hnd) {
384 if (!hnd->write_func) {
385 zend_throw_error(NULL, "Cannot modify readonly property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
386 return &EG(error_zval);
387 }
388
389 zend_property_info *prop = zend_get_property_info(object->ce, name, /* silent */ true);
390 ZEND_ASSERT(prop && ZEND_TYPE_IS_SET(prop->type));
391 zval tmp;
392 ZVAL_COPY(&tmp, value);
393 if (!zend_verify_property_type(prop, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) {
394 zval_ptr_dtor(&tmp);
395 return &EG(error_zval);
396 }
397 hnd->write_func(obj, &tmp);
398 zval_ptr_dtor(&tmp);
399
400 return value;
401 }
402
403 return zend_std_write_property(object, name, value, cache_slot);
404 }
405
406 /* {{{ dom_property_exists */
dom_property_exists(zend_object * object,zend_string * name,int check_empty,void ** cache_slot)407 static int dom_property_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
408 {
409 dom_object *obj = php_dom_obj_from_obj(object);
410 dom_prop_handler *hnd = NULL;
411 int retval = 0;
412
413 if (obj->prop_handler != NULL) {
414 hnd = zend_hash_find_ptr(obj->prop_handler, name);
415 }
416 if (hnd) {
417 zval tmp;
418
419 if (check_empty == 2) {
420 retval = 1;
421 } else if (hnd->read_func(obj, &tmp) == SUCCESS) {
422 if (check_empty == 1) {
423 retval = zend_is_true(&tmp);
424 } else if (check_empty == 0) {
425 retval = (Z_TYPE(tmp) != IS_NULL);
426 }
427 zval_ptr_dtor(&tmp);
428 }
429 } else {
430 retval = zend_std_has_property(object, name, check_empty, cache_slot);
431 }
432
433 return retval;
434 }
435 /* }}} */
436
dom_get_debug_info_helper(zend_object * object,int * is_temp)437 static HashTable* dom_get_debug_info_helper(zend_object *object, int *is_temp) /* {{{ */
438 {
439 dom_object *obj = php_dom_obj_from_obj(object);
440 HashTable *debug_info,
441 *prop_handlers = obj->prop_handler,
442 *std_props;
443 zend_string *string_key;
444 dom_prop_handler *entry;
445 zend_string *object_str;
446
447 *is_temp = 1;
448
449 std_props = zend_std_get_properties(object);
450 debug_info = zend_array_dup(std_props);
451
452 if (!prop_handlers) {
453 return debug_info;
454 }
455
456 object_str = ZSTR_INIT_LITERAL("(object value omitted)", false);
457
458 ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(prop_handlers, string_key, entry) {
459 zval value;
460
461 ZEND_ASSERT(string_key != NULL);
462
463 if (entry->read_func(obj, &value) == FAILURE) {
464 continue;
465 }
466
467 if (Z_TYPE(value) == IS_OBJECT) {
468 zval_ptr_dtor(&value);
469 ZVAL_NEW_STR(&value, object_str);
470 zend_string_addref(object_str);
471 }
472
473 zend_hash_update(debug_info, string_key, &value);
474 } ZEND_HASH_FOREACH_END();
475
476 zend_string_release_ex(object_str, false);
477
478 return debug_info;
479 }
480 /* }}} */
481
dom_get_debug_info(zend_object * object,int * is_temp)482 static HashTable* dom_get_debug_info(zend_object *object, int *is_temp) /* {{{ */
483 {
484 return dom_get_debug_info_helper(object, is_temp);
485 }
486 /* }}} */
487
php_dom_export_node(zval * object)488 void *php_dom_export_node(zval *object) /* {{{ */
489 {
490 php_libxml_node_object *intern;
491 xmlNodePtr nodep = NULL;
492
493 intern = (php_libxml_node_object *) Z_DOMOBJ_P(object);
494 if (intern->node) {
495 nodep = intern->node->node;
496 }
497
498 return nodep;
499 }
500 /* }}} */
501
502 /* {{{ Get a simplexml_element object from dom to allow for processing */
dom_import_simplexml_common(INTERNAL_FUNCTION_PARAMETERS,php_libxml_class_type new_class)503 static void dom_import_simplexml_common(INTERNAL_FUNCTION_PARAMETERS, php_libxml_class_type new_class)
504 {
505 zval *node;
506 xmlNodePtr nodep = NULL;
507 php_libxml_node_object *nodeobj;
508
509 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &node) == FAILURE) {
510 RETURN_THROWS();
511 }
512
513 nodeobj = (php_libxml_node_object *) ((char *) Z_OBJ_P(node) - Z_OBJ_HT_P(node)->offset);
514 nodep = php_libxml_import_node(node);
515
516 if (nodep && nodeobj && nodeobj->document && (nodep->type == XML_ELEMENT_NODE || nodep->type == XML_ATTRIBUTE_NODE)) {
517 php_libxml_class_type old_class_type = nodeobj->document->class_type;
518 if (old_class_type != PHP_LIBXML_CLASS_UNSET && old_class_type != new_class) {
519 if (new_class == PHP_LIBXML_CLASS_MODERN) {
520 zend_argument_type_error(1, "must not be already imported as a DOMNode");
521 } else {
522 zend_argument_type_error(1, "must not be already imported as a DOM\\Node");
523 }
524 RETURN_THROWS();
525 }
526
527 /* Lock the node class type to prevent creating multiple representations of the same node. */
528 nodeobj->document->class_type = new_class;
529
530 if (old_class_type != PHP_LIBXML_CLASS_MODERN && new_class == PHP_LIBXML_CLASS_MODERN && nodep->doc != NULL) {
531 dom_document_convert_to_modern(nodeobj->document, nodep->doc);
532 }
533
534 DOM_RET_OBJ((xmlNodePtr) nodep, (dom_object *)nodeobj);
535 } else {
536 zend_argument_type_error(1, "is not a valid node type");
537 RETURN_THROWS();
538 }
539 }
540
PHP_FUNCTION(dom_import_simplexml)541 PHP_FUNCTION(dom_import_simplexml)
542 {
543 dom_import_simplexml_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LIBXML_CLASS_LEGACY);
544 }
545
PHP_FUNCTION(DOM_import_simplexml)546 PHP_FUNCTION(DOM_import_simplexml)
547 {
548 dom_import_simplexml_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LIBXML_CLASS_MODERN);
549 }
550 /* }}} */
551
552 static dom_object* dom_objects_set_class(zend_class_entry *class_type);
553
php_dom_update_document_after_clone(dom_object * original,xmlNodePtr original_node,dom_object * clone,xmlNodePtr cloned_node)554 void php_dom_update_document_after_clone(dom_object *original, xmlNodePtr original_node, dom_object *clone, xmlNodePtr cloned_node)
555 {
556 dom_copy_document_ref(original->document, clone->document);
557 /* Workaround libxml2 bug, see https://gitlab.gnome.org/GNOME/libxml2/-/commit/07920b4381873187c02df53fa9b5d44aff3a7041 */
558 #if LIBXML_VERSION < 20911
559 if (original_node->type == XML_HTML_DOCUMENT_NODE) {
560 cloned_node->type = XML_HTML_DOCUMENT_NODE;
561 }
562 #endif
563 }
564
dom_update_refcount_after_clone(dom_object * original,xmlNodePtr original_node,dom_object * clone,xmlNodePtr cloned_node)565 static void dom_update_refcount_after_clone(dom_object *original, xmlNodePtr original_node, dom_object *clone, xmlNodePtr cloned_node)
566 {
567 /* If we cloned a document then we must create new doc proxy */
568 if (cloned_node->doc == original_node->doc) {
569 clone->document = original->document;
570 }
571 php_libxml_increment_doc_ref((php_libxml_node_object *)clone, cloned_node->doc);
572 php_libxml_increment_node_ptr((php_libxml_node_object *)clone, cloned_node, (void *)clone);
573 if (original->document != clone->document) {
574 php_dom_update_document_after_clone(original, original_node, clone, cloned_node);
575 }
576 }
577
dom_objects_store_clone_obj(zend_object * zobject)578 static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */
579 {
580 dom_object *intern = php_dom_obj_from_obj(zobject);
581 dom_object *clone = dom_objects_set_class(intern->std.ce);
582
583 if (instanceof_function(intern->std.ce, dom_node_class_entry) || instanceof_function(intern->std.ce, dom_modern_node_class_entry)) {
584 xmlNodePtr node = (xmlNodePtr)dom_object_get_node(intern);
585 if (node != NULL) {
586 php_dom_libxml_ns_mapper *ns_mapper = NULL;
587 if (php_dom_follow_spec_intern(intern)) {
588 if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) {
589 ns_mapper = php_dom_libxml_ns_mapper_create();
590 } else {
591 ns_mapper = php_dom_get_ns_mapper(intern);
592 }
593 }
594
595 xmlNodePtr cloned_node = dom_clone_node(ns_mapper, node, node->doc, true);
596 if (cloned_node != NULL) {
597 dom_update_refcount_after_clone(intern, node, clone, cloned_node);
598 }
599 clone->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
600 }
601 }
602
603 zend_objects_clone_members(&clone->std, &intern->std);
604
605 return &clone->std;
606 }
607 /* }}} */
608
dom_object_namespace_node_clone_obj(zend_object * zobject)609 static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject)
610 {
611 dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject);
612 zend_object *clone = dom_objects_namespace_node_new(intern->dom.std.ce);
613 dom_object_namespace_node *clone_intern = php_dom_namespace_node_obj_from_obj(clone);
614
615 xmlNodePtr original_node = dom_object_get_node(&intern->dom);
616 ZEND_ASSERT(original_node->type == XML_NAMESPACE_DECL);
617 xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(original_node->parent, original_node->ns);
618
619 if (intern->parent_intern) {
620 clone_intern->parent_intern = intern->parent_intern;
621 GC_ADDREF(&clone_intern->parent_intern->std);
622 }
623 dom_update_refcount_after_clone(&intern->dom, original_node, &clone_intern->dom, cloned_node);
624
625 zend_objects_clone_members(clone, &intern->dom.std);
626 return clone;
627 }
628
629 static const zend_module_dep dom_deps[] = {
630 ZEND_MOD_REQUIRED("libxml")
631 ZEND_MOD_CONFLICTS("domxml")
632 ZEND_MOD_END
633 };
634
635 zend_module_entry dom_module_entry = { /* {{{ */
636 STANDARD_MODULE_HEADER_EX, NULL,
637 dom_deps,
638 "dom",
639 ext_functions,
640 PHP_MINIT(dom),
641 PHP_MSHUTDOWN(dom),
642 NULL,
643 NULL,
644 PHP_MINFO(dom),
645 DOM_API_VERSION, /* Extension versionnumber */
646 STANDARD_MODULE_PROPERTIES
647 };
648 /* }}} */
649
650 #ifdef COMPILE_DL_DOM
651 ZEND_GET_MODULE(dom)
652 #endif
653
654 void dom_objects_free_storage(zend_object *object);
655 void dom_nnodemap_objects_free_storage(zend_object *object);
656 static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv);
657 static int dom_nodelist_has_dimension(zend_object *object, zval *member, int check_empty);
658 static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv);
659 static int dom_nodemap_has_dimension(zend_object *object, zval *member, int check_empty);
660 static zval *dom_modern_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv);
661 static int dom_modern_nodemap_has_dimension(zend_object *object, zval *member, int check_empty);
662 static zend_object *dom_objects_store_clone_obj(zend_object *zobject);
663
664 #ifdef LIBXML_XPATH_ENABLED
665 void dom_xpath_objects_free_storage(zend_object *object);
666 HashTable *dom_xpath_get_gc(zend_object *object, zval **table, int *n);
667 #endif
668
dom_malloc(size_t size)669 static void *dom_malloc(size_t size) {
670 return emalloc(size);
671 }
672
dom_realloc(void * dst,size_t size)673 static void *dom_realloc(void *dst, size_t size) {
674 return erealloc(dst, size);
675 }
676
dom_calloc(size_t num,size_t size)677 static void *dom_calloc(size_t num, size_t size) {
678 return ecalloc(num, size);
679 }
680
dom_free(void * ptr)681 static void dom_free(void *ptr) {
682 efree(ptr);
683 }
684
685 /* {{{ PHP_MINIT_FUNCTION(dom) */
PHP_MINIT_FUNCTION(dom)686 PHP_MINIT_FUNCTION(dom)
687 {
688 memcpy(&dom_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
689 dom_object_handlers.offset = XtOffsetOf(dom_object, std);
690 dom_object_handlers.free_obj = dom_objects_free_storage;
691 dom_object_handlers.read_property = dom_read_property;
692 dom_object_handlers.write_property = dom_write_property;
693 dom_object_handlers.get_property_ptr_ptr = dom_get_property_ptr_ptr;
694 dom_object_handlers.clone_obj = dom_objects_store_clone_obj;
695 dom_object_handlers.has_property = dom_property_exists;
696 dom_object_handlers.get_debug_info = dom_get_debug_info;
697
698 memcpy(&dom_modern_domimplementation_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
699 /* The IDL has the [SameObject] constraint, which is incompatible with cloning because it imposes that there is only
700 * one instance per parent object. */
701 dom_modern_domimplementation_object_handlers.clone_obj = NULL;
702
703 memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
704 dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage;
705 dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension;
706 dom_nnodemap_object_handlers.has_dimension = dom_nodemap_has_dimension;
707
708 memcpy(&dom_nodelist_object_handlers, &dom_nnodemap_object_handlers, sizeof(zend_object_handlers));
709 dom_nodelist_object_handlers.read_dimension = dom_nodelist_read_dimension;
710 dom_nodelist_object_handlers.has_dimension = dom_nodelist_has_dimension;
711
712 memcpy(&dom_modern_nnodemap_object_handlers, &dom_nnodemap_object_handlers, sizeof(zend_object_handlers));
713 dom_modern_nnodemap_object_handlers.read_dimension = dom_modern_nodemap_read_dimension;
714 dom_modern_nnodemap_object_handlers.has_dimension = dom_modern_nodemap_has_dimension;
715
716 memcpy(&dom_modern_nodelist_object_handlers, &dom_nodelist_object_handlers, sizeof(zend_object_handlers));
717 dom_modern_nodelist_object_handlers.read_dimension = dom_modern_nodelist_read_dimension;
718 dom_modern_nodelist_object_handlers.has_dimension = dom_modern_nodelist_has_dimension;
719
720 memcpy(&dom_html_collection_object_handlers, &dom_modern_nodelist_object_handlers, sizeof(zend_object_handlers));
721 dom_html_collection_object_handlers.read_dimension = dom_html_collection_read_dimension;
722 dom_html_collection_object_handlers.has_dimension = dom_html_collection_has_dimension;
723
724 memcpy(&dom_object_namespace_node_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
725 dom_object_namespace_node_handlers.offset = XtOffsetOf(dom_object_namespace_node, dom.std);
726 dom_object_namespace_node_handlers.free_obj = dom_object_namespace_node_free_storage;
727 dom_object_namespace_node_handlers.clone_obj = dom_object_namespace_node_clone_obj;
728
729 zend_hash_init(&classes, 0, NULL, NULL, true);
730
731 dom_domexception_class_entry = register_class_DOMException(zend_ce_exception);
732
733 dom_parentnode_class_entry = register_class_DOMParentNode();
734 dom_modern_parentnode_class_entry = register_class_DOM_ParentNode();
735 dom_childnode_class_entry = register_class_DOMChildNode();
736 dom_modern_childnode_class_entry = register_class_DOM_ChildNode();
737
738 dom_domimplementation_class_entry = register_class_DOMImplementation();
739 dom_domimplementation_class_entry->create_object = dom_objects_new;
740 dom_domimplementation_class_entry->default_object_handlers = &dom_object_handlers;
741
742 dom_modern_domimplementation_class_entry = register_class_DOM_Implementation();
743 dom_modern_domimplementation_class_entry->create_object = dom_objects_new;
744 dom_modern_domimplementation_class_entry->default_object_handlers = &dom_modern_domimplementation_object_handlers;
745
746 dom_node_class_entry = register_class_DOMNode();
747 dom_node_class_entry->create_object = dom_objects_new;
748 dom_node_class_entry->default_object_handlers = &dom_object_handlers;
749
750 zend_hash_init(&dom_node_prop_handlers, 0, NULL, NULL, true);
751 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL);
752 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeValue", dom_node_node_value_read, dom_node_node_value_write);
753 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL);
754 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL);
755 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL);
756 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "childNodes", dom_node_child_nodes_read, NULL);
757 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "firstChild", dom_node_first_child_read, NULL);
758 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "lastChild", dom_node_last_child_read, NULL);
759 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "previousSibling", dom_node_previous_sibling_read, NULL);
760 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nextSibling", dom_node_next_sibling_read, NULL);
761 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "attributes", dom_node_attributes_read, NULL);
762 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL);
763 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL);
764 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
765 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "prefix", dom_node_prefix_read, dom_node_prefix_write);
766 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "localName", dom_node_local_name_read, NULL);
767 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "baseURI", dom_node_base_uri_read, NULL);
768 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
769 zend_hash_add_new_ptr(&classes, dom_node_class_entry->name, &dom_node_prop_handlers);
770
771 dom_modern_node_class_entry = register_class_DOM_Node();
772 dom_modern_node_class_entry->create_object = dom_objects_new;
773 dom_modern_node_class_entry->default_object_handlers = &dom_object_handlers;
774
775 zend_hash_init(&dom_modern_node_prop_handlers, 0, NULL, NULL, true);
776 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL);
777 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL);
778 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "baseURI", dom_node_base_uri_read, NULL);
779 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL);
780 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL);
781 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL);
782 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL);
783 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "childNodes", dom_node_child_nodes_read, NULL);
784 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "firstChild", dom_node_first_child_read, NULL);
785 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "lastChild", dom_node_last_child_read, NULL);
786 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "previousSibling", dom_node_previous_sibling_read, NULL);
787 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nextSibling", dom_node_next_sibling_read, NULL);
788 /* We will set-up the setter for the derived classes afterwards on a class-by-class basis. */
789 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeValue", dom_node_node_value_read, NULL);
790 DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "textContent", dom_node_text_content_read, NULL);
791 zend_hash_add_new_ptr(&classes, dom_modern_node_class_entry->name, &dom_modern_node_prop_handlers);
792
793 dom_namespace_node_class_entry = register_class_DOMNameSpaceNode();
794 dom_namespace_node_class_entry->create_object = dom_objects_namespace_node_new;
795 dom_namespace_node_class_entry->default_object_handlers = &dom_object_namespace_node_handlers;
796
797 zend_hash_init(&dom_namespace_node_prop_handlers, 0, NULL, NULL, true);
798 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL);
799 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeValue", dom_node_node_value_read, NULL);
800 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL);
801 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "prefix", dom_node_prefix_read, NULL);
802 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "localName", dom_node_local_name_read, NULL);
803 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
804 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL);
805 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL);
806 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL);
807 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL);
808 zend_hash_add_new_ptr(&classes, dom_namespace_node_class_entry->name, &dom_namespace_node_prop_handlers);
809
810 dom_documentfragment_class_entry = register_class_DOMDocumentFragment(dom_node_class_entry, dom_parentnode_class_entry);
811 dom_documentfragment_class_entry->create_object = dom_objects_new;
812 dom_documentfragment_class_entry->default_object_handlers = &dom_object_handlers;
813 zend_hash_init(&dom_documentfragment_prop_handlers, 0, NULL, NULL, true);
814
815 DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
816 DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
817 DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
818 zend_hash_merge(&dom_documentfragment_prop_handlers, &dom_node_prop_handlers, NULL, false);
819 zend_hash_add_new_ptr(&classes, dom_documentfragment_class_entry->name, &dom_documentfragment_prop_handlers);
820
821 dom_modern_documentfragment_class_entry = register_class_DOM_DocumentFragment(dom_modern_node_class_entry, dom_modern_parentnode_class_entry);
822 dom_modern_documentfragment_class_entry->create_object = dom_objects_new;
823 dom_modern_documentfragment_class_entry->default_object_handlers = &dom_object_handlers;
824 zend_hash_init(&dom_modern_documentfragment_prop_handlers, 0, NULL, NULL, true);
825
826 DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
827 DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
828 DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
829 zend_hash_merge(&dom_modern_documentfragment_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
830 DOM_OVERWRITE_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
831 zend_hash_add_new_ptr(&classes, dom_modern_documentfragment_class_entry->name, &dom_modern_documentfragment_prop_handlers);
832
833 dom_abstract_base_document_class_entry = register_class_DOM_Document(dom_modern_node_class_entry, dom_modern_parentnode_class_entry);
834 dom_abstract_base_document_class_entry->default_object_handlers = &dom_object_handlers;
835 zend_hash_init(&dom_abstract_base_document_prop_handlers, 0, NULL, NULL, true);
836 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "implementation", dom_modern_document_implementation_read, NULL);
837 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "URL", dom_document_document_uri_read, dom_document_document_uri_write);
838 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentURI", dom_document_document_uri_read, dom_document_document_uri_write);
839 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "characterSet", dom_document_encoding_read, dom_html_document_encoding_write);
840 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "charset", dom_document_encoding_read, dom_html_document_encoding_write);
841 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "inputEncoding", dom_document_encoding_read, dom_html_document_encoding_write);
842 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "doctype", dom_document_doctype_read, NULL);
843 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentElement", dom_document_document_element_read, NULL);
844 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
845 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
846 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
847 zend_hash_merge(&dom_abstract_base_document_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
848 /* No need to register in &classes because this is an abstract class handler. */
849
850 dom_document_class_entry = register_class_DOMDocument(dom_node_class_entry, dom_parentnode_class_entry);
851 dom_document_class_entry->create_object = dom_objects_new;
852 dom_document_class_entry->default_object_handlers = &dom_object_handlers;
853 zend_hash_init(&dom_document_prop_handlers, 0, NULL, NULL, true);
854 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "doctype", dom_document_doctype_read, NULL);
855 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "implementation", dom_document_implementation_read, NULL);
856 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "documentElement", dom_document_document_element_read, NULL);
857 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "actualEncoding", dom_document_encoding_read, NULL);
858 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "encoding", dom_document_encoding_read, dom_document_encoding_write);
859 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "xmlEncoding", dom_document_encoding_read, NULL);
860 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "standalone", dom_document_standalone_read, dom_document_standalone_write);
861 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "xmlStandalone", dom_document_standalone_read, dom_document_standalone_write);
862 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "version", dom_document_version_read, dom_document_version_write);
863 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "xmlVersion", dom_document_version_read, dom_document_version_write);
864 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "strictErrorChecking", dom_document_strict_error_checking_read, dom_document_strict_error_checking_write);
865 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "documentURI", dom_document_document_uri_read, dom_document_document_uri_write);
866 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "config", dom_document_config_read, NULL);
867 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "formatOutput", dom_document_format_output_read, dom_document_format_output_write);
868 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "validateOnParse", dom_document_validate_on_parse_read, dom_document_validate_on_parse_write);
869 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "resolveExternals", dom_document_resolve_externals_read, dom_document_resolve_externals_write);
870 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "preserveWhiteSpace", dom_document_preserve_whitespace_read, dom_document_preserve_whitespace_write);
871 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "recover", dom_document_recover_read, dom_document_recover_write);
872 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "substituteEntities", dom_document_substitue_entities_read, dom_document_substitue_entities_write);
873 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
874 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
875 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
876
877 zend_hash_merge(&dom_document_prop_handlers, &dom_node_prop_handlers, NULL, false);
878 zend_hash_add_new_ptr(&classes, dom_document_class_entry->name, &dom_document_prop_handlers);
879
880 dom_html_document_class_entry = register_class_DOM_HTMLDocument(dom_abstract_base_document_class_entry);
881 dom_html_document_class_entry->default_object_handlers = &dom_object_handlers;
882 zend_hash_add_new_ptr(&classes, dom_html_document_class_entry->name, &dom_abstract_base_document_prop_handlers);
883
884 dom_xml_document_class_entry = register_class_DOM_XMLDocument(dom_abstract_base_document_class_entry);
885 dom_xml_document_class_entry->default_object_handlers = &dom_object_handlers;
886 zend_hash_init(&dom_xml_document_prop_handlers, 0, NULL, NULL, true);
887 DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "xmlEncoding", dom_document_encoding_read, NULL);
888 DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "xmlStandalone", dom_document_standalone_read, dom_document_standalone_write);
889 DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "xmlVersion", dom_document_version_read, dom_document_version_write);
890 DOM_REGISTER_PROP_HANDLER(&dom_xml_document_prop_handlers, "formatOutput", dom_document_format_output_read, dom_document_format_output_write);
891
892 zend_hash_merge(&dom_xml_document_prop_handlers, &dom_abstract_base_document_prop_handlers, NULL, false);
893 zend_hash_add_new_ptr(&classes, dom_xml_document_class_entry->name, &dom_xml_document_prop_handlers);
894
895 dom_nodelist_class_entry = register_class_DOMNodeList(zend_ce_aggregate, zend_ce_countable);
896 dom_nodelist_class_entry->create_object = dom_nnodemap_objects_new;
897 dom_nodelist_class_entry->default_object_handlers = &dom_nodelist_object_handlers;
898 dom_nodelist_class_entry->get_iterator = php_dom_get_iterator;
899
900 zend_hash_init(&dom_nodelist_prop_handlers, 0, NULL, NULL, true);
901 DOM_REGISTER_PROP_HANDLER(&dom_nodelist_prop_handlers, "length", dom_nodelist_length_read, NULL);
902 zend_hash_add_new_ptr(&classes, dom_nodelist_class_entry->name, &dom_nodelist_prop_handlers);
903
904 dom_modern_nodelist_class_entry = register_class_DOM_NodeList(zend_ce_aggregate, zend_ce_countable);
905 dom_modern_nodelist_class_entry->create_object = dom_nnodemap_objects_new;
906 dom_modern_nodelist_class_entry->default_object_handlers = &dom_modern_nodelist_object_handlers;
907 dom_modern_nodelist_class_entry->get_iterator = php_dom_get_iterator;
908
909 zend_hash_add_new_ptr(&classes, dom_modern_nodelist_class_entry->name, &dom_nodelist_prop_handlers);
910
911 dom_namednodemap_class_entry = register_class_DOMNamedNodeMap(zend_ce_aggregate, zend_ce_countable);
912 dom_namednodemap_class_entry->create_object = dom_nnodemap_objects_new;
913 dom_namednodemap_class_entry->default_object_handlers = &dom_nnodemap_object_handlers;
914 dom_namednodemap_class_entry->get_iterator = php_dom_get_iterator;
915
916 zend_hash_init(&dom_namednodemap_prop_handlers, 0, NULL, NULL, true);
917 DOM_REGISTER_PROP_HANDLER(&dom_namednodemap_prop_handlers, "length", dom_namednodemap_length_read, NULL);
918 zend_hash_add_new_ptr(&classes, dom_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers);
919
920 dom_modern_namednodemap_class_entry = register_class_DOM_NamedNodeMap(zend_ce_aggregate, zend_ce_countable);
921 dom_modern_namednodemap_class_entry->create_object = dom_nnodemap_objects_new;
922 dom_modern_namednodemap_class_entry->default_object_handlers = &dom_modern_nnodemap_object_handlers;
923 dom_modern_namednodemap_class_entry->get_iterator = php_dom_get_iterator;
924
925 zend_hash_add_new_ptr(&classes, dom_modern_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers);
926
927 dom_modern_dtd_namednodemap_class_entry = register_class_DOM_DTDNamedNodeMap(zend_ce_aggregate, zend_ce_countable);
928 dom_modern_dtd_namednodemap_class_entry->create_object = dom_nnodemap_objects_new;
929 dom_modern_dtd_namednodemap_class_entry->default_object_handlers = &dom_modern_nnodemap_object_handlers;
930 dom_modern_dtd_namednodemap_class_entry->get_iterator = php_dom_get_iterator;
931
932 zend_hash_add_new_ptr(&classes, dom_modern_dtd_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers);
933
934 dom_html_collection_class_entry = register_class_DOM_HTMLCollection(zend_ce_aggregate, zend_ce_countable);
935 dom_html_collection_class_entry->create_object = dom_nnodemap_objects_new;
936 dom_html_collection_class_entry->default_object_handlers = &dom_html_collection_object_handlers;
937 dom_html_collection_class_entry->get_iterator = php_dom_get_iterator;
938
939 zend_hash_add_new_ptr(&classes, dom_html_collection_class_entry->name, &dom_nodelist_prop_handlers);
940
941 dom_characterdata_class_entry = register_class_DOMCharacterData(dom_node_class_entry, dom_childnode_class_entry);
942 dom_characterdata_class_entry->create_object = dom_objects_new;
943 dom_characterdata_class_entry->default_object_handlers = &dom_object_handlers;
944
945 zend_hash_init(&dom_characterdata_prop_handlers, 0, NULL, NULL, true);
946 DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "data", dom_characterdata_data_read, dom_characterdata_data_write);
947 DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "length", dom_characterdata_length_read, NULL);
948 DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
949 DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
950 zend_hash_merge(&dom_characterdata_prop_handlers, &dom_node_prop_handlers, NULL, false);
951 zend_hash_add_new_ptr(&classes, dom_characterdata_class_entry->name, &dom_characterdata_prop_handlers);
952
953 dom_modern_characterdata_class_entry = register_class_DOM_CharacterData(dom_modern_node_class_entry, dom_modern_childnode_class_entry);
954 dom_modern_characterdata_class_entry->create_object = dom_objects_new;
955 dom_modern_characterdata_class_entry->default_object_handlers = &dom_object_handlers;
956
957 zend_hash_init(&dom_modern_characterdata_prop_handlers, 0, NULL, NULL, true);
958 DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "data", dom_characterdata_data_read, dom_characterdata_data_write);
959 DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "length", dom_characterdata_length_read, NULL);
960 DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
961 DOM_REGISTER_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
962 zend_hash_merge(&dom_modern_characterdata_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
963 DOM_OVERWRITE_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "nodeValue", dom_node_node_value_read, dom_node_node_value_write);
964 DOM_OVERWRITE_PROP_HANDLER(&dom_modern_characterdata_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
965 zend_hash_add_new_ptr(&classes, dom_modern_characterdata_class_entry->name, &dom_modern_characterdata_prop_handlers);
966
967 dom_attr_class_entry = register_class_DOMAttr(dom_node_class_entry);
968 dom_attr_class_entry->create_object = dom_objects_new;
969 dom_attr_class_entry->default_object_handlers = &dom_object_handlers;
970
971 zend_hash_init(&dom_attr_prop_handlers, 0, NULL, NULL, true);
972 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "name", dom_attr_name_read, NULL);
973 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "specified", dom_attr_specified_read, NULL);
974 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "value", dom_attr_value_read, dom_attr_value_write);
975 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "ownerElement", dom_attr_owner_element_read, NULL);
976 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "schemaTypeInfo", dom_attr_schema_type_info_read, NULL);
977 zend_hash_merge(&dom_attr_prop_handlers, &dom_node_prop_handlers, NULL, false);
978 zend_hash_add_new_ptr(&classes, dom_attr_class_entry->name, &dom_attr_prop_handlers);
979
980 dom_modern_attr_class_entry = register_class_DOM_Attr(dom_modern_node_class_entry);
981 dom_modern_attr_class_entry->create_object = dom_objects_new;
982 dom_modern_attr_class_entry->default_object_handlers = &dom_object_handlers;
983
984 zend_hash_init(&dom_modern_attr_prop_handlers, 0, NULL, NULL, true);
985 DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
986 DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "prefix", dom_modern_node_prefix_read, NULL);
987 DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "localName", dom_node_local_name_read, NULL);
988 DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "name", dom_attr_name_read, NULL);
989 DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "value", dom_attr_value_read, dom_attr_value_write);
990 DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "ownerElement", dom_attr_owner_element_read, NULL);
991 DOM_REGISTER_PROP_HANDLER(&dom_modern_attr_prop_handlers, "specified", dom_attr_specified_read, NULL);
992 zend_hash_merge(&dom_modern_attr_prop_handlers, &dom_node_prop_handlers, NULL, false);
993 DOM_OVERWRITE_PROP_HANDLER(&dom_modern_attr_prop_handlers, "nodeValue", dom_node_node_value_read, dom_node_node_value_write);
994 DOM_OVERWRITE_PROP_HANDLER(&dom_modern_attr_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
995 zend_hash_add_new_ptr(&classes, dom_modern_attr_class_entry->name, &dom_modern_attr_prop_handlers);
996
997 dom_element_class_entry = register_class_DOMElement(dom_node_class_entry, dom_parentnode_class_entry, dom_childnode_class_entry);
998 dom_element_class_entry->create_object = dom_objects_new;
999 dom_element_class_entry->default_object_handlers = &dom_object_handlers;
1000
1001 zend_hash_init(&dom_element_prop_handlers, 0, NULL, NULL, true);
1002 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "tagName", dom_element_tag_name_read, NULL);
1003 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "className", dom_element_class_name_read, dom_element_class_name_write);
1004 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "id", dom_element_id_read, dom_element_id_write);
1005 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "schemaTypeInfo", dom_element_schema_type_info_read, NULL);
1006 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
1007 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
1008 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
1009 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
1010 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
1011 zend_hash_merge(&dom_element_prop_handlers, &dom_node_prop_handlers, NULL, false);
1012 zend_hash_add_new_ptr(&classes, dom_element_class_entry->name, &dom_element_prop_handlers);
1013
1014 dom_modern_element_class_entry = register_class_DOM_Element(dom_modern_node_class_entry, dom_modern_parentnode_class_entry, dom_modern_childnode_class_entry);
1015 dom_modern_element_class_entry->create_object = dom_objects_new;
1016 dom_modern_element_class_entry->default_object_handlers = &dom_object_handlers;
1017
1018 zend_hash_init(&dom_modern_element_prop_handlers, 0, NULL, NULL, true);
1019 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
1020 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "prefix", dom_modern_node_prefix_read, NULL);
1021 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "localName", dom_node_local_name_read, NULL);
1022 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "tagName", dom_element_tag_name_read, NULL);
1023 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "id", dom_element_id_read, dom_element_id_write);
1024 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "className", dom_element_class_name_read, dom_element_class_name_write);
1025 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "attributes", dom_node_attributes_read, NULL);
1026 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
1027 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
1028 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
1029 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
1030 DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
1031 zend_hash_merge(&dom_modern_element_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
1032 DOM_OVERWRITE_PROP_HANDLER(&dom_modern_element_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
1033 zend_hash_add_new_ptr(&classes, dom_modern_element_class_entry->name, &dom_modern_element_prop_handlers);
1034
1035 dom_text_class_entry = register_class_DOMText(dom_characterdata_class_entry);
1036 dom_text_class_entry->create_object = dom_objects_new;
1037 dom_text_class_entry->default_object_handlers = &dom_object_handlers;
1038
1039 zend_hash_init(&dom_text_prop_handlers, 0, NULL, NULL, true);
1040 DOM_REGISTER_PROP_HANDLER(&dom_text_prop_handlers, "wholeText", dom_text_whole_text_read, NULL);
1041 zend_hash_merge(&dom_text_prop_handlers, &dom_characterdata_prop_handlers, NULL, false);
1042 zend_hash_add_new_ptr(&classes, dom_text_class_entry->name, &dom_text_prop_handlers);
1043
1044 dom_modern_text_class_entry = register_class_DOM_Text(dom_modern_characterdata_class_entry);
1045 dom_modern_text_class_entry->create_object = dom_objects_new;
1046 dom_modern_text_class_entry->default_object_handlers = &dom_object_handlers;
1047
1048 zend_hash_init(&dom_modern_text_prop_handlers, 0, NULL, NULL, true);
1049 DOM_REGISTER_PROP_HANDLER(&dom_modern_text_prop_handlers, "wholeText", dom_text_whole_text_read, NULL);
1050 zend_hash_merge(&dom_modern_text_prop_handlers, &dom_modern_characterdata_prop_handlers, NULL, false);
1051 zend_hash_add_new_ptr(&classes, dom_modern_text_class_entry->name, &dom_modern_text_prop_handlers);
1052
1053 dom_comment_class_entry = register_class_DOMComment(dom_characterdata_class_entry);
1054 dom_comment_class_entry->create_object = dom_objects_new;
1055 dom_comment_class_entry->default_object_handlers = &dom_object_handlers;
1056 zend_hash_add_new_ptr(&classes, dom_comment_class_entry->name, &dom_characterdata_prop_handlers);
1057
1058 dom_modern_comment_class_entry = register_class_DOM_Comment(dom_modern_characterdata_class_entry);
1059 dom_modern_comment_class_entry->create_object = dom_objects_new;
1060 dom_modern_comment_class_entry->default_object_handlers = &dom_object_handlers;
1061 zend_hash_add_new_ptr(&classes, dom_modern_comment_class_entry->name, &dom_modern_characterdata_prop_handlers);
1062
1063 dom_cdatasection_class_entry = register_class_DOMCdataSection(dom_text_class_entry);
1064 dom_cdatasection_class_entry->create_object = dom_objects_new;
1065 dom_cdatasection_class_entry->default_object_handlers = &dom_object_handlers;
1066 zend_hash_add_new_ptr(&classes, dom_cdatasection_class_entry->name, &dom_text_prop_handlers);
1067
1068 dom_modern_cdatasection_class_entry = register_class_DOM_CDATASection(dom_modern_text_class_entry);
1069 dom_modern_cdatasection_class_entry->create_object = dom_objects_new;
1070 dom_modern_cdatasection_class_entry->default_object_handlers = &dom_object_handlers;
1071 zend_hash_add_new_ptr(&classes, dom_modern_cdatasection_class_entry->name, &dom_modern_text_prop_handlers);
1072
1073 dom_documenttype_class_entry = register_class_DOMDocumentType(dom_node_class_entry);
1074 dom_documenttype_class_entry->create_object = dom_objects_new;
1075 dom_documenttype_class_entry->default_object_handlers = &dom_object_handlers;
1076
1077 zend_hash_init(&dom_documenttype_prop_handlers, 0, NULL, NULL, true);
1078 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "name", dom_documenttype_name_read, NULL);
1079 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "entities", dom_documenttype_entities_read, NULL);
1080 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "notations", dom_documenttype_notations_read, NULL);
1081 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "publicId", dom_documenttype_public_id_read, NULL);
1082 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "systemId", dom_documenttype_system_id_read, NULL);
1083 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "internalSubset", dom_documenttype_internal_subset_read, NULL);
1084 zend_hash_merge(&dom_documenttype_prop_handlers, &dom_node_prop_handlers, NULL, false);
1085 zend_hash_add_new_ptr(&classes, dom_documenttype_class_entry->name, &dom_documenttype_prop_handlers);
1086
1087 dom_modern_documenttype_class_entry = register_class_DOM_DocumentType(dom_modern_node_class_entry, dom_modern_childnode_class_entry);
1088 dom_modern_documenttype_class_entry->create_object = dom_objects_new;
1089 dom_modern_documenttype_class_entry->default_object_handlers = &dom_object_handlers;
1090
1091 zend_hash_init(&dom_modern_documenttype_prop_handlers, 0, NULL, NULL, true);
1092 DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "name", dom_documenttype_name_read, NULL);
1093 DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "entities", dom_documenttype_entities_read, NULL);
1094 DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "notations", dom_documenttype_notations_read, NULL);
1095 DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "publicId", dom_documenttype_public_id_read, NULL);
1096 DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "systemId", dom_documenttype_system_id_read, NULL);
1097 DOM_REGISTER_PROP_HANDLER(&dom_modern_documenttype_prop_handlers, "internalSubset", dom_documenttype_internal_subset_read, NULL);
1098 zend_hash_merge(&dom_modern_documenttype_prop_handlers, &dom_node_prop_handlers, NULL, false);
1099 zend_hash_add_new_ptr(&classes, dom_modern_documenttype_class_entry->name, &dom_modern_documenttype_prop_handlers);
1100
1101 dom_notation_class_entry = register_class_DOMNotation(dom_node_class_entry);
1102 dom_notation_class_entry->create_object = dom_objects_new;
1103 dom_notation_class_entry->default_object_handlers = &dom_object_handlers;
1104
1105 zend_hash_init(&dom_notation_prop_handlers, 0, NULL, NULL, true);
1106 DOM_REGISTER_PROP_HANDLER(&dom_notation_prop_handlers, "publicId", dom_notation_public_id_read, NULL);
1107 DOM_REGISTER_PROP_HANDLER(&dom_notation_prop_handlers, "systemId", dom_notation_system_id_read, NULL);
1108 zend_hash_merge(&dom_notation_prop_handlers, &dom_node_prop_handlers, NULL, false);
1109 zend_hash_add_new_ptr(&classes, dom_notation_class_entry->name, &dom_notation_prop_handlers);
1110
1111 dom_modern_notation_class_entry = register_class_DOM_Notation(dom_modern_node_class_entry);
1112 dom_modern_notation_class_entry->create_object = dom_objects_new;
1113 dom_modern_notation_class_entry->default_object_handlers = &dom_object_handlers;
1114
1115 zend_hash_init(&dom_modern_notation_prop_handlers, 0, NULL, NULL, true);
1116 DOM_REGISTER_PROP_HANDLER(&dom_modern_notation_prop_handlers, "publicId", dom_notation_public_id_read, NULL);
1117 DOM_REGISTER_PROP_HANDLER(&dom_modern_notation_prop_handlers, "systemId", dom_notation_system_id_read, NULL);
1118 zend_hash_merge(&dom_modern_notation_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
1119 zend_hash_add_new_ptr(&classes, dom_modern_notation_class_entry->name, &dom_modern_node_prop_handlers);
1120
1121 dom_entity_class_entry = register_class_DOMEntity(dom_node_class_entry);
1122 dom_entity_class_entry->create_object = dom_objects_new;
1123 dom_entity_class_entry->default_object_handlers = &dom_object_handlers;
1124
1125 zend_hash_init(&dom_entity_prop_handlers, 0, NULL, NULL, true);
1126 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "publicId", dom_entity_public_id_read, NULL);
1127 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "systemId", dom_entity_system_id_read, NULL);
1128 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "notationName", dom_entity_notation_name_read, NULL);
1129 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "actualEncoding", dom_entity_actual_encoding_read, NULL);
1130 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "encoding", dom_entity_encoding_read, NULL);
1131 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "version", dom_entity_version_read, NULL);
1132 zend_hash_merge(&dom_entity_prop_handlers, &dom_node_prop_handlers, NULL, false);
1133 zend_hash_add_new_ptr(&classes, dom_entity_class_entry->name, &dom_entity_prop_handlers);
1134
1135 dom_modern_entity_class_entry = register_class_DOM_Entity(dom_modern_node_class_entry);
1136 dom_modern_entity_class_entry->create_object = dom_objects_new;
1137 dom_modern_entity_class_entry->default_object_handlers = &dom_object_handlers;
1138
1139 zend_hash_init(&dom_modern_entity_prop_handlers, 0, NULL, NULL, true);
1140 DOM_REGISTER_PROP_HANDLER(&dom_modern_entity_prop_handlers, "publicId", dom_entity_public_id_read, NULL);
1141 DOM_REGISTER_PROP_HANDLER(&dom_modern_entity_prop_handlers, "systemId", dom_entity_system_id_read, NULL);
1142 DOM_REGISTER_PROP_HANDLER(&dom_modern_entity_prop_handlers, "notationName", dom_entity_notation_name_read, NULL);
1143 zend_hash_merge(&dom_modern_entity_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
1144 zend_hash_add_new_ptr(&classes, dom_modern_entity_class_entry->name, &dom_modern_entity_prop_handlers);
1145
1146 dom_entityreference_class_entry = register_class_DOMEntityReference(dom_node_class_entry);
1147 dom_entityreference_class_entry->create_object = dom_objects_new;
1148 dom_entityreference_class_entry->default_object_handlers = &dom_object_handlers;
1149 zend_hash_add_new_ptr(&classes, dom_entityreference_class_entry->name, &dom_node_prop_handlers);
1150
1151 dom_modern_entityreference_class_entry = register_class_DOM_EntityReference(dom_modern_node_class_entry);
1152 dom_modern_entityreference_class_entry->create_object = dom_objects_new;
1153 dom_modern_entityreference_class_entry->default_object_handlers = &dom_object_handlers;
1154 zend_hash_add_new_ptr(&classes, dom_modern_entityreference_class_entry->name, &dom_modern_node_prop_handlers);
1155
1156 dom_processinginstruction_class_entry = register_class_DOMProcessingInstruction(dom_node_class_entry);
1157 dom_processinginstruction_class_entry->create_object = dom_objects_new;
1158 dom_processinginstruction_class_entry->default_object_handlers = &dom_object_handlers;
1159
1160 zend_hash_init(&dom_processinginstruction_prop_handlers, 0, NULL, NULL, true);
1161 DOM_REGISTER_PROP_HANDLER(&dom_processinginstruction_prop_handlers, "target", dom_processinginstruction_target_read, NULL);
1162 DOM_REGISTER_PROP_HANDLER(&dom_processinginstruction_prop_handlers, "data", dom_processinginstruction_data_read, dom_processinginstruction_data_write);
1163 zend_hash_merge(&dom_processinginstruction_prop_handlers, &dom_node_prop_handlers, NULL, false);
1164 zend_hash_add_new_ptr(&classes, dom_processinginstruction_class_entry->name, &dom_processinginstruction_prop_handlers);
1165
1166 dom_modern_processinginstruction_class_entry = register_class_DOM_ProcessingInstruction(dom_modern_characterdata_class_entry);
1167 dom_modern_processinginstruction_class_entry->create_object = dom_objects_new;
1168 dom_modern_processinginstruction_class_entry->default_object_handlers = &dom_object_handlers;
1169
1170 zend_hash_init(&dom_modern_processinginstruction_prop_handlers, 0, NULL, NULL, true);
1171 DOM_REGISTER_PROP_HANDLER(&dom_modern_processinginstruction_prop_handlers, "target", dom_processinginstruction_target_read, NULL);
1172 DOM_REGISTER_PROP_HANDLER(&dom_modern_processinginstruction_prop_handlers, "data", dom_processinginstruction_data_read, dom_processinginstruction_data_write);
1173 zend_hash_merge(&dom_modern_processinginstruction_prop_handlers, &dom_modern_characterdata_prop_handlers, NULL, false);
1174 zend_hash_add_new_ptr(&classes, dom_modern_processinginstruction_class_entry->name, &dom_modern_processinginstruction_prop_handlers);
1175
1176 #ifdef LIBXML_XPATH_ENABLED
1177 memcpy(&dom_xpath_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
1178 dom_xpath_object_handlers.offset = XtOffsetOf(dom_xpath_object, dom) + XtOffsetOf(dom_object, std);
1179 dom_xpath_object_handlers.free_obj = dom_xpath_objects_free_storage;
1180 dom_xpath_object_handlers.get_gc = dom_xpath_get_gc;
1181 dom_xpath_object_handlers.clone_obj = NULL;
1182
1183 dom_xpath_class_entry = register_class_DOMXPath();
1184 dom_xpath_class_entry->create_object = dom_xpath_objects_new;
1185 dom_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers;
1186
1187 zend_hash_init(&dom_xpath_prop_handlers, 0, NULL, NULL, true);
1188 DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "document", dom_xpath_document_read, NULL);
1189 DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "registerNodeNamespaces", dom_xpath_register_node_ns_read, dom_xpath_register_node_ns_write);
1190 zend_hash_add_new_ptr(&classes, dom_xpath_class_entry->name, &dom_xpath_prop_handlers);
1191
1192 dom_modern_xpath_class_entry = register_class_DOM_XPath();
1193 dom_modern_xpath_class_entry->create_object = dom_xpath_objects_new;
1194 dom_modern_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers;
1195
1196 zend_hash_add_new_ptr(&classes, dom_modern_xpath_class_entry->name, &dom_xpath_prop_handlers);
1197 #endif
1198
1199 register_php_dom_symbols(module_number);
1200
1201 php_libxml_register_export(dom_node_class_entry, php_dom_export_node);
1202 php_libxml_register_export(dom_modern_node_class_entry, php_dom_export_node);
1203
1204 lexbor_memory_setup(dom_malloc, dom_realloc, dom_calloc, dom_free);
1205
1206 return SUCCESS;
1207 }
1208 /* }}} */
1209
1210 /* {{{ */
PHP_MINFO_FUNCTION(dom)1211 PHP_MINFO_FUNCTION(dom)
1212 {
1213 php_info_print_table_start();
1214 php_info_print_table_row(2, "DOM/XML", "enabled");
1215 php_info_print_table_row(2, "DOM/XML API Version", DOM_API_VERSION);
1216 php_info_print_table_row(2, "libxml Version", LIBXML_DOTTED_VERSION);
1217 #ifdef LIBXML_HTML_ENABLED
1218 php_info_print_table_row(2, "HTML Support", "enabled");
1219 #endif
1220 #ifdef LIBXML_XPATH_ENABLED
1221 php_info_print_table_row(2, "XPath Support", "enabled");
1222 #endif
1223 #ifdef LIBXML_XPTR_ENABLED
1224 php_info_print_table_row(2, "XPointer Support", "enabled");
1225 #endif
1226 #ifdef LIBXML_SCHEMAS_ENABLED
1227 php_info_print_table_row(2, "Schema Support", "enabled");
1228 php_info_print_table_row(2, "RelaxNG Support", "enabled");
1229 #endif
1230 php_info_print_table_end();
1231 }
1232 /* }}} */
1233
PHP_MSHUTDOWN_FUNCTION(dom)1234 PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */
1235 {
1236 zend_hash_destroy(&dom_document_prop_handlers);
1237 zend_hash_destroy(&dom_abstract_base_document_prop_handlers);
1238 zend_hash_destroy(&dom_xml_document_prop_handlers);
1239 zend_hash_destroy(&dom_documentfragment_prop_handlers);
1240 zend_hash_destroy(&dom_modern_documentfragment_prop_handlers);
1241 zend_hash_destroy(&dom_node_prop_handlers);
1242 zend_hash_destroy(&dom_modern_node_prop_handlers);
1243 zend_hash_destroy(&dom_namespace_node_prop_handlers);
1244 zend_hash_destroy(&dom_nodelist_prop_handlers);
1245 zend_hash_destroy(&dom_namednodemap_prop_handlers);
1246 zend_hash_destroy(&dom_characterdata_prop_handlers);
1247 zend_hash_destroy(&dom_modern_characterdata_prop_handlers);
1248 zend_hash_destroy(&dom_attr_prop_handlers);
1249 zend_hash_destroy(&dom_modern_attr_prop_handlers);
1250 zend_hash_destroy(&dom_element_prop_handlers);
1251 zend_hash_destroy(&dom_modern_element_prop_handlers);
1252 zend_hash_destroy(&dom_text_prop_handlers);
1253 zend_hash_destroy(&dom_modern_text_prop_handlers);
1254 zend_hash_destroy(&dom_documenttype_prop_handlers);
1255 zend_hash_destroy(&dom_modern_documenttype_prop_handlers);
1256 zend_hash_destroy(&dom_notation_prop_handlers);
1257 zend_hash_destroy(&dom_modern_notation_prop_handlers);
1258 zend_hash_destroy(&dom_entity_prop_handlers);
1259 zend_hash_destroy(&dom_modern_entity_prop_handlers);
1260 zend_hash_destroy(&dom_processinginstruction_prop_handlers);
1261 zend_hash_destroy(&dom_modern_processinginstruction_prop_handlers);
1262 #ifdef LIBXML_XPATH_ENABLED
1263 zend_hash_destroy(&dom_xpath_prop_handlers);
1264 #endif
1265 zend_hash_destroy(&classes);
1266
1267 /* If you want do find memleaks in this module, compile libxml2 with --with-mem-debug and
1268 uncomment the following line, this will tell you the amount of not freed memory
1269 and the total used memory into apaches error_log */
1270 /* xmlMemoryDump();*/
1271
1272 return SUCCESS;
1273 }
1274 /* }}} */
1275
1276 /* {{{ node_list_unlink */
node_list_unlink(xmlNodePtr node)1277 void node_list_unlink(xmlNodePtr node)
1278 {
1279 dom_object *wrapper;
1280
1281 while (node != NULL) {
1282
1283 wrapper = php_dom_object_get_data(node);
1284
1285 if (wrapper != NULL ) {
1286 xmlUnlinkNode(node);
1287 } else {
1288 if (node->type == XML_ENTITY_REF_NODE)
1289 break;
1290 node_list_unlink(node->children);
1291
1292 switch (node->type) {
1293 case XML_ATTRIBUTE_DECL:
1294 case XML_DTD_NODE:
1295 case XML_DOCUMENT_TYPE_NODE:
1296 case XML_ENTITY_DECL:
1297 case XML_ATTRIBUTE_NODE:
1298 case XML_TEXT_NODE:
1299 break;
1300 default:
1301 node_list_unlink((xmlNodePtr) node->properties);
1302 }
1303
1304 }
1305
1306 node = node->next;
1307 }
1308 }
1309 /* }}} end node_list_unlink */
1310
1311 /* {{{ dom_objects_free_storage */
dom_objects_free_storage(zend_object * object)1312 void dom_objects_free_storage(zend_object *object)
1313 {
1314 dom_object *intern = php_dom_obj_from_obj(object);
1315
1316 zend_object_std_dtor(&intern->std);
1317
1318 if (intern->ptr != NULL && ((php_libxml_node_ptr *)intern->ptr)->node != NULL) {
1319 if (((xmlNodePtr) ((php_libxml_node_ptr *)intern->ptr)->node)->type != XML_DOCUMENT_NODE && ((xmlNodePtr) ((php_libxml_node_ptr *)intern->ptr)->node)->type != XML_HTML_DOCUMENT_NODE) {
1320 php_libxml_node_decrement_resource((php_libxml_node_object *) intern);
1321 } else {
1322 php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1323 php_libxml_decrement_doc_ref((php_libxml_node_object *) intern);
1324 }
1325 intern->ptr = NULL;
1326 }
1327 }
1328 /* }}} */
1329
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)1330 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) /* {{{ */
1331 {
1332 dom_nnodemap_object *mapptr = (dom_nnodemap_object *) intern->ptr;
1333
1334 ZEND_ASSERT(basenode != NULL);
1335
1336 ZVAL_OBJ_COPY(&mapptr->baseobj_zv, &basenode->std);
1337
1338 xmlDocPtr doc = basenode->document ? basenode->document->ptr : NULL;
1339
1340 mapptr->baseobj = basenode;
1341 mapptr->nodetype = ntype;
1342 mapptr->ht = ht;
1343
1344 const xmlChar* tmp;
1345
1346 if (local) {
1347 int len = local_len > INT_MAX ? -1 : (int) local_len;
1348 if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)local, len)) != NULL) {
1349 mapptr->local = BAD_CAST tmp;
1350 } else {
1351 mapptr->local = xmlCharStrndup(local, len);
1352 mapptr->free_local = true;
1353 }
1354 mapptr->local_lower = BAD_CAST estrdup(local);
1355 if (len < 0) {
1356 zend_str_tolower((char *) mapptr->local_lower, strlen((const char *) mapptr->local_lower));
1357 } else {
1358 zend_str_tolower((char *) mapptr->local_lower, len);
1359 }
1360 }
1361
1362 if (ns) {
1363 int len = ns_len > INT_MAX ? -1 : (int) ns_len;
1364 if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ns, len)) != NULL) {
1365 mapptr->ns = BAD_CAST tmp;
1366 } else {
1367 mapptr->ns = xmlCharStrndup(ns, len);
1368 mapptr->free_ns = true;
1369 }
1370 }
1371 }
1372 /* }}} */
1373
dom_objects_set_class_ex(zend_class_entry * class_type,dom_object * intern)1374 static void dom_objects_set_class_ex(zend_class_entry *class_type, dom_object *intern)
1375 {
1376 zend_class_entry *base_class = class_type;
1377 while ((base_class->type != ZEND_INTERNAL_CLASS || base_class->info.internal.module->module_number != dom_module_entry.module_number) && base_class->parent != NULL) {
1378 base_class = base_class->parent;
1379 }
1380
1381 intern->prop_handler = zend_hash_find_ptr(&classes, base_class->name);
1382
1383 zend_object_std_init(&intern->std, class_type);
1384 object_properties_init(&intern->std, class_type);
1385 }
1386
dom_objects_set_class(zend_class_entry * class_type)1387 static dom_object* dom_objects_set_class(zend_class_entry *class_type)
1388 {
1389 dom_object *intern = zend_object_alloc(sizeof(dom_object), class_type);
1390 dom_objects_set_class_ex(class_type, intern);
1391 return intern;
1392 }
1393
1394 /* {{{ dom_objects_new */
dom_objects_new(zend_class_entry * class_type)1395 zend_object *dom_objects_new(zend_class_entry *class_type)
1396 {
1397 dom_object *intern = dom_objects_set_class(class_type);
1398 return &intern->std;
1399 }
1400 /* }}} */
1401
dom_objects_namespace_node_new(zend_class_entry * class_type)1402 static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type)
1403 {
1404 dom_object_namespace_node *intern = zend_object_alloc(sizeof(dom_object_namespace_node), class_type);
1405 dom_objects_set_class_ex(class_type, &intern->dom);
1406 return &intern->dom.std;
1407 }
1408
dom_object_namespace_node_free_storage(zend_object * object)1409 static void dom_object_namespace_node_free_storage(zend_object *object)
1410 {
1411 dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(object);
1412 if (intern->parent_intern != NULL) {
1413 zval tmp;
1414 ZVAL_OBJ(&tmp, &intern->parent_intern->std);
1415 zval_ptr_dtor(&tmp);
1416 }
1417 dom_objects_free_storage(object);
1418 }
1419
1420 #ifdef LIBXML_XPATH_ENABLED
1421
1422 /* {{{ zend_object dom_xpath_objects_new(zend_class_entry *class_type) */
dom_xpath_objects_new(zend_class_entry * class_type)1423 zend_object *dom_xpath_objects_new(zend_class_entry *class_type)
1424 {
1425 dom_xpath_object *intern = zend_object_alloc(sizeof(dom_xpath_object), class_type);
1426
1427 php_dom_xpath_callbacks_ctor(&intern->xpath_callbacks);
1428 intern->register_node_ns = 1;
1429
1430 intern->dom.prop_handler = &dom_xpath_prop_handlers;
1431
1432 zend_object_std_init(&intern->dom.std, class_type);
1433 object_properties_init(&intern->dom.std, class_type);
1434
1435 return &intern->dom.std;
1436 }
1437 /* }}} */
1438
1439 #endif
1440
dom_nnodemap_objects_free_storage(zend_object * object)1441 void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */
1442 {
1443 dom_object *intern = php_dom_obj_from_obj(object);
1444 dom_nnodemap_object *objmap = (dom_nnodemap_object *)intern->ptr;
1445
1446 if (objmap) {
1447 if (objmap->cached_obj && GC_DELREF(&objmap->cached_obj->std) == 0) {
1448 zend_objects_store_del(&objmap->cached_obj->std);
1449 }
1450 if (objmap->free_local) {
1451 xmlFree(objmap->local);
1452 }
1453 if (objmap->free_ns) {
1454 xmlFree(objmap->ns);
1455 }
1456 if (objmap->local_lower) {
1457 efree(objmap->local_lower);
1458 }
1459 if (!Z_ISUNDEF(objmap->baseobj_zv)) {
1460 zval_ptr_dtor(&objmap->baseobj_zv);
1461 }
1462 efree(objmap);
1463 intern->ptr = NULL;
1464 }
1465
1466 php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1467
1468 zend_object_std_dtor(&intern->std);
1469 }
1470 /* }}} */
1471
dom_nnodemap_objects_new(zend_class_entry * class_type)1472 zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type)
1473 {
1474 dom_object *intern;
1475 dom_nnodemap_object *objmap;
1476
1477 intern = dom_objects_set_class(class_type);
1478 intern->ptr = emalloc(sizeof(dom_nnodemap_object));
1479 objmap = (dom_nnodemap_object *)intern->ptr;
1480 ZVAL_UNDEF(&objmap->baseobj_zv);
1481 objmap->baseobj = NULL;
1482 objmap->nodetype = 0;
1483 objmap->ht = NULL;
1484 objmap->local = NULL;
1485 objmap->local_lower = NULL;
1486 objmap->free_local = false;
1487 objmap->ns = NULL;
1488 objmap->free_ns = false;
1489 objmap->cache_tag.modification_nr = 0;
1490 objmap->cached_length = -1;
1491 objmap->cached_obj = NULL;
1492 objmap->cached_obj_index = 0;
1493
1494 return &intern->std;
1495 }
1496
php_dom_create_iterator(zval * return_value,dom_iterator_type iterator_type,bool modern)1497 void php_dom_create_iterator(zval *return_value, dom_iterator_type iterator_type, bool modern) /* {{{ */
1498 {
1499 zend_class_entry *ce;
1500
1501 if (iterator_type == DOM_NAMEDNODEMAP) {
1502 ce = dom_get_namednodemap_ce(modern);
1503 } else if (iterator_type == DOM_HTMLCOLLECTION) {
1504 /* This only exists in modern DOM. */
1505 ZEND_ASSERT(modern);
1506 ce = dom_html_collection_class_entry;
1507 } else if (iterator_type == DOM_DTD_NAMEDNODEMAP) {
1508 ce = dom_get_dtd_namednodemap_ce(modern);
1509 } else {
1510 ZEND_ASSERT(iterator_type == DOM_NODELIST);
1511 ce = dom_get_nodelist_ce(modern);
1512 }
1513
1514 object_init_ex(return_value, ce);
1515 }
1516 /* }}} */
1517
1518 /* {{{ php_dom_create_object */
php_dom_create_object(xmlNodePtr obj,zval * return_value,dom_object * domobj)1519 PHP_DOM_EXPORT bool php_dom_create_object(xmlNodePtr obj, zval *return_value, dom_object *domobj)
1520 {
1521 dom_object *intern = php_dom_object_get_data(obj);
1522 if (intern) {
1523 ZVAL_OBJ_COPY(return_value, &intern->std);
1524 return true;
1525 }
1526
1527 bool modern = domobj && php_dom_follow_spec_intern(domobj);
1528
1529 zend_class_entry *ce;
1530 switch (obj->type) {
1531 case XML_DOCUMENT_NODE:
1532 {
1533 ce = dom_get_xml_document_ce(modern);
1534 break;
1535 }
1536 case XML_HTML_DOCUMENT_NODE:
1537 {
1538 ce = dom_get_html_document_ce(modern);
1539 break;
1540 }
1541 case XML_DTD_NODE:
1542 case XML_DOCUMENT_TYPE_NODE:
1543 {
1544 ce = dom_get_documenttype_ce(modern);
1545 break;
1546 }
1547 case XML_ELEMENT_NODE:
1548 {
1549 ce = dom_get_element_ce(modern);
1550 break;
1551 }
1552 case XML_ATTRIBUTE_NODE:
1553 {
1554 ce = dom_get_attr_ce(modern);
1555 break;
1556 }
1557 case XML_TEXT_NODE:
1558 {
1559 ce = dom_get_text_ce(modern);
1560 break;
1561 }
1562 case XML_COMMENT_NODE:
1563 {
1564 ce = dom_get_comment_ce(modern);
1565 break;
1566 }
1567 case XML_PI_NODE:
1568 {
1569 ce = dom_get_processinginstruction_ce(modern);
1570 break;
1571 }
1572 case XML_ENTITY_REF_NODE:
1573 {
1574 ce = dom_get_entityreference_ce(modern);
1575 break;
1576 }
1577 case XML_ENTITY_DECL:
1578 case XML_ELEMENT_DECL:
1579 {
1580 ce = dom_get_entity_ce(modern);
1581 break;
1582 }
1583 case XML_CDATA_SECTION_NODE:
1584 {
1585 ce = dom_get_cdatasection_ce(modern);
1586 break;
1587 }
1588 case XML_DOCUMENT_FRAG_NODE:
1589 {
1590 ce = dom_get_documentfragment_ce(modern);
1591 break;
1592 }
1593 case XML_NOTATION_NODE:
1594 {
1595 ce = dom_get_notation_ce(modern);
1596 break;
1597 }
1598 case XML_NAMESPACE_DECL:
1599 {
1600 /* This has no modern equivalent */
1601 ce = dom_namespace_node_class_entry;
1602 break;
1603 }
1604 default:
1605 /* TODO you can actually hit this with fixed attributes in the DTD for example... */
1606 zend_throw_error(NULL, "Unsupported node type: %d", obj->type);
1607 ZVAL_NULL(return_value);
1608 return false;
1609 }
1610
1611 if (domobj && domobj->document) {
1612 ce = dom_get_doc_classmap(domobj->document, ce);
1613 }
1614 php_dom_instantiate_object_helper(return_value, ce, obj, domobj);
1615 return false;
1616 }
1617 /* }}} end php_domobject_new */
1618
php_dom_instantiate_object_helper(zval * return_value,zend_class_entry * ce,xmlNodePtr obj,dom_object * parent)1619 dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_entry *ce, xmlNodePtr obj, dom_object *parent)
1620 {
1621 object_init_ex(return_value, ce);
1622
1623 dom_object *intern = Z_DOMOBJ_P(return_value);
1624 if (obj->doc != NULL) {
1625 if (parent != NULL) {
1626 intern->document = parent->document;
1627 }
1628 php_libxml_increment_doc_ref((php_libxml_node_object *)intern, obj->doc);
1629 }
1630
1631 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, obj, (void *)intern);
1632
1633 return intern;
1634 }
1635
php_dom_create_implementation(zval * retval,bool modern)1636 void php_dom_create_implementation(zval *retval, bool modern) {
1637 object_init_ex(retval, dom_get_domimplementation_ce(modern));
1638 }
1639
1640 /* {{{ int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child) */
dom_hierarchy(xmlNodePtr parent,xmlNodePtr child)1641 int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child)
1642 {
1643 xmlNodePtr nodep;
1644
1645 if (parent == NULL || child == NULL || child->doc != parent->doc) {
1646 return SUCCESS;
1647 }
1648
1649 if (child->type == XML_DOCUMENT_NODE) {
1650 return FAILURE;
1651 }
1652
1653 nodep = parent;
1654
1655 while (nodep) {
1656 if (nodep == child) {
1657 return FAILURE;
1658 }
1659 nodep = nodep->parent;
1660 }
1661
1662 return SUCCESS;
1663 }
1664 /* }}} end dom_hierarchy */
1665
1666 /* {{{ */
dom_has_feature(zend_string * feature,zend_string * version)1667 bool dom_has_feature(zend_string *feature, zend_string *version)
1668 {
1669 if (zend_string_equals_literal(version, "1.0")
1670 || zend_string_equals_literal(version, "2.0")
1671 || zend_string_equals_literal(version, "")
1672 ) {
1673 if (zend_string_equals_literal_ci(feature, "XML")
1674 || (zend_string_equals_literal_ci(feature, "Core") && zend_string_equals_literal(version, "1.0"))
1675 ) {
1676 return true;
1677 }
1678 }
1679
1680 return false;
1681 }
1682 /* }}} end dom_has_feature */
1683
dom_match_qualified_name_according_to_spec(const xmlChar * qname,const xmlNode * nodep)1684 bool dom_match_qualified_name_according_to_spec(const xmlChar *qname, const xmlNode *nodep)
1685 {
1686 const xmlChar *node_local = nodep->name;
1687
1688 /* The qualified name must be matched, which means either:
1689 * - The local parts are equal and there is no ns prefix for this element (i.e. the fqn is in the local name).
1690 * - There is a prefix, the prefixes are equal and the local parts are equal. */
1691 if (nodep->ns != NULL && nodep->ns->prefix != NULL) {
1692 const char *prefix = (const char *) nodep->ns->prefix;
1693 /* 1. match prefix up to |prefix| characters.
1694 * This won't overflow as it'll stop at the '\0' if the lengths don't match. */
1695 size_t prefix_len = strlen(prefix);
1696 if (strncmp((const char *) qname, prefix, prefix_len) != 0) {
1697 return false;
1698 }
1699 /* 2. match ':' */
1700 if (qname[prefix_len] != ':') {
1701 return false;
1702 }
1703 /* 3. match local name */
1704 return xmlStrEqual(qname + prefix_len + 1, node_local);
1705 } else {
1706 return xmlStrEqual(node_local, qname);
1707 }
1708 }
1709
dom_match_qualified_name_for_tag_name_equality(const xmlChar * local,const xmlChar * local_lower,const xmlNode * nodep,bool match_qname)1710 static bool dom_match_qualified_name_for_tag_name_equality(const xmlChar *local, const xmlChar *local_lower, const xmlNode *nodep, bool match_qname)
1711 {
1712 if (!match_qname) {
1713 return xmlStrEqual(nodep->name, local);
1714 }
1715
1716 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;
1717 return dom_match_qualified_name_according_to_spec(local_to_use, nodep);
1718 }
1719
dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep,xmlNodePtr nodep,xmlChar * ns,xmlChar * local,xmlChar * local_lower,int * cur,int index)1720 xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, xmlChar *ns, xmlChar *local, xmlChar *local_lower, int *cur, int index) /* {{{ */
1721 {
1722 /* Can happen with detached document */
1723 if (UNEXPECTED(nodep == NULL)) {
1724 return NULL;
1725 }
1726
1727 xmlNodePtr ret = NULL;
1728 bool local_match_any = local[0] == '*' && local[1] == '\0';
1729
1730 /* Note: The spec says that ns == '' must be transformed to ns == NULL. In other words, they are equivalent.
1731 * PHP however does not do this and internally uses the empty string everywhere when the user provides ns == NULL.
1732 * This is because for PHP ns == NULL has another meaning: "match every namespace" instead of "match the empty namespace". */
1733 bool ns_match_any = ns == NULL || (ns[0] == '*' && ns[1] == '\0');
1734
1735 bool match_qname = ns == NULL && php_dom_follow_spec_node(basep);
1736
1737 while (*cur <= index) {
1738 if (nodep->type == XML_ELEMENT_NODE) {
1739 if (local_match_any || dom_match_qualified_name_for_tag_name_equality(local, local_lower, nodep, match_qname)) {
1740 if (ns_match_any || (ns[0] == '\0' && nodep->ns == NULL) || (nodep->ns != NULL && xmlStrEqual(nodep->ns->href, ns))) {
1741 if (*cur == index) {
1742 ret = nodep;
1743 break;
1744 }
1745 (*cur)++;
1746 }
1747 }
1748
1749 if (nodep->children) {
1750 nodep = nodep->children;
1751 continue;
1752 }
1753 }
1754
1755 nodep = php_dom_next_in_tree_order(nodep, basep);
1756 if (!nodep) {
1757 return NULL;
1758 }
1759 }
1760 return ret;
1761 }
1762 /* }}} end dom_element_get_elements_by_tag_name_ns_raw */
1763
is_empty_node(xmlNodePtr nodep)1764 static inline bool is_empty_node(xmlNodePtr nodep)
1765 {
1766 return nodep->content == NULL || *nodep->content == '\0';
1767 }
1768
free_node(xmlNodePtr node)1769 static inline void free_node(xmlNodePtr node)
1770 {
1771 if (node->_private == NULL) {
1772 xmlFreeNode(node);
1773 }
1774 }
1775
dom_merge_adjacent_exclusive_text_nodes(xmlNodePtr node)1776 static void dom_merge_adjacent_exclusive_text_nodes(xmlNodePtr node)
1777 {
1778 xmlNodePtr nextp = node->next;
1779 while (nextp != NULL && nextp->type == XML_TEXT_NODE) {
1780 xmlNodePtr newnextp = nextp->next;
1781 xmlChar *strContent = nextp->content;
1782 if (strContent != NULL) {
1783 xmlNodeAddContent(node, strContent);
1784 }
1785 xmlUnlinkNode(nextp);
1786 free_node(nextp);
1787 nextp = newnextp;
1788 }
1789 }
1790
1791 /* {{{ void php_dom_normalize_legacy(xmlNodePtr nodep) */
php_dom_normalize_legacy(xmlNodePtr nodep)1792 void php_dom_normalize_legacy(xmlNodePtr nodep)
1793 {
1794 xmlNodePtr child = nodep->children;
1795 while(child != NULL) {
1796 switch (child->type) {
1797 case XML_TEXT_NODE:
1798 dom_merge_adjacent_exclusive_text_nodes(child);
1799 if (is_empty_node(child)) {
1800 xmlNodePtr nextp = child->next;
1801 xmlUnlinkNode(child);
1802 free_node(child);
1803 child = nextp;
1804 continue;
1805 }
1806 break;
1807 case XML_ELEMENT_NODE:
1808 php_dom_normalize_legacy(child);
1809 xmlAttrPtr attr = child->properties;
1810 while (attr != NULL) {
1811 php_dom_normalize_legacy((xmlNodePtr) attr);
1812 attr = attr->next;
1813 }
1814 break;
1815 default:
1816 break;
1817 }
1818 child = child->next;
1819 }
1820 }
1821 /* }}} end php_dom_normalize_legacy */
1822
1823 /* https://dom.spec.whatwg.org/#dom-node-normalize */
php_dom_normalize_modern(xmlNodePtr this)1824 void php_dom_normalize_modern(xmlNodePtr this)
1825 {
1826 /* for each descendant exclusive Text node node of this: */
1827 xmlNodePtr node = this->children;
1828 while (node != NULL) {
1829 if (node->type == XML_TEXT_NODE) {
1830 /* 1. Let length be node’s length.
1831 * 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. */
1832 bool is_empty = is_empty_node(node);
1833
1834 /* 2. If length is zero, then remove node and continue with the next exclusive Text node, if any. */
1835 if (is_empty) {
1836 xmlNodePtr next = node->next;
1837 xmlUnlinkNode(node);
1838 free_node(node);
1839 node = next;
1840 continue;
1841 }
1842
1843 /* 3. Let data be the concatenation of the data of node’s contiguous exclusive Text nodes (excluding itself), in tree order.
1844 * 4. Replace data with node node, offset length, count 0, and data data.
1845 * 7. Remove node’s contiguous exclusive Text nodes (excluding itself), in tree order.
1846 * => In other words: Concat every contiguous text node into node and delete the merged nodes. */
1847 dom_merge_adjacent_exclusive_text_nodes(node);
1848
1849 /* Steps 5-6 deal with mutation records, we don't do that here. */
1850 } else if (node->type == XML_ELEMENT_NODE) {
1851 php_dom_normalize_modern(node);
1852 }
1853 node = node->next;
1854 }
1855 }
1856
dom_reconcile_ns_internal(xmlDocPtr doc,xmlNodePtr nodep,xmlNodePtr search_parent)1857 static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr search_parent)
1858 {
1859 xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL;
1860
1861 /* Following if block primarily used for inserting nodes created via createElementNS */
1862 if (nodep->nsDef != NULL) {
1863 curns = nodep->nsDef;
1864 while (curns) {
1865 nsdftptr = curns->next;
1866 if (curns->href != NULL) {
1867 if((nsptr = xmlSearchNsByHref(doc, search_parent, curns->href)) &&
1868 (curns->prefix == NULL || xmlStrEqual(nsptr->prefix, curns->prefix))) {
1869 curns->next = NULL;
1870 if (prevns == NULL) {
1871 nodep->nsDef = nsdftptr;
1872 } else {
1873 prevns->next = nsdftptr;
1874 }
1875 /* Note: we can't get here if the ns is already on the oldNs list.
1876 * This is because in that case the definition won't be on the node, and
1877 * therefore won't be in the nodep->nsDef list. */
1878 php_libxml_set_old_ns(doc, curns);
1879 curns = prevns;
1880 }
1881 }
1882 prevns = curns;
1883 curns = nsdftptr;
1884 }
1885 }
1886 }
1887
dom_reconcile_ns(xmlDocPtr doc,xmlNodePtr nodep)1888 void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */
1889 {
1890 ZEND_ASSERT(nodep->type != XML_ATTRIBUTE_NODE);
1891
1892 /* Although the node type will be checked by the libxml2 API,
1893 * we still want to do the internal reconciliation conditionally. */
1894 if (nodep->type == XML_ELEMENT_NODE) {
1895 dom_reconcile_ns_internal(doc, nodep, nodep->parent);
1896 xmlReconciliateNs(doc, nodep);
1897 }
1898 }
1899 /* }}} */
1900
dom_reconcile_ns_list_internal(xmlDocPtr doc,xmlNodePtr nodep,xmlNodePtr last,xmlNodePtr search_parent)1901 static void dom_reconcile_ns_list_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last, xmlNodePtr search_parent)
1902 {
1903 ZEND_ASSERT(nodep != NULL);
1904 while (true) {
1905 if (nodep->type == XML_ELEMENT_NODE) {
1906 dom_reconcile_ns_internal(doc, nodep, search_parent);
1907 if (nodep->children) {
1908 dom_reconcile_ns_list_internal(doc, nodep->children, nodep->last /* process the whole children list */, search_parent);
1909 }
1910 }
1911 if (nodep == last) {
1912 break;
1913 }
1914 nodep = nodep->next;
1915 }
1916 }
1917
dom_reconcile_ns_list(xmlDocPtr doc,xmlNodePtr nodep,xmlNodePtr last)1918 void dom_reconcile_ns_list(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last)
1919 {
1920 dom_reconcile_ns_list_internal(doc, nodep, last, nodep->parent);
1921 /* The loop is outside of the recursion in the above call because
1922 * dom_libxml_reconcile_ensure_namespaces_are_declared() performs its own recursion. */
1923 while (true) {
1924 /* The internal libxml2 call will already check the node type, no need for us to do it here. */
1925 xmlReconciliateNs(doc, nodep);
1926 if (nodep == last) {
1927 break;
1928 }
1929 nodep = nodep->next;
1930 }
1931 }
1932
1933 /* https://dom.spec.whatwg.org/#validate-and-extract */
dom_validate_and_extract(const zend_string * namespace,const zend_string * qname,xmlChar ** localName,xmlChar ** prefix)1934 int dom_validate_and_extract(const zend_string *namespace, const zend_string *qname, xmlChar **localName, xmlChar **prefix)
1935 {
1936 /* 1. If namespace is the empty string, then set it to null.
1937 * However, we're going to cheat and do the opposite to make
1938 * implementation of the below steps with existing zend_ helpers easier. */
1939 if (namespace == NULL) {
1940 namespace = zend_empty_string;
1941 }
1942
1943 /* 2. Validate qualifiedName. */
1944 if (xmlValidateQName(BAD_CAST ZSTR_VAL(qname), /* allow spaces */ 0) != 0) {
1945 return INVALID_CHARACTER_ERR;
1946 }
1947
1948 /* Steps 3-5 */
1949 *localName = xmlSplitQName2(BAD_CAST ZSTR_VAL(qname), prefix);
1950
1951 /* 6. If prefix is non-null and namespace is null, then throw a "NamespaceError" DOMException.
1952 * Note that null namespace means empty string here becaue of step 1. */
1953 if (*prefix != NULL && ZSTR_VAL(namespace)[0] == '\0') {
1954 return NAMESPACE_ERR;
1955 }
1956
1957 /* 7. If prefix is "xml" and namespace is not the XML namespace, then throw a "NamespaceError" DOMException. */
1958 if (UNEXPECTED(!zend_string_equals_literal(namespace, "http://www.w3.org/XML/1998/namespace") && xmlStrEqual(*prefix, BAD_CAST "xml"))) {
1959 return NAMESPACE_ERR;
1960 }
1961
1962 /* 8. If either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace, then throw a "NamespaceError" DOMException. */
1963 if (UNEXPECTED((zend_string_equals_literal(qname, "xmlns") || xmlStrEqual(*prefix, BAD_CAST "xmlns")) && !zend_string_equals_literal(namespace, "http://www.w3.org/2000/xmlns/"))) {
1964 return NAMESPACE_ERR;
1965 }
1966
1967 /* 9. If namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns", then throw a "NamespaceError" DOMException. */
1968 if (UNEXPECTED(zend_string_equals_literal(namespace, "http://www.w3.org/2000/xmlns/") && !zend_string_equals_literal(qname, "xmlns") && !xmlStrEqual(*prefix, BAD_CAST "xmlns"))) {
1969 return NAMESPACE_ERR;
1970 }
1971
1972 if (*localName == NULL) {
1973 *localName = xmlStrdup(BAD_CAST ZSTR_VAL(qname));
1974 }
1975
1976 return 0;
1977 }
1978
1979 /*
1980 http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS
1981
1982 NAMESPACE_ERR: Raised if
1983
1984 1. the qualifiedName is a malformed qualified name
1985 2. the qualifiedName has a prefix and the namespaceURI is null
1986 */
1987
1988 /* {{{ int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len) */
dom_check_qname(char * qname,char ** localname,char ** prefix,int uri_len,int name_len)1989 int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len) {
1990 if (name_len == 0) {
1991 return NAMESPACE_ERR;
1992 }
1993
1994 *localname = (char *)xmlSplitQName2(BAD_CAST qname, (xmlChar **) prefix);
1995 if (*localname == NULL) {
1996 *localname = (char *)xmlStrdup(BAD_CAST qname);
1997 if (*prefix == NULL && uri_len == 0) {
1998 return 0;
1999 }
2000 }
2001
2002 /* 1 */
2003 if (xmlValidateQName(BAD_CAST qname, 0) != 0) {
2004 return NAMESPACE_ERR;
2005 }
2006
2007 /* 2 */
2008 if (*prefix != NULL && uri_len == 0) {
2009 return NAMESPACE_ERR;
2010 }
2011
2012 return 0;
2013 }
2014 /* }}} */
2015
2016 /* Creates a new namespace declaration with a random prefix with the given uri on the tree.
2017 * This is used to resolve a namespace prefix conflict in cases where spec does not want a
2018 * namespace error in case of conflicts, but demands a resolution. */
dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree,const char * uri)2019 xmlNsPtr dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree, const char *uri)
2020 {
2021 ZEND_ASSERT(tree != NULL);
2022 xmlDocPtr doc = tree->doc;
2023
2024 if (UNEXPECTED(doc == NULL)) {
2025 return NULL;
2026 }
2027
2028 /* Code adapted from libxml2 (2.10.4) */
2029 char prefix[50];
2030 int counter = 1;
2031 snprintf(prefix, sizeof(prefix), "default");
2032 xmlNsPtr nsptr = xmlSearchNs(doc, tree, (const xmlChar *) prefix);
2033 while (nsptr != NULL) {
2034 if (counter > 1000) {
2035 return NULL;
2036 }
2037 snprintf(prefix, sizeof(prefix), "default%d", counter++);
2038 nsptr = xmlSearchNs(doc, tree, (const xmlChar *) prefix);
2039 }
2040
2041 /* Search yielded no conflict */
2042 return xmlNewNs(tree, (const xmlChar *) uri, (const xmlChar *) prefix);
2043 }
2044
2045 /*
2046 http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS
2047
2048 NAMESPACE_ERR: Raised if
2049
2050 3. the qualifiedName has a prefix that is "xml" and the namespaceURI is different from "http://www.w3.org/XML/1998/namespace" [XML Namespaces]
2051 4. the qualifiedName or its prefix is "xmlns" and the namespaceURI is different from "http://www.w3.org/2000/xmlns/"
2052 5. the namespaceURI is "http://www.w3.org/2000/xmlns/" and neither the qualifiedName nor its prefix is "xmlns".
2053 */
2054
dom_get_ns_unchecked(xmlNodePtr nodep,char * uri,char * prefix)2055 xmlNsPtr dom_get_ns_unchecked(xmlNodePtr nodep, char *uri, char *prefix)
2056 {
2057 xmlNsPtr nsptr = xmlNewNs(nodep, BAD_CAST uri, BAD_CAST prefix);
2058 if (UNEXPECTED(nsptr == NULL)) {
2059 /* Either memory allocation failure, or it's because of a prefix conflict.
2060 * We'll assume a conflict and try again. If it was a memory allocation failure we'll just fail again, whatever.
2061 * 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?".
2062 * This branch will also be taken unlikely anyway as in those cases it'll be for allocation failure. */
2063 return dom_get_ns_resolve_prefix_conflict(nodep, uri);
2064 }
2065
2066 return nsptr;
2067 }
2068
2069 /* {{{ xmlNsPtr dom_get_ns(xmlNodePtr nodep, char *uri, int *errorcode, char *prefix) */
dom_get_ns(xmlNodePtr nodep,char * uri,int * errorcode,char * prefix)2070 xmlNsPtr dom_get_ns(xmlNodePtr nodep, char *uri, int *errorcode, char *prefix) {
2071 xmlNsPtr nsptr;
2072
2073 if (! ((prefix && !strcmp (prefix, "xml") && strcmp(uri, (char *)XML_XML_NAMESPACE)) ||
2074 (prefix && !strcmp (prefix, "xmlns") && strcmp(uri, DOM_XMLNS_NS_URI)) ||
2075 (prefix && !strcmp(uri, DOM_XMLNS_NS_URI) && strcmp (prefix, "xmlns")))) {
2076 nsptr = dom_get_ns_unchecked(nodep, uri, prefix);
2077 if (UNEXPECTED(nsptr == NULL)) {
2078 goto err;
2079 }
2080 } else {
2081 goto err;
2082 }
2083
2084 *errorcode = 0;
2085 return nsptr;
2086 err:
2087 *errorcode = NAMESPACE_ERR;
2088 return NULL;
2089 }
2090 /* }}} end dom_get_ns */
2091
2092 /* {{{ xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) */
dom_get_nsdecl(xmlNode * node,xmlChar * localName)2093 xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
2094 xmlNsPtr cur;
2095 xmlNs *ret = NULL;
2096 if (node == NULL)
2097 return NULL;
2098
2099 if (localName == NULL || localName[0] == '\0') {
2100 cur = node->nsDef;
2101 while (cur != NULL) {
2102 if (cur->prefix == NULL && cur->href != NULL) {
2103 ret = cur;
2104 break;
2105 }
2106 cur = cur->next;
2107 }
2108 } else {
2109 cur = node->nsDef;
2110 while (cur != NULL) {
2111 if (cur->prefix != NULL && xmlStrEqual(localName, cur->prefix)) {
2112 ret = cur;
2113 break;
2114 }
2115 cur = cur->next;
2116 }
2117 }
2118 return ret;
2119 }
2120 /* }}} end dom_get_nsdecl */
2121
php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep,xmlNsPtr original)2122 static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original)
2123 {
2124 xmlNodePtr attrp;
2125 xmlNsPtr curns = xmlNewNs(NULL, original->href, NULL);
2126 if (original->prefix) {
2127 curns->prefix = xmlStrdup(original->prefix);
2128 attrp = xmlNewDocNode(nodep->doc, NULL, BAD_CAST original->prefix, original->href);
2129 } else {
2130 attrp = xmlNewDocNode(nodep->doc, NULL, BAD_CAST "xmlns", original->href);
2131 }
2132 attrp->type = XML_NAMESPACE_DECL;
2133 attrp->parent = nodep;
2134 attrp->ns = curns;
2135 return attrp;
2136 }
2137
2138 /* Note: Assumes the additional lifetime was already added in the caller. */
php_dom_create_fake_namespace_decl(xmlNodePtr nodep,xmlNsPtr original,zval * return_value,dom_object * parent_intern)2139 xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern)
2140 {
2141 xmlNodePtr attrp = php_dom_create_fake_namespace_decl_node_ptr(nodep, original);
2142 php_dom_create_object(attrp, return_value, parent_intern);
2143 /* This object must exist, because we just created an object for it via php_dom_create_object(). */
2144 php_dom_namespace_node_obj_from_obj(Z_OBJ_P(return_value))->parent_intern = parent_intern;
2145 return attrp;
2146 }
2147
dom_nodemap_or_nodelist_process_offset_as_named(zval * offset,zend_long * lval)2148 static bool dom_nodemap_or_nodelist_process_offset_as_named(zval *offset, zend_long *lval)
2149 {
2150 if (Z_TYPE_P(offset) == IS_STRING) {
2151 /* See zval_get_long_func() */
2152 double dval;
2153 zend_uchar is_numeric_string_type;
2154 if (0 == (is_numeric_string_type = is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), lval, &dval, true))) {
2155 return true;
2156 } else if (is_numeric_string_type == IS_DOUBLE) {
2157 *lval = zend_dval_to_lval_cap(dval);
2158 }
2159 } else {
2160 *lval = zval_get_long(offset);
2161 }
2162 return false;
2163 }
2164
dom_nodelist_read_dimension(zend_object * object,zval * offset,int type,zval * rv)2165 static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
2166 {
2167 if (UNEXPECTED(!offset)) {
2168 zend_throw_error(NULL, "Cannot access %s without offset", ZSTR_VAL(object->ce->name));
2169 return NULL;
2170 }
2171
2172 ZVAL_DEREF(offset);
2173
2174 zend_long lval;
2175 if (dom_nodemap_or_nodelist_process_offset_as_named(offset, &lval)) {
2176 /* does not support named lookup */
2177 ZVAL_NULL(rv);
2178 return rv;
2179 }
2180
2181 php_dom_nodelist_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv);
2182 return rv;
2183 }
2184
dom_nodelist_has_dimension(zend_object * object,zval * member,int check_empty)2185 static int dom_nodelist_has_dimension(zend_object *object, zval *member, int check_empty)
2186 {
2187 ZVAL_DEREF(member);
2188
2189 /* If it exists, it cannot be empty because nodes aren't empty. */
2190 ZEND_IGNORE_VALUE(check_empty);
2191
2192 zend_long offset;
2193 if (dom_nodemap_or_nodelist_process_offset_as_named(member, &offset)) {
2194 /* does not support named lookup */
2195 return 0;
2196 }
2197
2198 return offset >= 0 && offset < php_dom_get_nodelist_length(php_dom_obj_from_obj(object));
2199 }
2200
dom_remove_all_children(xmlNodePtr nodep)2201 void dom_remove_all_children(xmlNodePtr nodep)
2202 {
2203 if (nodep->children) {
2204 node_list_unlink(nodep->children);
2205 php_libxml_node_free_list((xmlNodePtr) nodep->children);
2206 nodep->children = NULL;
2207 nodep->last = NULL;
2208 }
2209 }
2210
php_dom_get_content_into_zval(const xmlNode * nodep,zval * return_value,bool null_on_failure)2211 void php_dom_get_content_into_zval(const xmlNode *nodep, zval *return_value, bool null_on_failure)
2212 {
2213 ZEND_ASSERT(nodep != NULL);
2214
2215 switch (nodep->type) {
2216 case XML_TEXT_NODE:
2217 case XML_CDATA_SECTION_NODE:
2218 case XML_PI_NODE:
2219 case XML_COMMENT_NODE: {
2220 char *str = (char * ) nodep->content;
2221 if (str != NULL) {
2222 RETURN_STRING(str);
2223 }
2224
2225 break;
2226 }
2227
2228 case XML_ATTRIBUTE_NODE: {
2229 bool free;
2230 xmlChar *value = dom_attr_value((const xmlAttr *) nodep, &free);
2231 RETURN_STRING_FAST((const char *) value);
2232 if (free) {
2233 xmlFree(value);
2234 }
2235 return;
2236 }
2237
2238 default: {
2239 char *str = (char *) xmlNodeGetContent(nodep);
2240 if (str != NULL) {
2241 RETVAL_STRING(str);
2242 xmlFree(str);
2243 return;
2244 }
2245
2246 break;
2247 }
2248 }
2249
2250 if (null_on_failure) {
2251 RETURN_NULL();
2252 } else {
2253 RETURN_EMPTY_STRING();
2254 }
2255 }
2256
dom_nodemap_read_dimension(zend_object * object,zval * offset,int type,zval * rv)2257 static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
2258 {
2259 if (UNEXPECTED(!offset)) {
2260 zend_throw_error(NULL, "Cannot access %s without offset", ZSTR_VAL(object->ce->name));
2261 return NULL;
2262 }
2263
2264 ZVAL_DEREF(offset);
2265
2266 zend_long lval;
2267 if (dom_nodemap_or_nodelist_process_offset_as_named(offset, &lval)) {
2268 /* exceptional case, switch to named lookup */
2269 php_dom_named_node_map_get_named_item_into_zval(php_dom_obj_from_obj(object)->ptr, Z_STR_P(offset), rv);
2270 return rv;
2271 }
2272
2273 /* see PHP_METHOD(DOMNamedNodeMap, item) */
2274 if (UNEXPECTED(lval < 0 || ZEND_LONG_INT_OVFL(lval))) {
2275 zend_value_error("must be between 0 and %d", INT_MAX);
2276 return NULL;
2277 }
2278
2279 php_dom_named_node_map_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv);
2280 return rv;
2281 }
2282
dom_nodemap_has_dimension(zend_object * object,zval * member,int check_empty)2283 static int dom_nodemap_has_dimension(zend_object *object, zval *member, int check_empty)
2284 {
2285 ZVAL_DEREF(member);
2286
2287 /* If it exists, it cannot be empty because nodes aren't empty. */
2288 ZEND_IGNORE_VALUE(check_empty);
2289
2290 zend_long offset;
2291 if (dom_nodemap_or_nodelist_process_offset_as_named(member, &offset)) {
2292 /* exceptional case, switch to named lookup */
2293 return php_dom_named_node_map_get_named_item(php_dom_obj_from_obj(object)->ptr, Z_STR_P(member), false) != NULL;
2294 }
2295
2296 return offset >= 0 && offset < php_dom_get_namednodemap_length(php_dom_obj_from_obj(object));
2297 }
2298
dom_modern_nodemap_read_dimension(zend_object * object,zval * offset,int type,zval * rv)2299 static zval *dom_modern_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
2300 {
2301 if (UNEXPECTED(!offset)) {
2302 zend_throw_error(NULL, "Cannot append to %s", ZSTR_VAL(object->ce->name));
2303 return NULL;
2304 }
2305
2306 dom_nnodemap_object *map = php_dom_obj_from_obj(object)->ptr;
2307
2308 ZVAL_DEREF(offset);
2309 if (Z_TYPE_P(offset) == IS_STRING) {
2310 zend_ulong lval;
2311 if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), lval)) {
2312 php_dom_named_node_map_get_item_into_zval(map, (zend_long) lval, rv);
2313 } else {
2314 php_dom_named_node_map_get_named_item_into_zval(map, Z_STR_P(offset), rv);
2315 }
2316 } else if (Z_TYPE_P(offset) == IS_LONG) {
2317 php_dom_named_node_map_get_item_into_zval(map, Z_LVAL_P(offset), rv);
2318 } else if (Z_TYPE_P(offset) == IS_DOUBLE) {
2319 php_dom_named_node_map_get_item_into_zval(map, zend_dval_to_lval_safe(Z_DVAL_P(offset)), rv);
2320 } else {
2321 zend_illegal_container_offset(object->ce->name, offset, type);
2322 return NULL;
2323 }
2324
2325 return rv;
2326 }
2327
dom_modern_nodemap_has_dimension(zend_object * object,zval * member,int check_empty)2328 static int dom_modern_nodemap_has_dimension(zend_object *object, zval *member, int check_empty)
2329 {
2330 /* If it exists, it cannot be empty because nodes aren't empty. */
2331 ZEND_IGNORE_VALUE(check_empty);
2332
2333 dom_object *obj = php_dom_obj_from_obj(object);
2334 dom_nnodemap_object *map = obj->ptr;
2335
2336 ZVAL_DEREF(member);
2337 if (Z_TYPE_P(member) == IS_STRING) {
2338 zend_ulong lval;
2339 if (ZEND_HANDLE_NUMERIC(Z_STR_P(member), lval)) {
2340 return (zend_long) lval >= 0 && (zend_long) lval < php_dom_get_namednodemap_length(obj);
2341 } else {
2342 return php_dom_named_node_map_get_named_item(map, Z_STR_P(member), false) != NULL;
2343 }
2344 } else if (Z_TYPE_P(member) == IS_LONG) {
2345 zend_long offset = Z_LVAL_P(member);
2346 return offset >= 0 && offset < php_dom_get_namednodemap_length(obj);
2347 } else if (Z_TYPE_P(member) == IS_DOUBLE) {
2348 zend_long offset = zend_dval_to_lval_safe(Z_DVAL_P(member));
2349 return offset >= 0 && offset < php_dom_get_namednodemap_length(obj);
2350 } else {
2351 zend_illegal_container_offset(object->ce->name, member, BP_VAR_IS);
2352 return 0;
2353 }
2354 }
2355
dom_clone_container_helper(php_dom_libxml_ns_mapper * ns_mapper,xmlNodePtr src_node,xmlDocPtr dst_doc)2356 static xmlNodePtr dom_clone_container_helper(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr src_node, xmlDocPtr dst_doc)
2357 {
2358 xmlNodePtr clone = xmlDocCopyNode(src_node, dst_doc, 0);
2359 if (EXPECTED(clone != NULL)) {
2360 /* Set namespace to the original, reconciliation will fix this up. */
2361 clone->ns = src_node->ns;
2362
2363 if (src_node->type == XML_ELEMENT_NODE) {
2364 xmlAttrPtr last_added_attr = NULL;
2365
2366 if (src_node->nsDef != NULL) {
2367 xmlNsPtr current_ns = src_node->nsDef;
2368 do {
2369 php_dom_ns_compat_mark_attribute(ns_mapper, clone, current_ns);
2370 } while ((current_ns = current_ns->next) != NULL);
2371
2372 last_added_attr = clone->properties;
2373 while (last_added_attr->next != NULL) {
2374 last_added_attr = last_added_attr->next;
2375 }
2376 }
2377
2378 /* Attribute cloning logic. */
2379 for (xmlAttrPtr attr = src_node->properties; attr != NULL; attr = attr->next) {
2380 xmlAttrPtr new_attr = (xmlAttrPtr) xmlDocCopyNode((xmlNodePtr) attr, dst_doc, 0);
2381 if (UNEXPECTED(new_attr == NULL)) {
2382 xmlFreeNode(clone);
2383 return NULL;
2384 }
2385 if (last_added_attr == NULL) {
2386 clone->properties = new_attr;
2387 } else {
2388 new_attr->prev = last_added_attr;
2389 last_added_attr->next = new_attr;
2390 }
2391 new_attr->parent = clone;
2392 last_added_attr = new_attr;
2393
2394 /* Set namespace to the original, reconciliation will fix this up. */
2395 new_attr->ns = attr->ns;
2396 }
2397 }
2398 }
2399 return clone;
2400 }
2401
dom_clone_helper(php_dom_libxml_ns_mapper * ns_mapper,xmlNodePtr src_node,xmlDocPtr dst_doc,bool recursive)2402 static xmlNodePtr dom_clone_helper(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr src_node, xmlDocPtr dst_doc, bool recursive)
2403 {
2404 xmlNodePtr outer_clone = dom_clone_container_helper(ns_mapper, src_node, dst_doc);
2405
2406 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)) {
2407 return outer_clone;
2408 }
2409
2410 /* Handle dtd separately, because it is linked twice and requires a special copy function. */
2411 if (src_node->type == XML_DOCUMENT_NODE || src_node->type == XML_HTML_DOCUMENT_NODE) {
2412 dst_doc = (xmlDocPtr) outer_clone;
2413
2414 xmlDtdPtr original_subset = ((xmlDocPtr) src_node)->intSubset;
2415 if (original_subset != NULL) {
2416 dst_doc->intSubset = xmlCopyDtd(((xmlDocPtr) src_node)->intSubset);
2417 if (UNEXPECTED(dst_doc->intSubset == NULL)) {
2418 xmlFreeNode(outer_clone);
2419 return NULL;
2420 }
2421 dst_doc->intSubset->parent = dst_doc;
2422 xmlSetTreeDoc((xmlNodePtr) dst_doc->intSubset, dst_doc);
2423 dst_doc->children = dst_doc->last = (xmlNodePtr) dst_doc->intSubset;
2424 }
2425 }
2426
2427 xmlNodePtr cloned_parent = outer_clone;
2428 xmlNodePtr base = src_node;
2429 src_node = src_node->children;
2430 while (src_node != NULL) {
2431 ZEND_ASSERT(src_node != base);
2432
2433 xmlNodePtr cloned;
2434 if (src_node->type == XML_ELEMENT_NODE) {
2435 cloned = dom_clone_container_helper(ns_mapper, src_node, dst_doc);
2436 } else if (src_node->type == XML_DTD_NODE) {
2437 /* Already handled. */
2438 cloned = NULL;
2439 } else {
2440 cloned = xmlDocCopyNode(src_node, dst_doc, 1);
2441 }
2442
2443 if (EXPECTED(cloned != NULL)) {
2444 if (cloned_parent->children == NULL) {
2445 cloned_parent->children = cloned;
2446 } else {
2447 cloned->prev = cloned_parent->last;
2448 cloned_parent->last->next = cloned;
2449 }
2450 cloned->parent = cloned_parent;
2451 cloned_parent->last = cloned;
2452 }
2453
2454 if (src_node->type == XML_ELEMENT_NODE && src_node->children) {
2455 cloned_parent = cloned;
2456 src_node = src_node->children;
2457 } else if (src_node->next) {
2458 src_node = src_node->next;
2459 } else {
2460 /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
2461 do {
2462 src_node = src_node->parent;
2463 if (src_node == base) {
2464 return outer_clone;
2465 }
2466 cloned_parent = cloned_parent->parent;
2467 } while (src_node->next == NULL);
2468 src_node = src_node->next;
2469 }
2470 }
2471
2472 return outer_clone;
2473 }
2474
dom_clone_node(php_dom_libxml_ns_mapper * ns_mapper,xmlNodePtr node,xmlDocPtr doc,bool recursive)2475 xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive)
2476 {
2477 if (node->type == XML_DTD_NODE) {
2478 /* The behaviour w.r.t. the internal subset is implementation-defined according to DOM 3.
2479 * This follows what e.g. Java and C# do: copy it regardless of the recursiveness.
2480 * Makes sense as the subset is not exactly a child in the normal sense. */
2481 xmlDtdPtr dtd = xmlCopyDtd((xmlDtdPtr) node);
2482 xmlSetTreeDoc((xmlNodePtr) dtd, doc);
2483 return (xmlNodePtr) dtd;
2484 }
2485
2486 if (ns_mapper != NULL) {
2487 xmlNodePtr clone = dom_clone_helper(ns_mapper, node, doc, recursive);
2488 if (EXPECTED(clone != NULL)) {
2489 if (clone->type == XML_DOCUMENT_NODE || clone->type == XML_HTML_DOCUMENT_NODE || clone->type == XML_DOCUMENT_FRAG_NODE) {
2490 for (xmlNodePtr child = clone->children; child != NULL; child = child->next) {
2491 php_dom_libxml_reconcile_modern(ns_mapper, child);
2492 }
2493 } else {
2494 php_dom_libxml_reconcile_modern(ns_mapper, clone);
2495 }
2496 }
2497 return clone;
2498 } else {
2499 /* See http://www.xmlsoft.org/html/libxml-tree.html#xmlDocCopyNode for meaning of values */
2500 int extended_recursive = recursive;
2501 if (!recursive && node->type == XML_ELEMENT_NODE) {
2502 extended_recursive = 2;
2503 }
2504 return xmlDocCopyNode(node, doc, extended_recursive);
2505 }
2506 }
2507
php_dom_has_child_of_type(xmlNodePtr node,xmlElementType type)2508 bool php_dom_has_child_of_type(xmlNodePtr node, xmlElementType type)
2509 {
2510 xmlNodePtr child = node->children;
2511
2512 while (child != NULL) {
2513 if (child->type == type) {
2514 return true;
2515 }
2516
2517 child = child->next;
2518 }
2519
2520 return false;
2521 }
2522
php_dom_has_sibling_following_node(xmlNodePtr node,xmlElementType type)2523 bool php_dom_has_sibling_following_node(xmlNodePtr node, xmlElementType type)
2524 {
2525 xmlNodePtr next = node->next;
2526
2527 while (next != NULL) {
2528 if (next->type == type) {
2529 return true;
2530 }
2531
2532 next = next->next;
2533 }
2534
2535 return false;
2536 }
2537
php_dom_has_sibling_preceding_node(xmlNodePtr node,xmlElementType type)2538 bool php_dom_has_sibling_preceding_node(xmlNodePtr node, xmlElementType type)
2539 {
2540 xmlNodePtr prev = node->prev;
2541
2542 while (prev != NULL) {
2543 if (prev->type == type) {
2544 return true;
2545 }
2546
2547 prev = prev->prev;
2548 }
2549
2550 return false;
2551 }
2552
php_dom_get_attribute_node(xmlNodePtr elem,const xmlChar * name,size_t name_len)2553 xmlAttrPtr php_dom_get_attribute_node(xmlNodePtr elem, const xmlChar *name, size_t name_len)
2554 {
2555 xmlChar *name_processed = BAD_CAST name;
2556 if (php_dom_ns_is_html_and_document_is_html(elem)) {
2557 char *lowercase_copy = zend_str_tolower_dup_ex((char *) name, name_len);
2558 if (lowercase_copy != NULL) {
2559 name_processed = BAD_CAST lowercase_copy;
2560 }
2561 }
2562
2563 xmlAttrPtr ret = NULL;
2564 for (xmlAttrPtr attr = elem->properties; attr != NULL; attr = attr->next) {
2565 if (dom_match_qualified_name_according_to_spec(name_processed, (xmlNodePtr) attr)) {
2566 ret = attr;
2567 break;
2568 }
2569 }
2570
2571 if (name_processed != name) {
2572 efree(name_processed);
2573 }
2574
2575 return ret;
2576 }
2577
2578 /* Workaround for a libxml2 bug on Windows: https://gitlab.gnome.org/GNOME/libxml2/-/issues/611. */
php_dom_libxml_fix_file_path(xmlChar * path)2579 xmlChar *php_dom_libxml_fix_file_path(xmlChar *path)
2580 {
2581 if (strncmp((char *) path, "file:/", sizeof("file:/") - 1) == 0) {
2582 if (path[6] != '/' && path[6] != '\0' && path[7] != '/' && path[7] != '\0') {
2583 /* The path is file:/xx... where xx != "//", which is broken */
2584 xmlChar *new_path = xmlStrdup(BAD_CAST "file:///");
2585 if (UNEXPECTED(new_path == NULL)) {
2586 return path;
2587 }
2588 new_path = xmlStrcat(new_path, path + 6);
2589 xmlFree(path);
2590 return new_path;
2591 }
2592 }
2593 return path;
2594 }
2595
php_dom_create_html_doc(void)2596 xmlDocPtr php_dom_create_html_doc(void)
2597 {
2598 #ifdef LIBXML_HTML_ENABLED
2599 xmlDocPtr lxml_doc = htmlNewDocNoDtD(NULL, NULL);
2600 if (EXPECTED(lxml_doc)) {
2601 lxml_doc->dict = xmlDictCreate();
2602 }
2603 #else
2604 /* If HTML support is not enabled, then htmlNewDocNoDtD() is not available.
2605 * This code mimics the behaviour. */
2606 xmlDocPtr lxml_doc = xmlNewDoc((const xmlChar *) "1.0");
2607 if (EXPECTED(lxml_doc)) {
2608 lxml_doc->type = XML_HTML_DOCUMENT_NODE;
2609 lxml_doc->dict = xmlDictCreate();
2610 }
2611 #endif
2612 return lxml_doc;
2613 }
2614
2615 #endif /* HAVE_DOM */
2616