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