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 #include <libxml/SAX.h>
26 #include <libxml/xmlsave.h>
27 #ifdef LIBXML_SCHEMAS_ENABLED
28 #include <libxml/relaxng.h>
29 #include <libxml/xmlschemas.h>
30 #endif
31
32 typedef struct _idsIterator idsIterator;
33 struct _idsIterator {
34 xmlChar *elementId;
35 xmlNode *element;
36 };
37
38 #define DOM_LOAD_STRING 0
39 #define DOM_LOAD_FILE 1
40
41 /*
42 * class DOMDocument extends DOMNode
43 *
44 * URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-i-Document
45 * Since:
46 */
47
48 /* {{{ docType DOMDocumentType
49 readonly=yes
50 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-B63ED1A31
51 Since:
52 */
dom_document_doctype_read(dom_object * obj,zval * retval)53 int dom_document_doctype_read(dom_object *obj, zval *retval)
54 {
55 xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
56 xmlDtdPtr dtdptr;
57
58 if (docp == NULL) {
59 php_dom_throw_error(INVALID_STATE_ERR, 1);
60 return FAILURE;
61 }
62
63 dtdptr = xmlGetIntSubset(docp);
64 if (!dtdptr) {
65 ZVAL_NULL(retval);
66 return SUCCESS;
67 }
68
69 php_dom_create_object((xmlNodePtr) dtdptr, retval, obj);
70 return SUCCESS;
71 }
72
73 /* }}} */
74
75 /* {{{ implementation DOMImplementation
76 readonly=yes
77 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1B793EBA
78 Since:
79 */
dom_document_implementation_read(dom_object * obj,zval * retval)80 int dom_document_implementation_read(dom_object *obj, zval *retval)
81 {
82 php_dom_create_implementation(retval);
83 return SUCCESS;
84 }
85
86 /* }}} */
87
88 /* {{{ documentElement DOMElement
89 readonly=yes
90 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-87CD092
91 Since:
92 */
dom_document_document_element_read(dom_object * obj,zval * retval)93 int dom_document_document_element_read(dom_object *obj, zval *retval)
94 {
95 xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
96 xmlNode *root;
97
98 if (docp == NULL) {
99 php_dom_throw_error(INVALID_STATE_ERR, 1);
100 return FAILURE;
101 }
102
103 root = xmlDocGetRootElement(docp);
104 if (!root) {
105 ZVAL_NULL(retval);
106 return SUCCESS;
107 }
108
109 php_dom_create_object(root, retval, obj);
110 return SUCCESS;
111 }
112
113 /* }}} */
114
115 /* {{{ encoding string
116 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-encoding
117 Since: DOM Level 3
118 */
dom_document_encoding_read(dom_object * obj,zval * retval)119 int dom_document_encoding_read(dom_object *obj, zval *retval)
120 {
121 xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
122 char *encoding;
123
124 if (docp == NULL) {
125 php_dom_throw_error(INVALID_STATE_ERR, 1);
126 return FAILURE;
127 }
128
129 encoding = (char *) docp->encoding;
130
131 if (encoding != NULL) {
132 ZVAL_STRING(retval, encoding);
133 } else {
134 ZVAL_NULL(retval);
135 }
136
137 return SUCCESS;
138 }
139
dom_document_encoding_write(dom_object * obj,zval * newval)140 zend_result dom_document_encoding_write(dom_object *obj, zval *newval)
141 {
142 xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
143 xmlCharEncodingHandlerPtr handler;
144
145 if (docp == NULL) {
146 php_dom_throw_error(INVALID_STATE_ERR, 1);
147 return FAILURE;
148 }
149
150 /* Typed property, can only be IS_STRING or IS_NULL. */
151 ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING || Z_TYPE_P(newval) == IS_NULL);
152
153 if (Z_TYPE_P(newval) == IS_NULL) {
154 goto invalid_encoding;
155 }
156
157 zend_string *str = Z_STR_P(newval);
158
159 handler = xmlFindCharEncodingHandler(ZSTR_VAL(str));
160
161 if (handler != NULL) {
162 xmlCharEncCloseFunc(handler);
163 if (docp->encoding != NULL) {
164 xmlFree((xmlChar *)docp->encoding);
165 }
166 docp->encoding = xmlStrdup((const xmlChar *) ZSTR_VAL(str));
167 } else {
168 goto invalid_encoding;
169 }
170
171 return SUCCESS;
172
173 invalid_encoding:
174 zend_value_error("Invalid document encoding");
175 return FAILURE;
176 }
177
178 /* }}} */
179
180 /* {{{ standalone boolean
181 readonly=no
182 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-standalone
183 Since: DOM Level 3
184 */
dom_document_standalone_read(dom_object * obj,zval * retval)185 int dom_document_standalone_read(dom_object *obj, zval *retval)
186 {
187 xmlDoc *docp;
188
189 docp = (xmlDocPtr) dom_object_get_node(obj);
190
191 if (docp == NULL) {
192 php_dom_throw_error(INVALID_STATE_ERR, 1);
193 return FAILURE;
194 }
195
196 ZVAL_BOOL(retval, docp->standalone > 0);
197 return SUCCESS;
198 }
199
dom_document_standalone_write(dom_object * obj,zval * newval)200 int dom_document_standalone_write(dom_object *obj, zval *newval)
201 {
202 xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
203 zend_long standalone;
204
205 if (docp == NULL) {
206 php_dom_throw_error(INVALID_STATE_ERR, 1);
207 return FAILURE;
208 }
209
210 standalone = zval_get_long(newval);
211 docp->standalone = ZEND_NORMALIZE_BOOL(standalone);
212
213 return SUCCESS;
214 }
215
216 /* }}} */
217
218 /* {{{ version string
219 readonly=no
220 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-version
221 Since: DOM Level 3
222 */
dom_document_version_read(dom_object * obj,zval * retval)223 int dom_document_version_read(dom_object *obj, zval *retval)
224 {
225 xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
226 char *version;
227
228 if (docp == NULL) {
229 php_dom_throw_error(INVALID_STATE_ERR, 1);
230 return FAILURE;
231 }
232
233 version = (char *) docp->version;
234
235 if (version != NULL) {
236 ZVAL_STRING(retval, version);
237 } else {
238 ZVAL_NULL(retval);
239 }
240
241 return SUCCESS;
242 }
243
dom_document_version_write(dom_object * obj,zval * newval)244 int dom_document_version_write(dom_object *obj, zval *newval)
245 {
246 xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
247 zend_string *str;
248
249 if (docp == NULL) {
250 php_dom_throw_error(INVALID_STATE_ERR, 1);
251 return FAILURE;
252 }
253
254 str = zval_try_get_string(newval);
255 if (UNEXPECTED(!str)) {
256 return FAILURE;
257 }
258
259 if (docp->version != NULL) {
260 xmlFree((xmlChar *) docp->version );
261 }
262
263 docp->version = xmlStrdup((const xmlChar *) ZSTR_VAL(str));
264
265 zend_string_release_ex(str, 0);
266 return SUCCESS;
267 }
268
269 /* }}} */
270
271 /* {{{ strictErrorChecking boolean
272 readonly=no
273 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-strictErrorChecking
274 Since: DOM Level 3
275 */
dom_document_strict_error_checking_read(dom_object * obj,zval * retval)276 int dom_document_strict_error_checking_read(dom_object *obj, zval *retval)
277 {
278 if (obj->document) {
279 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
280 ZVAL_BOOL(retval, doc_prop->stricterror);
281 } else {
282 ZVAL_FALSE(retval);
283 }
284 return SUCCESS;
285 }
286
dom_document_strict_error_checking_write(dom_object * obj,zval * newval)287 int dom_document_strict_error_checking_write(dom_object *obj, zval *newval)
288 {
289
290 if (obj->document) {
291 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
292 doc_prop->stricterror = zend_is_true(newval);
293 }
294
295 return SUCCESS;
296 }
297
298 /* }}} */
299
300 /* {{{ formatOutput boolean
301 readonly=no
302 */
dom_document_format_output_read(dom_object * obj,zval * retval)303 int dom_document_format_output_read(dom_object *obj, zval *retval)
304 {
305 if (obj->document) {
306 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
307 ZVAL_BOOL(retval, doc_prop->formatoutput);
308 } else {
309 ZVAL_FALSE(retval);
310 }
311 return SUCCESS;
312 }
313
dom_document_format_output_write(dom_object * obj,zval * newval)314 int dom_document_format_output_write(dom_object *obj, zval *newval)
315 {
316 if (obj->document) {
317 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
318 doc_prop->formatoutput = zend_is_true(newval);
319 }
320
321 return SUCCESS;
322 }
323 /* }}} */
324
325 /* {{{ validateOnParse boolean
326 readonly=no
327 */
dom_document_validate_on_parse_read(dom_object * obj,zval * retval)328 int dom_document_validate_on_parse_read(dom_object *obj, zval *retval)
329 {
330 if (obj->document) {
331 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
332 ZVAL_BOOL(retval, doc_prop->validateonparse);
333 } else {
334 ZVAL_FALSE(retval);
335 }
336 return SUCCESS;
337 }
338
dom_document_validate_on_parse_write(dom_object * obj,zval * newval)339 int dom_document_validate_on_parse_write(dom_object *obj, zval *newval)
340 {
341 if (obj->document) {
342 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
343 doc_prop->validateonparse = zend_is_true(newval);
344 }
345
346 return SUCCESS;
347 }
348 /* }}} */
349
350 /* {{{ resolveExternals boolean
351 readonly=no
352 */
dom_document_resolve_externals_read(dom_object * obj,zval * retval)353 int dom_document_resolve_externals_read(dom_object *obj, zval *retval)
354 {
355 if (obj->document) {
356 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
357 ZVAL_BOOL(retval, doc_prop->resolveexternals);
358 } else {
359 ZVAL_FALSE(retval);
360 }
361 return SUCCESS;
362 }
363
dom_document_resolve_externals_write(dom_object * obj,zval * newval)364 int dom_document_resolve_externals_write(dom_object *obj, zval *newval)
365 {
366 if (obj->document) {
367 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
368 doc_prop->resolveexternals = zend_is_true(newval);
369 }
370
371 return SUCCESS;
372 }
373 /* }}} */
374
375 /* {{{ preserveWhiteSpace boolean
376 readonly=no
377 */
dom_document_preserve_whitespace_read(dom_object * obj,zval * retval)378 int dom_document_preserve_whitespace_read(dom_object *obj, zval *retval)
379 {
380 if (obj->document) {
381 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
382 ZVAL_BOOL(retval, doc_prop->preservewhitespace);
383 } else {
384 ZVAL_FALSE(retval);
385 }
386 return SUCCESS;
387 }
388
dom_document_preserve_whitespace_write(dom_object * obj,zval * newval)389 int dom_document_preserve_whitespace_write(dom_object *obj, zval *newval)
390 {
391 if (obj->document) {
392 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
393 doc_prop->preservewhitespace = zend_is_true(newval);
394 }
395
396 return SUCCESS;
397 }
398 /* }}} */
399
400 /* {{{ recover boolean
401 readonly=no
402 */
dom_document_recover_read(dom_object * obj,zval * retval)403 int dom_document_recover_read(dom_object *obj, zval *retval)
404 {
405 if (obj->document) {
406 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
407 ZVAL_BOOL(retval, doc_prop->recover);
408 } else {
409 ZVAL_FALSE(retval);
410 }
411 return SUCCESS;
412 }
413
dom_document_recover_write(dom_object * obj,zval * newval)414 int dom_document_recover_write(dom_object *obj, zval *newval)
415 {
416 if (obj->document) {
417 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
418 doc_prop->recover = zend_is_true(newval);
419 }
420
421 return SUCCESS;
422 }
423 /* }}} */
424
425 /* {{{ substituteEntities boolean
426 readonly=no
427 */
dom_document_substitue_entities_read(dom_object * obj,zval * retval)428 int dom_document_substitue_entities_read(dom_object *obj, zval *retval)
429 {
430 if (obj->document) {
431 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
432 ZVAL_BOOL(retval, doc_prop->substituteentities);
433 } else {
434 ZVAL_FALSE(retval);
435 }
436 return SUCCESS;
437 }
438
dom_document_substitue_entities_write(dom_object * obj,zval * newval)439 int dom_document_substitue_entities_write(dom_object *obj, zval *newval)
440 {
441 if (obj->document) {
442 dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
443 doc_prop->substituteentities = zend_is_true(newval);
444 }
445
446 return SUCCESS;
447 }
448 /* }}} */
449
450 /* {{{ documentURI string
451 readonly=no
452 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-documentURI
453 Since: DOM Level 3
454 */
dom_document_document_uri_read(dom_object * obj,zval * retval)455 int dom_document_document_uri_read(dom_object *obj, zval *retval)
456 {
457 xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
458 char *url;
459
460 if (docp == NULL) {
461 php_dom_throw_error(INVALID_STATE_ERR, 1);
462 return FAILURE;
463 }
464
465 url = (char *) docp->URL;
466 if (url != NULL) {
467 ZVAL_STRING(retval, url);
468 } else {
469 ZVAL_NULL(retval);
470 }
471
472 return SUCCESS;
473 }
474
dom_document_document_uri_write(dom_object * obj,zval * newval)475 int dom_document_document_uri_write(dom_object *obj, zval *newval)
476 {
477 xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj);
478 zend_string *str;
479
480 if (docp == NULL) {
481 php_dom_throw_error(INVALID_STATE_ERR, 1);
482 return FAILURE;
483 }
484
485 str = zval_try_get_string(newval);
486 if (UNEXPECTED(!str)) {
487 return FAILURE;
488 }
489
490 if (docp->URL != NULL) {
491 xmlFree((xmlChar *) docp->URL);
492 }
493
494 docp->URL = xmlStrdup((const xmlChar *) ZSTR_VAL(str));
495
496 zend_string_release_ex(str, 0);
497 return SUCCESS;
498 }
499
500 /* }}} */
501
502 /* {{{ config DOMConfiguration
503 readonly=yes
504 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-config
505 Since: DOM Level 3
506 */
dom_document_config_read(dom_object * obj,zval * retval)507 int dom_document_config_read(dom_object *obj, zval *retval)
508 {
509 ZVAL_NULL(retval);
510 return SUCCESS;
511 }
512
513 /* }}} */
514
515 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-2141741547
516 Since:
517 */
PHP_METHOD(DOMDocument,createElement)518 PHP_METHOD(DOMDocument, createElement)
519 {
520 zval *id;
521 xmlNode *node;
522 xmlDocPtr docp;
523 dom_object *intern;
524 int ret;
525 size_t name_len, value_len;
526 char *name, *value = NULL;
527
528 id = ZEND_THIS;
529 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &name, &name_len, &value, &value_len) == FAILURE) {
530 RETURN_THROWS();
531 }
532
533 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
534
535 if (xmlValidateName((xmlChar *) name, 0) != 0) {
536 php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
537 RETURN_FALSE;
538 }
539
540 node = xmlNewDocNode(docp, NULL, (xmlChar *) name, (xmlChar *) value);
541 if (!node) {
542 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
543 RETURN_THROWS();
544 }
545
546 DOM_RET_OBJ(node, &ret, intern);
547 }
548 /* }}} end dom_document_create_element */
549
550 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-35CB04B5
551 Since:
552 */
PHP_METHOD(DOMDocument,createDocumentFragment)553 PHP_METHOD(DOMDocument, createDocumentFragment)
554 {
555 zval *id;
556 xmlNode *node;
557 xmlDocPtr docp;
558 dom_object *intern;
559 int ret;
560
561 id = ZEND_THIS;
562 if (zend_parse_parameters_none() == FAILURE) {
563 RETURN_THROWS();
564 }
565
566 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
567
568 node = xmlNewDocFragment(docp);
569 if (!node) {
570 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
571 RETURN_THROWS();
572 }
573
574 DOM_RET_OBJ(node, &ret, intern);
575 }
576 /* }}} end dom_document_create_document_fragment */
577
578 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1975348127
579 Since:
580 */
PHP_METHOD(DOMDocument,createTextNode)581 PHP_METHOD(DOMDocument, createTextNode)
582 {
583 zval *id;
584 xmlNode *node;
585 xmlDocPtr docp;
586 int ret;
587 size_t value_len;
588 dom_object *intern;
589 char *value;
590
591 id = ZEND_THIS;
592 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &value, &value_len) == FAILURE) {
593 RETURN_THROWS();
594 }
595
596 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
597
598 node = xmlNewDocText(docp, (xmlChar *) value);
599 if (!node) {
600 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
601 RETURN_THROWS();
602 }
603
604 DOM_RET_OBJ(node, &ret, intern);
605 }
606 /* }}} end dom_document_create_text_node */
607
608 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1334481328
609 Since:
610 */
PHP_METHOD(DOMDocument,createComment)611 PHP_METHOD(DOMDocument, createComment)
612 {
613 zval *id;
614 xmlNode *node;
615 xmlDocPtr docp;
616 int ret;
617 size_t value_len;
618 dom_object *intern;
619 char *value;
620
621 id = ZEND_THIS;
622 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &value, &value_len) == FAILURE) {
623 RETURN_THROWS();
624 }
625
626 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
627
628 node = xmlNewDocComment(docp, (xmlChar *) value);
629 if (!node) {
630 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
631 RETURN_THROWS();
632 }
633
634 DOM_RET_OBJ(node, &ret, intern);
635 }
636 /* }}} end dom_document_create_comment */
637
638 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D26C0AF8
639 Since:
640 */
PHP_METHOD(DOMDocument,createCDATASection)641 PHP_METHOD(DOMDocument, createCDATASection)
642 {
643 zval *id;
644 xmlNode *node;
645 xmlDocPtr docp;
646 int ret;
647 size_t value_len;
648 dom_object *intern;
649 char *value;
650
651 id = ZEND_THIS;
652 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &value, &value_len) == FAILURE) {
653 RETURN_THROWS();
654 }
655
656 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
657
658 node = xmlNewCDataBlock(docp, (xmlChar *) value, value_len);
659 if (!node) {
660 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
661 RETURN_THROWS();
662 }
663
664 DOM_RET_OBJ(node, &ret, intern);
665 }
666 /* }}} end dom_document_create_cdatasection */
667
668 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-135944439
669 Since:
670 */
PHP_METHOD(DOMDocument,createProcessingInstruction)671 PHP_METHOD(DOMDocument, createProcessingInstruction)
672 {
673 zval *id;
674 xmlNode *node;
675 xmlDocPtr docp;
676 int ret;
677 size_t value_len, name_len = 0;
678 dom_object *intern;
679 char *name, *value = NULL;
680
681 id = ZEND_THIS;
682 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &name, &name_len, &value, &value_len) == FAILURE) {
683 RETURN_THROWS();
684 }
685
686 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
687
688 if (xmlValidateName((xmlChar *) name, 0) != 0) {
689 php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
690 RETURN_FALSE;
691 }
692
693 node = xmlNewPI((xmlChar *) name, (xmlChar *) value);
694 if (!node) {
695 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
696 RETURN_THROWS();
697 }
698
699 node->doc = docp;
700
701 DOM_RET_OBJ(node, &ret, intern);
702 }
703 /* }}} end dom_document_create_processing_instruction */
704
705 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1084891198
706 Since:
707 */
PHP_METHOD(DOMDocument,createAttribute)708 PHP_METHOD(DOMDocument, createAttribute)
709 {
710 zval *id;
711 xmlAttrPtr node;
712 xmlDocPtr docp;
713 int ret;
714 size_t name_len;
715 dom_object *intern;
716 char *name;
717
718 id = ZEND_THIS;
719 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
720 RETURN_THROWS();
721 }
722
723 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
724
725 if (xmlValidateName((xmlChar *) name, 0) != 0) {
726 php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
727 RETURN_FALSE;
728 }
729
730 node = xmlNewDocProp(docp, (xmlChar *) name, NULL);
731 if (!node) {
732 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
733 RETURN_THROWS();
734 }
735
736 DOM_RET_OBJ((xmlNodePtr) node, &ret, intern);
737
738 }
739 /* }}} end dom_document_create_attribute */
740
741 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-392B75AE
742 Since:
743 */
PHP_METHOD(DOMDocument,createEntityReference)744 PHP_METHOD(DOMDocument, createEntityReference)
745 {
746 zval *id;
747 xmlNode *node;
748 xmlDocPtr docp = NULL;
749 dom_object *intern;
750 int ret;
751 size_t name_len;
752 char *name;
753
754 id = ZEND_THIS;
755 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
756 RETURN_THROWS();
757 }
758
759 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
760
761 if (xmlValidateName((xmlChar *) name, 0) != 0) {
762 php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
763 RETURN_FALSE;
764 }
765
766 node = xmlNewReference(docp, (xmlChar *) name);
767 if (!node) {
768 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
769 RETURN_THROWS();
770 }
771
772 DOM_RET_OBJ((xmlNodePtr) node, &ret, intern);
773 }
774 /* }}} end dom_document_create_entity_reference */
775
776 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C9094
777 Since:
778 */
PHP_METHOD(DOMDocument,getElementsByTagName)779 PHP_METHOD(DOMDocument, getElementsByTagName)
780 {
781 zval *id;
782 xmlDocPtr docp;
783 size_t name_len;
784 dom_object *intern, *namednode;
785 char *name;
786 xmlChar *local;
787
788 id = ZEND_THIS;
789 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
790 RETURN_THROWS();
791 }
792
793 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
794
795 php_dom_create_iterator(return_value, DOM_NODELIST);
796 namednode = Z_DOMOBJ_P(return_value);
797 local = xmlCharStrndup(name, name_len);
798 dom_namednode_iter(intern, 0, namednode, NULL, local, NULL);
799 }
800 /* }}} end dom_document_get_elements_by_tag_name */
801
802 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Core-Document-importNode
803 Since: DOM Level 2
804 */
PHP_METHOD(DOMDocument,importNode)805 PHP_METHOD(DOMDocument, importNode)
806 {
807 zval *id, *node;
808 xmlDocPtr docp;
809 xmlNodePtr nodep, retnodep;
810 dom_object *intern, *nodeobj;
811 int ret;
812 bool recursive = 0;
813 /* See http://www.xmlsoft.org/html/libxml-tree.html#xmlDocCopyNode for meaning of values */
814 int extended_recursive;
815
816 id = ZEND_THIS;
817 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &node, dom_node_class_entry, &recursive) == FAILURE) {
818 RETURN_THROWS();
819 }
820
821 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
822
823 DOM_GET_OBJ(nodep, node, xmlNodePtr, nodeobj);
824
825 if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE
826 || nodep->type == XML_DOCUMENT_TYPE_NODE) {
827 php_error_docref(NULL, E_WARNING, "Cannot import: Node Type Not Supported");
828 RETURN_FALSE;
829 }
830
831 if (nodep->doc == docp) {
832 retnodep = nodep;
833 } else {
834 extended_recursive = recursive;
835 if ((recursive == 0) && (nodep->type == XML_ELEMENT_NODE)) {
836 extended_recursive = 2;
837 }
838 retnodep = xmlDocCopyNode(nodep, docp, extended_recursive);
839 if (!retnodep) {
840 RETURN_FALSE;
841 }
842
843 if ((retnodep->type == XML_ATTRIBUTE_NODE) && (nodep->ns != NULL)) {
844 xmlNsPtr nsptr = NULL;
845 xmlNodePtr root = xmlDocGetRootElement(docp);
846
847 nsptr = xmlSearchNsByHref (nodep->doc, root, nodep->ns->href);
848 if (nsptr == NULL) {
849 int errorcode;
850 nsptr = dom_get_ns(root, (char *) nodep->ns->href, &errorcode, (char *) nodep->ns->prefix);
851
852 /* If there is no root, the namespace cannot be attached to it, so we have to attach it to the old list. */
853 if (nsptr != NULL && root == NULL) {
854 dom_set_old_ns(nodep->doc, nsptr);
855 }
856 }
857 retnodep->ns = nsptr;
858 }
859 }
860
861 DOM_RET_OBJ((xmlNodePtr) retnodep, &ret, intern);
862 }
863 /* }}} end dom_document_import_node */
864
865 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS
866 Since: DOM Level 2
867 */
PHP_METHOD(DOMDocument,createElementNS)868 PHP_METHOD(DOMDocument, createElementNS)
869 {
870 zval *id;
871 xmlDocPtr docp;
872 xmlNodePtr nodep = NULL;
873 xmlNsPtr nsptr = NULL;
874 int ret;
875 size_t uri_len = 0, name_len = 0, value_len = 0;
876 char *uri, *name, *value = NULL;
877 char *localname = NULL, *prefix = NULL;
878 int errorcode;
879 dom_object *intern;
880
881 id = ZEND_THIS;
882 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s|s", &uri, &uri_len, &name, &name_len, &value, &value_len) == FAILURE) {
883 RETURN_THROWS();
884 }
885
886 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
887
888 errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
889
890 if (errorcode == 0) {
891 if (xmlValidateName((xmlChar *) localname, 0) == 0) {
892 nodep = xmlNewDocNode(docp, NULL, (xmlChar *) localname, (xmlChar *) value);
893 if (nodep != NULL && uri != NULL) {
894 nsptr = xmlSearchNsByHref(nodep->doc, nodep, (xmlChar *) uri);
895 if (nsptr == NULL) {
896 nsptr = dom_get_ns(nodep, uri, &errorcode, prefix);
897 }
898 xmlSetNs(nodep, nsptr);
899 }
900 } else {
901 errorcode = INVALID_CHARACTER_ERR;
902 }
903 }
904
905 xmlFree(localname);
906 if (prefix != NULL) {
907 xmlFree(prefix);
908 }
909
910 if (errorcode != 0) {
911 if (nodep != NULL) {
912 xmlFreeNode(nodep);
913 }
914 php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
915 RETURN_FALSE;
916 }
917
918 if (nodep == NULL) {
919 RETURN_FALSE;
920 }
921
922
923 nodep->ns = nsptr;
924
925 DOM_RET_OBJ(nodep, &ret, intern);
926 }
927 /* }}} end dom_document_create_element_ns */
928
929 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNS
930 Since: DOM Level 2
931 */
PHP_METHOD(DOMDocument,createAttributeNS)932 PHP_METHOD(DOMDocument, createAttributeNS)
933 {
934 zval *id;
935 xmlDocPtr docp;
936 xmlNodePtr nodep = NULL, root;
937 xmlNsPtr nsptr;
938 int ret;
939 size_t uri_len = 0, name_len = 0;
940 char *uri, *name;
941 char *localname = NULL, *prefix = NULL;
942 dom_object *intern;
943 int errorcode;
944
945 id = ZEND_THIS;
946 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
947 RETURN_THROWS();
948 }
949
950 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
951
952 root = xmlDocGetRootElement(docp);
953 if (root != NULL) {
954 errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
955 if (errorcode == 0) {
956 if (xmlValidateName((xmlChar *) localname, 0) == 0) {
957 nodep = (xmlNodePtr) xmlNewDocProp(docp, (xmlChar *) localname, NULL);
958 if (nodep != NULL && uri_len > 0) {
959 nsptr = xmlSearchNsByHref(nodep->doc, root, (xmlChar *) uri);
960 if (nsptr == NULL) {
961 nsptr = dom_get_ns(root, uri, &errorcode, prefix);
962 }
963 xmlSetNs(nodep, nsptr);
964 }
965 } else {
966 errorcode = INVALID_CHARACTER_ERR;
967 }
968 }
969 } else {
970 php_error_docref(NULL, E_WARNING, "Document Missing Root Element");
971 RETURN_FALSE;
972 }
973
974 xmlFree(localname);
975 if (prefix != NULL) {
976 xmlFree(prefix);
977 }
978
979 if (errorcode != 0) {
980 if (nodep != NULL) {
981 xmlFreeProp((xmlAttrPtr) nodep);
982 }
983 php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
984 RETURN_FALSE;
985 }
986
987 if (nodep == NULL) {
988 RETURN_FALSE;
989 }
990
991 DOM_RET_OBJ(nodep, &ret, intern);
992 }
993 /* }}} end dom_document_create_attribute_ns */
994
995 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBTNNS
996 Since: DOM Level 2
997 */
PHP_METHOD(DOMDocument,getElementsByTagNameNS)998 PHP_METHOD(DOMDocument, getElementsByTagNameNS)
999 {
1000 zval *id;
1001 xmlDocPtr docp;
1002 size_t uri_len, name_len;
1003 dom_object *intern, *namednode;
1004 char *uri, *name;
1005 xmlChar *local, *nsuri;
1006
1007 id = ZEND_THIS;
1008 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1009 RETURN_THROWS();
1010 }
1011
1012 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1013
1014 php_dom_create_iterator(return_value, DOM_NODELIST);
1015 namednode = Z_DOMOBJ_P(return_value);
1016 local = xmlCharStrndup(name, name_len);
1017 nsuri = xmlCharStrndup(uri ? uri : "", uri_len);
1018 dom_namednode_iter(intern, 0, namednode, NULL, local, nsuri);
1019 }
1020 /* }}} end dom_document_get_elements_by_tag_name_ns */
1021
php_dom_is_node_attached(const xmlNode * node)1022 static bool php_dom_is_node_attached(const xmlNode *node)
1023 {
1024 ZEND_ASSERT(node != NULL);
1025 node = node->parent;
1026 while (node != NULL) {
1027 if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) {
1028 return true;
1029 }
1030 node = node->parent;
1031 }
1032 return false;
1033 }
1034
1035 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBId
1036 Since: DOM Level 2
1037 */
PHP_METHOD(DOMDocument,getElementById)1038 PHP_METHOD(DOMDocument, getElementById)
1039 {
1040 zval *id;
1041 xmlDocPtr docp;
1042 xmlAttrPtr attrp;
1043 int ret;
1044 size_t idname_len;
1045 dom_object *intern;
1046 char *idname;
1047
1048 id = ZEND_THIS;
1049 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &idname, &idname_len) == FAILURE) {
1050 RETURN_THROWS();
1051 }
1052
1053 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1054
1055 attrp = xmlGetID(docp, (xmlChar *) idname);
1056
1057 /* From the moment an ID is created, libxml2's behaviour is to cache that element, even
1058 * if that element is not yet attached to the document. Similarly, only upon destruction of
1059 * the element the ID is actually removed by libxml2. Since libxml2 has such behaviour deeply
1060 * ingrained in the library, and uses the cache for various purposes, it seems like a bad
1061 * idea and lost cause to fight it. Instead, we'll simply walk the tree upwards to check
1062 * if the node is attached to the document. */
1063 if (attrp && attrp->parent && php_dom_is_node_attached(attrp->parent)) {
1064 DOM_RET_OBJ((xmlNodePtr) attrp->parent, &ret, intern);
1065 } else {
1066 RETVAL_NULL();
1067 }
1068
1069 }
1070 /* }}} end dom_document_get_element_by_id */
1071
1072 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNode
1073 Since: DOM Level 3
1074 */
PHP_METHOD(DOMDocument,adoptNode)1075 PHP_METHOD(DOMDocument, adoptNode)
1076 {
1077 zval *nodep = NULL;
1078
1079 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &nodep, dom_node_class_entry) == FAILURE) {
1080 RETURN_THROWS();
1081 }
1082
1083 DOM_NOT_IMPLEMENTED();
1084 }
1085 /* }}} end dom_document_adopt_node */
1086
1087 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocument
1088 Since: DOM Level 3
1089 */
PHP_METHOD(DOMDocument,normalizeDocument)1090 PHP_METHOD(DOMDocument, normalizeDocument)
1091 {
1092 zval *id;
1093 xmlDocPtr docp;
1094 dom_object *intern;
1095
1096 id = ZEND_THIS;
1097 if (zend_parse_parameters_none() == FAILURE) {
1098 RETURN_THROWS();
1099 }
1100
1101 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1102
1103 dom_normalize((xmlNodePtr) docp);
1104 }
1105 /* }}} end dom_document_normalize_document */
1106
1107 /* {{{ */
PHP_METHOD(DOMDocument,__construct)1108 PHP_METHOD(DOMDocument, __construct)
1109 {
1110 xmlDoc *docp = NULL, *olddoc;
1111 dom_object *intern;
1112 char *encoding, *version = NULL;
1113 size_t encoding_len = 0, version_len = 0;
1114 int refcount;
1115
1116 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &version, &version_len, &encoding, &encoding_len) == FAILURE) {
1117 RETURN_THROWS();
1118 }
1119
1120 docp = xmlNewDoc((xmlChar *) version);
1121
1122 if (!docp) {
1123 php_dom_throw_error(INVALID_STATE_ERR, 1);
1124 return;
1125 }
1126
1127 if (encoding_len > 0) {
1128 docp->encoding = (const xmlChar *) xmlStrdup((xmlChar *) encoding);
1129 }
1130
1131 intern = Z_DOMOBJ_P(ZEND_THIS);
1132 olddoc = (xmlDocPtr) dom_object_get_node(intern);
1133 if (olddoc != NULL) {
1134 php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1135 refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1136 if (refcount != 0) {
1137 olddoc->_private = NULL;
1138 }
1139 }
1140 intern->document = NULL;
1141 if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp) == -1) {
1142 /* docp is always non-null so php_libxml_increment_doc_ref() never returns -1 */
1143 ZEND_UNREACHABLE();
1144 }
1145 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)docp, (void *)intern);
1146 }
1147 /* }}} end DOMDocument::__construct */
1148
_dom_get_valid_file_path(char * source,char * resolved_path,int resolved_path_len)1149 char *_dom_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len ) /* {{{ */
1150 {
1151 xmlURI *uri;
1152 xmlChar *escsource;
1153 char *file_dest;
1154 int isFileUri = 0;
1155
1156 uri = xmlCreateURI();
1157 if (uri == NULL) {
1158 return NULL;
1159 }
1160 escsource = xmlURIEscapeStr((xmlChar *) source, (xmlChar *) ":");
1161 xmlParseURIReference(uri, (char *) escsource);
1162 xmlFree(escsource);
1163
1164 if (uri->scheme != NULL) {
1165 /* absolute file uris - libxml only supports localhost or empty host */
1166 #ifdef PHP_WIN32
1167 if (strncasecmp(source, "file://",7) == 0 && ':' == source[8]) {
1168 isFileUri = 1;
1169 source += 7;
1170 } else
1171 #endif
1172 if (strncasecmp(source, "file:///",8) == 0) {
1173 isFileUri = 1;
1174 #ifdef PHP_WIN32
1175 source += 8;
1176 #else
1177 source += 7;
1178 #endif
1179 } else if (strncasecmp(source, "file://localhost/",17) == 0) {
1180 isFileUri = 1;
1181 #ifdef PHP_WIN32
1182 source += 17;
1183 #else
1184 source += 16;
1185 #endif
1186 }
1187 }
1188
1189 file_dest = source;
1190
1191 if ((uri->scheme == NULL || isFileUri)) {
1192 /* XXX possible buffer overflow if VCWD_REALPATH does not know size of resolved_path */
1193 if (!VCWD_REALPATH(source, resolved_path) && !expand_filepath(source, resolved_path)) {
1194 xmlFreeURI(uri);
1195 return NULL;
1196 }
1197 file_dest = resolved_path;
1198 }
1199
1200 xmlFreeURI(uri);
1201
1202 return file_dest;
1203 }
1204 /* }}} */
1205
dom_document_parser(zval * id,int mode,char * source,size_t source_len,size_t options)1206 static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t source_len, size_t options) /* {{{ */
1207 {
1208 xmlDocPtr ret;
1209 xmlParserCtxtPtr ctxt = NULL;
1210 dom_doc_propsptr doc_props;
1211 dom_object *intern;
1212 php_libxml_ref_obj *document = NULL;
1213 int validate, recover, resolve_externals, keep_blanks, substitute_ent;
1214 int resolved_path_len;
1215 int old_error_reporting = 0;
1216 char *directory=NULL, resolved_path[MAXPATHLEN + 1];
1217
1218 if (id != NULL) {
1219 intern = Z_DOMOBJ_P(id);
1220 document = intern->document;
1221 }
1222
1223 doc_props = dom_get_doc_props(document);
1224 validate = doc_props->validateonparse;
1225 resolve_externals = doc_props->resolveexternals;
1226 keep_blanks = doc_props->preservewhitespace;
1227 substitute_ent = doc_props->substituteentities;
1228 recover = doc_props->recover;
1229
1230 if (document == NULL) {
1231 efree(doc_props);
1232 }
1233
1234 xmlInitParser();
1235
1236 if (mode == DOM_LOAD_FILE) {
1237 char *file_dest;
1238 if (CHECK_NULL_PATH(source, source_len)) {
1239 zend_value_error("Path to document must not contain any null bytes");
1240 return NULL;
1241 }
1242 file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1243 if (file_dest) {
1244 ctxt = xmlCreateFileParserCtxt(file_dest);
1245 }
1246
1247 } else {
1248 ctxt = xmlCreateMemoryParserCtxt(source, source_len);
1249 }
1250
1251 if (ctxt == NULL) {
1252 return(NULL);
1253 }
1254
1255 /* If loading from memory, we need to set the base directory for the document */
1256 if (mode != DOM_LOAD_FILE) {
1257 #ifdef HAVE_GETCWD
1258 directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
1259 #elif defined(HAVE_GETWD)
1260 directory = VCWD_GETWD(resolved_path);
1261 #endif
1262 if (directory) {
1263 if(ctxt->directory != NULL) {
1264 xmlFree((char *) ctxt->directory);
1265 }
1266 resolved_path_len = strlen(resolved_path);
1267 if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) {
1268 resolved_path[resolved_path_len] = DEFAULT_SLASH;
1269 resolved_path[++resolved_path_len] = '\0';
1270 }
1271 ctxt->directory = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
1272 }
1273 }
1274
1275 ctxt->vctxt.error = php_libxml_ctx_error;
1276 ctxt->vctxt.warning = php_libxml_ctx_warning;
1277
1278 if (ctxt->sax != NULL) {
1279 ctxt->sax->error = php_libxml_ctx_error;
1280 ctxt->sax->warning = php_libxml_ctx_warning;
1281 }
1282
1283 if (validate && ! (options & XML_PARSE_DTDVALID)) {
1284 options |= XML_PARSE_DTDVALID;
1285 }
1286 if (resolve_externals && ! (options & XML_PARSE_DTDATTR)) {
1287 options |= XML_PARSE_DTDATTR;
1288 }
1289 if (substitute_ent && ! (options & XML_PARSE_NOENT)) {
1290 options |= XML_PARSE_NOENT;
1291 }
1292 if (keep_blanks == 0 && ! (options & XML_PARSE_NOBLANKS)) {
1293 options |= XML_PARSE_NOBLANKS;
1294 }
1295 if (recover) {
1296 options |= XML_PARSE_RECOVER;
1297 }
1298
1299 php_libxml_sanitize_parse_ctxt_options(ctxt);
1300 xmlCtxtUseOptions(ctxt, options);
1301
1302 if (recover) {
1303 old_error_reporting = EG(error_reporting);
1304 EG(error_reporting) = old_error_reporting | E_WARNING;
1305 }
1306
1307 xmlParseDocument(ctxt);
1308
1309 if (ctxt->wellFormed || recover) {
1310 ret = ctxt->myDoc;
1311 if (recover) {
1312 EG(error_reporting) = old_error_reporting;
1313 }
1314 /* If loading from memory, set the base reference uri for the document */
1315 if (ret && ret->URL == NULL && ctxt->directory != NULL) {
1316 ret->URL = xmlStrdup((xmlChar *) ctxt->directory);
1317 }
1318 } else {
1319 ret = NULL;
1320 xmlFreeDoc(ctxt->myDoc);
1321 ctxt->myDoc = NULL;
1322 }
1323
1324 xmlFreeParserCtxt(ctxt);
1325
1326 return(ret);
1327 }
1328 /* }}} */
1329
1330 /* {{{ static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) */
dom_parse_document(INTERNAL_FUNCTION_PARAMETERS,int mode)1331 static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) {
1332 zval *id;
1333 xmlDoc *docp = NULL, *newdoc;
1334 dom_doc_propsptr doc_prop;
1335 dom_object *intern;
1336 char *source;
1337 size_t source_len;
1338 int refcount, ret;
1339 zend_long options = 0;
1340
1341 id = getThis();
1342 if (id != NULL && ! instanceof_function(Z_OBJCE_P(id), dom_document_class_entry)) {
1343 id = NULL;
1344 }
1345
1346 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
1347 RETURN_THROWS();
1348 }
1349
1350 if (!source_len) {
1351 zend_argument_value_error(1, "must not be empty");
1352 RETURN_THROWS();
1353 }
1354 if (ZEND_SIZE_T_INT_OVFL(source_len)) {
1355 php_error_docref(NULL, E_WARNING, "Input string is too long");
1356 RETURN_FALSE;
1357 }
1358 if (ZEND_LONG_EXCEEDS_INT(options)) {
1359 php_error_docref(NULL, E_WARNING, "Invalid options");
1360 RETURN_FALSE;
1361 }
1362
1363 newdoc = dom_document_parser(id, mode, source, source_len, options);
1364
1365 if (!newdoc)
1366 RETURN_FALSE;
1367
1368 if (id != NULL) {
1369 intern = Z_DOMOBJ_P(id);
1370 if (intern != NULL) {
1371 docp = (xmlDocPtr) dom_object_get_node(intern);
1372 doc_prop = NULL;
1373 if (docp != NULL) {
1374 php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1375 doc_prop = intern->document->doc_props;
1376 intern->document->doc_props = NULL;
1377 refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1378 if (refcount != 0) {
1379 docp->_private = NULL;
1380 }
1381 }
1382 intern->document = NULL;
1383 if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc) == -1) {
1384 RETURN_FALSE;
1385 }
1386 intern->document->doc_props = doc_prop;
1387 }
1388
1389 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
1390
1391 RETURN_TRUE;
1392 } else {
1393 DOM_RET_OBJ((xmlNodePtr) newdoc, &ret, NULL);
1394 }
1395 }
1396 /* }}} end dom_parser_document */
1397
1398 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-load
1399 Since: DOM Level 3
1400 */
PHP_METHOD(DOMDocument,load)1401 PHP_METHOD(DOMDocument, load)
1402 {
1403 dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1404 }
1405 /* }}} end dom_document_load */
1406
1407 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadXML
1408 Since: DOM Level 3
1409 */
PHP_METHOD(DOMDocument,loadXML)1410 PHP_METHOD(DOMDocument, loadXML)
1411 {
1412 dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1413 }
1414 /* }}} end dom_document_loadxml */
1415
1416 /* {{{ Convenience method to save to file */
PHP_METHOD(DOMDocument,save)1417 PHP_METHOD(DOMDocument, save)
1418 {
1419 zval *id;
1420 xmlDoc *docp;
1421 size_t file_len = 0;
1422 int bytes, format, saveempty = 0;
1423 dom_object *intern;
1424 dom_doc_propsptr doc_props;
1425 char *file;
1426 zend_long options = 0;
1427
1428 id = ZEND_THIS;
1429 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &file, &file_len, &options) == FAILURE) {
1430 RETURN_THROWS();
1431 }
1432
1433 if (file_len == 0) {
1434 zend_argument_value_error(1, "must not be empty");
1435 RETURN_THROWS();
1436 }
1437
1438 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1439
1440 /* encoding handled by property on doc */
1441
1442 doc_props = dom_get_doc_props(intern->document);
1443 format = doc_props->formatoutput;
1444 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1445 saveempty = xmlSaveNoEmptyTags;
1446 xmlSaveNoEmptyTags = 1;
1447 }
1448 bytes = xmlSaveFormatFileEnc(file, docp, NULL, format);
1449 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1450 xmlSaveNoEmptyTags = saveempty;
1451 }
1452 if (bytes == -1) {
1453 RETURN_FALSE;
1454 }
1455 RETURN_LONG(bytes);
1456 }
1457 /* }}} end dom_document_save */
1458
1459 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML
1460 Since: DOM Level 3
1461 */
PHP_METHOD(DOMDocument,saveXML)1462 PHP_METHOD(DOMDocument, saveXML)
1463 {
1464 zval *id, *nodep = NULL;
1465 xmlDoc *docp;
1466 xmlNode *node;
1467 xmlBufferPtr buf;
1468 xmlChar *mem;
1469 dom_object *intern, *nodeobj;
1470 dom_doc_propsptr doc_props;
1471 int size, format, saveempty = 0;
1472 zend_long options = 0;
1473
1474 id = ZEND_THIS;
1475 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!l", &nodep, dom_node_class_entry, &options) == FAILURE) {
1476 RETURN_THROWS();
1477 }
1478
1479 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1480
1481 doc_props = dom_get_doc_props(intern->document);
1482 format = doc_props->formatoutput;
1483
1484 if (nodep != NULL) {
1485 /* Dump contents of Node */
1486 DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
1487 if (node->doc != docp) {
1488 php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
1489 RETURN_FALSE;
1490 }
1491 buf = xmlBufferCreate();
1492 if (!buf) {
1493 php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
1494 RETURN_FALSE;
1495 }
1496 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1497 saveempty = xmlSaveNoEmptyTags;
1498 xmlSaveNoEmptyTags = 1;
1499 }
1500 xmlNodeDump(buf, docp, node, 0, format);
1501 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1502 xmlSaveNoEmptyTags = saveempty;
1503 }
1504 mem = (xmlChar*) xmlBufferContent(buf);
1505 if (!mem) {
1506 xmlBufferFree(buf);
1507 RETURN_FALSE;
1508 }
1509 RETVAL_STRING((char *) mem);
1510 xmlBufferFree(buf);
1511 } else {
1512 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1513 saveempty = xmlSaveNoEmptyTags;
1514 xmlSaveNoEmptyTags = 1;
1515 }
1516 /* Encoding is handled from the encoding property set on the document */
1517 xmlDocDumpFormatMemory(docp, &mem, &size, format);
1518 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1519 xmlSaveNoEmptyTags = saveempty;
1520 }
1521 if (!size || !mem) {
1522 RETURN_FALSE;
1523 }
1524 RETVAL_STRINGL((char *) mem, size);
1525 xmlFree(mem);
1526 }
1527 }
1528 /* }}} end dom_document_savexml */
1529
php_dom_free_xinclude_node(xmlNodePtr cur)1530 static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) /* {{{ */
1531 {
1532 xmlNodePtr xincnode;
1533
1534 xincnode = cur;
1535 cur = cur->next;
1536 xmlUnlinkNode(xincnode);
1537 php_libxml_node_free_resource(xincnode);
1538
1539 return cur;
1540 }
1541 /* }}} */
1542
php_dom_remove_xinclude_nodes(xmlNodePtr cur)1543 static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
1544 {
1545 while(cur) {
1546 if (cur->type == XML_XINCLUDE_START) {
1547 cur = php_dom_free_xinclude_node(cur);
1548
1549 /* XML_XINCLUDE_END node will be a sibling of XML_XINCLUDE_START */
1550 while(cur && cur->type != XML_XINCLUDE_END) {
1551 /* remove xinclude processing nodes from recursive xincludes */
1552 if (cur->type == XML_ELEMENT_NODE) {
1553 php_dom_remove_xinclude_nodes(cur->children);
1554 }
1555 cur = cur->next;
1556 }
1557
1558 if (cur && cur->type == XML_XINCLUDE_END) {
1559 cur = php_dom_free_xinclude_node(cur);
1560 }
1561 } else {
1562 if (cur->type == XML_ELEMENT_NODE) {
1563 php_dom_remove_xinclude_nodes(cur->children);
1564 }
1565 cur = cur->next;
1566 }
1567 }
1568 }
1569 /* }}} */
1570
1571 /* Backported from master branch xml_common.h */
php_dom_next_in_tree_order(const xmlNode * nodep,const xmlNode * basep)1572 static zend_always_inline xmlNodePtr php_dom_next_in_tree_order(const xmlNode *nodep, const xmlNode *basep)
1573 {
1574 if (nodep->type == XML_ELEMENT_NODE && nodep->children) {
1575 return nodep->children;
1576 }
1577
1578 if (nodep->next) {
1579 return nodep->next;
1580 } else {
1581 /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
1582 do {
1583 nodep = nodep->parent;
1584 if (nodep == basep) {
1585 return NULL;
1586 }
1587 } while (nodep->next == NULL);
1588 return nodep->next;
1589 }
1590 }
1591
dom_xinclude_strip_references(xmlNodePtr basep)1592 static void dom_xinclude_strip_references(xmlNodePtr basep)
1593 {
1594 php_libxml_node_free_resource(basep);
1595
1596 xmlNodePtr current = basep->children;
1597
1598 while (current) {
1599 php_libxml_node_free_resource(current);
1600 current = php_dom_next_in_tree_order(current, basep);
1601 }
1602 }
1603
1604 /* See GH-14702.
1605 * We have to remove userland references to xinclude fallback nodes because libxml2 will make clones of these
1606 * and remove the original nodes. If the originals are removed while there are still userland references
1607 * this will cause memory corruption. */
dom_xinclude_strip_fallback_references(const xmlNode * basep)1608 static void dom_xinclude_strip_fallback_references(const xmlNode *basep)
1609 {
1610 xmlNodePtr current = basep->children;
1611
1612 while (current) {
1613 if (current->type == XML_ELEMENT_NODE && current->ns != NULL && current->_private != NULL
1614 && xmlStrEqual(current->name, XINCLUDE_FALLBACK)
1615 && (xmlStrEqual(current->ns->href, XINCLUDE_NS) || xmlStrEqual(current->ns->href, XINCLUDE_OLD_NS))) {
1616 dom_xinclude_strip_references(current);
1617 }
1618
1619 current = php_dom_next_in_tree_order(current, basep);
1620 }
1621 }
1622
1623 /* {{{ Substitutues xincludes in a DomDocument */
PHP_METHOD(DOMDocument,xinclude)1624 PHP_METHOD(DOMDocument, xinclude)
1625 {
1626 zval *id;
1627 xmlDoc *docp;
1628 xmlNodePtr root;
1629 zend_long flags = 0;
1630 int err;
1631 dom_object *intern;
1632
1633 id = ZEND_THIS;
1634 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
1635 RETURN_THROWS();
1636 }
1637
1638 if (ZEND_LONG_EXCEEDS_INT(flags)) {
1639 php_error_docref(NULL, E_WARNING, "Invalid flags");
1640 RETURN_FALSE;
1641 }
1642
1643 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1644
1645 dom_xinclude_strip_fallback_references((const xmlNode *) docp);
1646
1647 PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
1648 err = xmlXIncludeProcessFlags(docp, (int)flags);
1649 PHP_LIBXML_RESTORE_GLOBALS(xinclude);
1650
1651 /* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
1652 are added via xmlXIncludeProcess to mark beginning and ending of xincluded document
1653 but are not wanted in resulting document - must be done even if err as it could fail after
1654 having processed some xincludes */
1655 root = (xmlNodePtr) docp->children;
1656 while(root && root->type != XML_ELEMENT_NODE && root->type != XML_XINCLUDE_START) {
1657 root = root->next;
1658 }
1659 if (root) {
1660 php_dom_remove_xinclude_nodes(root);
1661 }
1662
1663 if (err) {
1664 RETVAL_LONG(err);
1665 } else {
1666 RETVAL_FALSE;
1667 }
1668
1669 }
1670 /* }}} */
1671
1672 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,validate)1673 PHP_METHOD(DOMDocument, validate)
1674 {
1675 zval *id;
1676 xmlDoc *docp;
1677 dom_object *intern;
1678 xmlValidCtxt *cvp;
1679
1680 id = ZEND_THIS;
1681 if (zend_parse_parameters_none() == FAILURE) {
1682 RETURN_THROWS();
1683 }
1684
1685 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1686
1687 PHP_LIBXML_SANITIZE_GLOBALS(validate);
1688 cvp = xmlNewValidCtxt();
1689
1690 cvp->userData = NULL;
1691 cvp->error = (xmlValidityErrorFunc) php_libxml_error_handler;
1692 cvp->warning = (xmlValidityErrorFunc) php_libxml_error_handler;
1693
1694 if (xmlValidateDocument(cvp, docp)) {
1695 RETVAL_TRUE;
1696 } else {
1697 RETVAL_FALSE;
1698 }
1699 PHP_LIBXML_RESTORE_GLOBALS(validate);
1700
1701 xmlFreeValidCtxt(cvp);
1702
1703 }
1704 /* }}} */
1705
1706 #ifdef LIBXML_SCHEMAS_ENABLED
_dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1707 static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1708 {
1709 zval *id;
1710 xmlDoc *docp;
1711 dom_object *intern;
1712 char *source = NULL, *valid_file = NULL;
1713 size_t source_len = 0;
1714 int valid_opts = 0;
1715 zend_long flags = 0;
1716 xmlSchemaParserCtxtPtr parser;
1717 xmlSchemaPtr sptr;
1718 xmlSchemaValidCtxtPtr vptr;
1719 int is_valid;
1720 char resolved_path[MAXPATHLEN + 1];
1721
1722 id = ZEND_THIS;
1723 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &flags) == FAILURE) {
1724 RETURN_THROWS();
1725 }
1726
1727 if (!source_len) {
1728 zend_argument_value_error(1, "must not be empty");
1729 RETURN_THROWS();
1730 }
1731
1732 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1733
1734 PHP_LIBXML_SANITIZE_GLOBALS(new_parser_ctxt);
1735
1736 switch (type) {
1737 case DOM_LOAD_FILE:
1738 if (CHECK_NULL_PATH(source, source_len)) {
1739 PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1740 zend_argument_value_error(1, "must not contain any null bytes");
1741 RETURN_THROWS();
1742 }
1743 valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1744 if (!valid_file) {
1745 PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1746 php_error_docref(NULL, E_WARNING, "Invalid Schema file source");
1747 RETURN_FALSE;
1748 }
1749 parser = xmlSchemaNewParserCtxt(valid_file);
1750 break;
1751 case DOM_LOAD_STRING:
1752 parser = xmlSchemaNewMemParserCtxt(source, source_len);
1753 /* If loading from memory, we need to set the base directory for the document
1754 but it is not apparent how to do that for schema's */
1755 break;
1756 default:
1757 return;
1758 }
1759
1760 xmlSchemaSetParserErrors(parser,
1761 (xmlSchemaValidityErrorFunc) php_libxml_error_handler,
1762 (xmlSchemaValidityWarningFunc) php_libxml_error_handler,
1763 parser);
1764 sptr = xmlSchemaParse(parser);
1765 xmlSchemaFreeParserCtxt(parser);
1766 PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1767 if (!sptr) {
1768 if (!EG(exception)) {
1769 php_error_docref(NULL, E_WARNING, "Invalid Schema");
1770 }
1771 RETURN_FALSE;
1772 }
1773
1774 docp = (xmlDocPtr) dom_object_get_node(intern);
1775
1776 vptr = xmlSchemaNewValidCtxt(sptr);
1777 if (!vptr) {
1778 xmlSchemaFree(sptr);
1779 zend_throw_error(NULL, "Invalid Schema Validation Context");
1780 RETURN_THROWS();
1781 }
1782
1783 if (flags & XML_SCHEMA_VAL_VC_I_CREATE) {
1784 valid_opts |= XML_SCHEMA_VAL_VC_I_CREATE;
1785 }
1786
1787 PHP_LIBXML_SANITIZE_GLOBALS(validate);
1788 xmlSchemaSetValidOptions(vptr, valid_opts);
1789 xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
1790 is_valid = xmlSchemaValidateDoc(vptr, docp);
1791 xmlSchemaFree(sptr);
1792 xmlSchemaFreeValidCtxt(vptr);
1793 PHP_LIBXML_RESTORE_GLOBALS(validate);
1794
1795 if (is_valid == 0) {
1796 RETURN_TRUE;
1797 } else {
1798 RETURN_FALSE;
1799 }
1800 }
1801 /* }}} */
1802
1803 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidate)1804 PHP_METHOD(DOMDocument, schemaValidate)
1805 {
1806 _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1807 }
1808 /* }}} end dom_document_schema_validate_file */
1809
1810 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidateSource)1811 PHP_METHOD(DOMDocument, schemaValidateSource)
1812 {
1813 _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1814 }
1815 /* }}} end dom_document_schema_validate */
1816
_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1817 static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1818 {
1819 zval *id;
1820 xmlDoc *docp;
1821 dom_object *intern;
1822 char *source = NULL, *valid_file = NULL;
1823 size_t source_len = 0;
1824 xmlRelaxNGParserCtxtPtr parser;
1825 xmlRelaxNGPtr sptr;
1826 xmlRelaxNGValidCtxtPtr vptr;
1827 int is_valid;
1828 char resolved_path[MAXPATHLEN + 1];
1829
1830 id = ZEND_THIS;
1831 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &source, &source_len) == FAILURE) {
1832 RETURN_THROWS();
1833 }
1834
1835 if (!source_len) {
1836 zend_argument_value_error(1, "must not be empty");
1837 RETURN_THROWS();
1838 }
1839
1840 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1841
1842 switch (type) {
1843 case DOM_LOAD_FILE:
1844 if (CHECK_NULL_PATH(source, source_len)) {
1845 zend_argument_value_error(1, "must not contain any null bytes");
1846 RETURN_THROWS();
1847 }
1848 valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1849 if (!valid_file) {
1850 php_error_docref(NULL, E_WARNING, "Invalid RelaxNG file source");
1851 RETURN_FALSE;
1852 }
1853 parser = xmlRelaxNGNewParserCtxt(valid_file);
1854 break;
1855 case DOM_LOAD_STRING:
1856 parser = xmlRelaxNGNewMemParserCtxt(source, source_len);
1857 /* If loading from memory, we need to set the base directory for the document
1858 but it is not apparent how to do that for schema's */
1859 break;
1860 default:
1861 return;
1862 }
1863
1864 PHP_LIBXML_SANITIZE_GLOBALS(parse);
1865 xmlRelaxNGSetParserErrors(parser,
1866 (xmlRelaxNGValidityErrorFunc) php_libxml_error_handler,
1867 (xmlRelaxNGValidityWarningFunc) php_libxml_error_handler,
1868 parser);
1869 sptr = xmlRelaxNGParse(parser);
1870 xmlRelaxNGFreeParserCtxt(parser);
1871 PHP_LIBXML_RESTORE_GLOBALS(parse);
1872 if (!sptr) {
1873 php_error_docref(NULL, E_WARNING, "Invalid RelaxNG");
1874 RETURN_FALSE;
1875 }
1876
1877 docp = (xmlDocPtr) dom_object_get_node(intern);
1878
1879 vptr = xmlRelaxNGNewValidCtxt(sptr);
1880 if (!vptr) {
1881 xmlRelaxNGFree(sptr);
1882 zend_throw_error(NULL, "Invalid RelaxNG Validation Context");
1883 RETURN_THROWS();
1884 }
1885
1886 xmlRelaxNGSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
1887 is_valid = xmlRelaxNGValidateDoc(vptr, docp);
1888 xmlRelaxNGFree(sptr);
1889 xmlRelaxNGFreeValidCtxt(vptr);
1890
1891 if (is_valid == 0) {
1892 RETURN_TRUE;
1893 } else {
1894 RETURN_FALSE;
1895 }
1896 }
1897 /* }}} */
1898
1899 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidate)1900 PHP_METHOD(DOMDocument, relaxNGValidate)
1901 {
1902 _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1903 }
1904 /* }}} end dom_document_relaxNG_validate_file */
1905
1906 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidateSource)1907 PHP_METHOD(DOMDocument, relaxNGValidateSource)
1908 {
1909 _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1910 }
1911 /* }}} end dom_document_relaxNG_validate_xml */
1912
1913 #endif
1914
1915 #ifdef LIBXML_HTML_ENABLED
1916
dom_load_html(INTERNAL_FUNCTION_PARAMETERS,int mode)1917 static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
1918 {
1919 zval *id;
1920 xmlDoc *docp = NULL, *newdoc;
1921 dom_object *intern;
1922 dom_doc_propsptr doc_prop;
1923 char *source;
1924 size_t source_len;
1925 int refcount, ret;
1926 zend_long options = 0;
1927 htmlParserCtxtPtr ctxt;
1928
1929 id = getThis();
1930
1931 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
1932 RETURN_THROWS();
1933 }
1934
1935 if (!source_len) {
1936 zend_argument_value_error(1, "must not be empty");
1937 RETURN_THROWS();
1938 }
1939
1940 if (ZEND_LONG_EXCEEDS_INT(options)) {
1941 php_error_docref(NULL, E_WARNING, "Invalid options");
1942 RETURN_FALSE;
1943 }
1944
1945 if (mode == DOM_LOAD_FILE) {
1946 if (CHECK_NULL_PATH(source, source_len)) {
1947 zend_argument_value_error(1, "must not contain any null bytes");
1948 RETURN_THROWS();
1949 }
1950 ctxt = htmlCreateFileParserCtxt(source, NULL);
1951 } else {
1952 if (ZEND_SIZE_T_INT_OVFL(source_len)) {
1953 php_error_docref(NULL, E_WARNING, "Input string is too long");
1954 RETURN_FALSE;
1955 }
1956 ctxt = htmlCreateMemoryParserCtxt(source, (int)source_len);
1957 }
1958
1959 if (!ctxt) {
1960 RETURN_FALSE;
1961 }
1962
1963
1964 ctxt->vctxt.error = php_libxml_ctx_error;
1965 ctxt->vctxt.warning = php_libxml_ctx_warning;
1966 if (ctxt->sax != NULL) {
1967 ctxt->sax->error = php_libxml_ctx_error;
1968 ctxt->sax->warning = php_libxml_ctx_warning;
1969 }
1970 php_libxml_sanitize_parse_ctxt_options(ctxt);
1971 if (options) {
1972 htmlCtxtUseOptions(ctxt, (int)options);
1973 }
1974 htmlParseDocument(ctxt);
1975 newdoc = ctxt->myDoc;
1976 htmlFreeParserCtxt(ctxt);
1977
1978 if (!newdoc)
1979 RETURN_FALSE;
1980
1981 if (id != NULL && instanceof_function(Z_OBJCE_P(id), dom_document_class_entry)) {
1982 intern = Z_DOMOBJ_P(id);
1983 if (intern != NULL) {
1984 docp = (xmlDocPtr) dom_object_get_node(intern);
1985 doc_prop = NULL;
1986 if (docp != NULL) {
1987 php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1988 doc_prop = intern->document->doc_props;
1989 intern->document->doc_props = NULL;
1990 refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1991 if (refcount != 0) {
1992 docp->_private = NULL;
1993 }
1994 }
1995 intern->document = NULL;
1996 if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc) == -1) {
1997 RETURN_FALSE;
1998 }
1999 intern->document->doc_props = doc_prop;
2000 }
2001
2002 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
2003
2004 RETURN_TRUE;
2005 } else {
2006 DOM_RET_OBJ((xmlNodePtr) newdoc, &ret, NULL);
2007 }
2008 }
2009 /* }}} */
2010
2011 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTMLFile)2012 PHP_METHOD(DOMDocument, loadHTMLFile)
2013 {
2014 dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
2015 }
2016 /* }}} end dom_document_load_html_file */
2017
2018 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTML)2019 PHP_METHOD(DOMDocument, loadHTML)
2020 {
2021 dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
2022 }
2023 /* }}} end dom_document_load_html */
2024
2025 /* {{{ Convenience method to save to file as html */
PHP_METHOD(DOMDocument,saveHTMLFile)2026 PHP_METHOD(DOMDocument, saveHTMLFile)
2027 {
2028 zval *id;
2029 xmlDoc *docp;
2030 size_t file_len;
2031 int bytes, format;
2032 dom_object *intern;
2033 dom_doc_propsptr doc_props;
2034 char *file;
2035 const char *encoding;
2036
2037 id = ZEND_THIS;
2038 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &file, &file_len) == FAILURE) {
2039 RETURN_THROWS();
2040 }
2041
2042 if (file_len == 0) {
2043 zend_argument_value_error(1, "must not be empty");
2044 RETURN_THROWS();
2045 }
2046
2047 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2048
2049
2050 encoding = (const char *) htmlGetMetaEncoding(docp);
2051
2052 doc_props = dom_get_doc_props(intern->document);
2053 format = doc_props->formatoutput;
2054 bytes = htmlSaveFileFormat(file, docp, encoding, format);
2055
2056 if (bytes == -1) {
2057 RETURN_FALSE;
2058 }
2059 RETURN_LONG(bytes);
2060 }
2061 /* }}} end dom_document_save_html_file */
2062
2063 /* {{{ Convenience method to output as html */
PHP_METHOD(DOMDocument,saveHTML)2064 PHP_METHOD(DOMDocument, saveHTML)
2065 {
2066 zval *id, *nodep = NULL;
2067 xmlDoc *docp;
2068 xmlNode *node;
2069 xmlOutputBufferPtr outBuf;
2070 xmlBufferPtr buf;
2071 dom_object *intern, *nodeobj;
2072 xmlChar *mem = NULL;
2073 int format;
2074 dom_doc_propsptr doc_props;
2075
2076 id = ZEND_THIS;
2077 if (zend_parse_parameters(ZEND_NUM_ARGS(),
2078 "|O!", &nodep, dom_node_class_entry)
2079 == FAILURE) {
2080 RETURN_THROWS();
2081 }
2082
2083 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2084
2085 doc_props = dom_get_doc_props(intern->document);
2086 format = doc_props->formatoutput;
2087
2088 if (nodep != NULL) {
2089 /* Dump contents of Node */
2090 DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
2091 if (node->doc != docp) {
2092 php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
2093 RETURN_FALSE;
2094 }
2095
2096 buf = xmlBufferCreate();
2097 if (!buf) {
2098 php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
2099 RETURN_FALSE;
2100 }
2101 outBuf = xmlOutputBufferCreateBuffer(buf, NULL);
2102 if (!outBuf) {
2103 xmlBufferFree(buf);
2104 php_error_docref(NULL, E_WARNING, "Could not fetch output buffer");
2105 RETURN_FALSE;
2106 }
2107
2108 if (node->type == XML_DOCUMENT_FRAG_NODE) {
2109 for (node = node->children; node; node = node->next) {
2110 htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2111 if (outBuf->error) {
2112 break;
2113 }
2114 }
2115 } else {
2116 htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2117 }
2118 if (!outBuf->error) {
2119 xmlOutputBufferFlush(outBuf);
2120 mem = (xmlChar*) xmlBufferContent(buf);
2121 if (!mem) {
2122 RETVAL_FALSE;
2123 } else {
2124 int size = xmlBufferLength(buf);
2125 RETVAL_STRINGL((const char*) mem, size);
2126 }
2127 } else {
2128 php_error_docref(NULL, E_WARNING, "Error dumping HTML node");
2129 RETVAL_FALSE;
2130 }
2131 xmlOutputBufferClose(outBuf);
2132 xmlBufferFree(buf);
2133 } else {
2134 int size = 0;
2135 htmlDocDumpMemoryFormat(docp, &mem, &size, format);
2136 if (!size || !mem) {
2137 RETVAL_FALSE;
2138 } else {
2139 RETVAL_STRINGL((const char*) mem, size);
2140 }
2141 if (mem)
2142 xmlFree(mem);
2143 }
2144
2145 }
2146 /* }}} end dom_document_save_html */
2147
2148 #endif /* defined(LIBXML_HTML_ENABLED) */
2149
2150 /* {{{ Register extended class used to create base node type */
PHP_METHOD(DOMDocument,registerNodeClass)2151 PHP_METHOD(DOMDocument, registerNodeClass)
2152 {
2153 zval *id;
2154 xmlDoc *docp;
2155 zend_class_entry *basece = dom_node_class_entry, *ce = NULL;
2156 dom_object *intern;
2157
2158 id = ZEND_THIS;
2159 if (zend_parse_parameters(ZEND_NUM_ARGS(), "CC!", &basece, &ce) == FAILURE) {
2160 RETURN_THROWS();
2161 }
2162
2163 if (ce == NULL || instanceof_function(ce, basece)) {
2164 if (UNEXPECTED(ce != NULL && (ce->ce_flags & ZEND_ACC_ABSTRACT))) {
2165 zend_argument_value_error(2, "must not be an abstract class");
2166 RETURN_THROWS();
2167 }
2168 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2169 dom_set_doc_classmap(intern->document, basece, ce);
2170 RETURN_TRUE;
2171 }
2172
2173 zend_argument_error(NULL, 2, "must be a class name derived from %s or null, %s given", ZSTR_VAL(basece->name), ZSTR_VAL(ce->name));
2174 }
2175 /* }}} */
2176
2177 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-append
2178 Since: DOM Living Standard (DOM4)
2179 */
PHP_METHOD(DOMDocument,append)2180 PHP_METHOD(DOMDocument, append)
2181 {
2182 int argc = 0;
2183 zval *args, *id;
2184 dom_object *intern;
2185 xmlNode *context;
2186
2187 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
2188 RETURN_THROWS();
2189 }
2190
2191 id = ZEND_THIS;
2192 DOM_GET_OBJ(context, id, xmlNodePtr, intern);
2193
2194 dom_parent_node_append(intern, args, argc);
2195 }
2196 /* }}} */
2197
2198 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
2199 Since: DOM Living Standard (DOM4)
2200 */
PHP_METHOD(DOMDocument,prepend)2201 PHP_METHOD(DOMDocument, prepend)
2202 {
2203 int argc = 0;
2204 zval *args, *id;
2205 dom_object *intern;
2206 xmlNode *context;
2207
2208 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
2209 RETURN_THROWS();
2210 }
2211
2212 id = ZEND_THIS;
2213 DOM_GET_OBJ(context, id, xmlNodePtr, intern);
2214
2215 dom_parent_node_prepend(intern, args, argc);
2216 }
2217 /* }}} */
2218
2219 #endif /* HAVE_LIBXML && HAVE_DOM */
2220