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