xref: /PHP-8.3/ext/dom/node.c (revision 1890d47c)
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    +----------------------------------------------------------------------+
16 */
17 
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 
22 #include "php.h"
23 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
24 #include "php_dom.h"
25 
26 /*
27 * class DOMNode
28 *
29 * URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1950641247
30 * Since:
31 */
32 
dom_node_concatenated_name_helper(size_t name_len,const char * name,size_t prefix_len,const char * prefix)33 zend_string *dom_node_concatenated_name_helper(size_t name_len, const char *name, size_t prefix_len, const char *prefix)
34 {
35 	if (UNEXPECTED(prefix_len > ZSTR_MAX_LEN / 2 - 1 || name_len > ZSTR_MAX_LEN / 2 - 1)) {
36 		return zend_empty_string;
37 	}
38 	zend_string *str = zend_string_alloc(prefix_len + 1 + name_len, false);
39 	memcpy(ZSTR_VAL(str), prefix, prefix_len);
40 	ZSTR_VAL(str)[prefix_len] = ':';
41 	memcpy(ZSTR_VAL(str) + prefix_len + 1, name, name_len + 1 /* include \0 */);
42 	return str;
43 }
44 
dom_node_get_node_name_attribute_or_element(const xmlNode * nodep)45 zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep)
46 {
47 	size_t name_len = strlen((const char *) nodep->name);
48 	if (nodep->ns != NULL && nodep->ns->prefix != NULL) {
49 		return dom_node_concatenated_name_helper(name_len, (const char *) nodep->name, strlen((const char *) nodep->ns->prefix), (const char *) nodep->ns->prefix);
50 	} else {
51 		return zend_string_init((const char *) nodep->name, name_len, false);
52 	}
53 }
54 
php_dom_is_node_connected(const xmlNode * node)55 bool php_dom_is_node_connected(const xmlNode *node)
56 {
57 	ZEND_ASSERT(node != NULL);
58 	do {
59 		if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) {
60 			return true;
61 		}
62 		node = node->parent;
63 	} while (node != NULL);
64 	return false;
65 }
66 
67 /* {{{ nodeName	string
68 readonly=yes
69 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68D095
70 Since:
71 */
dom_node_node_name_read(dom_object * obj,zval * retval)72 int dom_node_node_name_read(dom_object *obj, zval *retval)
73 {
74 	xmlNode *nodep = dom_object_get_node(obj);
75 
76 	if (nodep == NULL) {
77 		php_dom_throw_error(INVALID_STATE_ERR, 1);
78 		return FAILURE;
79 	}
80 
81 	switch (nodep->type) {
82 		case XML_ATTRIBUTE_NODE:
83 		case XML_ELEMENT_NODE:
84 			ZVAL_STR(retval, dom_node_get_node_name_attribute_or_element(nodep));
85 			break;
86 		case XML_NAMESPACE_DECL: {
87 			xmlNsPtr ns = nodep->ns;
88 			if (ns != NULL && ns->prefix) {
89 				xmlChar *qname = xmlStrdup((xmlChar *) "xmlns");
90 				qname = xmlStrcat(qname, (xmlChar *) ":");
91 				qname = xmlStrcat(qname, nodep->name);
92 				ZVAL_STRING(retval, (const char *) qname);
93 				xmlFree(qname);
94 			} else {
95 				ZVAL_STRING(retval, (const char *) nodep->name);
96 			}
97 			break;
98 		}
99 		case XML_DOCUMENT_TYPE_NODE:
100 		case XML_DTD_NODE:
101 		case XML_PI_NODE:
102 		case XML_ENTITY_DECL:
103 		case XML_ENTITY_REF_NODE:
104 		case XML_NOTATION_NODE:
105 			ZVAL_STRING(retval, (char *) nodep->name);
106 			break;
107 		case XML_CDATA_SECTION_NODE:
108 			ZVAL_STRING(retval, "#cdata-section");
109 			break;
110 		case XML_COMMENT_NODE:
111 			ZVAL_STRING(retval, "#comment");
112 			break;
113 		case XML_HTML_DOCUMENT_NODE:
114 		case XML_DOCUMENT_NODE:
115 			ZVAL_STRING(retval, "#document");
116 			break;
117 		case XML_DOCUMENT_FRAG_NODE:
118 			ZVAL_STRING(retval, "#document-fragment");
119 			break;
120 		case XML_TEXT_NODE:
121 			ZVAL_STRING(retval, "#text");
122 			break;
123 		EMPTY_SWITCH_DEFAULT_CASE();
124 	}
125 
126 	return SUCCESS;
127 }
128 
129 /* }}} */
130 
131 /* {{{ nodeValue	string
132 readonly=no
133 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68D080
134 Since:
135 */
dom_node_node_value_read(dom_object * obj,zval * retval)136 int dom_node_node_value_read(dom_object *obj, zval *retval)
137 {
138 	xmlNode *nodep = dom_object_get_node(obj);
139 
140 	if (nodep == NULL) {
141 		php_dom_throw_error(INVALID_STATE_ERR, 1);
142 		return FAILURE;
143 	}
144 
145 	/* Access to Element node is implemented as a convenience method */
146 	switch (nodep->type) {
147 		case XML_ATTRIBUTE_NODE:
148 		case XML_TEXT_NODE:
149 		case XML_ELEMENT_NODE:
150 		case XML_COMMENT_NODE:
151 		case XML_CDATA_SECTION_NODE:
152 		case XML_PI_NODE:
153 			php_dom_get_content_into_zval(nodep, retval, true);
154 			break;
155 		case XML_NAMESPACE_DECL: {
156 			char *str = (char *) xmlNodeGetContent(nodep->children);
157 			if (str != NULL) {
158 				ZVAL_STRING(retval, str);
159 				xmlFree(str);
160 			} else {
161 				ZVAL_NULL(retval);
162 			}
163 			break;
164 		}
165 		default:
166 			ZVAL_NULL(retval);
167 			break;
168 	}
169 
170 	return SUCCESS;
171 }
172 
dom_node_node_value_write(dom_object * obj,zval * newval)173 int dom_node_node_value_write(dom_object *obj, zval *newval)
174 {
175 	xmlNode *nodep = dom_object_get_node(obj);
176 	zend_string *str;
177 
178 	if (nodep == NULL) {
179 		php_dom_throw_error(INVALID_STATE_ERR, 1);
180 		return FAILURE;
181 	}
182 
183 	str = zval_try_get_string(newval);
184 	if (UNEXPECTED(!str)) {
185 		return FAILURE;
186 	}
187 
188 	/* Access to Element node is implemented as a convenience method */
189 	switch (nodep->type) {
190 		case XML_ATTRIBUTE_NODE:
191 		case XML_ELEMENT_NODE:
192 			dom_remove_all_children(nodep);
193 			ZEND_FALLTHROUGH;
194 		case XML_TEXT_NODE:
195 		case XML_COMMENT_NODE:
196 		case XML_CDATA_SECTION_NODE:
197 		case XML_PI_NODE:
198 			xmlNodeSetContentLen(nodep, (xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str));
199 			break;
200 		default:
201 			break;
202 	}
203 
204 	php_libxml_invalidate_node_list_cache(obj->document);
205 
206 	zend_string_release_ex(str, 0);
207 	return SUCCESS;
208 }
209 
210 /* }}} */
211 
212 /* {{{ nodeType	int
213 readonly=yes
214 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-111237558
215 Since:
216 */
dom_node_node_type_read(dom_object * obj,zval * retval)217 int dom_node_node_type_read(dom_object *obj, zval *retval)
218 {
219 	xmlNode *nodep;
220 
221 	nodep = dom_object_get_node(obj);
222 
223 	if (nodep == NULL) {
224 		php_dom_throw_error(INVALID_STATE_ERR, 1);
225 		return FAILURE;
226 	}
227 
228 	/* Specs dictate that they are both type XML_DOCUMENT_TYPE_NODE */
229 	if (nodep->type == XML_DTD_NODE) {
230 		ZVAL_LONG(retval, XML_DOCUMENT_TYPE_NODE);
231 	} else {
232 		ZVAL_LONG(retval, nodep->type);
233 	}
234 
235 	return SUCCESS;
236 }
237 
238 /* }}} */
239 
dom_node_parent_get(dom_object * obj,zval * retval,bool only_element)240 static zend_result dom_node_parent_get(dom_object *obj, zval *retval, bool only_element)
241 {
242 	xmlNodePtr nodep = dom_object_get_node(obj);
243 
244 	if (nodep == NULL) {
245 		php_dom_throw_error(INVALID_STATE_ERR, 1);
246 		return FAILURE;
247 	}
248 
249 	xmlNodePtr nodeparent = nodep->parent;
250 	if (!nodeparent || (only_element && nodeparent->type != XML_ELEMENT_NODE)) {
251 		ZVAL_NULL(retval);
252 		return SUCCESS;
253 	}
254 
255 	php_dom_create_object(nodeparent, retval, obj);
256 	return SUCCESS;
257 }
258 
259 /* {{{ parentNode	?DomNode
260 readonly=yes
261 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1060184317
262 Since:
263 */
dom_node_parent_node_read(dom_object * obj,zval * retval)264 int dom_node_parent_node_read(dom_object *obj, zval *retval)
265 {
266 	return dom_node_parent_get(obj, retval, false);
267 }
268 
269 /* }}} */
270 
271 /* {{{ parentElement	?DomElement
272 readonly=yes
273 URL: https://dom.spec.whatwg.org/#parent-element
274 Since:
275 */
dom_node_parent_element_read(dom_object * obj,zval * retval)276 int dom_node_parent_element_read(dom_object *obj, zval *retval)
277 {
278 	return dom_node_parent_get(obj, retval, true);
279 }
280 
281 /* }}} */
282 
283 /* {{{ childNodes	DomNodeList
284 readonly=yes
285 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1451460987
286 Since:
287 */
dom_node_child_nodes_read(dom_object * obj,zval * retval)288 int dom_node_child_nodes_read(dom_object *obj, zval *retval)
289 {
290 	xmlNode *nodep = dom_object_get_node(obj);
291 	dom_object *intern;
292 
293 	if (nodep == NULL) {
294 		php_dom_throw_error(INVALID_STATE_ERR, 1);
295 		return FAILURE;
296 	}
297 
298 	php_dom_create_iterator(retval, DOM_NODELIST);
299 	intern = Z_DOMOBJ_P(retval);
300 	dom_namednode_iter(obj, XML_ELEMENT_NODE, intern, NULL, NULL, 0, NULL, 0);
301 
302 	return SUCCESS;
303 }
304 /* }}} */
305 
306 /* {{{ firstChild DomNode
307 readonly=yes
308 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-169727388
309 Since:
310 */
dom_node_first_child_read(dom_object * obj,zval * retval)311 int dom_node_first_child_read(dom_object *obj, zval *retval)
312 {
313 	xmlNode *nodep, *first = NULL;
314 
315 	nodep = dom_object_get_node(obj);
316 
317 	if (nodep == NULL) {
318 		php_dom_throw_error(INVALID_STATE_ERR, 1);
319 		return FAILURE;
320 	}
321 
322 	if (dom_node_children_valid(nodep) == SUCCESS) {
323 		first = nodep->children;
324 	}
325 
326 	if (!first) {
327 		ZVAL_NULL(retval);
328 		return SUCCESS;
329 	}
330 
331 	php_dom_create_object(first, retval, obj);
332 	return SUCCESS;
333 }
334 
335 /* }}} */
336 
337 /* {{{ lastChild	DomNode
338 readonly=yes
339 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-61AD09FB
340 Since:
341 */
dom_node_last_child_read(dom_object * obj,zval * retval)342 int dom_node_last_child_read(dom_object *obj, zval *retval)
343 {
344 	xmlNode *nodep, *last = NULL;
345 
346 	nodep = dom_object_get_node(obj);
347 
348 	if (nodep == NULL) {
349 		php_dom_throw_error(INVALID_STATE_ERR, 1);
350 		return FAILURE;
351 	}
352 
353 	if (dom_node_children_valid(nodep) == SUCCESS) {
354 		last = nodep->last;
355 	}
356 
357 	if (!last) {
358 		ZVAL_NULL(retval);
359 		return SUCCESS;
360 	}
361 
362 	php_dom_create_object(last, retval, obj);
363 	return SUCCESS;
364 }
365 
366 /* }}} */
367 
368 /* {{{ previousSibling	DomNode
369 readonly=yes
370 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-640FB3C8
371 Since:
372 */
dom_node_previous_sibling_read(dom_object * obj,zval * retval)373 int dom_node_previous_sibling_read(dom_object *obj, zval *retval)
374 {
375 	xmlNode *nodep, *prevsib;
376 
377 	nodep = dom_object_get_node(obj);
378 
379 	if (nodep == NULL) {
380 		php_dom_throw_error(INVALID_STATE_ERR, 1);
381 		return FAILURE;
382 	}
383 
384 	prevsib = nodep->prev;
385 	if (!prevsib) {
386 		ZVAL_NULL(retval);
387 		return SUCCESS;
388 	}
389 
390 	php_dom_create_object(prevsib, retval, obj);
391 	return SUCCESS;
392 }
393 
394 /* }}} */
395 
396 /* {{{ nextSibling	DomNode
397 readonly=yes
398 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6AC54C2F
399 Since:
400 */
dom_node_next_sibling_read(dom_object * obj,zval * retval)401 int dom_node_next_sibling_read(dom_object *obj, zval *retval)
402 {
403 	xmlNode *nodep, *nextsib;
404 
405 	nodep = dom_object_get_node(obj);
406 
407 	if (nodep == NULL) {
408 		php_dom_throw_error(INVALID_STATE_ERR, 1);
409 		return FAILURE;
410 	}
411 
412 	nextsib = nodep->next;
413 	if (!nextsib) {
414 		ZVAL_NULL(retval);
415 		return SUCCESS;
416 	}
417 
418 	php_dom_create_object(nextsib, retval, obj);
419 	return SUCCESS;
420 }
421 
422 /* }}} */
423 
424 /* {{{ previousElementSibling	DomNode
425 readonly=yes
426 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-640FB3C8
427 Since:
428 */
dom_node_previous_element_sibling_read(dom_object * obj,zval * retval)429 int dom_node_previous_element_sibling_read(dom_object *obj, zval *retval)
430 {
431 	xmlNode *nodep, *prevsib;
432 
433 	nodep = dom_object_get_node(obj);
434 
435 	if (nodep == NULL) {
436 		php_dom_throw_error(INVALID_STATE_ERR, 1);
437 		return FAILURE;
438 	}
439 
440 	prevsib = nodep->prev;
441 
442 	while (prevsib && prevsib->type != XML_ELEMENT_NODE) {
443 		prevsib = prevsib->prev;
444 	}
445 
446 	if (!prevsib) {
447 		ZVAL_NULL(retval);
448 		return SUCCESS;
449 	}
450 
451 	php_dom_create_object(prevsib, retval, obj);
452 	return SUCCESS;
453 }
454 
455 /* }}} */
456 
457 /* {{{ nextElementSibling	DomNode
458 readonly=yes
459 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6AC54C2F
460 Since:
461 */
dom_node_next_element_sibling_read(dom_object * obj,zval * retval)462 int dom_node_next_element_sibling_read(dom_object *obj, zval *retval)
463 {
464 	xmlNode *nodep, *nextsib;
465 
466 	nodep = dom_object_get_node(obj);
467 
468 	if (nodep == NULL) {
469 		php_dom_throw_error(INVALID_STATE_ERR, 1);
470 		return FAILURE;
471 	}
472 
473 	nextsib = nodep->next;
474 
475 	while (nextsib != NULL && nextsib->type != XML_ELEMENT_NODE) {
476 		nextsib = nextsib->next;
477 	}
478 
479 	if (!nextsib) {
480 		ZVAL_NULL(retval);
481 		return SUCCESS;
482 	}
483 
484 	php_dom_create_object(nextsib, retval, obj);
485 	return SUCCESS;
486 }
487 
488 /* }}} */
489 
490 /* {{{ attributes	DomNamedNodeMap
491 readonly=yes
492 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-84CF096
493 Since:
494 */
dom_node_attributes_read(dom_object * obj,zval * retval)495 int dom_node_attributes_read(dom_object *obj, zval *retval)
496 {
497 	xmlNode *nodep = dom_object_get_node(obj);
498 	dom_object *intern;
499 
500 	if (nodep == NULL) {
501 		php_dom_throw_error(INVALID_STATE_ERR, 1);
502 		return FAILURE;
503 	}
504 
505 	if (nodep->type == XML_ELEMENT_NODE) {
506 		php_dom_create_iterator(retval, DOM_NAMEDNODEMAP);
507 		intern = Z_DOMOBJ_P(retval);
508 		dom_namednode_iter(obj, XML_ATTRIBUTE_NODE, intern, NULL, NULL, 0, NULL, 0);
509 	} else {
510 		ZVAL_NULL(retval);
511 	}
512 
513 	return SUCCESS;
514 }
515 
516 /* }}} */
517 
518 /* {{{ isConnected	boolean
519 readonly=yes
520 URL: https://dom.spec.whatwg.org/#dom-node-isconnected
521 Since:
522 */
dom_node_is_connected_read(dom_object * obj,zval * retval)523 int dom_node_is_connected_read(dom_object *obj, zval *retval)
524 {
525 	xmlNode *nodep = dom_object_get_node(obj);
526 
527 	if (nodep == NULL) {
528 		php_dom_throw_error(INVALID_STATE_ERR, 1);
529 		return FAILURE;
530 	}
531 
532 	ZVAL_BOOL(retval, php_dom_is_node_connected(nodep));
533 	return SUCCESS;
534 }
535 /* }}} */
536 
537 /* {{{ ownerDocument	DomDocument
538 readonly=yes
539 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-node-ownerDoc
540 Since:
541 */
dom_node_owner_document_read(dom_object * obj,zval * retval)542 int dom_node_owner_document_read(dom_object *obj, zval *retval)
543 {
544 	xmlNode *nodep = dom_object_get_node(obj);
545 	xmlDocPtr docp;
546 
547 	if (nodep == NULL) {
548 		php_dom_throw_error(INVALID_STATE_ERR, 1);
549 		return FAILURE;
550 	}
551 
552 	if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
553 		ZVAL_NULL(retval);
554 		return SUCCESS;
555 	}
556 
557 	docp = nodep->doc;
558 	if (!docp) {
559 		return FAILURE;
560 	}
561 
562 	php_dom_create_object((xmlNodePtr) docp, retval, obj);
563 	return SUCCESS;
564 }
565 
566 /* }}} */
567 
568 /* {{{ namespaceUri	string
569 readonly=yes
570 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-NodeNSname
571 Since: DOM Level 2
572 */
dom_node_namespace_uri_read(dom_object * obj,zval * retval)573 int dom_node_namespace_uri_read(dom_object *obj, zval *retval)
574 {
575 	xmlNode *nodep = dom_object_get_node(obj);
576 	char *str = NULL;
577 
578 	if (nodep == NULL) {
579 		php_dom_throw_error(INVALID_STATE_ERR, 1);
580 		return FAILURE;
581 	}
582 
583 	switch (nodep->type) {
584 		case XML_ELEMENT_NODE:
585 		case XML_ATTRIBUTE_NODE:
586 		case XML_NAMESPACE_DECL:
587 			if (nodep->ns != NULL) {
588 				str = (char *) nodep->ns->href;
589 			}
590 			break;
591 		default:
592 			str = NULL;
593 			break;
594 	}
595 
596 	if (str != NULL) {
597 		ZVAL_STRING(retval, str);
598 	} else {
599 		ZVAL_NULL(retval);
600 	}
601 
602 	return SUCCESS;
603 }
604 
605 /* }}} */
606 
607 /* {{{ prefix	string
608 readonly=no
609 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-NodeNSPrefix
610 Since: DOM Level 2
611 */
dom_node_prefix_read(dom_object * obj,zval * retval)612 int dom_node_prefix_read(dom_object *obj, zval *retval)
613 {
614 	xmlNode *nodep = dom_object_get_node(obj);
615 	xmlNsPtr ns;
616 	char *str = NULL;
617 
618 	if (nodep == NULL) {
619 		php_dom_throw_error(INVALID_STATE_ERR, 1);
620 		return FAILURE;
621 	}
622 
623 	switch (nodep->type) {
624 		case XML_ELEMENT_NODE:
625 		case XML_ATTRIBUTE_NODE:
626 		case XML_NAMESPACE_DECL:
627 			ns = nodep->ns;
628 			if (ns != NULL && ns->prefix) {
629 				str = (char *) ns->prefix;
630 			}
631 			break;
632 		default:
633 			str = NULL;
634 			break;
635 	}
636 
637 	if (str == NULL) {
638 		ZVAL_EMPTY_STRING(retval);
639 	} else {
640 		ZVAL_STRING(retval, str);
641 	}
642 	return SUCCESS;
643 
644 }
645 
dom_node_prefix_write(dom_object * obj,zval * newval)646 int dom_node_prefix_write(dom_object *obj, zval *newval)
647 {
648 	zend_string *prefix_str;
649 	xmlNode *nodep, *nsnode = NULL;
650 	xmlNsPtr ns = NULL, curns;
651 	char *strURI;
652 	char *prefix;
653 
654 	nodep = dom_object_get_node(obj);
655 
656 	if (nodep == NULL) {
657 		php_dom_throw_error(INVALID_STATE_ERR, 1);
658 		return FAILURE;
659 	}
660 
661 	switch (nodep->type) {
662 		case XML_ELEMENT_NODE:
663 			nsnode = nodep;
664 			ZEND_FALLTHROUGH;
665 		case XML_ATTRIBUTE_NODE:
666 			if (nsnode == NULL) {
667 				nsnode = nodep->parent;
668 				if (nsnode == NULL) {
669 					nsnode = xmlDocGetRootElement(nodep->doc);
670 				}
671 			}
672 			/* Typed property, this is already a string */
673 			ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING);
674 			prefix_str = Z_STR_P(newval);
675 
676 			prefix = ZSTR_VAL(prefix_str);
677 			if (nsnode && nodep->ns != NULL && !xmlStrEqual(nodep->ns->prefix, (xmlChar *)prefix)) {
678 				strURI = (char *) nodep->ns->href;
679 				if (strURI == NULL ||
680 					(zend_string_equals_literal(prefix_str, "xml") && strcmp(strURI, (char *) XML_XML_NAMESPACE)) ||
681 					(nodep->type == XML_ATTRIBUTE_NODE && zend_string_equals_literal(prefix_str, "xmlns") &&
682 					 strcmp(strURI, (char *) DOM_XMLNS_NAMESPACE)) ||
683 					(nodep->type == XML_ATTRIBUTE_NODE && !strcmp((char *) nodep->name, "xmlns"))) {
684 					ns = NULL;
685 				} else {
686 					curns = nsnode->nsDef;
687 					while (curns != NULL) {
688 						if (xmlStrEqual((xmlChar *)prefix, curns->prefix) && xmlStrEqual(nodep->ns->href, curns->href)) {
689 							ns = curns;
690 							break;
691 						}
692 						curns = curns->next;
693 					}
694 					if (ns == NULL) {
695 						ns = xmlNewNs(nsnode, nodep->ns->href, (xmlChar *)prefix);
696 					}
697 				}
698 
699 				if (ns == NULL) {
700 					php_dom_throw_error(NAMESPACE_ERR, dom_get_strict_error(obj->document));
701 					return FAILURE;
702 				}
703 
704 				xmlSetNs(nodep, ns);
705 			}
706 			break;
707 		default:
708 			break;
709 	}
710 
711 	return SUCCESS;
712 }
713 
714 /* }}} */
715 
716 /* {{{ localName	string
717 readonly=yes
718 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-NodeNSLocalN
719 Since: DOM Level 2
720 */
dom_node_local_name_read(dom_object * obj,zval * retval)721 int dom_node_local_name_read(dom_object *obj, zval *retval)
722 {
723 	xmlNode *nodep = dom_object_get_node(obj);
724 
725 	if (nodep == NULL) {
726 		php_dom_throw_error(INVALID_STATE_ERR, 1);
727 		return FAILURE;
728 	}
729 
730 	if (nodep->type == XML_ELEMENT_NODE || nodep->type == XML_ATTRIBUTE_NODE || nodep->type == XML_NAMESPACE_DECL) {
731 		ZVAL_STRING(retval, (char *) (nodep->name));
732 	} else {
733 		ZVAL_NULL(retval);
734 	}
735 
736 	return SUCCESS;
737 }
738 
739 /* }}} */
740 
741 /* {{{ baseURI	string
742 readonly=yes
743 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-baseURI
744 Since: DOM Level 3
745 */
dom_node_base_uri_read(dom_object * obj,zval * retval)746 int dom_node_base_uri_read(dom_object *obj, zval *retval)
747 {
748 	xmlNode *nodep = dom_object_get_node(obj);
749 	xmlChar *baseuri;
750 
751 	if (nodep == NULL) {
752 		php_dom_throw_error(INVALID_STATE_ERR, 1);
753 		return FAILURE;
754 	}
755 
756 	baseuri = xmlNodeGetBase(nodep->doc, nodep);
757 	if (baseuri) {
758 		ZVAL_STRING(retval, (char *) (baseuri));
759 		xmlFree(baseuri);
760 	} else {
761 		ZVAL_NULL(retval);
762 	}
763 
764 	return SUCCESS;
765 }
766 
767 /* }}} */
768 
769 /* {{{ textContent	string
770 readonly=no
771 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-textContent
772 Since: DOM Level 3
773 */
dom_node_text_content_read(dom_object * obj,zval * retval)774 int dom_node_text_content_read(dom_object *obj, zval *retval)
775 {
776 	xmlNode *nodep = dom_object_get_node(obj);
777 
778 	if (nodep == NULL) {
779 		php_dom_throw_error(INVALID_STATE_ERR, 1);
780 		return FAILURE;
781 	}
782 
783 	php_dom_get_content_into_zval(nodep, retval, false);
784 
785 	return SUCCESS;
786 }
787 
dom_node_text_content_write(dom_object * obj,zval * newval)788 int dom_node_text_content_write(dom_object *obj, zval *newval)
789 {
790 	xmlNode *nodep = dom_object_get_node(obj);
791 
792 	if (nodep == NULL) {
793 		php_dom_throw_error(INVALID_STATE_ERR, 1);
794 		return FAILURE;
795 	}
796 
797 	php_libxml_invalidate_node_list_cache(obj->document);
798 
799 	/* Typed property, this is already a string */
800 	ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING);
801 	const xmlChar *xmlChars = (const xmlChar *) Z_STRVAL_P(newval);
802 	int type = nodep->type;
803 
804 	/* We can't directly call xmlNodeSetContent, because it might encode the string through
805 	 * xmlStringLenGetNodeList for types XML_DOCUMENT_FRAG_NODE, XML_ELEMENT_NODE, XML_ATTRIBUTE_NODE.
806 	 * See tree.c:xmlNodeSetContent in libxml.
807 	 * In these cases we need to use a text node to avoid the encoding.
808 	 * For the other cases, we *can* rely on xmlNodeSetContent because it is either a no-op, or handles
809 	 * the content without encoding. */
810 	if (type == XML_DOCUMENT_FRAG_NODE || type == XML_ELEMENT_NODE || type == XML_ATTRIBUTE_NODE) {
811 		dom_remove_all_children(nodep);
812 		xmlNode *textNode = xmlNewText(xmlChars);
813 		xmlAddChild(nodep, textNode);
814 	} else {
815 		xmlNodeSetContent(nodep, xmlChars);
816 	}
817 
818 	return SUCCESS;
819 }
820 
821 /* }}} */
822 
_php_dom_insert_fragment(xmlNodePtr nodep,xmlNodePtr prevsib,xmlNodePtr nextsib,xmlNodePtr fragment,dom_object * intern,dom_object * childobj)823 static xmlNodePtr _php_dom_insert_fragment(xmlNodePtr nodep, xmlNodePtr prevsib, xmlNodePtr nextsib, xmlNodePtr fragment, dom_object *intern, dom_object *childobj) /* {{{ */
824 {
825 	xmlNodePtr newchild, node;
826 
827 	newchild = fragment->children;
828 
829 	if (newchild) {
830 		if (prevsib == NULL) {
831 			nodep->children = newchild;
832 		} else {
833 			prevsib->next = newchild;
834 		}
835 		newchild->prev = prevsib;
836 		if (nextsib == NULL) {
837 			nodep->last = fragment->last;
838 		} else {
839 			fragment->last->next = nextsib;
840 			nextsib->prev = fragment->last;
841 		}
842 
843 		node = newchild;
844 		while (node != NULL) {
845 			node->parent = nodep;
846 			if (node->doc != nodep->doc) {
847 				xmlSetTreeDoc(node, nodep->doc);
848 				if (node->_private != NULL) {
849 					childobj = node->_private;
850 					childobj->document = intern->document;
851 					php_libxml_increment_doc_ref((php_libxml_node_object *)childobj, NULL);
852 				}
853 			}
854 			if (node == fragment->last) {
855 				break;
856 			}
857 			node = node->next;
858 		}
859 
860 		fragment->children = NULL;
861 		fragment->last = NULL;
862 	}
863 
864 	return newchild;
865 }
866 /* }}} */
867 
868 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-952280727
869 Since:
870 */
PHP_METHOD(DOMNode,insertBefore)871 PHP_METHOD(DOMNode, insertBefore)
872 {
873 	zval *id, *node, *ref = NULL;
874 	xmlNodePtr child, new_child, parentp, refp;
875 	dom_object *intern, *childobj, *refpobj;
876 	int ret, stricterror;
877 
878 	id = ZEND_THIS;
879 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|O!", &node, dom_node_class_entry, &ref, dom_node_class_entry) == FAILURE) {
880 		RETURN_THROWS();
881 	}
882 
883 	DOM_GET_OBJ(parentp, id, xmlNodePtr, intern);
884 
885 	if (dom_node_children_valid(parentp) == FAILURE) {
886 		RETURN_FALSE;
887 	}
888 
889 	DOM_GET_OBJ(child, node, xmlNodePtr, childobj);
890 
891 	new_child = NULL;
892 
893 	stricterror = dom_get_strict_error(intern->document);
894 
895 	if (dom_node_is_read_only(parentp) == SUCCESS ||
896 		(child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
897 		php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
898 		RETURN_FALSE;
899 	}
900 
901 	if (dom_hierarchy(parentp, child) == FAILURE) {
902 		php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
903 		RETURN_FALSE;
904 	}
905 
906 	if (child->doc != parentp->doc && child->doc != NULL) {
907 		php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror);
908 		RETURN_FALSE;
909 	}
910 
911 	if (child->type == XML_DOCUMENT_FRAG_NODE && child->children == NULL) {
912 		/* TODO Drop Warning? */
913 		php_error_docref(NULL, E_WARNING, "Document Fragment is empty");
914 		RETURN_FALSE;
915 	}
916 
917 	if (child->doc == NULL && parentp->doc != NULL) {
918 		childobj->document = intern->document;
919 		php_libxml_increment_doc_ref((php_libxml_node_object *)childobj, NULL);
920 	}
921 
922 	php_libxml_invalidate_node_list_cache(intern->document);
923 
924 	if (ref != NULL) {
925 		DOM_GET_OBJ(refp, ref, xmlNodePtr, refpobj);
926 		if (refp->parent != parentp) {
927 			php_dom_throw_error(NOT_FOUND_ERR, stricterror);
928 			RETURN_FALSE;
929 		}
930 
931 		if (child->parent != NULL) {
932 			xmlUnlinkNode(child);
933 		}
934 
935 		if (child->type == XML_TEXT_NODE && (refp->type == XML_TEXT_NODE ||
936 			(refp->prev != NULL && refp->prev->type == XML_TEXT_NODE))) {
937 			if (child->doc == NULL) {
938 				xmlSetTreeDoc(child, parentp->doc);
939 			}
940 			new_child = child;
941 			new_child->parent = refp->parent;
942 			new_child->next = refp;
943 			new_child->prev = refp->prev;
944 			refp->prev = new_child;
945 			if (new_child->prev != NULL) {
946 				new_child->prev->next = new_child;
947 			}
948 			if (new_child->parent != NULL) {
949 				if (new_child->parent->children == refp) {
950 					new_child->parent->children = new_child;
951 				}
952 			}
953 
954 		} else if (child->type == XML_ATTRIBUTE_NODE) {
955 			xmlAttrPtr lastattr;
956 
957 			if (child->ns == NULL)
958 				lastattr = xmlHasProp(refp->parent, child->name);
959 			else
960 				lastattr = xmlHasNsProp(refp->parent, child->name, child->ns->href);
961 			if (lastattr != NULL && lastattr->type != XML_ATTRIBUTE_DECL) {
962 				if (lastattr != (xmlAttrPtr) child) {
963 					xmlUnlinkNode((xmlNodePtr) lastattr);
964 					php_libxml_node_free_resource((xmlNodePtr) lastattr);
965 				} else {
966 					DOM_RET_OBJ(child, &ret, intern);
967 					return;
968 				}
969 			}
970 			new_child = xmlAddPrevSibling(refp, child);
971 			if (UNEXPECTED(NULL == new_child)) {
972 				goto cannot_add;
973 			}
974 		} else if (child->type == XML_DOCUMENT_FRAG_NODE) {
975 			xmlNodePtr last = child->last;
976 			new_child = _php_dom_insert_fragment(parentp, refp->prev, refp, child, intern, childobj);
977 			dom_reconcile_ns_list(parentp->doc, new_child, last);
978 		} else {
979 			new_child = xmlAddPrevSibling(refp, child);
980 			if (UNEXPECTED(NULL == new_child)) {
981 				goto cannot_add;
982 			}
983 			dom_reconcile_ns(parentp->doc, new_child);
984 		}
985 	} else {
986 		if (child->parent != NULL){
987 			xmlUnlinkNode(child);
988 		}
989 		if (child->type == XML_TEXT_NODE && parentp->last != NULL && parentp->last->type == XML_TEXT_NODE) {
990 			child->parent = parentp;
991 			if (child->doc == NULL) {
992 				xmlSetTreeDoc(child, parentp->doc);
993 			}
994 			new_child = child;
995 			if (parentp->children == NULL) {
996 				parentp->children = child;
997 				parentp->last = child;
998 			} else {
999 				child = parentp->last;
1000 				child->next = new_child;
1001 				new_child->prev = child;
1002 				parentp->last = new_child;
1003 			}
1004 		} else 	if (child->type == XML_ATTRIBUTE_NODE) {
1005 			xmlAttrPtr lastattr;
1006 
1007 			if (child->ns == NULL)
1008 				lastattr = xmlHasProp(parentp, child->name);
1009 			else
1010 				lastattr = xmlHasNsProp(parentp, child->name, child->ns->href);
1011 			if (lastattr != NULL && lastattr->type != XML_ATTRIBUTE_DECL) {
1012 				if (lastattr != (xmlAttrPtr) child) {
1013 					xmlUnlinkNode((xmlNodePtr) lastattr);
1014 					php_libxml_node_free_resource((xmlNodePtr) lastattr);
1015 				} else {
1016 					DOM_RET_OBJ(child, &ret, intern);
1017 					return;
1018 				}
1019 			}
1020 			new_child = xmlAddChild(parentp, child);
1021 			if (UNEXPECTED(NULL == new_child)) {
1022 				goto cannot_add;
1023 			}
1024 		} else if (child->type == XML_DOCUMENT_FRAG_NODE) {
1025 			xmlNodePtr last = child->last;
1026 			new_child = _php_dom_insert_fragment(parentp, parentp->last, NULL, child, intern, childobj);
1027 			dom_reconcile_ns_list(parentp->doc, new_child, last);
1028 		} else {
1029 			new_child = xmlAddChild(parentp, child);
1030 			if (UNEXPECTED(NULL == new_child)) {
1031 				goto cannot_add;
1032 			}
1033 			dom_reconcile_ns(parentp->doc, new_child);
1034 		}
1035 	}
1036 
1037 	DOM_RET_OBJ(new_child, &ret, intern);
1038 	return;
1039 cannot_add:
1040 	zend_throw_error(NULL, "Cannot add newnode as the previous sibling of refnode");
1041 	RETURN_THROWS();
1042 }
1043 /* }}} end dom_node_insert_before */
1044 
1045 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-785887307
1046 Since:
1047 */
PHP_METHOD(DOMNode,replaceChild)1048 PHP_METHOD(DOMNode, replaceChild)
1049 {
1050 	zval *id, *newnode, *oldnode;
1051 	xmlNodePtr newchild, oldchild, nodep;
1052 	dom_object *intern, *newchildobj, *oldchildobj;
1053 	int stricterror;
1054 	bool replacedoctype = false;
1055 
1056 	int ret;
1057 
1058 	id = ZEND_THIS;
1059 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OO", &newnode, dom_node_class_entry, &oldnode, dom_node_class_entry) == FAILURE) {
1060 		RETURN_THROWS();
1061 	}
1062 
1063 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1064 
1065 	if (dom_node_children_valid(nodep) == FAILURE) {
1066 		RETURN_FALSE;
1067 	}
1068 
1069 	DOM_GET_OBJ(newchild, newnode, xmlNodePtr, newchildobj);
1070 	DOM_GET_OBJ(oldchild, oldnode, xmlNodePtr, oldchildobj);
1071 
1072 	if (!nodep->children) {
1073 		RETURN_FALSE;
1074 	}
1075 
1076 	stricterror = dom_get_strict_error(intern->document);
1077 
1078 	if (dom_node_is_read_only(nodep) == SUCCESS ||
1079 		(newchild->parent != NULL && dom_node_is_read_only(newchild->parent) == SUCCESS)) {
1080 		php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
1081 		RETURN_FALSE;
1082 	}
1083 
1084 	if (newchild->doc != nodep->doc && newchild->doc != NULL) {
1085 		php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror);
1086 		RETURN_FALSE;
1087 	}
1088 
1089 	if (dom_hierarchy(nodep, newchild) == FAILURE) {
1090 		php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
1091 		RETURN_FALSE;
1092 	}
1093 
1094 	if (oldchild->parent != nodep) {
1095 		php_dom_throw_error(NOT_FOUND_ERR, stricterror);
1096 		RETURN_FALSE;
1097 	}
1098 
1099 	if (newchild->type == XML_DOCUMENT_FRAG_NODE) {
1100 		xmlNodePtr prevsib, nextsib;
1101 		prevsib = oldchild->prev;
1102 		nextsib = oldchild->next;
1103 
1104 		xmlUnlinkNode(oldchild);
1105 
1106 		xmlNodePtr last = newchild->last;
1107 		newchild = _php_dom_insert_fragment(nodep, prevsib, nextsib, newchild, intern, newchildobj);
1108 		if (newchild) {
1109 			dom_reconcile_ns_list(nodep->doc, newchild, last);
1110 		}
1111 	} else if (oldchild != newchild) {
1112 		xmlDtdPtr intSubset = xmlGetIntSubset(nodep->doc);
1113 		replacedoctype = (intSubset == (xmlDtd *) oldchild);
1114 
1115 		if (newchild->doc == NULL && nodep->doc != NULL) {
1116 			xmlSetTreeDoc(newchild, nodep->doc);
1117 			newchildobj->document = intern->document;
1118 			php_libxml_increment_doc_ref((php_libxml_node_object *)newchildobj, NULL);
1119 		}
1120 		xmlReplaceNode(oldchild, newchild);
1121 		dom_reconcile_ns(nodep->doc, newchild);
1122 
1123 		if (replacedoctype) {
1124 			nodep->doc->intSubset = (xmlDtd *) newchild;
1125 		}
1126 	}
1127 	php_libxml_invalidate_node_list_cache(intern->document);
1128 	DOM_RET_OBJ(oldchild, &ret, intern);
1129 }
1130 /* }}} end dom_node_replace_child */
1131 
1132 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1734834066
1133 Since:
1134 */
PHP_METHOD(DOMNode,removeChild)1135 PHP_METHOD(DOMNode, removeChild)
1136 {
1137 	zval *id, *node;
1138 	xmlNodePtr child, nodep;
1139 	dom_object *intern, *childobj;
1140 	int ret, stricterror;
1141 
1142 	id = ZEND_THIS;
1143 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) {
1144 		RETURN_THROWS();
1145 	}
1146 
1147 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1148 
1149 	if (dom_node_children_valid(nodep) == FAILURE) {
1150 		RETURN_FALSE;
1151 	}
1152 
1153 	DOM_GET_OBJ(child, node, xmlNodePtr, childobj);
1154 
1155 	stricterror = dom_get_strict_error(intern->document);
1156 
1157 	if (dom_node_is_read_only(nodep) == SUCCESS ||
1158 		(child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
1159 		php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
1160 		RETURN_FALSE;
1161 	}
1162 
1163 	if (!nodep->children || child->parent != nodep) {
1164 		php_dom_throw_error(NOT_FOUND_ERR, stricterror);
1165 		RETURN_FALSE;
1166 	}
1167 
1168 	xmlUnlinkNode(child);
1169 	php_libxml_invalidate_node_list_cache(intern->document);
1170 	DOM_RET_OBJ(child, &ret, intern);
1171 }
1172 /* }}} end dom_node_remove_child */
1173 
1174 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-184E7107
1175 Since:
1176 */
PHP_METHOD(DOMNode,appendChild)1177 PHP_METHOD(DOMNode, appendChild)
1178 {
1179 	zval *id, *node;
1180 	xmlNodePtr child, nodep, new_child = NULL;
1181 	dom_object *intern, *childobj;
1182 	int ret, stricterror;
1183 
1184 	id = ZEND_THIS;
1185 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) {
1186 		RETURN_THROWS();
1187 	}
1188 
1189 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1190 
1191 	if (dom_node_children_valid(nodep) == FAILURE) {
1192 		RETURN_FALSE;
1193 	}
1194 
1195 	DOM_GET_OBJ(child, node, xmlNodePtr, childobj);
1196 
1197 	stricterror = dom_get_strict_error(intern->document);
1198 
1199 	if (dom_node_is_read_only(nodep) == SUCCESS ||
1200 		(child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
1201 		php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
1202 		RETURN_FALSE;
1203 	}
1204 
1205 	if (dom_hierarchy(nodep, child) == FAILURE) {
1206 		php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
1207 		RETURN_FALSE;
1208 	}
1209 
1210 	if (!(child->doc == NULL || child->doc == nodep->doc)) {
1211 		php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror);
1212 		RETURN_FALSE;
1213 	}
1214 
1215 	if (child->type == XML_DOCUMENT_FRAG_NODE && child->children == NULL) {
1216 		/* TODO Drop Warning? */
1217 		php_error_docref(NULL, E_WARNING, "Document Fragment is empty");
1218 		RETURN_FALSE;
1219 	}
1220 
1221 	if (child->doc == NULL && nodep->doc != NULL) {
1222 		childobj->document = intern->document;
1223 		php_libxml_increment_doc_ref((php_libxml_node_object *)childobj, NULL);
1224 	}
1225 
1226 	if (child->parent != NULL){
1227 		xmlUnlinkNode(child);
1228 	}
1229 
1230 	if (child->type == XML_TEXT_NODE && nodep->last != NULL && nodep->last->type == XML_TEXT_NODE) {
1231 		child->parent = nodep;
1232 		if (child->doc == NULL) {
1233 			xmlSetTreeDoc(child, nodep->doc);
1234 		}
1235 		new_child = child;
1236 		if (nodep->children == NULL) {
1237 			nodep->children = child;
1238 			nodep->last = child;
1239 		} else {
1240 			child = nodep->last;
1241 			child->next = new_child;
1242 			new_child->prev = child;
1243 			nodep->last = new_child;
1244 		}
1245 	} else 	if (child->type == XML_ATTRIBUTE_NODE) {
1246 		xmlAttrPtr lastattr;
1247 
1248 		if (child->ns == NULL)
1249 			lastattr = xmlHasProp(nodep, child->name);
1250 		else
1251 			lastattr = xmlHasNsProp(nodep, child->name, child->ns->href);
1252 		if (lastattr != NULL && lastattr->type != XML_ATTRIBUTE_DECL) {
1253 			if (lastattr != (xmlAttrPtr) child) {
1254 				xmlUnlinkNode((xmlNodePtr) lastattr);
1255 				php_libxml_node_free_resource((xmlNodePtr) lastattr);
1256 			}
1257 		}
1258 		new_child = xmlAddChild(nodep, child);
1259 		if (UNEXPECTED(new_child == NULL)) {
1260 			goto cannot_add;
1261 		}
1262 	} else if (child->type == XML_DOCUMENT_FRAG_NODE) {
1263 		xmlNodePtr last = child->last;
1264 		new_child = _php_dom_insert_fragment(nodep, nodep->last, NULL, child, intern, childobj);
1265 		dom_reconcile_ns_list(nodep->doc, new_child, last);
1266 	} else {
1267 		new_child = xmlAddChild(nodep, child);
1268 		if (UNEXPECTED(new_child == NULL)) {
1269 			goto cannot_add;
1270 		}
1271 		dom_reconcile_ns(nodep->doc, new_child);
1272 	}
1273 
1274 	php_libxml_invalidate_node_list_cache(intern->document);
1275 
1276 	DOM_RET_OBJ(new_child, &ret, intern);
1277 	return;
1278 cannot_add:
1279 	// TODO Convert to Error?
1280 	php_error_docref(NULL, E_WARNING, "Couldn't append node");
1281 	RETURN_FALSE;
1282 }
1283 /* }}} end dom_node_append_child */
1284 
1285 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-810594187
1286 Since:
1287 */
PHP_METHOD(DOMNode,hasChildNodes)1288 PHP_METHOD(DOMNode, hasChildNodes)
1289 {
1290 	zval *id;
1291 	xmlNode *nodep;
1292 	dom_object *intern;
1293 
1294 	id = ZEND_THIS;
1295 	if (zend_parse_parameters_none() == FAILURE) {
1296 		RETURN_THROWS();
1297 	}
1298 
1299 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1300 
1301 	if (dom_node_children_valid(nodep) == FAILURE) {
1302 		RETURN_FALSE;
1303 	}
1304 
1305 	if (nodep->children) {
1306 		RETURN_TRUE;
1307 	} else {
1308 		RETURN_FALSE;
1309 	}
1310 }
1311 /* }}} end dom_node_has_child_nodes */
1312 
1313 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-3A0ED0A4
1314 Since:
1315 */
PHP_METHOD(DOMNode,cloneNode)1316 PHP_METHOD(DOMNode, cloneNode)
1317 {
1318 	zval *id;
1319 	xmlNode *n, *node;
1320 	int ret;
1321 	dom_object *intern;
1322 	bool recursive = 0;
1323 
1324 	id = ZEND_THIS;
1325 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &recursive) == FAILURE) {
1326 		RETURN_THROWS();
1327 	}
1328 
1329 	DOM_GET_OBJ(n, id, xmlNodePtr, intern);
1330 
1331 	node = xmlDocCopyNode(n, n->doc, recursive);
1332 
1333 	if (!node) {
1334 		RETURN_FALSE;
1335 	}
1336 
1337 	/* When deep is false Element nodes still require the attributes
1338 	Following taken from libxml as xmlDocCopyNode doesn't do this */
1339 	if (n->type == XML_ELEMENT_NODE && recursive == 0) {
1340 		if (n->nsDef != NULL) {
1341 			node->nsDef = xmlCopyNamespaceList(n->nsDef);
1342 		}
1343 		if (n->ns != NULL) {
1344 			xmlNsPtr ns;
1345 			ns = xmlSearchNs(n->doc, node, n->ns->prefix);
1346 			if (ns == NULL) {
1347 				ns = xmlSearchNs(n->doc, n, n->ns->prefix);
1348 				if (ns != NULL) {
1349 					xmlNodePtr root = node;
1350 
1351 					while (root->parent != NULL) {
1352 						root = root->parent;
1353 					}
1354 					node->ns = xmlNewNs(root, ns->href, ns->prefix);
1355 				}
1356 			} else {
1357 				node->ns = ns;
1358 			}
1359 		}
1360 		if (n->properties != NULL) {
1361 			node->properties = xmlCopyPropList(node, n->properties);
1362 		}
1363 	}
1364 
1365 	/* If document cloned we want a new document proxy */
1366 	if (node->doc != n->doc) {
1367 		intern = NULL;
1368 	}
1369 
1370 	DOM_RET_OBJ(node, &ret, intern);
1371 }
1372 /* }}} end dom_node_clone_node */
1373 
1374 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-normalize
1375 Since:
1376 */
PHP_METHOD(DOMNode,normalize)1377 PHP_METHOD(DOMNode, normalize)
1378 {
1379 	zval *id;
1380 	xmlNode *nodep;
1381 	dom_object *intern;
1382 
1383 	id = ZEND_THIS;
1384 	if (zend_parse_parameters_none() == FAILURE) {
1385 		RETURN_THROWS();
1386 	}
1387 
1388 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1389 
1390 	php_libxml_invalidate_node_list_cache(intern->document);
1391 
1392 	dom_normalize(nodep);
1393 
1394 }
1395 /* }}} end dom_node_normalize */
1396 
1397 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Level-2-Core-Node-supports
1398 Since: DOM Level 2
1399 */
PHP_METHOD(DOMNode,isSupported)1400 PHP_METHOD(DOMNode, isSupported)
1401 {
1402 	zend_string *feature, *version;
1403 
1404 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &feature, &version) == FAILURE) {
1405 		RETURN_THROWS();
1406 	}
1407 
1408 	RETURN_BOOL(dom_has_feature(feature, version));
1409 }
1410 /* }}} end dom_node_is_supported */
1411 
1412 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-NodeHasAttrs
1413 Since: DOM Level 2
1414 */
PHP_METHOD(DOMNode,hasAttributes)1415 PHP_METHOD(DOMNode, hasAttributes)
1416 {
1417 	zval *id;
1418 	xmlNode *nodep;
1419 	dom_object *intern;
1420 
1421 	id = ZEND_THIS;
1422 	if (zend_parse_parameters_none() == FAILURE) {
1423 		RETURN_THROWS();
1424 	}
1425 
1426 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1427 
1428 	if (nodep->type != XML_ELEMENT_NODE)
1429 		RETURN_FALSE;
1430 
1431 	if (nodep->properties) {
1432 		RETURN_TRUE;
1433 	} else {
1434 		RETURN_FALSE;
1435 	}
1436 }
1437 /* }}} end dom_node_has_attributes */
1438 
1439 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-isSameNode
1440 Since: DOM Level 3
1441 */
PHP_METHOD(DOMNode,isSameNode)1442 PHP_METHOD(DOMNode, isSameNode)
1443 {
1444 	zval *id, *node;
1445 	xmlNodePtr nodeotherp, nodep;
1446 	dom_object *intern, *nodeotherobj;
1447 
1448 	id = ZEND_THIS;
1449 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) {
1450 		RETURN_THROWS();
1451 	}
1452 
1453 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1454 
1455 	DOM_GET_OBJ(nodeotherp, node, xmlNodePtr, nodeotherobj);
1456 
1457 	if (nodep == nodeotherp) {
1458 		RETURN_TRUE;
1459 	} else {
1460 		RETURN_FALSE;
1461 	}
1462 }
1463 /* }}} end dom_node_is_same_node */
1464 
php_dom_node_is_content_equal(const xmlNode * this,const xmlNode * other)1465 static bool php_dom_node_is_content_equal(const xmlNode *this, const xmlNode *other)
1466 {
1467 	xmlChar *this_content = xmlNodeGetContent(this);
1468 	xmlChar *other_content = xmlNodeGetContent(other);
1469 	bool result = xmlStrEqual(this_content, other_content);
1470 	xmlFree(this_content);
1471 	xmlFree(other_content);
1472 	return result;
1473 }
1474 
php_dom_node_is_ns_uri_equal(const xmlNode * this,const xmlNode * other)1475 static bool php_dom_node_is_ns_uri_equal(const xmlNode *this, const xmlNode *other)
1476 {
1477 	const xmlChar *this_ns = this->ns ? this->ns->href : NULL;
1478 	const xmlChar *other_ns = other->ns ? other->ns->href : NULL;
1479 	return xmlStrEqual(this_ns, other_ns);
1480 }
1481 
php_dom_node_is_ns_prefix_equal(const xmlNode * this,const xmlNode * other)1482 static bool php_dom_node_is_ns_prefix_equal(const xmlNode *this, const xmlNode *other)
1483 {
1484 	const xmlChar *this_ns = this->ns ? this->ns->prefix : NULL;
1485 	const xmlChar *other_ns = other->ns ? other->ns->prefix : NULL;
1486 	return xmlStrEqual(this_ns, other_ns);
1487 }
1488 
1489 static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other);
1490 
1491 #define PHP_DOM_FUNC_CAT(prefix, suffix) prefix##_##suffix
1492 /* xmlNode and xmlNs have incompatible struct layouts, i.e. the next field is in a different offset */
1493 #define PHP_DOM_DEFINE_LIST_COUNTER_HELPER(type)																		\
1494 	static size_t PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(const type *node)								\
1495 	{																													\
1496 		size_t counter = 0;																								\
1497 		while (node) {																									\
1498 			counter++;																									\
1499 			node = node->next;																							\
1500 		}																												\
1501 		return counter;																									\
1502 	}
1503 #define PHP_DOM_DEFINE_LIST_EQUALITY_ORDERED_HELPER(type)																\
1504 	static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check_ordered, type)(const type *list1, const type *list2)	\
1505 	{																													\
1506 		size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1);										\
1507 		if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) {										\
1508 			return false;																								\
1509 		}																												\
1510 		for (size_t i = 0; i < count; i++) {																			\
1511 			if (!php_dom_node_is_equal_node((const xmlNode *) list1, (const xmlNode *) list2)) {						\
1512 				return false;																							\
1513 			}																											\
1514 			list1 = list1->next;																						\
1515 			list2 = list2->next;																						\
1516 		}																												\
1517 		return true;																									\
1518 	}
1519 #define PHP_DOM_DEFINE_LIST_EQUALITY_UNORDERED_HELPER(type)																\
1520 	static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check_unordered, type)(const type *list1, const type *list2)\
1521 	{																													\
1522 		size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1);										\
1523 		if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) {										\
1524 			return false;																								\
1525 		}																												\
1526 		for (const type *n1 = list1; n1 != NULL; n1 = n1->next) {														\
1527 			bool found = false;																							\
1528 			for (const type *n2 = list2; n2 != NULL && !found; n2 = n2->next) {											\
1529 				if (php_dom_node_is_equal_node((const xmlNode *) n1, (const xmlNode *) n2)) {							\
1530 					found = true;																						\
1531 				}																										\
1532 			}																											\
1533 			if (!found) {																								\
1534 				return false;																							\
1535 			}																											\
1536 		}																												\
1537 		return true;																									\
1538 	}
1539 
1540 PHP_DOM_DEFINE_LIST_COUNTER_HELPER(xmlNode)
PHP_DOM_DEFINE_LIST_COUNTER_HELPER(xmlNs)1541 PHP_DOM_DEFINE_LIST_COUNTER_HELPER(xmlNs)
1542 PHP_DOM_DEFINE_LIST_EQUALITY_ORDERED_HELPER(xmlNode)
1543 PHP_DOM_DEFINE_LIST_EQUALITY_UNORDERED_HELPER(xmlNode)
1544 PHP_DOM_DEFINE_LIST_EQUALITY_UNORDERED_HELPER(xmlNs)
1545 
1546 static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other)
1547 {
1548 	ZEND_ASSERT(this != NULL);
1549 	ZEND_ASSERT(other != NULL);
1550 
1551 	if (this->type != other->type) {
1552 		return false;
1553 	}
1554 
1555 	/* Notes:
1556 	 *   - XML_DOCUMENT_TYPE_NODE is no longer created by libxml2, we only have to support XML_DTD_NODE.
1557 	 *   - element and attribute declarations are not exposed as nodes in DOM, so no comparison is needed for those. */
1558 	if (this->type == XML_ELEMENT_NODE) {
1559 		return xmlStrEqual(this->name, other->name)
1560 			&& php_dom_node_is_ns_prefix_equal(this, other)
1561 			&& php_dom_node_is_ns_uri_equal(this, other)
1562 			/* Check attributes first, then namespace declarations, then children */
1563 			&& php_dom_node_list_equality_check_unordered_xmlNode((const xmlNode *) this->properties, (const xmlNode *) other->properties)
1564 			&& php_dom_node_list_equality_check_unordered_xmlNs(this->nsDef, other->nsDef)
1565 			&& php_dom_node_list_equality_check_ordered_xmlNode(this->children, other->children);
1566 	} else if (this->type == XML_DTD_NODE) {
1567 		/* Note: in the living spec entity declarations and notations are no longer compared because they're considered obsolete. */
1568 		const xmlDtd *this_dtd = (const xmlDtd *) this;
1569 		const xmlDtd *other_dtd = (const xmlDtd *) other;
1570 		return xmlStrEqual(this_dtd->name, other_dtd->name)
1571 			&& xmlStrEqual(this_dtd->ExternalID, other_dtd->ExternalID)
1572 			&& xmlStrEqual(this_dtd->SystemID, other_dtd->SystemID);
1573 	} else if (this->type == XML_PI_NODE) {
1574 		return xmlStrEqual(this->name, other->name) && xmlStrEqual(this->content, other->content);
1575 	} else if (this->type == XML_TEXT_NODE || this->type == XML_COMMENT_NODE || this->type == XML_CDATA_SECTION_NODE) {
1576 		return xmlStrEqual(this->content, other->content);
1577 	} else if (this->type == XML_ATTRIBUTE_NODE) {
1578 		const xmlAttr *this_attr = (const xmlAttr *) this;
1579 		const xmlAttr *other_attr = (const xmlAttr *) other;
1580 		return xmlStrEqual(this_attr->name, other_attr->name)
1581 			&& php_dom_node_is_ns_uri_equal(this, other)
1582 			&& php_dom_node_is_content_equal(this, other);
1583 	} else if (this->type == XML_ENTITY_REF_NODE) {
1584 		return xmlStrEqual(this->name, other->name);
1585 	} else if (this->type == XML_ENTITY_DECL || this->type == XML_NOTATION_NODE || this->type == XML_ENTITY_NODE) {
1586 		const xmlEntity *this_entity = (const xmlEntity *) this;
1587 		const xmlEntity *other_entity = (const xmlEntity *) other;
1588 		return this_entity->etype == other_entity->etype
1589 			&& xmlStrEqual(this_entity->name, other_entity->name)
1590 			&& xmlStrEqual(this_entity->ExternalID, other_entity->ExternalID)
1591 			&& xmlStrEqual(this_entity->SystemID, other_entity->SystemID)
1592 			&& php_dom_node_is_content_equal(this, other);
1593 	} else if (this->type == XML_NAMESPACE_DECL) {
1594 		const xmlNs *this_ns = (const xmlNs *) this;
1595 		const xmlNs *other_ns = (const xmlNs *) other;
1596 		return xmlStrEqual(this_ns->prefix, other_ns->prefix) && xmlStrEqual(this_ns->href, other_ns->href);
1597 	} else if (this->type == XML_DOCUMENT_FRAG_NODE || this->type == XML_HTML_DOCUMENT_NODE || this->type == XML_DOCUMENT_NODE) {
1598 		return php_dom_node_list_equality_check_ordered_xmlNode(this->children, other->children);
1599 	}
1600 
1601 	return false;
1602 }
1603 
1604 /* {{{ URL: https://dom.spec.whatwg.org/#dom-node-isequalnode (for everything still in the living spec)
1605 *      URL: https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/DOM3-Core.html#core-Node3-isEqualNode (for old nodes removed from the living spec)
1606 Since: DOM Level 3
1607 */
PHP_METHOD(DOMNode,isEqualNode)1608 PHP_METHOD(DOMNode, isEqualNode)
1609 {
1610 	zval *id, *node;
1611 	xmlNodePtr otherp, nodep;
1612 	dom_object *unused_intern;
1613 
1614 	id = ZEND_THIS;
1615 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O!", &node, dom_node_class_entry) == FAILURE) {
1616 		RETURN_THROWS();
1617 	}
1618 
1619 	if (node == NULL) {
1620 		RETURN_FALSE;
1621 	}
1622 
1623 	DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, unused_intern);
1624 	DOM_GET_OBJ(otherp, node, xmlNodePtr, unused_intern);
1625 
1626 	if (nodep == otherp) {
1627 		RETURN_TRUE;
1628 	}
1629 
1630 	/* Empty fragments/documents only match if they're both empty */
1631 	if (UNEXPECTED(nodep == NULL || otherp == NULL)) {
1632 		RETURN_BOOL(nodep == NULL && otherp == NULL);
1633 	}
1634 
1635 	RETURN_BOOL(php_dom_node_is_equal_node(nodep, otherp));
1636 }
1637 /* }}} end DOMNode::isEqualNode */
1638 
1639 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-lookupNamespacePrefix
1640 Since: DOM Level 3
1641 */
PHP_METHOD(DOMNode,lookupPrefix)1642 PHP_METHOD(DOMNode, lookupPrefix)
1643 {
1644 	zval *id;
1645 	xmlNodePtr nodep, lookupp = NULL;
1646 	dom_object *intern;
1647 	xmlNsPtr nsptr;
1648 	size_t uri_len = 0;
1649 	char *uri;
1650 
1651 	id = ZEND_THIS;
1652 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &uri, &uri_len) == FAILURE) {
1653 		RETURN_THROWS();
1654 	}
1655 
1656 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1657 
1658 	if (uri_len > 0) {
1659 		switch (nodep->type) {
1660 			case XML_ELEMENT_NODE:
1661 				lookupp = nodep;
1662 				break;
1663 			case XML_DOCUMENT_NODE:
1664 			case XML_HTML_DOCUMENT_NODE:
1665 				lookupp = xmlDocGetRootElement((xmlDocPtr) nodep);
1666 				break;
1667 			case XML_ENTITY_NODE :
1668 			case XML_NOTATION_NODE:
1669 			case XML_DOCUMENT_FRAG_NODE:
1670 			case XML_DOCUMENT_TYPE_NODE:
1671 			case XML_DTD_NODE:
1672 				RETURN_NULL();
1673 				break;
1674 			default:
1675 				lookupp =  nodep->parent;
1676 		}
1677 
1678 		if (lookupp != NULL) {
1679 			nsptr = xmlSearchNsByHref(lookupp->doc, lookupp, (xmlChar *) uri);
1680 			if (nsptr && nsptr->prefix != NULL) {
1681 				RETURN_STRING((char *) nsptr->prefix);
1682 			}
1683 		}
1684 	}
1685 
1686 	RETURN_NULL();
1687 }
1688 /* }}} end dom_node_lookup_prefix */
1689 
1690 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace
1691 Since: DOM Level 3
1692 */
PHP_METHOD(DOMNode,isDefaultNamespace)1693 PHP_METHOD(DOMNode, isDefaultNamespace)
1694 {
1695 	zval *id;
1696 	xmlNodePtr nodep;
1697 	dom_object *intern;
1698 	xmlNsPtr nsptr;
1699 	size_t uri_len = 0;
1700 	char *uri;
1701 
1702 	id = ZEND_THIS;
1703 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &uri, &uri_len) == FAILURE) {
1704 		RETURN_THROWS();
1705 	}
1706 
1707 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1708 	if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
1709 		nodep = xmlDocGetRootElement((xmlDocPtr) nodep);
1710 	}
1711 
1712 	if (nodep && uri_len > 0) {
1713 		nsptr = xmlSearchNs(nodep->doc, nodep, NULL);
1714 		if (nsptr && xmlStrEqual(nsptr->href, (xmlChar *) uri)) {
1715 			RETURN_TRUE;
1716 		}
1717 	}
1718 
1719 	RETURN_FALSE;
1720 }
1721 /* }}} end dom_node_is_default_namespace */
1722 
1723 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI
1724 Since: DOM Level 3
1725 */
PHP_METHOD(DOMNode,lookupNamespaceURI)1726 PHP_METHOD(DOMNode, lookupNamespaceURI)
1727 {
1728 	zval *id;
1729 	xmlNodePtr nodep;
1730 	dom_object *intern;
1731 	xmlNsPtr nsptr;
1732 	size_t prefix_len;
1733 	char *prefix;
1734 
1735 	id = ZEND_THIS;
1736 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &prefix, &prefix_len) == FAILURE) {
1737 		RETURN_THROWS();
1738 	}
1739 
1740 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1741 	if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
1742 		nodep = xmlDocGetRootElement((xmlDocPtr) nodep);
1743 		if (nodep == NULL) {
1744 			RETURN_NULL();
1745 		}
1746 	}
1747 
1748 	nsptr = xmlSearchNs(nodep->doc, nodep, (xmlChar *) prefix);
1749 	if (nsptr && nsptr->href != NULL) {
1750 		RETURN_STRING((char *) nsptr->href);
1751 	}
1752 
1753 	RETURN_NULL();
1754 }
1755 /* }}} end dom_node_lookup_namespace_uri */
1756 
dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS,int mode)1757 static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
1758 {
1759 	zval *id;
1760 	zval *xpath_array=NULL, *ns_prefixes=NULL;
1761 	xmlNodePtr nodep;
1762 	xmlDocPtr docp;
1763 	xmlNodeSetPtr nodeset = NULL;
1764 	dom_object *intern;
1765 	bool exclusive=0, with_comments=0;
1766 	xmlChar **inclusive_ns_prefixes = NULL;
1767 	char *file = NULL;
1768 	int ret = -1;
1769 	size_t file_len = 0;
1770 	xmlOutputBufferPtr buf;
1771 	xmlXPathContextPtr ctxp=NULL;
1772 	xmlXPathObjectPtr xpathobjp=NULL;
1773 
1774 	id = ZEND_THIS;
1775 	if (mode == 0) {
1776 		if (zend_parse_parameters(ZEND_NUM_ARGS(),
1777 			"|bba!a!", &exclusive, &with_comments,
1778 			&xpath_array, &ns_prefixes) == FAILURE) {
1779 			RETURN_THROWS();
1780 		}
1781 	} else {
1782 		if (zend_parse_parameters(ZEND_NUM_ARGS(),
1783 			"s|bba!a!", &file, &file_len, &exclusive,
1784 			&with_comments, &xpath_array, &ns_prefixes) == FAILURE) {
1785 			RETURN_THROWS();
1786 		}
1787 	}
1788 
1789 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1790 
1791 	docp = nodep->doc;
1792 
1793 	if (! docp) {
1794 		zend_throw_error(NULL, "Node must be associated with a document");
1795 		RETURN_THROWS();
1796 	}
1797 
1798 	php_libxml_invalidate_node_list_cache_from_doc(docp);
1799 
1800 	if (xpath_array == NULL) {
1801 		if (nodep->type != XML_DOCUMENT_NODE) {
1802 			ctxp = xmlXPathNewContext(docp);
1803 			ctxp->node = nodep;
1804 			xpathobjp = xmlXPathEvalExpression((xmlChar *) "(.//. | .//@* | .//namespace::*)", ctxp);
1805 			ctxp->node = NULL;
1806 			if (xpathobjp && xpathobjp->type == XPATH_NODESET) {
1807 				nodeset = xpathobjp->nodesetval;
1808 			} else {
1809 				if (xpathobjp) {
1810 					xmlXPathFreeObject(xpathobjp);
1811 				}
1812 				xmlXPathFreeContext(ctxp);
1813 				zend_throw_error(NULL, "XPath query did not return a nodeset");
1814 				RETURN_THROWS();
1815 			}
1816 		}
1817 	} else {
1818 		/*xpath query from xpath_array */
1819 		HashTable *ht = Z_ARRVAL_P(xpath_array);
1820 		zval *tmp;
1821 		char *xquery;
1822 
1823 		/* Find "query" key */
1824 		tmp = zend_hash_find_deref(ht, ZSTR_KNOWN(ZEND_STR_QUERY));
1825 		if (!tmp) {
1826 			/* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */
1827 			zend_argument_value_error(3 + mode, "must have a \"query\" key");
1828 			RETURN_THROWS();
1829 		}
1830 		if (Z_TYPE_P(tmp) != IS_STRING) {
1831 			/* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */
1832 			zend_argument_type_error(3 + mode, "\"query\" option must be a string, %s given", zend_zval_value_name(tmp));
1833 			RETURN_THROWS();
1834 		}
1835 		xquery = Z_STRVAL_P(tmp);
1836 
1837 		ctxp = xmlXPathNewContext(docp);
1838 		ctxp->node = nodep;
1839 
1840 		tmp = zend_hash_str_find_deref(ht, "namespaces", sizeof("namespaces")-1);
1841 		if (tmp && Z_TYPE_P(tmp) == IS_ARRAY && !HT_IS_PACKED(Z_ARRVAL_P(tmp))) {
1842 			zval *tmpns;
1843 			zend_string *prefix;
1844 
1845 			ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), prefix, tmpns) {
1846 				ZVAL_DEREF(tmpns);
1847 				if (Z_TYPE_P(tmpns) == IS_STRING) {
1848 					if (prefix) {
1849 						xmlXPathRegisterNs(ctxp, (xmlChar *) ZSTR_VAL(prefix), (xmlChar *) Z_STRVAL_P(tmpns));
1850 					}
1851 				}
1852 			} ZEND_HASH_FOREACH_END();
1853 		}
1854 
1855 		xpathobjp = xmlXPathEvalExpression((xmlChar *) xquery, ctxp);
1856 		ctxp->node = NULL;
1857 		if (xpathobjp && xpathobjp->type == XPATH_NODESET) {
1858 			nodeset = xpathobjp->nodesetval;
1859 		} else {
1860 			if (xpathobjp) {
1861 				xmlXPathFreeObject(xpathobjp);
1862 			}
1863 			xmlXPathFreeContext(ctxp);
1864 			zend_throw_error(NULL, "XPath query did not return a nodeset");
1865 			RETURN_THROWS();
1866 		}
1867 	}
1868 
1869 	if (ns_prefixes != NULL) {
1870 		if (exclusive) {
1871 			zval *tmpns;
1872 			int nscount = 0;
1873 
1874 			inclusive_ns_prefixes = safe_emalloc(zend_hash_num_elements(Z_ARRVAL_P(ns_prefixes)) + 1,
1875 				sizeof(xmlChar *), 0);
1876 			ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(ns_prefixes), tmpns) {
1877 				ZVAL_DEREF(tmpns);
1878 				if (Z_TYPE_P(tmpns) == IS_STRING) {
1879 					inclusive_ns_prefixes[nscount++] = (xmlChar *) Z_STRVAL_P(tmpns);
1880 				}
1881 			} ZEND_HASH_FOREACH_END();
1882 			inclusive_ns_prefixes[nscount] = NULL;
1883 		} else {
1884 			php_error_docref(NULL, E_NOTICE,
1885 				"Inclusive namespace prefixes only allowed in exclusive mode.");
1886 		}
1887 	}
1888 
1889 	if (mode == 1) {
1890 		buf = xmlOutputBufferCreateFilename(file, NULL, 0);
1891 	} else {
1892 		buf = xmlAllocOutputBuffer(NULL);
1893 	}
1894 
1895 	if (buf != NULL) {
1896 		ret = xmlC14NDocSaveTo(docp, nodeset, exclusive, inclusive_ns_prefixes,
1897 			with_comments, buf);
1898 	}
1899 
1900 	if (inclusive_ns_prefixes != NULL) {
1901 		efree(inclusive_ns_prefixes);
1902 	}
1903 	if (xpathobjp != NULL) {
1904 		xmlXPathFreeObject(xpathobjp);
1905 	}
1906 	if (ctxp != NULL) {
1907 		xmlXPathFreeContext(ctxp);
1908 	}
1909 
1910 	if (buf == NULL || ret < 0) {
1911 		RETVAL_FALSE;
1912 	} else {
1913 		if (mode == 0) {
1914 #ifdef LIBXML2_NEW_BUFFER
1915 			ret = xmlOutputBufferGetSize(buf);
1916 #else
1917 			ret = buf->buffer->use;
1918 #endif
1919 			if (ret > 0) {
1920 #ifdef LIBXML2_NEW_BUFFER
1921 				RETVAL_STRINGL((char *) xmlOutputBufferGetContent(buf), ret);
1922 #else
1923 				RETVAL_STRINGL((char *) buf->buffer->content, ret);
1924 #endif
1925 			} else {
1926 				RETVAL_EMPTY_STRING();
1927 			}
1928 		}
1929 	}
1930 
1931 	if (buf) {
1932 		int bytes;
1933 
1934 		bytes = xmlOutputBufferClose(buf);
1935 		if (mode == 1 && (ret >= 0)) {
1936 			RETURN_LONG(bytes);
1937 		}
1938 	}
1939 }
1940 /* }}} */
1941 
1942 /* {{{ Canonicalize nodes to a string */
PHP_METHOD(DOMNode,C14N)1943 PHP_METHOD(DOMNode, C14N)
1944 {
1945 	dom_canonicalization(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1946 }
1947 /* }}} */
1948 
1949 /* {{{ Canonicalize nodes to a file */
PHP_METHOD(DOMNode,C14NFile)1950 PHP_METHOD(DOMNode, C14NFile)
1951 {
1952 	dom_canonicalization(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1953 }
1954 /* }}} */
1955 
1956 /* {{{ Gets an xpath for a node */
PHP_METHOD(DOMNode,getNodePath)1957 PHP_METHOD(DOMNode, getNodePath)
1958 {
1959 	zval *id;
1960 	xmlNode *nodep;
1961 	dom_object *intern;
1962 	char *value;
1963 
1964 	if (zend_parse_parameters_none() == FAILURE) {
1965 		RETURN_THROWS();
1966 	}
1967 
1968 	DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
1969 
1970 	value = (char *) xmlGetNodePath(nodep);
1971 	if (value == NULL) {
1972 		/* TODO Research if can return empty string */
1973 		RETURN_NULL();
1974 	} else {
1975 		RETVAL_STRING(value);
1976 		xmlFree(value);
1977 	}
1978 }
1979 /* }}} */
1980 
1981 /* {{{ Gets line number for a node */
PHP_METHOD(DOMNode,getLineNo)1982 PHP_METHOD(DOMNode, getLineNo)
1983 {
1984 	zval *id;
1985 	xmlNode *nodep;
1986 	dom_object *intern;
1987 
1988 	if (zend_parse_parameters_none() == FAILURE) {
1989 		RETURN_THROWS();
1990 	}
1991 
1992 	DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
1993 
1994 	RETURN_LONG(xmlGetLineNo(nodep));
1995 }
1996 /* }}} */
1997 
1998 /* {{{ URL: https://dom.spec.whatwg.org/#dom-node-contains
1999 Since:
2000 */
PHP_METHOD(DOMNode,contains)2001 PHP_METHOD(DOMNode, contains)
2002 {
2003 	zval *other, *id;
2004 	xmlNodePtr otherp, thisp;
2005 	dom_object *unused_intern;
2006 
2007 	ZEND_PARSE_PARAMETERS_START(1, 1)
2008 		Z_PARAM_OBJECT_OR_NULL(other)
2009 	ZEND_PARSE_PARAMETERS_END();
2010 
2011 	if (other == NULL) {
2012 		RETURN_FALSE;
2013 	}
2014 
2015 	if (UNEXPECTED(!instanceof_function(Z_OBJCE_P(other), dom_node_class_entry) && !instanceof_function(Z_OBJCE_P(other), dom_namespace_node_class_entry))) {
2016 		zend_argument_type_error(1, "must be of type DOMNode|DOMNameSpaceNode|null, %s given", zend_zval_value_name(other));
2017 		RETURN_THROWS();
2018 	}
2019 
2020 	DOM_GET_OBJ(otherp, other, xmlNodePtr, unused_intern);
2021 	DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, unused_intern);
2022 
2023 	do {
2024 		if (otherp == thisp) {
2025 			RETURN_TRUE;
2026 		}
2027 		otherp = otherp->parent;
2028 	} while (otherp);
2029 
2030 	RETURN_FALSE;
2031 }
2032 /* }}} */
2033 
2034 /* {{{ URL: https://dom.spec.whatwg.org/#dom-node-getrootnode
2035 Since:
2036 */
PHP_METHOD(DOMNode,getRootNode)2037 PHP_METHOD(DOMNode, getRootNode)
2038 {
2039 	zval *id;
2040 	xmlNodePtr thisp;
2041 	dom_object *intern;
2042 	/* Unused now because we don't support the shadow DOM nodes. Options only influence shadow DOM nodes. */
2043 	zval *options = NULL;
2044 
2045 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &options) == FAILURE) {
2046 		RETURN_THROWS();
2047 	}
2048 
2049 	DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
2050 
2051 	while (thisp->parent) {
2052 		thisp = thisp->parent;
2053 	}
2054 
2055 	int ret;
2056 	DOM_RET_OBJ(thisp, &ret, intern);
2057 }
2058 /* }}} */
2059 
2060 /**
2061  * We want to block the serialization and unserialization of DOM classes.
2062  * However, using @not-serializable makes the child classes also not serializable, even if the user implements the methods.
2063  * So instead, we implement the methods wherein we throw exceptions.
2064  * The reason we choose these methods is because:
2065  *   - If the user implements __serialize / __unserialize, the respective throwing methods are not called.
2066  *   - If the user implements __sleep / __wakeup, then it's also not a problem because they will not enter the throwing methods.
2067  */
2068 
PHP_METHOD(DOMNode,__sleep)2069 PHP_METHOD(DOMNode, __sleep)
2070 {
2071 	if (zend_parse_parameters_none() != SUCCESS) {
2072 		RETURN_THROWS();
2073 	}
2074 
2075 	zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed, unless serialization methods are implemented in a subclass", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2076 	RETURN_THROWS();
2077 }
2078 
PHP_METHOD(DOMNode,__wakeup)2079 PHP_METHOD(DOMNode, __wakeup)
2080 {
2081 	if (zend_parse_parameters_none() != SUCCESS) {
2082 		RETURN_THROWS();
2083 	}
2084 
2085 	zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed, unless unserialization methods are implemented in a subclass", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2086 	RETURN_THROWS();
2087 }
2088 
2089 #endif
2090