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