1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Christian Stocker <chregu@php.net> |
14 | Rob Richards <rrichards@php.net> |
15 | Marcus Borger <helly@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "php.h"
24 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
25 #include "php_dom.h"
26 #include "php_dom_arginfo.h"
27 #include "dom_properties.h"
28 #include "zend_interfaces.h"
29 #include "lexbor/lexbor/core/types.h"
30 #include "lexbor/lexbor/core/lexbor.h"
31
32 #include "ext/standard/info.h"
33
34 /* {{{ class entries */
35 PHP_DOM_EXPORT zend_class_entry *dom_node_class_entry;
36 PHP_DOM_EXPORT zend_class_entry *dom_domexception_class_entry;
37 PHP_DOM_EXPORT zend_class_entry *dom_parentnode_class_entry;
38 PHP_DOM_EXPORT zend_class_entry *dom_childnode_class_entry;
39 PHP_DOM_EXPORT zend_class_entry *dom_domimplementation_class_entry;
40 PHP_DOM_EXPORT zend_class_entry *dom_documentfragment_class_entry;
41 PHP_DOM_EXPORT zend_class_entry *dom_document_class_entry;
42 PHP_DOM_EXPORT zend_class_entry *dom_html_document_class_entry;
43 PHP_DOM_EXPORT zend_class_entry *dom_xml_document_class_entry;
44 PHP_DOM_EXPORT zend_class_entry *dom_nodelist_class_entry;
45 PHP_DOM_EXPORT zend_class_entry *dom_namednodemap_class_entry;
46 PHP_DOM_EXPORT zend_class_entry *dom_characterdata_class_entry;
47 PHP_DOM_EXPORT zend_class_entry *dom_attr_class_entry;
48 PHP_DOM_EXPORT zend_class_entry *dom_element_class_entry;
49 PHP_DOM_EXPORT zend_class_entry *dom_text_class_entry;
50 PHP_DOM_EXPORT zend_class_entry *dom_comment_class_entry;
51 PHP_DOM_EXPORT zend_class_entry *dom_cdatasection_class_entry;
52 PHP_DOM_EXPORT zend_class_entry *dom_documenttype_class_entry;
53 PHP_DOM_EXPORT zend_class_entry *dom_notation_class_entry;
54 PHP_DOM_EXPORT zend_class_entry *dom_entity_class_entry;
55 PHP_DOM_EXPORT zend_class_entry *dom_entityreference_class_entry;
56 PHP_DOM_EXPORT zend_class_entry *dom_processinginstruction_class_entry;
57 PHP_DOM_EXPORT zend_class_entry *dom_abstract_base_document_class_entry;
58 #ifdef LIBXML_XPATH_ENABLED
59 PHP_DOM_EXPORT zend_class_entry *dom_xpath_class_entry;
60 #endif
61 PHP_DOM_EXPORT zend_class_entry *dom_namespace_node_class_entry;
62 /* }}} */
63
64 zend_object_handlers dom_object_handlers;
65 zend_object_handlers dom_nnodemap_object_handlers;
66 zend_object_handlers dom_nodelist_object_handlers;
67 zend_object_handlers dom_object_namespace_node_handlers;
68 #ifdef LIBXML_XPATH_ENABLED
69 zend_object_handlers dom_xpath_object_handlers;
70 #endif
71
72 static HashTable classes;
73 /* {{{ prop handler tables */
74 static HashTable dom_document_prop_handlers;
75 static HashTable dom_xml_document_prop_handlers;
76 static HashTable dom_html_document_prop_handlers;
77 static HashTable dom_documentfragment_prop_handlers;
78 static HashTable dom_node_prop_handlers;
79 static HashTable dom_nodelist_prop_handlers;
80 static HashTable dom_namednodemap_prop_handlers;
81 static HashTable dom_characterdata_prop_handlers;
82 static HashTable dom_attr_prop_handlers;
83 static HashTable dom_element_prop_handlers;
84 static HashTable dom_text_prop_handlers;
85 static HashTable dom_documenttype_prop_handlers;
86 static HashTable dom_notation_prop_handlers;
87 static HashTable dom_entity_prop_handlers;
88 static HashTable dom_processinginstruction_prop_handlers;
89 static HashTable dom_namespace_node_prop_handlers;
90 #ifdef LIBXML_XPATH_ENABLED
91 static HashTable dom_xpath_prop_handlers;
92 #endif
93 /* }}} */
94
95 static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type);
96 static void dom_object_namespace_node_free_storage(zend_object *object);
97 static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original);
98
99 typedef zend_result (*dom_read_t)(dom_object *obj, zval *retval);
100 typedef zend_result (*dom_write_t)(dom_object *obj, zval *newval);
101
102 typedef struct _dom_prop_handler {
103 dom_read_t read_func;
104 dom_write_t write_func;
105 } dom_prop_handler;
106
107 /* {{{ int dom_node_is_read_only(xmlNodePtr node) */
dom_node_is_read_only(xmlNodePtr node)108 int dom_node_is_read_only(xmlNodePtr node) {
109 switch (node->type) {
110 case XML_ENTITY_REF_NODE:
111 case XML_ENTITY_NODE:
112 case XML_DOCUMENT_TYPE_NODE:
113 case XML_NOTATION_NODE:
114 case XML_DTD_NODE:
115 case XML_ELEMENT_DECL:
116 case XML_ATTRIBUTE_DECL:
117 case XML_ENTITY_DECL:
118 case XML_NAMESPACE_DECL:
119 return SUCCESS;
120 break;
121 default:
122 if (node->doc == NULL) {
123 return SUCCESS;
124 } else {
125 return FAILURE;
126 }
127 }
128 }
129 /* }}} end dom_node_is_read_only */
130
131 /* {{{ int dom_node_children_valid(xmlNodePtr node) */
dom_node_children_valid(xmlNodePtr node)132 int dom_node_children_valid(xmlNodePtr node) {
133 switch (node->type) {
134 case XML_DOCUMENT_TYPE_NODE:
135 case XML_DTD_NODE:
136 case XML_PI_NODE:
137 case XML_COMMENT_NODE:
138 case XML_TEXT_NODE:
139 case XML_CDATA_SECTION_NODE:
140 case XML_NOTATION_NODE:
141 return FAILURE;
142 break;
143 default:
144 return SUCCESS;
145 }
146 }
147 /* }}} end dom_node_children_valid */
148
149 static const libxml_doc_props default_doc_props = {
150 .formatoutput = false,
151 .validateonparse = false,
152 .resolveexternals = false,
153 .preservewhitespace = true,
154 .substituteentities = false,
155 .stricterror = true,
156 .recover = false,
157 .classmap = NULL,
158 };
159
160 /* {{{ dom_get_doc_props() */
dom_get_doc_props(php_libxml_ref_obj * document)161 dom_doc_propsptr dom_get_doc_props(php_libxml_ref_obj *document)
162 {
163 dom_doc_propsptr doc_props;
164
165 if (document && document->doc_props) {
166 return document->doc_props;
167 } else {
168 doc_props = emalloc(sizeof(libxml_doc_props));
169 memcpy(doc_props, &default_doc_props, sizeof(libxml_doc_props));
170 if (document) {
171 document->doc_props = doc_props;
172 }
173 return doc_props;
174 }
175 }
176 /* }}} */
177
dom_get_doc_props_read_only(const php_libxml_ref_obj * document)178 libxml_doc_props const* dom_get_doc_props_read_only(const php_libxml_ref_obj *document)
179 {
180 if (document && document->doc_props) {
181 return document->doc_props;
182 } else {
183 return &default_doc_props;
184 }
185 }
186
dom_copy_doc_props(php_libxml_ref_obj * source_doc,php_libxml_ref_obj * dest_doc)187 static void dom_copy_doc_props(php_libxml_ref_obj *source_doc, php_libxml_ref_obj *dest_doc)
188 {
189 dom_doc_propsptr dest;
190
191 if (source_doc && dest_doc) {
192
193 libxml_doc_props const* source = dom_get_doc_props_read_only(source_doc);
194 dest = dom_get_doc_props(dest_doc);
195
196 dest->formatoutput = source->formatoutput;
197 dest->validateonparse = source->validateonparse;
198 dest->resolveexternals = source->resolveexternals;
199 dest->preservewhitespace = source->preservewhitespace;
200 dest->substituteentities = source->substituteentities;
201 dest->stricterror = source->stricterror;
202 dest->recover = source->recover;
203 if (source->classmap) {
204 ALLOC_HASHTABLE(dest->classmap);
205 zend_hash_init(dest->classmap, 0, NULL, NULL, false);
206 zend_hash_copy(dest->classmap, source->classmap, NULL);
207 }
208
209 dest_doc->is_modern_api_class = source_doc->is_modern_api_class;
210 }
211 }
212
dom_set_doc_classmap(php_libxml_ref_obj * document,zend_class_entry * basece,zend_class_entry * ce)213 void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce)
214 {
215 dom_doc_propsptr doc_props;
216
217 if (document) {
218 doc_props = dom_get_doc_props(document);
219 if (doc_props->classmap == NULL) {
220 if (ce == NULL) {
221 return;
222 }
223 ALLOC_HASHTABLE(doc_props->classmap);
224 zend_hash_init(doc_props->classmap, 0, NULL, NULL, false);
225 }
226 if (ce) {
227 zend_hash_update_ptr(doc_props->classmap, basece->name, ce);
228 } else {
229 zend_hash_del(doc_props->classmap, basece->name);
230 }
231 }
232 }
233
dom_get_doc_classmap(php_libxml_ref_obj * document,zend_class_entry * basece)234 zend_class_entry *dom_get_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece)
235 {
236 if (document) {
237 libxml_doc_props const* doc_props = dom_get_doc_props_read_only(document);
238 if (doc_props->classmap) {
239 zend_class_entry *ce = zend_hash_find_ptr(doc_props->classmap, basece->name);
240 if (ce) {
241 return ce;
242 }
243 }
244 }
245
246 return basece;
247 }
248 /* }}} */
249
250 /* {{{ dom_get_strict_error() */
dom_get_strict_error(php_libxml_ref_obj * document)251 int dom_get_strict_error(php_libxml_ref_obj *document) {
252 return dom_get_doc_props_read_only(document)->stricterror;
253 }
254 /* }}} */
255
256 /* {{{ xmlNodePtr dom_object_get_node(dom_object *obj) */
dom_object_get_node(dom_object * obj)257 PHP_DOM_EXPORT xmlNodePtr dom_object_get_node(dom_object *obj)
258 {
259 if (obj && obj->ptr != NULL) {
260 return ((php_libxml_node_ptr *)obj->ptr)->node;
261 } else {
262 return NULL;
263 }
264 }
265 /* }}} end dom_object_get_node */
266
267 /* {{{ dom_object *php_dom_object_get_data(xmlNodePtr obj) */
php_dom_object_get_data(xmlNodePtr obj)268 PHP_DOM_EXPORT dom_object *php_dom_object_get_data(xmlNodePtr obj)
269 {
270 if (obj && obj->_private != NULL) {
271 return (dom_object *) ((php_libxml_node_ptr *) obj->_private)->_private;
272 } else {
273 return NULL;
274 }
275 }
276 /* }}} end php_dom_object_get_data */
277
dom_register_prop_handler(HashTable * prop_handler,const char * name,size_t name_len,const dom_prop_handler * hnd)278 static void dom_register_prop_handler(HashTable *prop_handler, const char *name, size_t name_len, const dom_prop_handler *hnd)
279 {
280 zend_string *str = zend_string_init_interned(name, name_len, true);
281 zend_hash_add_new_ptr(prop_handler, str, (void *) hnd);
282 zend_string_release_ex(str, true);
283 }
284
285 #define DOM_REGISTER_PROP_HANDLER(prop_handler, name, prop_read_func, prop_write_func) do { \
286 static const dom_prop_handler hnd = {.read_func = prop_read_func, .write_func = prop_write_func}; \
287 dom_register_prop_handler(prop_handler, "" name, sizeof("" name) - 1, &hnd); \
288 } while (0)
289
dom_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)290 static zval *dom_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
291 {
292 dom_object *obj = php_dom_obj_from_obj(object);
293
294 if (!obj->prop_handler || !zend_hash_exists(obj->prop_handler, name)) {
295 return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
296 }
297
298 return NULL;
299 }
300
301 /* {{{ dom_read_property */
dom_read_property(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)302 zval *dom_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
303 {
304 dom_object *obj = php_dom_obj_from_obj(object);
305 zval *retval;
306 dom_prop_handler *hnd = NULL;
307
308 if (obj->prop_handler != NULL) {
309 hnd = zend_hash_find_ptr(obj->prop_handler, name);
310 }
311
312 if (hnd) {
313 int ret = hnd->read_func(obj, rv);
314 if (ret == SUCCESS) {
315 retval = rv;
316 } else {
317 retval = &EG(uninitialized_zval);
318 }
319 } else {
320 retval = zend_std_read_property(object, name, type, cache_slot, rv);
321 }
322
323 return retval;
324 }
325 /* }}} */
326
dom_write_property(zend_object * object,zend_string * name,zval * value,void ** cache_slot)327 zval *dom_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
328 {
329 dom_object *obj = php_dom_obj_from_obj(object);
330 dom_prop_handler *hnd = NULL;
331
332 if (obj->prop_handler != NULL) {
333 hnd = zend_hash_find_ptr(obj->prop_handler, name);
334 }
335
336 if (hnd) {
337 if (!hnd->write_func) {
338 zend_throw_error(NULL, "Cannot write read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
339 return &EG(error_zval);
340 }
341
342 zend_property_info *prop = zend_get_property_info(object->ce, name, /* silent */ true);
343 if (prop && ZEND_TYPE_IS_SET(prop->type)) {
344 zval tmp;
345 ZVAL_COPY(&tmp, value);
346 if (!zend_verify_property_type(prop, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) {
347 zval_ptr_dtor(&tmp);
348 return &EG(error_zval);
349 }
350 hnd->write_func(obj, &tmp);
351 zval_ptr_dtor(&tmp);
352 } else {
353 hnd->write_func(obj, value);
354 }
355
356 return value;
357 }
358
359 return zend_std_write_property(object, name, value, cache_slot);
360 }
361
362 /* {{{ dom_property_exists */
dom_property_exists(zend_object * object,zend_string * name,int check_empty,void ** cache_slot)363 static int dom_property_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
364 {
365 dom_object *obj = php_dom_obj_from_obj(object);
366 dom_prop_handler *hnd = NULL;
367 int retval = 0;
368
369 if (obj->prop_handler != NULL) {
370 hnd = zend_hash_find_ptr(obj->prop_handler, name);
371 }
372 if (hnd) {
373 zval tmp;
374
375 if (check_empty == 2) {
376 retval = 1;
377 } else if (hnd->read_func(obj, &tmp) == SUCCESS) {
378 if (check_empty == 1) {
379 retval = zend_is_true(&tmp);
380 } else if (check_empty == 0) {
381 retval = (Z_TYPE(tmp) != IS_NULL);
382 }
383 zval_ptr_dtor(&tmp);
384 }
385 } else {
386 retval = zend_std_has_property(object, name, check_empty, cache_slot);
387 }
388
389 return retval;
390 }
391 /* }}} */
392
dom_get_debug_info_helper(zend_object * object,int * is_temp)393 static HashTable* dom_get_debug_info_helper(zend_object *object, int *is_temp) /* {{{ */
394 {
395 dom_object *obj = php_dom_obj_from_obj(object);
396 HashTable *debug_info,
397 *prop_handlers = obj->prop_handler,
398 *std_props;
399 zend_string *string_key;
400 dom_prop_handler *entry;
401 zend_string *object_str;
402
403 *is_temp = 1;
404
405 std_props = zend_std_get_properties(object);
406 debug_info = zend_array_dup(std_props);
407
408 if (!prop_handlers) {
409 return debug_info;
410 }
411
412 object_str = ZSTR_INIT_LITERAL("(object value omitted)", false);
413
414 ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(prop_handlers, string_key, entry) {
415 zval value;
416
417 ZEND_ASSERT(string_key != NULL);
418
419 if (entry->read_func(obj, &value) == FAILURE) {
420 continue;
421 }
422
423 if (Z_TYPE(value) == IS_OBJECT) {
424 zval_ptr_dtor(&value);
425 ZVAL_NEW_STR(&value, object_str);
426 zend_string_addref(object_str);
427 }
428
429 zend_hash_update(debug_info, string_key, &value);
430 } ZEND_HASH_FOREACH_END();
431
432 zend_string_release_ex(object_str, false);
433
434 return debug_info;
435 }
436 /* }}} */
437
dom_get_debug_info(zend_object * object,int * is_temp)438 static HashTable* dom_get_debug_info(zend_object *object, int *is_temp) /* {{{ */
439 {
440 return dom_get_debug_info_helper(object, is_temp);
441 }
442 /* }}} */
443
php_dom_export_node(zval * object)444 void *php_dom_export_node(zval *object) /* {{{ */
445 {
446 php_libxml_node_object *intern;
447 xmlNodePtr nodep = NULL;
448
449 intern = (php_libxml_node_object *) Z_DOMOBJ_P(object);
450 if (intern->node) {
451 nodep = intern->node->node;
452 }
453
454 return nodep;
455 }
456 /* }}} */
457
458 /* {{{ Get a simplexml_element object from dom to allow for processing */
PHP_FUNCTION(dom_import_simplexml)459 PHP_FUNCTION(dom_import_simplexml)
460 {
461 zval *node;
462 xmlNodePtr nodep = NULL;
463 php_libxml_node_object *nodeobj;
464 int ret;
465
466 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &node) == FAILURE) {
467 RETURN_THROWS();
468 }
469
470 nodeobj = (php_libxml_node_object *) ((char *) Z_OBJ_P(node) - Z_OBJ_HT_P(node)->offset);
471 nodep = php_libxml_import_node(node);
472
473 if (nodep && nodeobj && (nodep->type == XML_ELEMENT_NODE || nodep->type == XML_ATTRIBUTE_NODE)) {
474 DOM_RET_OBJ((xmlNodePtr) nodep, &ret, (dom_object *)nodeobj);
475 } else {
476 zend_argument_value_error(1, "is not a valid node type");
477 RETURN_THROWS();
478 }
479 }
480 /* }}} */
481
482 static dom_object* dom_objects_set_class(zend_class_entry *class_type);
483
dom_update_refcount_after_clone(dom_object * original,xmlNodePtr original_node,dom_object * clone,xmlNodePtr cloned_node)484 static void dom_update_refcount_after_clone(dom_object *original, xmlNodePtr original_node, dom_object *clone, xmlNodePtr cloned_node)
485 {
486 /* If we cloned a document then we must create new doc proxy */
487 if (cloned_node->doc == original_node->doc) {
488 clone->document = original->document;
489 }
490 php_libxml_increment_doc_ref((php_libxml_node_object *)clone, cloned_node->doc);
491 php_libxml_increment_node_ptr((php_libxml_node_object *)clone, cloned_node, (void *)clone);
492 if (original->document != clone->document) {
493 dom_copy_doc_props(original->document, clone->document);
494 /* Workaround libxml2 bug, see https://gitlab.gnome.org/GNOME/libxml2/-/commit/07920b4381873187c02df53fa9b5d44aff3a7041 */
495 #if LIBXML_VERSION < 20911
496 if (original_node->type == XML_HTML_DOCUMENT_NODE) {
497 cloned_node->type = XML_HTML_DOCUMENT_NODE;
498 }
499 #endif
500 }
501 }
502
dom_objects_store_clone_obj(zend_object * zobject)503 static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */
504 {
505 dom_object *intern = php_dom_obj_from_obj(zobject);
506 dom_object *clone = dom_objects_set_class(intern->std.ce);
507
508 clone->std.handlers = &dom_object_handlers;
509
510 if (instanceof_function(intern->std.ce, dom_node_class_entry)) {
511 xmlNodePtr node = (xmlNodePtr)dom_object_get_node(intern);
512 if (node != NULL) {
513 xmlNodePtr cloned_node = xmlDocCopyNode(node, node->doc, 1);
514 if (cloned_node != NULL) {
515 dom_update_refcount_after_clone(intern, node, clone, cloned_node);
516 }
517
518 }
519 }
520
521 zend_objects_clone_members(&clone->std, &intern->std);
522
523 return &clone->std;
524 }
525 /* }}} */
526
dom_object_namespace_node_clone_obj(zend_object * zobject)527 static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject)
528 {
529 dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject);
530 zend_object *clone = dom_objects_namespace_node_new(intern->dom.std.ce);
531 dom_object_namespace_node *clone_intern = php_dom_namespace_node_obj_from_obj(clone);
532
533 xmlNodePtr original_node = dom_object_get_node(&intern->dom);
534 ZEND_ASSERT(original_node->type == XML_NAMESPACE_DECL);
535 xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(original_node->parent, original_node->ns);
536
537 if (intern->parent_intern) {
538 clone_intern->parent_intern = intern->parent_intern;
539 GC_ADDREF(&clone_intern->parent_intern->std);
540 }
541 dom_update_refcount_after_clone(&intern->dom, original_node, &clone_intern->dom, cloned_node);
542
543 zend_objects_clone_members(clone, &intern->dom.std);
544 return clone;
545 }
546
547 static const zend_module_dep dom_deps[] = {
548 ZEND_MOD_REQUIRED("libxml")
549 ZEND_MOD_CONFLICTS("domxml")
550 ZEND_MOD_END
551 };
552
553 zend_module_entry dom_module_entry = { /* {{{ */
554 STANDARD_MODULE_HEADER_EX, NULL,
555 dom_deps,
556 "dom",
557 ext_functions,
558 PHP_MINIT(dom),
559 PHP_MSHUTDOWN(dom),
560 NULL,
561 NULL,
562 PHP_MINFO(dom),
563 DOM_API_VERSION, /* Extension versionnumber */
564 STANDARD_MODULE_PROPERTIES
565 };
566 /* }}} */
567
568 #ifdef COMPILE_DL_DOM
569 ZEND_GET_MODULE(dom)
570 #endif
571
572 void dom_objects_free_storage(zend_object *object);
573 void dom_nnodemap_objects_free_storage(zend_object *object);
574 static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv);
575 static int dom_nodelist_has_dimension(zend_object *object, zval *member, int check_empty);
576 static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv);
577 static int dom_nodemap_has_dimension(zend_object *object, zval *member, int check_empty);
578 static zend_object *dom_objects_store_clone_obj(zend_object *zobject);
579
580 #ifdef LIBXML_XPATH_ENABLED
581 void dom_xpath_objects_free_storage(zend_object *object);
582 HashTable *dom_xpath_get_gc(zend_object *object, zval **table, int *n);
583 #endif
584
dom_malloc(size_t size)585 static void *dom_malloc(size_t size) {
586 return emalloc(size);
587 }
588
dom_realloc(void * dst,size_t size)589 static void *dom_realloc(void *dst, size_t size) {
590 return erealloc(dst, size);
591 }
592
dom_calloc(size_t num,size_t size)593 static void *dom_calloc(size_t num, size_t size) {
594 return ecalloc(num, size);
595 }
596
dom_free(void * ptr)597 static void dom_free(void *ptr) {
598 efree(ptr);
599 }
600
register_nondeprecated_xml_props(HashTable * table)601 static void register_nondeprecated_xml_props(HashTable *table)
602 {
603 DOM_REGISTER_PROP_HANDLER(table, "encoding", dom_document_encoding_read, dom_document_encoding_write);
604 DOM_REGISTER_PROP_HANDLER(table, "xmlEncoding", dom_document_encoding_read, NULL);
605 DOM_REGISTER_PROP_HANDLER(table, "standalone", dom_document_standalone_read, dom_document_standalone_write);
606 DOM_REGISTER_PROP_HANDLER(table, "xmlStandalone", dom_document_standalone_read, dom_document_standalone_write);
607 DOM_REGISTER_PROP_HANDLER(table, "version", dom_document_version_read, dom_document_version_write);
608 DOM_REGISTER_PROP_HANDLER(table, "xmlVersion", dom_document_version_read, dom_document_version_write);
609 DOM_REGISTER_PROP_HANDLER(table, "formatOutput", dom_document_format_output_read, dom_document_format_output_write);
610 DOM_REGISTER_PROP_HANDLER(table, "validateOnParse", dom_document_validate_on_parse_read, dom_document_validate_on_parse_write);
611 DOM_REGISTER_PROP_HANDLER(table, "resolveExternals", dom_document_resolve_externals_read, dom_document_resolve_externals_write);
612 DOM_REGISTER_PROP_HANDLER(table, "preserveWhiteSpace", dom_document_preserve_whitespace_read, dom_document_preserve_whitespace_write);
613 DOM_REGISTER_PROP_HANDLER(table, "recover", dom_document_recover_read, dom_document_recover_write);
614 DOM_REGISTER_PROP_HANDLER(table, "substituteEntities", dom_document_substitue_entities_read, dom_document_substitue_entities_write);
615 }
616
617 /* {{{ PHP_MINIT_FUNCTION(dom) */
PHP_MINIT_FUNCTION(dom)618 PHP_MINIT_FUNCTION(dom)
619 {
620 memcpy(&dom_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
621 dom_object_handlers.offset = XtOffsetOf(dom_object, std);
622 dom_object_handlers.free_obj = dom_objects_free_storage;
623 dom_object_handlers.read_property = dom_read_property;
624 dom_object_handlers.write_property = dom_write_property;
625 dom_object_handlers.get_property_ptr_ptr = dom_get_property_ptr_ptr;
626 dom_object_handlers.clone_obj = dom_objects_store_clone_obj;
627 dom_object_handlers.has_property = dom_property_exists;
628 dom_object_handlers.get_debug_info = dom_get_debug_info;
629
630 memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
631 dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage;
632 dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension;
633 dom_nnodemap_object_handlers.has_dimension = dom_nodemap_has_dimension;
634
635 memcpy(&dom_nodelist_object_handlers, &dom_nnodemap_object_handlers, sizeof(zend_object_handlers));
636 dom_nodelist_object_handlers.read_dimension = dom_nodelist_read_dimension;
637 dom_nodelist_object_handlers.has_dimension = dom_nodelist_has_dimension;
638
639 memcpy(&dom_object_namespace_node_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
640 dom_object_namespace_node_handlers.offset = XtOffsetOf(dom_object_namespace_node, dom.std);
641 dom_object_namespace_node_handlers.free_obj = dom_object_namespace_node_free_storage;
642 dom_object_namespace_node_handlers.clone_obj = dom_object_namespace_node_clone_obj;
643
644 zend_hash_init(&classes, 0, NULL, NULL, true);
645
646 dom_domexception_class_entry = register_class_DOMException(zend_ce_exception);
647
648 dom_parentnode_class_entry = register_class_DOMParentNode();
649
650 dom_childnode_class_entry = register_class_DOMChildNode();
651
652 dom_domimplementation_class_entry = register_class_DOMImplementation();
653 dom_domimplementation_class_entry->create_object = dom_objects_new;
654
655 dom_node_class_entry = register_class_DOMNode();
656 dom_node_class_entry->create_object = dom_objects_new;
657
658 zend_hash_init(&dom_node_prop_handlers, 0, NULL, NULL, true);
659 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL);
660 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeValue", dom_node_node_value_read, dom_node_node_value_write);
661 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL);
662 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL);
663 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL);
664 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "childNodes", dom_node_child_nodes_read, NULL);
665 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "firstChild", dom_node_first_child_read, NULL);
666 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "lastChild", dom_node_last_child_read, NULL);
667 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "previousSibling", dom_node_previous_sibling_read, NULL);
668 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "nextSibling", dom_node_next_sibling_read, NULL);
669 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "attributes", dom_node_attributes_read, NULL);
670 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL);
671 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL);
672 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
673 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "prefix", dom_node_prefix_read, dom_node_prefix_write);
674 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "localName", dom_node_local_name_read, NULL);
675 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "baseURI", dom_node_base_uri_read, NULL);
676 DOM_REGISTER_PROP_HANDLER(&dom_node_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
677 zend_hash_add_new_ptr(&classes, dom_node_class_entry->name, &dom_node_prop_handlers);
678
679 dom_namespace_node_class_entry = register_class_DOMNameSpaceNode();
680 dom_namespace_node_class_entry->create_object = dom_objects_namespace_node_new;
681
682 zend_hash_init(&dom_namespace_node_prop_handlers, 0, NULL, NULL, true);
683 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL);
684 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeValue", dom_node_node_value_read, NULL);
685 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL);
686 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "prefix", dom_node_prefix_read, NULL);
687 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "localName", dom_node_local_name_read, NULL);
688 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL);
689 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL);
690 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL);
691 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL);
692 DOM_REGISTER_PROP_HANDLER(&dom_namespace_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL);
693 zend_hash_add_new_ptr(&classes, dom_namespace_node_class_entry->name, &dom_namespace_node_prop_handlers);
694
695 dom_documentfragment_class_entry = register_class_DOMDocumentFragment(dom_node_class_entry, dom_parentnode_class_entry);
696 dom_documentfragment_class_entry->create_object = dom_objects_new;
697 zend_hash_init(&dom_documentfragment_prop_handlers, 0, NULL, NULL, true);
698
699 DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
700 DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
701 DOM_REGISTER_PROP_HANDLER(&dom_documentfragment_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
702
703 zend_hash_merge(&dom_documentfragment_prop_handlers, &dom_node_prop_handlers, NULL, false);
704 zend_hash_add_new_ptr(&classes, dom_documentfragment_class_entry->name, &dom_documentfragment_prop_handlers);
705
706 dom_abstract_base_document_class_entry = register_class_DOM_Document(dom_node_class_entry, dom_parentnode_class_entry);
707 /* No need to set create_object as it's abstract. */
708 HashTable dom_abstract_base_document_prop_handlers;
709 zend_hash_init(&dom_abstract_base_document_prop_handlers, 0, NULL, NULL, true);
710 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "doctype", dom_document_doctype_read, NULL);
711 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentElement", dom_document_document_element_read, NULL);
712 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "strictErrorChecking", dom_document_strict_error_checking_read, dom_document_strict_error_checking_write);
713 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentURI", dom_document_document_uri_read, dom_document_document_uri_write);
714 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
715 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
716 DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
717 zend_hash_merge(&dom_abstract_base_document_prop_handlers, &dom_node_prop_handlers, NULL, false);
718 /* No need to register in &classes, because this is only used for merging. This is destroyed down below. */
719
720 dom_document_class_entry = register_class_DOMDocument(dom_abstract_base_document_class_entry);
721 dom_document_class_entry->create_object = dom_objects_new;
722 zend_hash_init(&dom_document_prop_handlers, 0, NULL, NULL, true);
723 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "implementation", dom_document_implementation_read, NULL);
724 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "actualEncoding", dom_document_encoding_read, NULL);
725 DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "config", dom_document_config_read, NULL);
726 register_nondeprecated_xml_props(&dom_document_prop_handlers);
727
728 zend_hash_merge(&dom_document_prop_handlers, &dom_abstract_base_document_prop_handlers, NULL, false);
729 zend_hash_add_new_ptr(&classes, dom_document_class_entry->name, &dom_document_prop_handlers);
730
731 dom_html_document_class_entry = register_class_DOM_HTMLDocument(dom_abstract_base_document_class_entry);
732 dom_document_class_entry->create_object = dom_objects_new;
733 zend_hash_init(&dom_html_document_prop_handlers, 0, NULL, NULL, true);
734 DOM_REGISTER_PROP_HANDLER(&dom_html_document_prop_handlers, "encoding", dom_document_encoding_read, dom_html_document_encoding_write);
735
736 zend_hash_merge(&dom_html_document_prop_handlers, &dom_abstract_base_document_prop_handlers, NULL, false);
737 zend_hash_add_new_ptr(&classes, dom_html_document_class_entry->name, &dom_html_document_prop_handlers);
738
739 dom_xml_document_class_entry = register_class_DOM_XMLDocument(dom_abstract_base_document_class_entry);
740 dom_xml_document_class_entry->create_object = dom_objects_new;
741 zend_hash_init(&dom_xml_document_prop_handlers, 0, NULL, NULL, true);
742 register_nondeprecated_xml_props(&dom_xml_document_prop_handlers);
743
744 zend_hash_merge(&dom_xml_document_prop_handlers, &dom_abstract_base_document_prop_handlers, NULL, false);
745 zend_hash_add_new_ptr(&classes, dom_xml_document_class_entry->name, &dom_xml_document_prop_handlers);
746
747 zend_hash_destroy(&dom_abstract_base_document_prop_handlers);
748
749 dom_nodelist_class_entry = register_class_DOMNodeList(zend_ce_aggregate, zend_ce_countable);
750 dom_nodelist_class_entry->create_object = dom_nnodemap_objects_new;
751 dom_nodelist_class_entry->default_object_handlers = &dom_nodelist_object_handlers;
752 dom_nodelist_class_entry->get_iterator = php_dom_get_iterator;
753
754 zend_hash_init(&dom_nodelist_prop_handlers, 0, NULL, NULL, true);
755 DOM_REGISTER_PROP_HANDLER(&dom_nodelist_prop_handlers, "length", dom_nodelist_length_read, NULL);
756 zend_hash_add_new_ptr(&classes, dom_nodelist_class_entry->name, &dom_nodelist_prop_handlers);
757
758 dom_namednodemap_class_entry = register_class_DOMNamedNodeMap(zend_ce_aggregate, zend_ce_countable);
759 dom_namednodemap_class_entry->create_object = dom_nnodemap_objects_new;
760 dom_namednodemap_class_entry->default_object_handlers = &dom_nnodemap_object_handlers;
761 dom_namednodemap_class_entry->get_iterator = php_dom_get_iterator;
762
763 zend_hash_init(&dom_namednodemap_prop_handlers, 0, NULL, NULL, true);
764 DOM_REGISTER_PROP_HANDLER(&dom_namednodemap_prop_handlers, "length", dom_namednodemap_length_read, NULL);
765 zend_hash_add_new_ptr(&classes, dom_namednodemap_class_entry->name, &dom_namednodemap_prop_handlers);
766
767 dom_characterdata_class_entry = register_class_DOMCharacterData(dom_node_class_entry, dom_childnode_class_entry);
768 dom_characterdata_class_entry->create_object = dom_objects_new;
769
770 zend_hash_init(&dom_characterdata_prop_handlers, 0, NULL, NULL, true);
771 DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "data", dom_characterdata_data_read, dom_characterdata_data_write);
772 DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "length", dom_characterdata_length_read, NULL);
773 DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
774 DOM_REGISTER_PROP_HANDLER(&dom_characterdata_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
775 zend_hash_merge(&dom_characterdata_prop_handlers, &dom_node_prop_handlers, NULL, false);
776 zend_hash_add_new_ptr(&classes, dom_characterdata_class_entry->name, &dom_characterdata_prop_handlers);
777
778 dom_attr_class_entry = register_class_DOMAttr(dom_node_class_entry);
779 dom_attr_class_entry->create_object = dom_objects_new;
780
781 zend_hash_init(&dom_attr_prop_handlers, 0, NULL, NULL, true);
782 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "name", dom_attr_name_read, NULL);
783 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "specified", dom_attr_specified_read, NULL);
784 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "value", dom_attr_value_read, dom_attr_value_write);
785 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "ownerElement", dom_attr_owner_element_read, NULL);
786 DOM_REGISTER_PROP_HANDLER(&dom_attr_prop_handlers, "schemaTypeInfo", dom_attr_schema_type_info_read, NULL);
787 zend_hash_merge(&dom_attr_prop_handlers, &dom_node_prop_handlers, NULL, false);
788 zend_hash_add_new_ptr(&classes, dom_attr_class_entry->name, &dom_attr_prop_handlers);
789
790 dom_element_class_entry = register_class_DOMElement(dom_node_class_entry, dom_parentnode_class_entry, dom_childnode_class_entry);
791 dom_element_class_entry->create_object = dom_objects_new;
792
793 zend_hash_init(&dom_element_prop_handlers, 0, NULL, NULL, true);
794 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "tagName", dom_element_tag_name_read, NULL);
795 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "className", dom_element_class_name_read, dom_element_class_name_write);
796 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "id", dom_element_id_read, dom_element_id_write);
797 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "schemaTypeInfo", dom_element_schema_type_info_read, NULL);
798 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
799 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
800 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
801 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
802 DOM_REGISTER_PROP_HANDLER(&dom_element_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
803 zend_hash_merge(&dom_element_prop_handlers, &dom_node_prop_handlers, NULL, false);
804 zend_hash_add_new_ptr(&classes, dom_element_class_entry->name, &dom_element_prop_handlers);
805
806 dom_text_class_entry = register_class_DOMText(dom_characterdata_class_entry);
807 dom_text_class_entry->create_object = dom_objects_new;
808
809 zend_hash_init(&dom_text_prop_handlers, 0, NULL, NULL, true);
810 DOM_REGISTER_PROP_HANDLER(&dom_text_prop_handlers, "wholeText", dom_text_whole_text_read, NULL);
811 zend_hash_merge(&dom_text_prop_handlers, &dom_characterdata_prop_handlers, NULL, false);
812 zend_hash_add_new_ptr(&classes, dom_text_class_entry->name, &dom_text_prop_handlers);
813
814 dom_comment_class_entry = register_class_DOMComment(dom_characterdata_class_entry);
815 dom_comment_class_entry->create_object = dom_objects_new;
816 zend_hash_add_new_ptr(&classes, dom_comment_class_entry->name, &dom_characterdata_prop_handlers);
817
818 dom_cdatasection_class_entry = register_class_DOMCdataSection(dom_text_class_entry);
819 dom_cdatasection_class_entry->create_object = dom_objects_new;
820 zend_hash_add_new_ptr(&classes, dom_cdatasection_class_entry->name, &dom_text_prop_handlers);
821
822 dom_documenttype_class_entry = register_class_DOMDocumentType(dom_node_class_entry);
823 dom_documenttype_class_entry->create_object = dom_objects_new;
824
825 zend_hash_init(&dom_documenttype_prop_handlers, 0, NULL, NULL, true);
826 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "name", dom_documenttype_name_read, NULL);
827 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "entities", dom_documenttype_entities_read, NULL);
828 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "notations", dom_documenttype_notations_read, NULL);
829 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "publicId", dom_documenttype_public_id_read, NULL);
830 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "systemId", dom_documenttype_system_id_read, NULL);
831 DOM_REGISTER_PROP_HANDLER(&dom_documenttype_prop_handlers, "internalSubset", dom_documenttype_internal_subset_read, NULL);
832 zend_hash_merge(&dom_documenttype_prop_handlers, &dom_node_prop_handlers, NULL, false);
833 zend_hash_add_new_ptr(&classes, dom_documenttype_class_entry->name, &dom_documenttype_prop_handlers);
834
835 dom_notation_class_entry = register_class_DOMNotation(dom_node_class_entry);
836 dom_notation_class_entry->create_object = dom_objects_new;
837
838 zend_hash_init(&dom_notation_prop_handlers, 0, NULL, NULL, true);
839 DOM_REGISTER_PROP_HANDLER(&dom_notation_prop_handlers, "publicId", dom_notation_public_id_read, NULL);
840 DOM_REGISTER_PROP_HANDLER(&dom_notation_prop_handlers, "systemId", dom_notation_system_id_read, NULL);
841 zend_hash_merge(&dom_notation_prop_handlers, &dom_node_prop_handlers, NULL, false);
842 zend_hash_add_new_ptr(&classes, dom_notation_class_entry->name, &dom_notation_prop_handlers);
843
844 dom_entity_class_entry = register_class_DOMEntity(dom_node_class_entry);
845 dom_entity_class_entry->create_object = dom_objects_new;
846
847 zend_hash_init(&dom_entity_prop_handlers, 0, NULL, NULL, true);
848 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "publicId", dom_entity_public_id_read, NULL);
849 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "systemId", dom_entity_system_id_read, NULL);
850 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "notationName", dom_entity_notation_name_read, NULL);
851 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "actualEncoding", dom_entity_actual_encoding_read, NULL);
852 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "encoding", dom_entity_encoding_read, NULL);
853 DOM_REGISTER_PROP_HANDLER(&dom_entity_prop_handlers, "version", dom_entity_version_read, NULL);
854 zend_hash_merge(&dom_entity_prop_handlers, &dom_node_prop_handlers, NULL, false);
855 zend_hash_add_new_ptr(&classes, dom_entity_class_entry->name, &dom_entity_prop_handlers);
856
857 dom_entityreference_class_entry = register_class_DOMEntityReference(dom_node_class_entry);
858 dom_entityreference_class_entry->create_object = dom_objects_new;
859 zend_hash_add_new_ptr(&classes, dom_entityreference_class_entry->name, &dom_node_prop_handlers);
860
861 dom_processinginstruction_class_entry = register_class_DOMProcessingInstruction(dom_node_class_entry);
862 dom_processinginstruction_class_entry->create_object = dom_objects_new;
863
864 zend_hash_init(&dom_processinginstruction_prop_handlers, 0, NULL, NULL, true);
865 DOM_REGISTER_PROP_HANDLER(&dom_processinginstruction_prop_handlers, "target", dom_processinginstruction_target_read, NULL);
866 DOM_REGISTER_PROP_HANDLER(&dom_processinginstruction_prop_handlers, "data", dom_processinginstruction_data_read, dom_processinginstruction_data_write);
867 zend_hash_merge(&dom_processinginstruction_prop_handlers, &dom_node_prop_handlers, NULL, false);
868 zend_hash_add_new_ptr(&classes, dom_processinginstruction_class_entry->name, &dom_processinginstruction_prop_handlers);
869
870 #ifdef LIBXML_XPATH_ENABLED
871 memcpy(&dom_xpath_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
872 dom_xpath_object_handlers.offset = XtOffsetOf(dom_xpath_object, dom) + XtOffsetOf(dom_object, std);
873 dom_xpath_object_handlers.free_obj = dom_xpath_objects_free_storage;
874 dom_xpath_object_handlers.get_gc = dom_xpath_get_gc;
875
876 dom_xpath_class_entry = register_class_DOMXPath();
877 dom_xpath_class_entry->create_object = dom_xpath_objects_new;
878 dom_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers;
879
880 zend_hash_init(&dom_xpath_prop_handlers, 0, NULL, NULL, true);
881 DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "document", dom_xpath_document_read, NULL);
882 DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "registerNodeNamespaces", dom_xpath_register_node_ns_read, dom_xpath_register_node_ns_write);
883 zend_hash_add_new_ptr(&classes, dom_xpath_class_entry->name, &dom_xpath_prop_handlers);
884 #endif
885
886 register_php_dom_symbols(module_number);
887
888 php_libxml_register_export(dom_node_class_entry, php_dom_export_node);
889
890 lexbor_memory_setup(dom_malloc, dom_realloc, dom_calloc, dom_free);
891
892 return SUCCESS;
893 }
894 /* }}} */
895
896 /* {{{ */
PHP_MINFO_FUNCTION(dom)897 PHP_MINFO_FUNCTION(dom)
898 {
899 php_info_print_table_start();
900 php_info_print_table_row(2, "DOM/XML", "enabled");
901 php_info_print_table_row(2, "DOM/XML API Version", DOM_API_VERSION);
902 php_info_print_table_row(2, "libxml Version", LIBXML_DOTTED_VERSION);
903 #ifdef LIBXML_HTML_ENABLED
904 php_info_print_table_row(2, "HTML Support", "enabled");
905 #endif
906 #ifdef LIBXML_XPATH_ENABLED
907 php_info_print_table_row(2, "XPath Support", "enabled");
908 #endif
909 #ifdef LIBXML_XPTR_ENABLED
910 php_info_print_table_row(2, "XPointer Support", "enabled");
911 #endif
912 #ifdef LIBXML_SCHEMAS_ENABLED
913 php_info_print_table_row(2, "Schema Support", "enabled");
914 php_info_print_table_row(2, "RelaxNG Support", "enabled");
915 #endif
916 php_info_print_table_end();
917 }
918 /* }}} */
919
PHP_MSHUTDOWN_FUNCTION(dom)920 PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */
921 {
922 zend_hash_destroy(&dom_document_prop_handlers);
923 zend_hash_destroy(&dom_html_document_prop_handlers);
924 zend_hash_destroy(&dom_xml_document_prop_handlers);
925 zend_hash_destroy(&dom_documentfragment_prop_handlers);
926 zend_hash_destroy(&dom_node_prop_handlers);
927 zend_hash_destroy(&dom_namespace_node_prop_handlers);
928 zend_hash_destroy(&dom_nodelist_prop_handlers);
929 zend_hash_destroy(&dom_namednodemap_prop_handlers);
930 zend_hash_destroy(&dom_characterdata_prop_handlers);
931 zend_hash_destroy(&dom_attr_prop_handlers);
932 zend_hash_destroy(&dom_element_prop_handlers);
933 zend_hash_destroy(&dom_text_prop_handlers);
934 zend_hash_destroy(&dom_documenttype_prop_handlers);
935 zend_hash_destroy(&dom_notation_prop_handlers);
936 zend_hash_destroy(&dom_entity_prop_handlers);
937 zend_hash_destroy(&dom_processinginstruction_prop_handlers);
938 #ifdef LIBXML_XPATH_ENABLED
939 zend_hash_destroy(&dom_xpath_prop_handlers);
940 #endif
941 zend_hash_destroy(&classes);
942
943 /* If you want do find memleaks in this module, compile libxml2 with --with-mem-debug and
944 uncomment the following line, this will tell you the amount of not freed memory
945 and the total used memory into apaches error_log */
946 /* xmlMemoryDump();*/
947
948 return SUCCESS;
949 }
950 /* }}} */
951
952 /* {{{ node_list_unlink */
node_list_unlink(xmlNodePtr node)953 void node_list_unlink(xmlNodePtr node)
954 {
955 dom_object *wrapper;
956
957 while (node != NULL) {
958
959 wrapper = php_dom_object_get_data(node);
960
961 if (wrapper != NULL ) {
962 xmlUnlinkNode(node);
963 } else {
964 if (node->type == XML_ENTITY_REF_NODE)
965 break;
966 node_list_unlink(node->children);
967
968 switch (node->type) {
969 case XML_ATTRIBUTE_DECL:
970 case XML_DTD_NODE:
971 case XML_DOCUMENT_TYPE_NODE:
972 case XML_ENTITY_DECL:
973 case XML_ATTRIBUTE_NODE:
974 case XML_TEXT_NODE:
975 break;
976 default:
977 node_list_unlink((xmlNodePtr) node->properties);
978 }
979
980 }
981
982 node = node->next;
983 }
984 }
985 /* }}} end node_list_unlink */
986
987 /* {{{ dom_objects_free_storage */
dom_objects_free_storage(zend_object * object)988 void dom_objects_free_storage(zend_object *object)
989 {
990 dom_object *intern = php_dom_obj_from_obj(object);
991
992 zend_object_std_dtor(&intern->std);
993
994 if (intern->ptr != NULL && ((php_libxml_node_ptr *)intern->ptr)->node != NULL) {
995 if (((xmlNodePtr) ((php_libxml_node_ptr *)intern->ptr)->node)->type != XML_DOCUMENT_NODE && ((xmlNodePtr) ((php_libxml_node_ptr *)intern->ptr)->node)->type != XML_HTML_DOCUMENT_NODE) {
996 php_libxml_node_decrement_resource((php_libxml_node_object *) intern);
997 } else {
998 php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
999 php_libxml_decrement_doc_ref((php_libxml_node_object *) intern);
1000 }
1001 intern->ptr = NULL;
1002 }
1003 }
1004 /* }}} */
1005
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)1006 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) /* {{{ */
1007 {
1008 dom_nnodemap_object *mapptr = (dom_nnodemap_object *) intern->ptr;
1009
1010 ZEND_ASSERT(basenode != NULL);
1011
1012 ZVAL_OBJ_COPY(&mapptr->baseobj_zv, &basenode->std);
1013
1014 xmlDocPtr doc = basenode->document ? basenode->document->ptr : NULL;
1015
1016 mapptr->baseobj = basenode;
1017 mapptr->nodetype = ntype;
1018 mapptr->ht = ht;
1019
1020 const xmlChar* tmp;
1021
1022 if (local) {
1023 int len = local_len > INT_MAX ? -1 : (int) local_len;
1024 if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)local, len)) != NULL) {
1025 mapptr->local = (xmlChar*) tmp;
1026 } else {
1027 mapptr->local = xmlCharStrndup(local, len);
1028 mapptr->free_local = true;
1029 }
1030 }
1031
1032 if (ns) {
1033 int len = ns_len > INT_MAX ? -1 : (int) ns_len;
1034 if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ns, len)) != NULL) {
1035 mapptr->ns = (xmlChar*) tmp;
1036 } else {
1037 mapptr->ns = xmlCharStrndup(ns, len);
1038 mapptr->free_ns = true;
1039 }
1040 }
1041 }
1042 /* }}} */
1043
dom_objects_set_class_ex(zend_class_entry * class_type,dom_object * intern)1044 static void dom_objects_set_class_ex(zend_class_entry *class_type, dom_object *intern)
1045 {
1046 zend_class_entry *base_class = class_type;
1047 while ((base_class->type != ZEND_INTERNAL_CLASS || base_class->info.internal.module->module_number != dom_module_entry.module_number) && base_class->parent != NULL) {
1048 base_class = base_class->parent;
1049 }
1050
1051 intern->prop_handler = zend_hash_find_ptr(&classes, base_class->name);
1052
1053 zend_object_std_init(&intern->std, class_type);
1054 object_properties_init(&intern->std, class_type);
1055 }
1056
dom_objects_set_class(zend_class_entry * class_type)1057 static dom_object* dom_objects_set_class(zend_class_entry *class_type)
1058 {
1059 dom_object *intern = zend_object_alloc(sizeof(dom_object), class_type);
1060 dom_objects_set_class_ex(class_type, intern);
1061 return intern;
1062 }
1063
1064 /* {{{ dom_objects_new */
dom_objects_new(zend_class_entry * class_type)1065 zend_object *dom_objects_new(zend_class_entry *class_type)
1066 {
1067 dom_object *intern = dom_objects_set_class(class_type);
1068 intern->std.handlers = &dom_object_handlers;
1069 return &intern->std;
1070 }
1071 /* }}} */
1072
dom_objects_namespace_node_new(zend_class_entry * class_type)1073 static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type)
1074 {
1075 dom_object_namespace_node *intern = zend_object_alloc(sizeof(dom_object_namespace_node), class_type);
1076 dom_objects_set_class_ex(class_type, &intern->dom);
1077 intern->dom.std.handlers = &dom_object_namespace_node_handlers;
1078 return &intern->dom.std;
1079 }
1080
dom_object_namespace_node_free_storage(zend_object * object)1081 static void dom_object_namespace_node_free_storage(zend_object *object)
1082 {
1083 dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(object);
1084 if (intern->parent_intern != NULL) {
1085 zval tmp;
1086 ZVAL_OBJ(&tmp, &intern->parent_intern->std);
1087 zval_ptr_dtor(&tmp);
1088 }
1089 dom_objects_free_storage(object);
1090 }
1091
1092 #ifdef LIBXML_XPATH_ENABLED
1093
1094 /* {{{ zend_object dom_xpath_objects_new(zend_class_entry *class_type) */
dom_xpath_objects_new(zend_class_entry * class_type)1095 zend_object *dom_xpath_objects_new(zend_class_entry *class_type)
1096 {
1097 dom_xpath_object *intern = zend_object_alloc(sizeof(dom_xpath_object), class_type);
1098
1099 php_dom_xpath_callbacks_ctor(&intern->xpath_callbacks);
1100 intern->register_node_ns = 1;
1101
1102 intern->dom.prop_handler = &dom_xpath_prop_handlers;
1103
1104 zend_object_std_init(&intern->dom.std, class_type);
1105 object_properties_init(&intern->dom.std, class_type);
1106
1107 return &intern->dom.std;
1108 }
1109 /* }}} */
1110
1111 #endif
1112
dom_nnodemap_objects_free_storage(zend_object * object)1113 void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */
1114 {
1115 dom_object *intern = php_dom_obj_from_obj(object);
1116 dom_nnodemap_object *objmap = (dom_nnodemap_object *)intern->ptr;
1117
1118 if (objmap) {
1119 if (objmap->cached_obj && GC_DELREF(&objmap->cached_obj->std) == 0) {
1120 zend_objects_store_del(&objmap->cached_obj->std);
1121 }
1122 if (objmap->free_local) {
1123 xmlFree(objmap->local);
1124 }
1125 if (objmap->free_ns) {
1126 xmlFree(objmap->ns);
1127 }
1128 if (!Z_ISUNDEF(objmap->baseobj_zv)) {
1129 zval_ptr_dtor(&objmap->baseobj_zv);
1130 }
1131 efree(objmap);
1132 intern->ptr = NULL;
1133 }
1134
1135 php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1136
1137 zend_object_std_dtor(&intern->std);
1138 }
1139 /* }}} */
1140
dom_nnodemap_objects_new(zend_class_entry * class_type)1141 zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type)
1142 {
1143 dom_object *intern;
1144 dom_nnodemap_object *objmap;
1145
1146 intern = dom_objects_set_class(class_type);
1147 intern->ptr = emalloc(sizeof(dom_nnodemap_object));
1148 objmap = (dom_nnodemap_object *)intern->ptr;
1149 ZVAL_UNDEF(&objmap->baseobj_zv);
1150 objmap->baseobj = NULL;
1151 objmap->nodetype = 0;
1152 objmap->ht = NULL;
1153 objmap->local = NULL;
1154 objmap->free_local = false;
1155 objmap->ns = NULL;
1156 objmap->free_ns = false;
1157 objmap->cache_tag.modification_nr = 0;
1158 objmap->cached_length = -1;
1159 objmap->cached_obj = NULL;
1160 objmap->cached_obj_index = 0;
1161
1162 return &intern->std;
1163 }
1164
php_dom_create_iterator(zval * return_value,int ce_type)1165 void php_dom_create_iterator(zval *return_value, int ce_type) /* {{{ */
1166 {
1167 zend_class_entry *ce;
1168
1169 if (ce_type == DOM_NAMEDNODEMAP) {
1170 ce = dom_namednodemap_class_entry;
1171 } else {
1172 ce = dom_nodelist_class_entry;
1173 }
1174
1175 object_init_ex(return_value, ce);
1176 }
1177 /* }}} */
1178
1179 /* {{{ php_dom_create_object */
php_dom_create_object(xmlNodePtr obj,zval * return_value,dom_object * domobj)1180 PHP_DOM_EXPORT bool php_dom_create_object(xmlNodePtr obj, zval *return_value, dom_object *domobj)
1181 {
1182 zend_class_entry *ce;
1183 dom_object *intern;
1184
1185 if (!obj) {
1186 ZVAL_NULL(return_value);
1187 return 0;
1188 }
1189
1190 if ((intern = (dom_object *) php_dom_object_get_data((void *) obj))) {
1191 ZVAL_OBJ_COPY(return_value, &intern->std);
1192 return 1;
1193 }
1194
1195 switch (obj->type) {
1196 case XML_DOCUMENT_NODE:
1197 {
1198 if (domobj && domobj->document->is_modern_api_class) {
1199 ce = dom_xml_document_class_entry;
1200 } else {
1201 ce = dom_document_class_entry;
1202 }
1203 break;
1204 }
1205 case XML_HTML_DOCUMENT_NODE:
1206 {
1207 if (domobj && domobj->document->is_modern_api_class) {
1208 ce = dom_html_document_class_entry;
1209 } else {
1210 ce = dom_document_class_entry;
1211 }
1212 break;
1213 }
1214 case XML_DTD_NODE:
1215 case XML_DOCUMENT_TYPE_NODE:
1216 {
1217 ce = dom_documenttype_class_entry;
1218 break;
1219 }
1220 case XML_ELEMENT_NODE:
1221 {
1222 ce = dom_element_class_entry;
1223 break;
1224 }
1225 case XML_ATTRIBUTE_NODE:
1226 {
1227 ce = dom_attr_class_entry;
1228 break;
1229 }
1230 case XML_TEXT_NODE:
1231 {
1232 ce = dom_text_class_entry;
1233 break;
1234 }
1235 case XML_COMMENT_NODE:
1236 {
1237 ce = dom_comment_class_entry;
1238 break;
1239 }
1240 case XML_PI_NODE:
1241 {
1242 ce = dom_processinginstruction_class_entry;
1243 break;
1244 }
1245 case XML_ENTITY_REF_NODE:
1246 {
1247 ce = dom_entityreference_class_entry;
1248 break;
1249 }
1250 case XML_ENTITY_DECL:
1251 case XML_ELEMENT_DECL:
1252 {
1253 ce = dom_entity_class_entry;
1254 break;
1255 }
1256 case XML_CDATA_SECTION_NODE:
1257 {
1258 ce = dom_cdatasection_class_entry;
1259 break;
1260 }
1261 case XML_DOCUMENT_FRAG_NODE:
1262 {
1263 ce = dom_documentfragment_class_entry;
1264 break;
1265 }
1266 case XML_NOTATION_NODE:
1267 {
1268 ce = dom_notation_class_entry;
1269 break;
1270 }
1271 case XML_NAMESPACE_DECL:
1272 {
1273 ce = dom_namespace_node_class_entry;
1274 break;
1275 }
1276 default:
1277 /* TODO Convert to a ZEND assertion? */
1278 zend_throw_error(NULL, "Unsupported node type: %d", obj->type);
1279 ZVAL_NULL(return_value);
1280 return 0;
1281 }
1282
1283 if (domobj && domobj->document) {
1284 ce = dom_get_doc_classmap(domobj->document, ce);
1285 }
1286 php_dom_instantiate_object_helper(return_value, ce, obj, domobj);
1287 return 0;
1288 }
1289 /* }}} end php_domobject_new */
1290
php_dom_instantiate_object_helper(zval * return_value,zend_class_entry * ce,xmlNodePtr obj,dom_object * parent)1291 dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_entry *ce, xmlNodePtr obj, dom_object *parent)
1292 {
1293 object_init_ex(return_value, ce);
1294
1295 dom_object *intern = Z_DOMOBJ_P(return_value);
1296 if (obj->doc != NULL) {
1297 if (parent != NULL) {
1298 intern->document = parent->document;
1299 }
1300 php_libxml_increment_doc_ref((php_libxml_node_object *)intern, obj->doc);
1301 }
1302
1303 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, obj, (void *)intern);
1304
1305 return intern;
1306 }
1307
php_dom_create_implementation(zval * retval)1308 void php_dom_create_implementation(zval *retval) {
1309 object_init_ex(retval, dom_domimplementation_class_entry);
1310 }
1311
1312 /* {{{ int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child) */
dom_hierarchy(xmlNodePtr parent,xmlNodePtr child)1313 int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child)
1314 {
1315 xmlNodePtr nodep;
1316
1317 if (parent == NULL || child == NULL || child->doc != parent->doc) {
1318 return SUCCESS;
1319 }
1320
1321 if (child->type == XML_DOCUMENT_NODE) {
1322 return FAILURE;
1323 }
1324
1325 nodep = parent;
1326
1327 while (nodep) {
1328 if (nodep == child) {
1329 return FAILURE;
1330 }
1331 nodep = nodep->parent;
1332 }
1333
1334 return SUCCESS;
1335 }
1336 /* }}} end dom_hierarchy */
1337
1338 /* {{{ */
dom_has_feature(zend_string * feature,zend_string * version)1339 bool dom_has_feature(zend_string *feature, zend_string *version)
1340 {
1341 if (zend_string_equals_literal(version, "1.0")
1342 || zend_string_equals_literal(version, "2.0")
1343 || zend_string_equals_literal(version, "")
1344 ) {
1345 if (zend_string_equals_literal_ci(feature, "XML")
1346 || (zend_string_equals_literal_ci(feature, "Core") && zend_string_equals_literal(version, "1.0"))
1347 ) {
1348 return true;
1349 }
1350 }
1351
1352 return false;
1353 }
1354 /* }}} end dom_has_feature */
1355
dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep,xmlNodePtr nodep,char * ns,char * local,int * cur,int index)1356 xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, char *ns, char *local, int *cur, int index) /* {{{ */
1357 {
1358 /* Can happen with detached document */
1359 if (UNEXPECTED(nodep == NULL)) {
1360 return NULL;
1361 }
1362
1363 xmlNodePtr ret = NULL;
1364 bool local_match_any = local[0] == '*' && local[1] == '\0';
1365
1366 /* Note: The spec says that ns == '' must be transformed to ns == NULL. In other words, they are equivalent.
1367 * PHP however does not do this and internally uses the empty string everywhere when the user provides ns == NULL.
1368 * This is because for PHP ns == NULL has another meaning: "match every namespace" instead of "match the empty namespace". */
1369 bool ns_match_any = ns == NULL || (ns[0] == '*' && ns[1] == '\0');
1370
1371 while (*cur <= index) {
1372 if (nodep->type == XML_ELEMENT_NODE) {
1373 if (local_match_any || xmlStrEqual(nodep->name, (xmlChar *)local)) {
1374 if (ns_match_any || (ns[0] == '\0' && nodep->ns == NULL) || (nodep->ns != NULL && xmlStrEqual(nodep->ns->href, (xmlChar *)ns))) {
1375 if (*cur == index) {
1376 ret = nodep;
1377 break;
1378 }
1379 (*cur)++;
1380 }
1381 }
1382
1383 if (nodep->children) {
1384 nodep = nodep->children;
1385 continue;
1386 }
1387 }
1388
1389 if (nodep->next) {
1390 nodep = nodep->next;
1391 } else {
1392 /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
1393 do {
1394 nodep = nodep->parent;
1395 if (nodep == basep) {
1396 return NULL;
1397 }
1398 /* This shouldn't happen, unless there's an invalidation bug somewhere. */
1399 if (UNEXPECTED(nodep == NULL)) {
1400 zend_throw_error(NULL, "Current node in traversal is not in the document. Please report this as a bug in php-src.");
1401 return NULL;
1402 }
1403 } while (nodep->next == NULL);
1404 nodep = nodep->next;
1405 }
1406 }
1407 return ret;
1408 }
1409 /* }}} end dom_element_get_elements_by_tag_name_ns_raw */
1410
is_empty_node(xmlNodePtr nodep)1411 static inline bool is_empty_node(xmlNodePtr nodep)
1412 {
1413 xmlChar *strContent = xmlNodeGetContent(nodep);
1414 bool ret = strContent == NULL || *strContent == '\0';
1415 xmlFree(strContent);
1416 return ret;
1417 }
1418
1419 /* {{{ void dom_normalize (xmlNodePtr nodep) */
dom_normalize(xmlNodePtr nodep)1420 void dom_normalize (xmlNodePtr nodep)
1421 {
1422 xmlNodePtr child, nextp, newnextp;
1423 xmlAttrPtr attr;
1424 xmlChar *strContent;
1425
1426 child = nodep->children;
1427 while(child != NULL) {
1428 switch (child->type) {
1429 case XML_TEXT_NODE:
1430 nextp = child->next;
1431 while (nextp != NULL) {
1432 if (nextp->type == XML_TEXT_NODE) {
1433 newnextp = nextp->next;
1434 strContent = xmlNodeGetContent(nextp);
1435 xmlNodeAddContent(child, strContent);
1436 xmlFree(strContent);
1437 xmlUnlinkNode(nextp);
1438 php_libxml_node_free_resource(nextp);
1439 nextp = newnextp;
1440 } else {
1441 break;
1442 }
1443 }
1444 if (is_empty_node(child)) {
1445 nextp = child->next;
1446 xmlUnlinkNode(child);
1447 php_libxml_node_free_resource(child);
1448 child = nextp;
1449 continue;
1450 }
1451 break;
1452 case XML_ELEMENT_NODE:
1453 dom_normalize (child);
1454 attr = child->properties;
1455 while (attr != NULL) {
1456 dom_normalize((xmlNodePtr) attr);
1457 attr = attr->next;
1458 }
1459 break;
1460 case XML_ATTRIBUTE_NODE:
1461 dom_normalize (child);
1462 break;
1463 default:
1464 break;
1465 }
1466 child = child->next;
1467 }
1468 }
1469 /* }}} end dom_normalize */
1470
dom_reconcile_ns_internal(xmlDocPtr doc,xmlNodePtr nodep,xmlNodePtr search_parent)1471 static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr search_parent)
1472 {
1473 xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL;
1474
1475 /* Following if block primarily used for inserting nodes created via createElementNS */
1476 if (nodep->nsDef != NULL) {
1477 curns = nodep->nsDef;
1478 while (curns) {
1479 nsdftptr = curns->next;
1480 if (curns->href != NULL) {
1481 if((nsptr = xmlSearchNsByHref(doc, search_parent, curns->href)) &&
1482 (curns->prefix == NULL || xmlStrEqual(nsptr->prefix, curns->prefix))) {
1483 curns->next = NULL;
1484 if (prevns == NULL) {
1485 nodep->nsDef = nsdftptr;
1486 } else {
1487 prevns->next = nsdftptr;
1488 }
1489 /* Note: we can't get here if the ns is already on the oldNs list.
1490 * This is because in that case the definition won't be on the node, and
1491 * therefore won't be in the nodep->nsDef list. */
1492 php_libxml_set_old_ns(doc, curns);
1493 curns = prevns;
1494 }
1495 }
1496 prevns = curns;
1497 curns = nsdftptr;
1498 }
1499 }
1500 }
1501
php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp)1502 void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp)
1503 {
1504 ZEND_ASSERT(attrp != NULL);
1505
1506 if (attrp->ns != NULL) {
1507 /* Try to link to an existing namespace. If that won't work, reconcile. */
1508 xmlNodePtr nodep = attrp->parent;
1509 xmlNsPtr matching_ns = xmlSearchNs(nodep->doc, nodep, attrp->ns->prefix);
1510 if (matching_ns && xmlStrEqual(matching_ns->href, attrp->ns->href)) {
1511 attrp->ns = matching_ns;
1512 } else {
1513 if (attrp->ns->prefix != NULL) {
1514 xmlReconciliateNs(nodep->doc, nodep);
1515 }
1516 }
1517 }
1518 }
1519
dom_libxml_reconcile_ensure_namespaces_are_declared(xmlNodePtr nodep)1520 static void dom_libxml_reconcile_ensure_namespaces_are_declared(xmlNodePtr nodep)
1521 {
1522 /* Ideally we'd use the DOM-wrapped version, but we cannot: https://github.com/php/php-src/pull/12308. */
1523 #if 0
1524 /* Put on stack to avoid allocation.
1525 * Although libxml2 currently does not use this for the reconciliation, it still
1526 * makes sense to do this just in case libxml2's internal change in the future. */
1527 xmlDOMWrapCtxt dummy_ctxt = {0};
1528 xmlDOMWrapReconcileNamespaces(&dummy_ctxt, nodep, /* options */ 0);
1529 #else
1530 xmlReconciliateNs(nodep->doc, nodep);
1531 #endif
1532 }
1533
dom_reconcile_ns(xmlDocPtr doc,xmlNodePtr nodep)1534 void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */
1535 {
1536 ZEND_ASSERT(nodep->type != XML_ATTRIBUTE_NODE);
1537
1538 /* Although the node type will be checked by the libxml2 API,
1539 * we still want to do the internal reconciliation conditionally. */
1540 if (nodep->type == XML_ELEMENT_NODE) {
1541 dom_reconcile_ns_internal(doc, nodep, nodep->parent);
1542 dom_libxml_reconcile_ensure_namespaces_are_declared(nodep);
1543 }
1544 }
1545 /* }}} */
1546
dom_reconcile_ns_list_internal(xmlDocPtr doc,xmlNodePtr nodep,xmlNodePtr last,xmlNodePtr search_parent)1547 static void dom_reconcile_ns_list_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last, xmlNodePtr search_parent)
1548 {
1549 ZEND_ASSERT(nodep != NULL);
1550 while (true) {
1551 if (nodep->type == XML_ELEMENT_NODE) {
1552 dom_reconcile_ns_internal(doc, nodep, search_parent);
1553 if (nodep->children) {
1554 dom_reconcile_ns_list_internal(doc, nodep->children, nodep->last /* process the whole children list */, search_parent);
1555 }
1556 }
1557 if (nodep == last) {
1558 break;
1559 }
1560 nodep = nodep->next;
1561 }
1562 }
1563
dom_reconcile_ns_list(xmlDocPtr doc,xmlNodePtr nodep,xmlNodePtr last)1564 void dom_reconcile_ns_list(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last)
1565 {
1566 dom_reconcile_ns_list_internal(doc, nodep, last, nodep->parent);
1567 /* The loop is outside of the recursion in the above call because
1568 * dom_libxml_reconcile_ensure_namespaces_are_declared() performs its own recursion. */
1569 while (true) {
1570 /* The internal libxml2 call will already check the node type, no need for us to do it here. */
1571 dom_libxml_reconcile_ensure_namespaces_are_declared(nodep);
1572 if (nodep == last) {
1573 break;
1574 }
1575 nodep = nodep->next;
1576 }
1577 }
1578
1579 /*
1580 http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS
1581
1582 NAMESPACE_ERR: Raised if
1583
1584 1. the qualifiedName is a malformed qualified name
1585 2. the qualifiedName has a prefix and the namespaceURI is null
1586 */
1587
1588 /* {{{ 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)1589 int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len) {
1590 if (name_len == 0) {
1591 return NAMESPACE_ERR;
1592 }
1593
1594 *localname = (char *)xmlSplitQName2((xmlChar *)qname, (xmlChar **) prefix);
1595 if (*localname == NULL) {
1596 *localname = (char *)xmlStrdup((xmlChar *)qname);
1597 if (*prefix == NULL && uri_len == 0) {
1598 return 0;
1599 }
1600 }
1601
1602 /* 1 */
1603 if (xmlValidateQName((xmlChar *) qname, 0) != 0) {
1604 return NAMESPACE_ERR;
1605 }
1606
1607 /* 2 */
1608 if (*prefix != NULL && uri_len == 0) {
1609 return NAMESPACE_ERR;
1610 }
1611
1612 return 0;
1613 }
1614 /* }}} */
1615
1616 /* Creates a new namespace declaration with a random prefix with the given uri on the tree.
1617 * This is used to resolve a namespace prefix conflict in cases where spec does not want a
1618 * namespace error in case of conflicts, but demands a resolution. */
dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree,const char * uri)1619 xmlNsPtr dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree, const char *uri)
1620 {
1621 ZEND_ASSERT(tree != NULL);
1622 xmlDocPtr doc = tree->doc;
1623
1624 if (UNEXPECTED(doc == NULL)) {
1625 return NULL;
1626 }
1627
1628 /* Code adapted from libxml2 (2.10.4) */
1629 char prefix[50];
1630 int counter = 1;
1631 snprintf(prefix, sizeof(prefix), "default");
1632 xmlNsPtr nsptr = xmlSearchNs(doc, tree, (const xmlChar *) prefix);
1633 while (nsptr != NULL) {
1634 if (counter > 1000) {
1635 return NULL;
1636 }
1637 snprintf(prefix, sizeof(prefix), "default%d", counter++);
1638 nsptr = xmlSearchNs(doc, tree, (const xmlChar *) prefix);
1639 }
1640
1641 /* Search yielded no conflict */
1642 return xmlNewNs(tree, (const xmlChar *) uri, (const xmlChar *) prefix);
1643 }
1644
1645 /*
1646 http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS
1647
1648 NAMESPACE_ERR: Raised if
1649
1650 3. the qualifiedName has a prefix that is "xml" and the namespaceURI is different from "http://www.w3.org/XML/1998/namespace" [XML Namespaces]
1651 4. the qualifiedName or its prefix is "xmlns" and the namespaceURI is different from "http://www.w3.org/2000/xmlns/"
1652 5. the namespaceURI is "http://www.w3.org/2000/xmlns/" and neither the qualifiedName nor its prefix is "xmlns".
1653 */
1654
dom_get_ns_unchecked(xmlNodePtr nodep,char * uri,char * prefix)1655 xmlNsPtr dom_get_ns_unchecked(xmlNodePtr nodep, char *uri, char *prefix)
1656 {
1657 xmlNsPtr nsptr = xmlNewNs(nodep, (xmlChar *)uri, (xmlChar *)prefix);
1658 if (UNEXPECTED(nsptr == NULL)) {
1659 /* Either memory allocation failure, or it's because of a prefix conflict.
1660 * We'll assume a conflict and try again. If it was a memory allocation failure we'll just fail again, whatever.
1661 * 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?".
1662 * This branch will also be taken unlikely anyway as in those cases it'll be for allocation failure. */
1663 return dom_get_ns_resolve_prefix_conflict(nodep, uri);
1664 }
1665
1666 return nsptr;
1667 }
1668
1669 /* {{{ xmlNsPtr dom_get_ns(xmlNodePtr nodep, char *uri, int *errorcode, char *prefix) */
dom_get_ns(xmlNodePtr nodep,char * uri,int * errorcode,char * prefix)1670 xmlNsPtr dom_get_ns(xmlNodePtr nodep, char *uri, int *errorcode, char *prefix) {
1671 xmlNsPtr nsptr;
1672
1673 if (! ((prefix && !strcmp (prefix, "xml") && strcmp(uri, (char *)XML_XML_NAMESPACE)) ||
1674 (prefix && !strcmp (prefix, "xmlns") && strcmp(uri, (char *)DOM_XMLNS_NAMESPACE)) ||
1675 (prefix && !strcmp(uri, (char *)DOM_XMLNS_NAMESPACE) && strcmp (prefix, "xmlns")))) {
1676 nsptr = dom_get_ns_unchecked(nodep, uri, prefix);
1677 if (UNEXPECTED(nsptr == NULL)) {
1678 goto err;
1679 }
1680 } else {
1681 goto err;
1682 }
1683
1684 *errorcode = 0;
1685 return nsptr;
1686 err:
1687 *errorcode = NAMESPACE_ERR;
1688 return NULL;
1689 }
1690 /* }}} end dom_get_ns */
1691
1692 /* {{{ xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) */
dom_get_nsdecl(xmlNode * node,xmlChar * localName)1693 xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
1694 xmlNsPtr cur;
1695 xmlNs *ret = NULL;
1696 if (node == NULL)
1697 return NULL;
1698
1699 if (localName == NULL || localName[0] == '\0') {
1700 cur = node->nsDef;
1701 while (cur != NULL) {
1702 if (cur->prefix == NULL && cur->href != NULL) {
1703 ret = cur;
1704 break;
1705 }
1706 cur = cur->next;
1707 }
1708 } else {
1709 cur = node->nsDef;
1710 while (cur != NULL) {
1711 if (cur->prefix != NULL && xmlStrEqual(localName, cur->prefix)) {
1712 ret = cur;
1713 break;
1714 }
1715 cur = cur->next;
1716 }
1717 }
1718 return ret;
1719 }
1720 /* }}} end dom_get_nsdecl */
1721
php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep,xmlNsPtr original)1722 static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original)
1723 {
1724 xmlNodePtr attrp;
1725 xmlNsPtr curns = xmlNewNs(NULL, original->href, NULL);
1726 if (original->prefix) {
1727 curns->prefix = xmlStrdup(original->prefix);
1728 attrp = xmlNewDocNode(nodep->doc, NULL, (xmlChar *) original->prefix, original->href);
1729 } else {
1730 attrp = xmlNewDocNode(nodep->doc, NULL, (xmlChar *)"xmlns", original->href);
1731 }
1732 attrp->type = XML_NAMESPACE_DECL;
1733 attrp->parent = nodep;
1734 attrp->ns = curns;
1735 return attrp;
1736 }
1737
1738 /* 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)1739 xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern)
1740 {
1741 xmlNodePtr attrp = php_dom_create_fake_namespace_decl_node_ptr(nodep, original);
1742 php_dom_create_object(attrp, return_value, parent_intern);
1743 /* This object must exist, because we just created an object for it via php_dom_create_object(). */
1744 php_dom_namespace_node_obj_from_obj(Z_OBJ_P(return_value))->parent_intern = parent_intern;
1745 return attrp;
1746 }
1747
dom_nodemap_or_nodelist_process_offset_as_named(zval * offset,zend_long * lval)1748 static bool dom_nodemap_or_nodelist_process_offset_as_named(zval *offset, zend_long *lval)
1749 {
1750 if (Z_TYPE_P(offset) == IS_STRING) {
1751 /* See zval_get_long_func() */
1752 double dval;
1753 zend_uchar is_numeric_string_type;
1754 if (0 == (is_numeric_string_type = is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), lval, &dval, true))) {
1755 return true;
1756 } else if (is_numeric_string_type == IS_DOUBLE) {
1757 *lval = zend_dval_to_lval_cap(dval);
1758 }
1759 } else {
1760 *lval = zval_get_long(offset);
1761 }
1762 return false;
1763 }
1764
dom_nodelist_read_dimension(zend_object * object,zval * offset,int type,zval * rv)1765 static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv) /* {{{ */
1766 {
1767 if (UNEXPECTED(!offset)) {
1768 zend_throw_error(NULL, "Cannot access DOMNodeList without offset");
1769 return NULL;
1770 }
1771
1772 zend_long lval;
1773 if (dom_nodemap_or_nodelist_process_offset_as_named(offset, &lval)) {
1774 /* does not support named lookup */
1775 ZVAL_NULL(rv);
1776 return rv;
1777 }
1778
1779 php_dom_nodelist_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv);
1780 return rv;
1781 } /* }}} end dom_nodelist_read_dimension */
1782
dom_nodelist_has_dimension(zend_object * object,zval * member,int check_empty)1783 static int dom_nodelist_has_dimension(zend_object *object, zval *member, int check_empty)
1784 {
1785 zend_long offset;
1786 if (dom_nodemap_or_nodelist_process_offset_as_named(member, &offset)) {
1787 /* does not support named lookup */
1788 return 0;
1789 }
1790
1791 return offset >= 0 && offset < php_dom_get_nodelist_length(php_dom_obj_from_obj(object));
1792 } /* }}} end dom_nodelist_has_dimension */
1793
dom_remove_all_children(xmlNodePtr nodep)1794 void dom_remove_all_children(xmlNodePtr nodep)
1795 {
1796 if (nodep->children) {
1797 node_list_unlink(nodep->children);
1798 php_libxml_node_free_list((xmlNodePtr) nodep->children);
1799 nodep->children = NULL;
1800 nodep->last = NULL;
1801 }
1802 }
1803
php_dom_get_content_into_zval(const xmlNode * nodep,zval * retval,bool default_is_null)1804 void php_dom_get_content_into_zval(const xmlNode *nodep, zval *retval, bool default_is_null)
1805 {
1806 ZEND_ASSERT(nodep != NULL);
1807
1808 if (nodep->type == XML_TEXT_NODE
1809 || nodep->type == XML_CDATA_SECTION_NODE
1810 || nodep->type == XML_PI_NODE
1811 || nodep->type == XML_COMMENT_NODE) {
1812 char *str = (char * ) nodep->content;
1813 if (str != NULL) {
1814 ZVAL_STRING(retval, str);
1815 } else {
1816 goto failure;
1817 }
1818 return;
1819 }
1820
1821 char *str = (char *) xmlNodeGetContent(nodep);
1822
1823 if (str != NULL) {
1824 ZVAL_STRING(retval, str);
1825 xmlFree(str);
1826 return;
1827 }
1828
1829 failure:
1830 if (default_is_null) {
1831 ZVAL_NULL(retval);
1832 } else {
1833 ZVAL_EMPTY_STRING(retval);
1834 }
1835 }
1836
dom_nodemap_read_dimension(zend_object * object,zval * offset,int type,zval * rv)1837 static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int type, zval *rv) /* {{{ */
1838 {
1839 if (UNEXPECTED(!offset)) {
1840 zend_throw_error(NULL, "Cannot access DOMNamedNodeMap without offset");
1841 return NULL;
1842 }
1843
1844 zend_long lval;
1845 if (dom_nodemap_or_nodelist_process_offset_as_named(offset, &lval)) {
1846 /* exceptional case, switch to named lookup */
1847 php_dom_named_node_map_get_named_item_into_zval(php_dom_obj_from_obj(object)->ptr, Z_STRVAL_P(offset), rv);
1848 return rv;
1849 }
1850
1851 /* see PHP_METHOD(DOMNamedNodeMap, item) */
1852 if (UNEXPECTED(lval < 0 || ZEND_LONG_INT_OVFL(lval))) {
1853 zend_value_error("must be between 0 and %d", INT_MAX);
1854 return NULL;
1855 }
1856
1857 php_dom_named_node_map_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv);
1858 return rv;
1859 } /* }}} end dom_nodemap_read_dimension */
1860
dom_nodemap_has_dimension(zend_object * object,zval * member,int check_empty)1861 static int dom_nodemap_has_dimension(zend_object *object, zval *member, int check_empty)
1862 {
1863 zend_long offset;
1864 if (dom_nodemap_or_nodelist_process_offset_as_named(member, &offset)) {
1865 /* exceptional case, switch to named lookup */
1866 return php_dom_named_node_map_get_named_item(php_dom_obj_from_obj(object)->ptr, Z_STRVAL_P(member), false) != NULL;
1867 }
1868
1869 return offset >= 0 && offset < php_dom_get_namednodemap_length(php_dom_obj_from_obj(object));
1870 } /* }}} end dom_nodemap_has_dimension */
1871
dom_clone_node(xmlNodePtr node,xmlDocPtr doc,const dom_object * intern,bool recursive)1872 xmlNodePtr dom_clone_node(xmlNodePtr node, xmlDocPtr doc, const dom_object *intern, bool recursive)
1873 {
1874 /* See http://www.xmlsoft.org/html/libxml-tree.html#xmlDocCopyNode for meaning of values */
1875 int extended_recursive = recursive;
1876 if (!recursive && node->type == XML_ELEMENT_NODE) {
1877 extended_recursive = 2;
1878 }
1879 xmlNodePtr copy = xmlDocCopyNode(node, doc, extended_recursive);
1880 if (UNEXPECTED(!copy)) {
1881 return NULL;
1882 }
1883
1884 if (intern->document && intern->document->is_modern_api_class) {
1885 dom_mark_namespaces_for_copy_based_on_copy(copy, node);
1886 }
1887
1888 return copy;
1889 }
1890
1891 #endif /* HAVE_DOM */
1892