xref: /PHP-8.2/ext/dom/document.c (revision 4fe82131)
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