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
823 /* Returns true if the node had the same document reference, false otherwise. */
dom_set_document_ref_obj_single(xmlNodePtr node,php_libxml_ref_obj * document)824 static bool dom_set_document_ref_obj_single(xmlNodePtr node, php_libxml_ref_obj *document)
825 {
826 dom_object *childobj = php_dom_object_get_data(node);
827 if (!childobj) {
828 return true;
829 }
830 if (!childobj->document) {
831 childobj->document = document;
832 document->refcount++;
833 return true;
834 }
835 return false;
836 }
837
dom_set_document_ref_pointers_attr(xmlAttrPtr attr,php_libxml_ref_obj * document)838 void dom_set_document_ref_pointers_attr(xmlAttrPtr attr, php_libxml_ref_obj *document)
839 {
840 ZEND_ASSERT(document != NULL);
841
842 dom_set_document_ref_obj_single((xmlNodePtr) attr, document);
843 for (xmlNodePtr attr_child = attr->children; attr_child; attr_child = attr_child->next) {
844 dom_set_document_ref_obj_single(attr_child, document);
845 }
846 }
847
dom_set_document_ref_pointers_node(xmlNodePtr node,php_libxml_ref_obj * document)848 static bool dom_set_document_ref_pointers_node(xmlNodePtr node, php_libxml_ref_obj *document)
849 {
850 ZEND_ASSERT(document != NULL);
851
852 if (!dom_set_document_ref_obj_single(node, document)) {
853 return false;
854 }
855
856 if (node->type == XML_ELEMENT_NODE) {
857 for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
858 dom_set_document_ref_pointers_attr(attr, document);
859 }
860 }
861
862 return true;
863 }
864
865 /* TODO: on 8.4 replace the loop with the tree walk helper function. */
dom_set_document_ref_pointers(xmlNodePtr node,php_libxml_ref_obj * document)866 void dom_set_document_ref_pointers(xmlNodePtr node, php_libxml_ref_obj *document)
867 {
868 if (!document) {
869 return;
870 }
871
872 if (!dom_set_document_ref_pointers_node(node, document)) {
873 return;
874 }
875
876 xmlNodePtr base = node;
877 node = node->children;
878 while (node != NULL) {
879 ZEND_ASSERT(node != base);
880
881 if (!dom_set_document_ref_pointers_node(node, document)) {
882 break;
883 }
884
885 if (node->type == XML_ELEMENT_NODE) {
886 if (node->children) {
887 node = node->children;
888 continue;
889 }
890 }
891
892 if (node->next) {
893 node = node->next;
894 } else {
895 /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
896 do {
897 node = node->parent;
898 if (node == base) {
899 return;
900 }
901 } while (node->next == NULL);
902 node = node->next;
903 }
904 }
905 }
906
_php_dom_insert_fragment(xmlNodePtr nodep,xmlNodePtr prevsib,xmlNodePtr nextsib,xmlNodePtr fragment,dom_object * intern,dom_object * childobj)907 static xmlNodePtr _php_dom_insert_fragment(xmlNodePtr nodep, xmlNodePtr prevsib, xmlNodePtr nextsib, xmlNodePtr fragment, dom_object *intern, dom_object *childobj) /* {{{ */
908 {
909 xmlNodePtr newchild = fragment->children;
910
911 if (newchild) {
912 if (prevsib == NULL) {
913 nodep->children = newchild;
914 } else {
915 prevsib->next = newchild;
916 }
917 newchild->prev = prevsib;
918 if (nextsib == NULL) {
919 nodep->last = fragment->last;
920 } else {
921 fragment->last->next = nextsib;
922 nextsib->prev = fragment->last;
923 }
924
925 /* Assign parent node pointer */
926 xmlNodePtr node = newchild;
927 while (node != NULL) {
928 node->parent = nodep;
929 if (node == fragment->last) {
930 break;
931 }
932 node = node->next;
933 }
934
935 fragment->children = NULL;
936 fragment->last = NULL;
937 }
938
939 return newchild;
940 }
941 /* }}} */
942
dom_node_check_legacy_insertion_validity(xmlNodePtr parentp,xmlNodePtr child,bool stricterror,bool warn_empty_fragment)943 static bool dom_node_check_legacy_insertion_validity(xmlNodePtr parentp, xmlNodePtr child, bool stricterror, bool warn_empty_fragment)
944 {
945 if (dom_node_is_read_only(parentp) == SUCCESS ||
946 (child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
947 php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
948 return false;
949 }
950
951 if (dom_hierarchy(parentp, child) == FAILURE) {
952 php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
953 return false;
954 }
955
956 if (child->doc != parentp->doc && child->doc != NULL) {
957 php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror);
958 return false;
959 }
960
961 if (warn_empty_fragment && child->type == XML_DOCUMENT_FRAG_NODE && child->children == NULL) {
962 /* TODO Drop Warning? */
963 php_error_docref(NULL, E_WARNING, "Document Fragment is empty");
964 return false;
965 }
966
967 /* In old DOM only text nodes and entity nodes can be added as children to attributes. */
968 if (parentp->type == XML_ATTRIBUTE_NODE && child->type != XML_TEXT_NODE && child->type != XML_ENTITY_REF_NODE) {
969 php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
970 return false;
971 }
972 /* Attributes must be in elements. */
973 if (child->type == XML_ATTRIBUTE_NODE && parentp->type != XML_ELEMENT_NODE) {
974 php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
975 return false;
976 }
977
978 /* Documents can never be a child. */
979 if (child->type == XML_DOCUMENT_NODE || child->type == XML_HTML_DOCUMENT_NODE) {
980 php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
981 return false;
982 }
983
984 return true;
985 }
986
987 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-952280727
988 Since:
989 */
PHP_METHOD(DOMNode,insertBefore)990 PHP_METHOD(DOMNode, insertBefore)
991 {
992 zval *id, *node, *ref = NULL;
993 xmlNodePtr child, new_child, parentp, refp = NULL;
994 dom_object *intern, *childobj, *refpobj;
995 int ret, stricterror;
996
997 id = ZEND_THIS;
998 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|O!", &node, dom_node_class_entry, &ref, dom_node_class_entry) == FAILURE) {
999 RETURN_THROWS();
1000 }
1001
1002 DOM_GET_OBJ(parentp, id, xmlNodePtr, intern);
1003
1004 if (dom_node_children_valid(parentp) == FAILURE) {
1005 RETURN_FALSE;
1006 }
1007
1008 DOM_GET_OBJ(child, node, xmlNodePtr, childobj);
1009
1010 new_child = NULL;
1011
1012 stricterror = dom_get_strict_error(intern->document);
1013
1014 if (!dom_node_check_legacy_insertion_validity(parentp, child, stricterror, true)) {
1015 RETURN_FALSE;
1016 }
1017
1018 if (ref != NULL) {
1019 DOM_GET_OBJ(refp, ref, xmlNodePtr, refpobj);
1020 if (refp->parent != parentp) {
1021 php_dom_throw_error(NOT_FOUND_ERR, stricterror);
1022 RETURN_FALSE;
1023 }
1024 }
1025
1026 if (child->doc == NULL && parentp->doc != NULL) {
1027 dom_set_document_ref_pointers(child, intern->document);
1028 }
1029
1030 php_libxml_invalidate_node_list_cache(intern->document);
1031
1032 if (ref != NULL) {
1033 if (child->parent != NULL) {
1034 xmlUnlinkNode(child);
1035 }
1036
1037 if (child->type == XML_TEXT_NODE && (refp->type == XML_TEXT_NODE ||
1038 (refp->prev != NULL && refp->prev->type == XML_TEXT_NODE))) {
1039 new_child = child;
1040 new_child->parent = refp->parent;
1041 new_child->next = refp;
1042 new_child->prev = refp->prev;
1043 refp->prev = new_child;
1044 if (new_child->prev != NULL) {
1045 new_child->prev->next = new_child;
1046 }
1047 if (new_child->parent != NULL) {
1048 if (new_child->parent->children == refp) {
1049 new_child->parent->children = new_child;
1050 }
1051 }
1052
1053 } else if (child->type == XML_ATTRIBUTE_NODE) {
1054 xmlAttrPtr lastattr;
1055
1056 if (child->ns == NULL)
1057 lastattr = xmlHasProp(refp->parent, child->name);
1058 else
1059 lastattr = xmlHasNsProp(refp->parent, child->name, child->ns->href);
1060 if (lastattr != NULL && lastattr->type != XML_ATTRIBUTE_DECL) {
1061 if (lastattr != (xmlAttrPtr) child) {
1062 xmlUnlinkNode((xmlNodePtr) lastattr);
1063 php_libxml_node_free_resource((xmlNodePtr) lastattr);
1064 } else {
1065 DOM_RET_OBJ(child, &ret, intern);
1066 return;
1067 }
1068 }
1069 new_child = xmlAddPrevSibling(refp, child);
1070 if (UNEXPECTED(NULL == new_child)) {
1071 goto cannot_add;
1072 }
1073 } else if (child->type == XML_DOCUMENT_FRAG_NODE) {
1074 xmlNodePtr last = child->last;
1075 new_child = _php_dom_insert_fragment(parentp, refp->prev, refp, child, intern, childobj);
1076 dom_reconcile_ns_list(parentp->doc, new_child, last);
1077 } else {
1078 new_child = xmlAddPrevSibling(refp, child);
1079 if (UNEXPECTED(NULL == new_child)) {
1080 goto cannot_add;
1081 }
1082 dom_reconcile_ns(parentp->doc, new_child);
1083 }
1084 } else {
1085 if (child->parent != NULL){
1086 xmlUnlinkNode(child);
1087 }
1088 if (child->type == XML_TEXT_NODE && parentp->last != NULL && parentp->last->type == XML_TEXT_NODE) {
1089 child->parent = parentp;
1090 new_child = child;
1091 if (parentp->children == NULL) {
1092 parentp->children = child;
1093 parentp->last = child;
1094 } else {
1095 child = parentp->last;
1096 child->next = new_child;
1097 new_child->prev = child;
1098 parentp->last = new_child;
1099 }
1100 } else if (child->type == XML_ATTRIBUTE_NODE) {
1101 xmlAttrPtr lastattr;
1102
1103 if (child->ns == NULL)
1104 lastattr = xmlHasProp(parentp, child->name);
1105 else
1106 lastattr = xmlHasNsProp(parentp, child->name, child->ns->href);
1107 if (lastattr != NULL && lastattr->type != XML_ATTRIBUTE_DECL) {
1108 if (lastattr != (xmlAttrPtr) child) {
1109 xmlUnlinkNode((xmlNodePtr) lastattr);
1110 php_libxml_node_free_resource((xmlNodePtr) lastattr);
1111 } else {
1112 DOM_RET_OBJ(child, &ret, intern);
1113 return;
1114 }
1115 }
1116 new_child = xmlAddChild(parentp, child);
1117 if (UNEXPECTED(NULL == new_child)) {
1118 goto cannot_add;
1119 }
1120 } else if (child->type == XML_DOCUMENT_FRAG_NODE) {
1121 xmlNodePtr last = child->last;
1122 new_child = _php_dom_insert_fragment(parentp, parentp->last, NULL, child, intern, childobj);
1123 dom_reconcile_ns_list(parentp->doc, new_child, last);
1124 } else {
1125 new_child = xmlAddChild(parentp, child);
1126 if (UNEXPECTED(NULL == new_child)) {
1127 goto cannot_add;
1128 }
1129 dom_reconcile_ns(parentp->doc, new_child);
1130 }
1131 }
1132
1133 DOM_RET_OBJ(new_child, &ret, intern);
1134 return;
1135 cannot_add:
1136 zend_throw_error(NULL, "Cannot add newnode as the previous sibling of refnode");
1137 RETURN_THROWS();
1138 }
1139 /* }}} end dom_node_insert_before */
1140
1141 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-785887307
1142 Since:
1143 */
PHP_METHOD(DOMNode,replaceChild)1144 PHP_METHOD(DOMNode, replaceChild)
1145 {
1146 zval *id, *newnode, *oldnode;
1147 xmlNodePtr newchild, oldchild, nodep;
1148 dom_object *intern, *newchildobj, *oldchildobj;
1149 int stricterror;
1150 bool replacedoctype = false;
1151
1152 int ret;
1153
1154 id = ZEND_THIS;
1155 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OO", &newnode, dom_node_class_entry, &oldnode, dom_node_class_entry) == FAILURE) {
1156 RETURN_THROWS();
1157 }
1158
1159 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1160
1161 if (dom_node_children_valid(nodep) == FAILURE) {
1162 RETURN_FALSE;
1163 }
1164
1165 DOM_GET_OBJ(newchild, newnode, xmlNodePtr, newchildobj);
1166 DOM_GET_OBJ(oldchild, oldnode, xmlNodePtr, oldchildobj);
1167
1168 if (!nodep->children) {
1169 RETURN_FALSE;
1170 }
1171
1172 stricterror = dom_get_strict_error(intern->document);
1173
1174 if (!dom_node_check_legacy_insertion_validity(nodep, newchild, stricterror, false)) {
1175 RETURN_FALSE;
1176 }
1177
1178 /* This is already disallowed by libxml, but we should check it here to avoid
1179 * breaking assumptions and assertions. */
1180 if ((oldchild->type == XML_ATTRIBUTE_NODE) != (newchild->type == XML_ATTRIBUTE_NODE)) {
1181 php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
1182 RETURN_FALSE;
1183 }
1184
1185 if (oldchild->parent != nodep) {
1186 php_dom_throw_error(NOT_FOUND_ERR, stricterror);
1187 RETURN_FALSE;
1188 }
1189
1190 if (newchild->doc == NULL && nodep->doc != NULL) {
1191 dom_set_document_ref_pointers(newchild, intern->document);
1192 }
1193
1194 if (newchild->type == XML_DOCUMENT_FRAG_NODE) {
1195 xmlNodePtr prevsib, nextsib;
1196 prevsib = oldchild->prev;
1197 nextsib = oldchild->next;
1198
1199 xmlUnlinkNode(oldchild);
1200
1201 xmlNodePtr last = newchild->last;
1202 newchild = _php_dom_insert_fragment(nodep, prevsib, nextsib, newchild, intern, newchildobj);
1203 if (newchild) {
1204 dom_reconcile_ns_list(nodep->doc, newchild, last);
1205 }
1206 } else if (oldchild != newchild) {
1207 xmlDtdPtr intSubset = xmlGetIntSubset(nodep->doc);
1208 replacedoctype = (intSubset == (xmlDtd *) oldchild);
1209
1210 xmlReplaceNode(oldchild, newchild);
1211 dom_reconcile_ns(nodep->doc, newchild);
1212
1213 if (replacedoctype) {
1214 nodep->doc->intSubset = (xmlDtd *) newchild;
1215 }
1216 }
1217 php_libxml_invalidate_node_list_cache(intern->document);
1218 DOM_RET_OBJ(oldchild, &ret, intern);
1219 }
1220 /* }}} end dom_node_replace_child */
1221
1222 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1734834066
1223 Since:
1224 */
PHP_METHOD(DOMNode,removeChild)1225 PHP_METHOD(DOMNode, removeChild)
1226 {
1227 zval *id, *node;
1228 xmlNodePtr child, nodep;
1229 dom_object *intern, *childobj;
1230 int ret, stricterror;
1231
1232 id = ZEND_THIS;
1233 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) {
1234 RETURN_THROWS();
1235 }
1236
1237 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1238
1239 if (dom_node_children_valid(nodep) == FAILURE) {
1240 RETURN_FALSE;
1241 }
1242
1243 DOM_GET_OBJ(child, node, xmlNodePtr, childobj);
1244
1245 stricterror = dom_get_strict_error(intern->document);
1246
1247 if (dom_node_is_read_only(nodep) == SUCCESS ||
1248 (child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
1249 php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
1250 RETURN_FALSE;
1251 }
1252
1253 if (!nodep->children || child->parent != nodep) {
1254 php_dom_throw_error(NOT_FOUND_ERR, stricterror);
1255 RETURN_FALSE;
1256 }
1257
1258 xmlUnlinkNode(child);
1259 php_libxml_invalidate_node_list_cache(intern->document);
1260 DOM_RET_OBJ(child, &ret, intern);
1261 }
1262 /* }}} end dom_node_remove_child */
1263
1264 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-184E7107
1265 Since:
1266 */
PHP_METHOD(DOMNode,appendChild)1267 PHP_METHOD(DOMNode, appendChild)
1268 {
1269 zval *id, *node;
1270 xmlNodePtr child, nodep, new_child = NULL;
1271 dom_object *intern, *childobj;
1272 int ret, stricterror;
1273
1274 id = ZEND_THIS;
1275 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) {
1276 RETURN_THROWS();
1277 }
1278
1279 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1280
1281 if (dom_node_children_valid(nodep) == FAILURE) {
1282 RETURN_FALSE;
1283 }
1284
1285 DOM_GET_OBJ(child, node, xmlNodePtr, childobj);
1286
1287 stricterror = dom_get_strict_error(intern->document);
1288
1289 if (!dom_node_check_legacy_insertion_validity(nodep, child, stricterror, true)) {
1290 RETURN_FALSE;
1291 }
1292
1293 if (child->doc == NULL && nodep->doc != NULL) {
1294 dom_set_document_ref_pointers(child, intern->document);
1295 }
1296
1297 if (child->parent != NULL){
1298 xmlUnlinkNode(child);
1299 }
1300
1301 if (child->type == XML_TEXT_NODE && nodep->last != NULL && nodep->last->type == XML_TEXT_NODE) {
1302 child->parent = nodep;
1303 new_child = child;
1304 if (nodep->children == NULL) {
1305 nodep->children = child;
1306 nodep->last = child;
1307 } else {
1308 child = nodep->last;
1309 child->next = new_child;
1310 new_child->prev = child;
1311 nodep->last = new_child;
1312 }
1313 } else if (child->type == XML_ATTRIBUTE_NODE) {
1314 xmlAttrPtr lastattr;
1315
1316 if (child->ns == NULL)
1317 lastattr = xmlHasProp(nodep, child->name);
1318 else
1319 lastattr = xmlHasNsProp(nodep, child->name, child->ns->href);
1320 if (lastattr != NULL && lastattr->type != XML_ATTRIBUTE_DECL) {
1321 if (lastattr != (xmlAttrPtr) child) {
1322 xmlUnlinkNode((xmlNodePtr) lastattr);
1323 php_libxml_node_free_resource((xmlNodePtr) lastattr);
1324 }
1325 }
1326 new_child = xmlAddChild(nodep, child);
1327 if (UNEXPECTED(new_child == NULL)) {
1328 goto cannot_add;
1329 }
1330 } else if (child->type == XML_DOCUMENT_FRAG_NODE) {
1331 xmlNodePtr last = child->last;
1332 new_child = _php_dom_insert_fragment(nodep, nodep->last, NULL, child, intern, childobj);
1333 dom_reconcile_ns_list(nodep->doc, new_child, last);
1334 } else {
1335 new_child = xmlAddChild(nodep, child);
1336 if (UNEXPECTED(new_child == NULL)) {
1337 goto cannot_add;
1338 }
1339 dom_reconcile_ns(nodep->doc, new_child);
1340 }
1341
1342 php_libxml_invalidate_node_list_cache(intern->document);
1343
1344 DOM_RET_OBJ(new_child, &ret, intern);
1345 return;
1346 cannot_add:
1347 // TODO Convert to Error?
1348 php_error_docref(NULL, E_WARNING, "Couldn't append node");
1349 RETURN_FALSE;
1350 }
1351 /* }}} end dom_node_append_child */
1352
1353 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-810594187
1354 Since:
1355 */
PHP_METHOD(DOMNode,hasChildNodes)1356 PHP_METHOD(DOMNode, hasChildNodes)
1357 {
1358 zval *id;
1359 xmlNode *nodep;
1360 dom_object *intern;
1361
1362 id = ZEND_THIS;
1363 if (zend_parse_parameters_none() == FAILURE) {
1364 RETURN_THROWS();
1365 }
1366
1367 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1368
1369 if (dom_node_children_valid(nodep) == FAILURE) {
1370 RETURN_FALSE;
1371 }
1372
1373 if (nodep->children) {
1374 RETURN_TRUE;
1375 } else {
1376 RETURN_FALSE;
1377 }
1378 }
1379 /* }}} end dom_node_has_child_nodes */
1380
1381 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-3A0ED0A4
1382 Since:
1383 */
PHP_METHOD(DOMNode,cloneNode)1384 PHP_METHOD(DOMNode, cloneNode)
1385 {
1386 zval *id;
1387 xmlNode *n, *node;
1388 int ret;
1389 dom_object *intern;
1390 bool recursive = 0;
1391
1392 id = ZEND_THIS;
1393 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &recursive) == FAILURE) {
1394 RETURN_THROWS();
1395 }
1396
1397 DOM_GET_OBJ(n, id, xmlNodePtr, intern);
1398
1399 node = xmlDocCopyNode(n, n->doc, recursive);
1400
1401 if (!node) {
1402 RETURN_FALSE;
1403 }
1404
1405 /* When deep is false Element nodes still require the attributes
1406 Following taken from libxml as xmlDocCopyNode doesn't do this */
1407 if (n->type == XML_ELEMENT_NODE && recursive == 0) {
1408 if (n->nsDef != NULL) {
1409 node->nsDef = xmlCopyNamespaceList(n->nsDef);
1410 }
1411 if (n->ns != NULL) {
1412 xmlNsPtr ns;
1413 ns = xmlSearchNs(n->doc, node, n->ns->prefix);
1414 if (ns == NULL) {
1415 ns = xmlSearchNs(n->doc, n, n->ns->prefix);
1416 if (ns != NULL) {
1417 xmlNodePtr root = node;
1418
1419 while (root->parent != NULL) {
1420 root = root->parent;
1421 }
1422 node->ns = xmlNewNs(root, ns->href, ns->prefix);
1423 }
1424 } else {
1425 node->ns = ns;
1426 }
1427 }
1428 if (n->properties != NULL) {
1429 node->properties = xmlCopyPropList(node, n->properties);
1430 }
1431 }
1432
1433 /* If document cloned we want a new document proxy */
1434 if (node->doc != n->doc) {
1435 intern = NULL;
1436 }
1437
1438 DOM_RET_OBJ(node, &ret, intern);
1439 }
1440 /* }}} end dom_node_clone_node */
1441
1442 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-normalize
1443 Since:
1444 */
PHP_METHOD(DOMNode,normalize)1445 PHP_METHOD(DOMNode, normalize)
1446 {
1447 zval *id;
1448 xmlNode *nodep;
1449 dom_object *intern;
1450
1451 id = ZEND_THIS;
1452 if (zend_parse_parameters_none() == FAILURE) {
1453 RETURN_THROWS();
1454 }
1455
1456 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1457
1458 php_libxml_invalidate_node_list_cache(intern->document);
1459
1460 dom_normalize(nodep);
1461
1462 }
1463 /* }}} end dom_node_normalize */
1464
1465 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Level-2-Core-Node-supports
1466 Since: DOM Level 2
1467 */
PHP_METHOD(DOMNode,isSupported)1468 PHP_METHOD(DOMNode, isSupported)
1469 {
1470 zend_string *feature, *version;
1471
1472 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &feature, &version) == FAILURE) {
1473 RETURN_THROWS();
1474 }
1475
1476 RETURN_BOOL(dom_has_feature(feature, version));
1477 }
1478 /* }}} end dom_node_is_supported */
1479
1480 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-NodeHasAttrs
1481 Since: DOM Level 2
1482 */
PHP_METHOD(DOMNode,hasAttributes)1483 PHP_METHOD(DOMNode, hasAttributes)
1484 {
1485 zval *id;
1486 xmlNode *nodep;
1487 dom_object *intern;
1488
1489 id = ZEND_THIS;
1490 if (zend_parse_parameters_none() == FAILURE) {
1491 RETURN_THROWS();
1492 }
1493
1494 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1495
1496 if (nodep->type != XML_ELEMENT_NODE)
1497 RETURN_FALSE;
1498
1499 if (nodep->properties) {
1500 RETURN_TRUE;
1501 } else {
1502 RETURN_FALSE;
1503 }
1504 }
1505 /* }}} end dom_node_has_attributes */
1506
1507 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-isSameNode
1508 Since: DOM Level 3
1509 */
PHP_METHOD(DOMNode,isSameNode)1510 PHP_METHOD(DOMNode, isSameNode)
1511 {
1512 zval *id, *node;
1513 xmlNodePtr nodeotherp, nodep;
1514 dom_object *intern, *nodeotherobj;
1515
1516 id = ZEND_THIS;
1517 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_node_class_entry) == FAILURE) {
1518 RETURN_THROWS();
1519 }
1520
1521 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1522
1523 DOM_GET_OBJ(nodeotherp, node, xmlNodePtr, nodeotherobj);
1524
1525 if (nodep == nodeotherp) {
1526 RETURN_TRUE;
1527 } else {
1528 RETURN_FALSE;
1529 }
1530 }
1531 /* }}} end dom_node_is_same_node */
1532
php_dom_node_is_content_equal(const xmlNode * this,const xmlNode * other)1533 static bool php_dom_node_is_content_equal(const xmlNode *this, const xmlNode *other)
1534 {
1535 xmlChar *this_content = xmlNodeGetContent(this);
1536 xmlChar *other_content = xmlNodeGetContent(other);
1537 bool result = xmlStrEqual(this_content, other_content);
1538 xmlFree(this_content);
1539 xmlFree(other_content);
1540 return result;
1541 }
1542
php_dom_node_is_ns_uri_equal(const xmlNode * this,const xmlNode * other)1543 static bool php_dom_node_is_ns_uri_equal(const xmlNode *this, const xmlNode *other)
1544 {
1545 const xmlChar *this_ns = this->ns ? this->ns->href : NULL;
1546 const xmlChar *other_ns = other->ns ? other->ns->href : NULL;
1547 return xmlStrEqual(this_ns, other_ns);
1548 }
1549
php_dom_node_is_ns_prefix_equal(const xmlNode * this,const xmlNode * other)1550 static bool php_dom_node_is_ns_prefix_equal(const xmlNode *this, const xmlNode *other)
1551 {
1552 const xmlChar *this_ns = this->ns ? this->ns->prefix : NULL;
1553 const xmlChar *other_ns = other->ns ? other->ns->prefix : NULL;
1554 return xmlStrEqual(this_ns, other_ns);
1555 }
1556
1557 static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other);
1558
1559 #define PHP_DOM_FUNC_CAT(prefix, suffix) prefix##_##suffix
1560 /* xmlNode and xmlNs have incompatible struct layouts, i.e. the next field is in a different offset */
1561 #define PHP_DOM_DEFINE_LIST_COUNTER_HELPER(type) \
1562 static size_t PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(const type *node) \
1563 { \
1564 size_t counter = 0; \
1565 while (node) { \
1566 counter++; \
1567 node = node->next; \
1568 } \
1569 return counter; \
1570 }
1571 #define PHP_DOM_DEFINE_LIST_EQUALITY_ORDERED_HELPER(type) \
1572 static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check_ordered, type)(const type *list1, const type *list2) \
1573 { \
1574 size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1); \
1575 if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) { \
1576 return false; \
1577 } \
1578 for (size_t i = 0; i < count; i++) { \
1579 if (!php_dom_node_is_equal_node((const xmlNode *) list1, (const xmlNode *) list2)) { \
1580 return false; \
1581 } \
1582 list1 = list1->next; \
1583 list2 = list2->next; \
1584 } \
1585 return true; \
1586 }
1587 #define PHP_DOM_DEFINE_LIST_EQUALITY_UNORDERED_HELPER(type) \
1588 static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check_unordered, type)(const type *list1, const type *list2)\
1589 { \
1590 size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1); \
1591 if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) { \
1592 return false; \
1593 } \
1594 for (const type *n1 = list1; n1 != NULL; n1 = n1->next) { \
1595 bool found = false; \
1596 for (const type *n2 = list2; n2 != NULL && !found; n2 = n2->next) { \
1597 if (php_dom_node_is_equal_node((const xmlNode *) n1, (const xmlNode *) n2)) { \
1598 found = true; \
1599 } \
1600 } \
1601 if (!found) { \
1602 return false; \
1603 } \
1604 } \
1605 return true; \
1606 }
1607
1608 PHP_DOM_DEFINE_LIST_COUNTER_HELPER(xmlNode)
PHP_DOM_DEFINE_LIST_COUNTER_HELPER(xmlNs)1609 PHP_DOM_DEFINE_LIST_COUNTER_HELPER(xmlNs)
1610 PHP_DOM_DEFINE_LIST_EQUALITY_ORDERED_HELPER(xmlNode)
1611 PHP_DOM_DEFINE_LIST_EQUALITY_UNORDERED_HELPER(xmlNode)
1612 PHP_DOM_DEFINE_LIST_EQUALITY_UNORDERED_HELPER(xmlNs)
1613
1614 static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other)
1615 {
1616 ZEND_ASSERT(this != NULL);
1617 ZEND_ASSERT(other != NULL);
1618
1619 if (this->type != other->type) {
1620 return false;
1621 }
1622
1623 /* Notes:
1624 * - XML_DOCUMENT_TYPE_NODE is no longer created by libxml2, we only have to support XML_DTD_NODE.
1625 * - element and attribute declarations are not exposed as nodes in DOM, so no comparison is needed for those. */
1626 if (this->type == XML_ELEMENT_NODE) {
1627 return xmlStrEqual(this->name, other->name)
1628 && php_dom_node_is_ns_prefix_equal(this, other)
1629 && php_dom_node_is_ns_uri_equal(this, other)
1630 /* Check attributes first, then namespace declarations, then children */
1631 && php_dom_node_list_equality_check_unordered_xmlNode((const xmlNode *) this->properties, (const xmlNode *) other->properties)
1632 && php_dom_node_list_equality_check_unordered_xmlNs(this->nsDef, other->nsDef)
1633 && php_dom_node_list_equality_check_ordered_xmlNode(this->children, other->children);
1634 } else if (this->type == XML_DTD_NODE) {
1635 /* Note: in the living spec entity declarations and notations are no longer compared because they're considered obsolete. */
1636 const xmlDtd *this_dtd = (const xmlDtd *) this;
1637 const xmlDtd *other_dtd = (const xmlDtd *) other;
1638 return xmlStrEqual(this_dtd->name, other_dtd->name)
1639 && xmlStrEqual(this_dtd->ExternalID, other_dtd->ExternalID)
1640 && xmlStrEqual(this_dtd->SystemID, other_dtd->SystemID);
1641 } else if (this->type == XML_PI_NODE) {
1642 return xmlStrEqual(this->name, other->name) && xmlStrEqual(this->content, other->content);
1643 } else if (this->type == XML_TEXT_NODE || this->type == XML_COMMENT_NODE || this->type == XML_CDATA_SECTION_NODE) {
1644 return xmlStrEqual(this->content, other->content);
1645 } else if (this->type == XML_ATTRIBUTE_NODE) {
1646 const xmlAttr *this_attr = (const xmlAttr *) this;
1647 const xmlAttr *other_attr = (const xmlAttr *) other;
1648 return xmlStrEqual(this_attr->name, other_attr->name)
1649 && php_dom_node_is_ns_uri_equal(this, other)
1650 && php_dom_node_is_content_equal(this, other);
1651 } else if (this->type == XML_ENTITY_REF_NODE) {
1652 return xmlStrEqual(this->name, other->name);
1653 } else if (this->type == XML_ENTITY_DECL || this->type == XML_NOTATION_NODE || this->type == XML_ENTITY_NODE) {
1654 const xmlEntity *this_entity = (const xmlEntity *) this;
1655 const xmlEntity *other_entity = (const xmlEntity *) other;
1656 return this_entity->etype == other_entity->etype
1657 && xmlStrEqual(this_entity->name, other_entity->name)
1658 && xmlStrEqual(this_entity->ExternalID, other_entity->ExternalID)
1659 && xmlStrEqual(this_entity->SystemID, other_entity->SystemID)
1660 && php_dom_node_is_content_equal(this, other);
1661 } else if (this->type == XML_NAMESPACE_DECL) {
1662 const xmlNs *this_ns = (const xmlNs *) this;
1663 const xmlNs *other_ns = (const xmlNs *) other;
1664 return xmlStrEqual(this_ns->prefix, other_ns->prefix) && xmlStrEqual(this_ns->href, other_ns->href);
1665 } else if (this->type == XML_DOCUMENT_FRAG_NODE || this->type == XML_HTML_DOCUMENT_NODE || this->type == XML_DOCUMENT_NODE) {
1666 return php_dom_node_list_equality_check_ordered_xmlNode(this->children, other->children);
1667 }
1668
1669 return false;
1670 }
1671
1672 /* {{{ URL: https://dom.spec.whatwg.org/#dom-node-isequalnode (for everything still in the living spec)
1673 * 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)
1674 Since: DOM Level 3
1675 */
PHP_METHOD(DOMNode,isEqualNode)1676 PHP_METHOD(DOMNode, isEqualNode)
1677 {
1678 zval *id, *node;
1679 xmlNodePtr otherp, nodep;
1680 dom_object *unused_intern;
1681
1682 id = ZEND_THIS;
1683 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O!", &node, dom_node_class_entry) == FAILURE) {
1684 RETURN_THROWS();
1685 }
1686
1687 if (node == NULL) {
1688 RETURN_FALSE;
1689 }
1690
1691 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, unused_intern);
1692 DOM_GET_OBJ(otherp, node, xmlNodePtr, unused_intern);
1693
1694 if (nodep == otherp) {
1695 RETURN_TRUE;
1696 }
1697
1698 /* Empty fragments/documents only match if they're both empty */
1699 if (UNEXPECTED(nodep == NULL || otherp == NULL)) {
1700 RETURN_BOOL(nodep == NULL && otherp == NULL);
1701 }
1702
1703 RETURN_BOOL(php_dom_node_is_equal_node(nodep, otherp));
1704 }
1705 /* }}} end DOMNode::isEqualNode */
1706
1707 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-lookupNamespacePrefix
1708 Since: DOM Level 3
1709 */
PHP_METHOD(DOMNode,lookupPrefix)1710 PHP_METHOD(DOMNode, lookupPrefix)
1711 {
1712 zval *id;
1713 xmlNodePtr nodep, lookupp = NULL;
1714 dom_object *intern;
1715 xmlNsPtr nsptr;
1716 size_t uri_len = 0;
1717 char *uri;
1718
1719 id = ZEND_THIS;
1720 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &uri, &uri_len) == FAILURE) {
1721 RETURN_THROWS();
1722 }
1723
1724 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1725
1726 if (uri_len > 0) {
1727 switch (nodep->type) {
1728 case XML_ELEMENT_NODE:
1729 lookupp = nodep;
1730 break;
1731 case XML_DOCUMENT_NODE:
1732 case XML_HTML_DOCUMENT_NODE:
1733 lookupp = xmlDocGetRootElement((xmlDocPtr) nodep);
1734 break;
1735 case XML_ENTITY_NODE :
1736 case XML_NOTATION_NODE:
1737 case XML_DOCUMENT_FRAG_NODE:
1738 case XML_DOCUMENT_TYPE_NODE:
1739 case XML_DTD_NODE:
1740 RETURN_NULL();
1741 break;
1742 default:
1743 lookupp = nodep->parent;
1744 }
1745
1746 if (lookupp != NULL) {
1747 nsptr = xmlSearchNsByHref(lookupp->doc, lookupp, (xmlChar *) uri);
1748 if (nsptr && nsptr->prefix != NULL) {
1749 RETURN_STRING((char *) nsptr->prefix);
1750 }
1751 }
1752 }
1753
1754 RETURN_NULL();
1755 }
1756 /* }}} end dom_node_lookup_prefix */
1757
1758 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace
1759 Since: DOM Level 3
1760 */
PHP_METHOD(DOMNode,isDefaultNamespace)1761 PHP_METHOD(DOMNode, isDefaultNamespace)
1762 {
1763 zval *id;
1764 xmlNodePtr nodep;
1765 dom_object *intern;
1766 xmlNsPtr nsptr;
1767 size_t uri_len = 0;
1768 char *uri;
1769
1770 id = ZEND_THIS;
1771 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &uri, &uri_len) == FAILURE) {
1772 RETURN_THROWS();
1773 }
1774
1775 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1776 if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
1777 nodep = xmlDocGetRootElement((xmlDocPtr) nodep);
1778 }
1779
1780 if (nodep && uri_len > 0) {
1781 nsptr = xmlSearchNs(nodep->doc, nodep, NULL);
1782 if (nsptr && xmlStrEqual(nsptr->href, (xmlChar *) uri)) {
1783 RETURN_TRUE;
1784 }
1785 }
1786
1787 RETURN_FALSE;
1788 }
1789 /* }}} end dom_node_is_default_namespace */
1790
1791 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI
1792 Since: DOM Level 3
1793 */
PHP_METHOD(DOMNode,lookupNamespaceURI)1794 PHP_METHOD(DOMNode, lookupNamespaceURI)
1795 {
1796 zval *id;
1797 xmlNodePtr nodep;
1798 dom_object *intern;
1799 xmlNsPtr nsptr;
1800 size_t prefix_len;
1801 char *prefix;
1802
1803 id = ZEND_THIS;
1804 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &prefix, &prefix_len) == FAILURE) {
1805 RETURN_THROWS();
1806 }
1807
1808 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1809 if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
1810 nodep = xmlDocGetRootElement((xmlDocPtr) nodep);
1811 if (nodep == NULL) {
1812 RETURN_NULL();
1813 }
1814 }
1815
1816 nsptr = xmlSearchNs(nodep->doc, nodep, (xmlChar *) prefix);
1817 if (nsptr && nsptr->href != NULL) {
1818 RETURN_STRING((char *) nsptr->href);
1819 }
1820
1821 RETURN_NULL();
1822 }
1823 /* }}} end dom_node_lookup_namespace_uri */
1824
dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS,int mode)1825 static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
1826 {
1827 zval *id;
1828 zval *xpath_array=NULL, *ns_prefixes=NULL;
1829 xmlNodePtr nodep;
1830 xmlDocPtr docp;
1831 xmlNodeSetPtr nodeset = NULL;
1832 dom_object *intern;
1833 bool exclusive=0, with_comments=0;
1834 xmlChar **inclusive_ns_prefixes = NULL;
1835 char *file = NULL;
1836 int ret = -1;
1837 size_t file_len = 0;
1838 xmlOutputBufferPtr buf;
1839 xmlXPathContextPtr ctxp=NULL;
1840 xmlXPathObjectPtr xpathobjp=NULL;
1841
1842 id = ZEND_THIS;
1843 if (mode == 0) {
1844 if (zend_parse_parameters(ZEND_NUM_ARGS(),
1845 "|bba!a!", &exclusive, &with_comments,
1846 &xpath_array, &ns_prefixes) == FAILURE) {
1847 RETURN_THROWS();
1848 }
1849 } else {
1850 if (zend_parse_parameters(ZEND_NUM_ARGS(),
1851 "s|bba!a!", &file, &file_len, &exclusive,
1852 &with_comments, &xpath_array, &ns_prefixes) == FAILURE) {
1853 RETURN_THROWS();
1854 }
1855 }
1856
1857 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1858
1859 docp = nodep->doc;
1860
1861 if (! docp) {
1862 zend_throw_error(NULL, "Node must be associated with a document");
1863 RETURN_THROWS();
1864 }
1865
1866 php_libxml_invalidate_node_list_cache_from_doc(docp);
1867
1868 if (xpath_array == NULL) {
1869 if (nodep->type != XML_DOCUMENT_NODE) {
1870 ctxp = xmlXPathNewContext(docp);
1871 ctxp->node = nodep;
1872 xpathobjp = xmlXPathEvalExpression((xmlChar *) "(.//. | .//@* | .//namespace::*)", ctxp);
1873 ctxp->node = NULL;
1874 if (xpathobjp && xpathobjp->type == XPATH_NODESET) {
1875 nodeset = xpathobjp->nodesetval;
1876 } else {
1877 if (xpathobjp) {
1878 xmlXPathFreeObject(xpathobjp);
1879 }
1880 xmlXPathFreeContext(ctxp);
1881 zend_throw_error(NULL, "XPath query did not return a nodeset");
1882 RETURN_THROWS();
1883 }
1884 }
1885 } else {
1886 /*xpath query from xpath_array */
1887 HashTable *ht = Z_ARRVAL_P(xpath_array);
1888 zval *tmp;
1889 char *xquery;
1890
1891 /* Find "query" key */
1892 tmp = zend_hash_find_deref(ht, ZSTR_KNOWN(ZEND_STR_QUERY));
1893 if (!tmp) {
1894 /* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */
1895 zend_argument_value_error(3 + mode, "must have a \"query\" key");
1896 RETURN_THROWS();
1897 }
1898 if (Z_TYPE_P(tmp) != IS_STRING) {
1899 /* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */
1900 zend_argument_type_error(3 + mode, "\"query\" option must be a string, %s given", zend_zval_value_name(tmp));
1901 RETURN_THROWS();
1902 }
1903 xquery = Z_STRVAL_P(tmp);
1904
1905 ctxp = xmlXPathNewContext(docp);
1906 ctxp->node = nodep;
1907
1908 tmp = zend_hash_str_find_deref(ht, "namespaces", sizeof("namespaces")-1);
1909 if (tmp && Z_TYPE_P(tmp) == IS_ARRAY && !HT_IS_PACKED(Z_ARRVAL_P(tmp))) {
1910 zval *tmpns;
1911 zend_string *prefix;
1912
1913 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), prefix, tmpns) {
1914 ZVAL_DEREF(tmpns);
1915 if (Z_TYPE_P(tmpns) == IS_STRING) {
1916 if (prefix) {
1917 xmlXPathRegisterNs(ctxp, (xmlChar *) ZSTR_VAL(prefix), (xmlChar *) Z_STRVAL_P(tmpns));
1918 }
1919 }
1920 } ZEND_HASH_FOREACH_END();
1921 }
1922
1923 xpathobjp = xmlXPathEvalExpression((xmlChar *) xquery, ctxp);
1924 ctxp->node = NULL;
1925 if (xpathobjp && xpathobjp->type == XPATH_NODESET) {
1926 nodeset = xpathobjp->nodesetval;
1927 } else {
1928 if (xpathobjp) {
1929 xmlXPathFreeObject(xpathobjp);
1930 }
1931 xmlXPathFreeContext(ctxp);
1932 zend_throw_error(NULL, "XPath query did not return a nodeset");
1933 RETURN_THROWS();
1934 }
1935 }
1936
1937 if (ns_prefixes != NULL) {
1938 if (exclusive) {
1939 zval *tmpns;
1940 int nscount = 0;
1941
1942 inclusive_ns_prefixes = safe_emalloc(zend_hash_num_elements(Z_ARRVAL_P(ns_prefixes)) + 1,
1943 sizeof(xmlChar *), 0);
1944 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(ns_prefixes), tmpns) {
1945 ZVAL_DEREF(tmpns);
1946 if (Z_TYPE_P(tmpns) == IS_STRING) {
1947 inclusive_ns_prefixes[nscount++] = (xmlChar *) Z_STRVAL_P(tmpns);
1948 }
1949 } ZEND_HASH_FOREACH_END();
1950 inclusive_ns_prefixes[nscount] = NULL;
1951 } else {
1952 php_error_docref(NULL, E_NOTICE,
1953 "Inclusive namespace prefixes only allowed in exclusive mode.");
1954 }
1955 }
1956
1957 if (mode == 1) {
1958 buf = xmlOutputBufferCreateFilename(file, NULL, 0);
1959 } else {
1960 buf = xmlAllocOutputBuffer(NULL);
1961 }
1962
1963 if (buf != NULL) {
1964 ret = xmlC14NDocSaveTo(docp, nodeset, exclusive, inclusive_ns_prefixes,
1965 with_comments, buf);
1966 }
1967
1968 if (inclusive_ns_prefixes != NULL) {
1969 efree(inclusive_ns_prefixes);
1970 }
1971 if (xpathobjp != NULL) {
1972 xmlXPathFreeObject(xpathobjp);
1973 }
1974 if (ctxp != NULL) {
1975 xmlXPathFreeContext(ctxp);
1976 }
1977
1978 if (buf == NULL || ret < 0) {
1979 RETVAL_FALSE;
1980 } else {
1981 if (mode == 0) {
1982 #ifdef LIBXML2_NEW_BUFFER
1983 ret = xmlOutputBufferGetSize(buf);
1984 #else
1985 ret = buf->buffer->use;
1986 #endif
1987 if (ret > 0) {
1988 #ifdef LIBXML2_NEW_BUFFER
1989 RETVAL_STRINGL((char *) xmlOutputBufferGetContent(buf), ret);
1990 #else
1991 RETVAL_STRINGL((char *) buf->buffer->content, ret);
1992 #endif
1993 } else {
1994 RETVAL_EMPTY_STRING();
1995 }
1996 }
1997 }
1998
1999 if (buf) {
2000 int bytes;
2001
2002 bytes = xmlOutputBufferClose(buf);
2003 if (mode == 1 && (ret >= 0)) {
2004 RETURN_LONG(bytes);
2005 }
2006 }
2007 }
2008 /* }}} */
2009
2010 /* {{{ Canonicalize nodes to a string */
PHP_METHOD(DOMNode,C14N)2011 PHP_METHOD(DOMNode, C14N)
2012 {
2013 dom_canonicalization(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2014 }
2015 /* }}} */
2016
2017 /* {{{ Canonicalize nodes to a file */
PHP_METHOD(DOMNode,C14NFile)2018 PHP_METHOD(DOMNode, C14NFile)
2019 {
2020 dom_canonicalization(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2021 }
2022 /* }}} */
2023
2024 /* {{{ Gets an xpath for a node */
PHP_METHOD(DOMNode,getNodePath)2025 PHP_METHOD(DOMNode, getNodePath)
2026 {
2027 zval *id;
2028 xmlNode *nodep;
2029 dom_object *intern;
2030 char *value;
2031
2032 if (zend_parse_parameters_none() == FAILURE) {
2033 RETURN_THROWS();
2034 }
2035
2036 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
2037
2038 value = (char *) xmlGetNodePath(nodep);
2039 if (value == NULL) {
2040 /* TODO Research if can return empty string */
2041 RETURN_NULL();
2042 } else {
2043 RETVAL_STRING(value);
2044 xmlFree(value);
2045 }
2046 }
2047 /* }}} */
2048
2049 /* {{{ Gets line number for a node */
PHP_METHOD(DOMNode,getLineNo)2050 PHP_METHOD(DOMNode, getLineNo)
2051 {
2052 zval *id;
2053 xmlNode *nodep;
2054 dom_object *intern;
2055
2056 if (zend_parse_parameters_none() == FAILURE) {
2057 RETURN_THROWS();
2058 }
2059
2060 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
2061
2062 RETURN_LONG(xmlGetLineNo(nodep));
2063 }
2064 /* }}} */
2065
2066 /* {{{ URL: https://dom.spec.whatwg.org/#dom-node-contains
2067 Since:
2068 */
PHP_METHOD(DOMNode,contains)2069 PHP_METHOD(DOMNode, contains)
2070 {
2071 zval *other, *id;
2072 xmlNodePtr otherp, thisp;
2073 dom_object *unused_intern;
2074
2075 ZEND_PARSE_PARAMETERS_START(1, 1)
2076 Z_PARAM_OBJECT_OR_NULL(other)
2077 ZEND_PARSE_PARAMETERS_END();
2078
2079 if (other == NULL) {
2080 RETURN_FALSE;
2081 }
2082
2083 if (UNEXPECTED(!instanceof_function(Z_OBJCE_P(other), dom_node_class_entry) && !instanceof_function(Z_OBJCE_P(other), dom_namespace_node_class_entry))) {
2084 zend_argument_type_error(1, "must be of type DOMNode|DOMNameSpaceNode|null, %s given", zend_zval_value_name(other));
2085 RETURN_THROWS();
2086 }
2087
2088 DOM_GET_OBJ(otherp, other, xmlNodePtr, unused_intern);
2089 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, unused_intern);
2090
2091 do {
2092 if (otherp == thisp) {
2093 RETURN_TRUE;
2094 }
2095 otherp = otherp->parent;
2096 } while (otherp);
2097
2098 RETURN_FALSE;
2099 }
2100 /* }}} */
2101
2102 /* {{{ URL: https://dom.spec.whatwg.org/#dom-node-getrootnode
2103 Since:
2104 */
PHP_METHOD(DOMNode,getRootNode)2105 PHP_METHOD(DOMNode, getRootNode)
2106 {
2107 zval *id;
2108 xmlNodePtr thisp;
2109 dom_object *intern;
2110 /* Unused now because we don't support the shadow DOM nodes. Options only influence shadow DOM nodes. */
2111 zval *options = NULL;
2112
2113 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &options) == FAILURE) {
2114 RETURN_THROWS();
2115 }
2116
2117 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
2118
2119 while (thisp->parent) {
2120 thisp = thisp->parent;
2121 }
2122
2123 int ret;
2124 DOM_RET_OBJ(thisp, &ret, intern);
2125 }
2126 /* }}} */
2127
2128 /**
2129 * We want to block the serialization and unserialization of DOM classes.
2130 * However, using @not-serializable makes the child classes also not serializable, even if the user implements the methods.
2131 * So instead, we implement the methods wherein we throw exceptions.
2132 * The reason we choose these methods is because:
2133 * - If the user implements __serialize / __unserialize, the respective throwing methods are not called.
2134 * - If the user implements __sleep / __wakeup, then it's also not a problem because they will not enter the throwing methods.
2135 */
2136
PHP_METHOD(DOMNode,__sleep)2137 PHP_METHOD(DOMNode, __sleep)
2138 {
2139 if (zend_parse_parameters_none() != SUCCESS) {
2140 RETURN_THROWS();
2141 }
2142
2143 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));
2144 RETURN_THROWS();
2145 }
2146
PHP_METHOD(DOMNode,__wakeup)2147 PHP_METHOD(DOMNode, __wakeup)
2148 {
2149 if (zend_parse_parameters_none() != SUCCESS) {
2150 RETURN_THROWS();
2151 }
2152
2153 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));
2154 RETURN_THROWS();
2155 }
2156
2157 #endif
2158