xref: /PHP-8.1/ext/dom/document.c (revision 6a76e5d0)
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 			xmlSetNs(retnodep, nsptr);
853 		}
854 	}
855 
856 	DOM_RET_OBJ((xmlNodePtr) retnodep, &ret, intern);
857 }
858 /* }}} end dom_document_import_node */
859 
860 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS
861 Since: DOM Level 2
862 */
PHP_METHOD(DOMDocument,createElementNS)863 PHP_METHOD(DOMDocument, createElementNS)
864 {
865 	zval *id;
866 	xmlDocPtr docp;
867 	xmlNodePtr nodep = NULL;
868 	xmlNsPtr nsptr = NULL;
869 	int ret;
870 	size_t uri_len = 0, name_len = 0, value_len = 0;
871 	char *uri, *name, *value = NULL;
872 	char *localname = NULL, *prefix = NULL;
873 	int errorcode;
874 	dom_object *intern;
875 
876 	id = ZEND_THIS;
877 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s|s", &uri, &uri_len, &name, &name_len, &value, &value_len) == FAILURE) {
878 		RETURN_THROWS();
879 	}
880 
881 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
882 
883 	errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
884 
885 	if (errorcode == 0) {
886 		if (xmlValidateName((xmlChar *) localname, 0) == 0) {
887 			nodep = xmlNewDocNode(docp, NULL, (xmlChar *) localname, (xmlChar *) value);
888 			if (nodep != NULL && uri != NULL) {
889 				nsptr = xmlSearchNsByHref(nodep->doc, nodep, (xmlChar *) uri);
890 				if (nsptr == NULL) {
891 					nsptr = dom_get_ns(nodep, uri, &errorcode, prefix);
892 				}
893 				xmlSetNs(nodep, nsptr);
894 			}
895 		} else {
896 			errorcode = INVALID_CHARACTER_ERR;
897 		}
898 	}
899 
900 	xmlFree(localname);
901 	if (prefix != NULL) {
902 		xmlFree(prefix);
903 	}
904 
905 	if (errorcode != 0) {
906 		if (nodep != NULL) {
907 			xmlFreeNode(nodep);
908 		}
909 		php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
910 		RETURN_FALSE;
911 	}
912 
913 	if (nodep == NULL) {
914 		RETURN_FALSE;
915 	}
916 
917 
918 	nodep->ns = nsptr;
919 
920 	DOM_RET_OBJ(nodep, &ret, intern);
921 }
922 /* }}} end dom_document_create_element_ns */
923 
924 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNS
925 Since: DOM Level 2
926 */
PHP_METHOD(DOMDocument,createAttributeNS)927 PHP_METHOD(DOMDocument, createAttributeNS)
928 {
929 	zval *id;
930 	xmlDocPtr docp;
931 	xmlNodePtr nodep = NULL, root;
932 	xmlNsPtr nsptr;
933 	int ret;
934 	size_t uri_len = 0, name_len = 0;
935 	char *uri, *name;
936 	char *localname = NULL, *prefix = NULL;
937 	dom_object *intern;
938 	int errorcode;
939 
940 	id = ZEND_THIS;
941 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
942 		RETURN_THROWS();
943 	}
944 
945 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
946 
947 	root = xmlDocGetRootElement(docp);
948 	if (root != NULL) {
949 		errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
950 		if (errorcode == 0) {
951 			if (xmlValidateName((xmlChar *) localname, 0) == 0) {
952 				nodep = (xmlNodePtr) xmlNewDocProp(docp, (xmlChar *) localname, NULL);
953 				if (nodep != NULL && uri_len > 0) {
954 					nsptr = xmlSearchNsByHref(nodep->doc, root, (xmlChar *) uri);
955 					if (nsptr == NULL) {
956 						nsptr = dom_get_ns(root, uri, &errorcode, prefix);
957 					}
958 					xmlSetNs(nodep, nsptr);
959 				}
960 			} else {
961 				errorcode = INVALID_CHARACTER_ERR;
962 			}
963 		}
964 	} else {
965 		php_error_docref(NULL, E_WARNING, "Document Missing Root Element");
966 		RETURN_FALSE;
967 	}
968 
969 	xmlFree(localname);
970 	if (prefix != NULL) {
971 		xmlFree(prefix);
972 	}
973 
974 	if (errorcode != 0) {
975 		if (nodep != NULL) {
976 			xmlFreeProp((xmlAttrPtr) nodep);
977 		}
978 		php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
979 		RETURN_FALSE;
980 	}
981 
982 	if (nodep == NULL) {
983 		RETURN_FALSE;
984 	}
985 
986 	DOM_RET_OBJ(nodep, &ret, intern);
987 }
988 /* }}} end dom_document_create_attribute_ns */
989 
990 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBTNNS
991 Since: DOM Level 2
992 */
PHP_METHOD(DOMDocument,getElementsByTagNameNS)993 PHP_METHOD(DOMDocument, getElementsByTagNameNS)
994 {
995 	zval *id;
996 	xmlDocPtr docp;
997 	size_t uri_len, name_len;
998 	dom_object *intern, *namednode;
999 	char *uri, *name;
1000 	xmlChar *local, *nsuri;
1001 
1002 	id = ZEND_THIS;
1003 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1004 		RETURN_THROWS();
1005 	}
1006 
1007 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1008 
1009 	php_dom_create_iterator(return_value, DOM_NODELIST);
1010 	namednode = Z_DOMOBJ_P(return_value);
1011 	local = xmlCharStrndup(name, name_len);
1012 	nsuri = xmlCharStrndup(uri ? uri : "", uri_len);
1013 	dom_namednode_iter(intern, 0, namednode, NULL, local, nsuri);
1014 }
1015 /* }}} end dom_document_get_elements_by_tag_name_ns */
1016 
php_dom_is_node_attached(const xmlNode * node)1017 static bool php_dom_is_node_attached(const xmlNode *node)
1018 {
1019 	ZEND_ASSERT(node != NULL);
1020 	node = node->parent;
1021 	while (node != NULL) {
1022 		if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) {
1023 			return true;
1024 		}
1025 		node = node->parent;
1026 	}
1027 	return false;
1028 }
1029 
1030 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBId
1031 Since: DOM Level 2
1032 */
PHP_METHOD(DOMDocument,getElementById)1033 PHP_METHOD(DOMDocument, getElementById)
1034 {
1035 	zval *id;
1036 	xmlDocPtr docp;
1037 	xmlAttrPtr  attrp;
1038 	int ret;
1039 	size_t idname_len;
1040 	dom_object *intern;
1041 	char *idname;
1042 
1043 	id = ZEND_THIS;
1044 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &idname, &idname_len) == FAILURE) {
1045 		RETURN_THROWS();
1046 	}
1047 
1048 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1049 
1050 	attrp = xmlGetID(docp, (xmlChar *) idname);
1051 
1052 	/* From the moment an ID is created, libxml2's behaviour is to cache that element, even
1053 	 * if that element is not yet attached to the document. Similarly, only upon destruction of
1054 	 * the element the ID is actually removed by libxml2. Since libxml2 has such behaviour deeply
1055 	 * ingrained in the library, and uses the cache for various purposes, it seems like a bad
1056 	 * idea and lost cause to fight it. Instead, we'll simply walk the tree upwards to check
1057 	 * if the node is attached to the document. */
1058 	if (attrp && attrp->parent && php_dom_is_node_attached(attrp->parent)) {
1059 		DOM_RET_OBJ((xmlNodePtr) attrp->parent, &ret, intern);
1060 	} else {
1061 		RETVAL_NULL();
1062 	}
1063 
1064 }
1065 /* }}} end dom_document_get_element_by_id */
1066 
1067 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNode
1068 Since: DOM Level 3
1069 */
PHP_METHOD(DOMDocument,adoptNode)1070 PHP_METHOD(DOMDocument, adoptNode)
1071 {
1072 	zval *nodep = NULL;
1073 
1074 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &nodep, dom_node_class_entry) == FAILURE) {
1075 		RETURN_THROWS();
1076 	}
1077 
1078 	DOM_NOT_IMPLEMENTED();
1079 }
1080 /* }}} end dom_document_adopt_node */
1081 
1082 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocument
1083 Since: DOM Level 3
1084 */
PHP_METHOD(DOMDocument,normalizeDocument)1085 PHP_METHOD(DOMDocument, normalizeDocument)
1086 {
1087 	zval *id;
1088 	xmlDocPtr docp;
1089 	dom_object *intern;
1090 
1091 	id = ZEND_THIS;
1092 	if (zend_parse_parameters_none() == FAILURE) {
1093 		RETURN_THROWS();
1094 	}
1095 
1096 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1097 
1098 	dom_normalize((xmlNodePtr) docp);
1099 }
1100 /* }}} end dom_document_normalize_document */
1101 
1102 /* {{{ */
PHP_METHOD(DOMDocument,__construct)1103 PHP_METHOD(DOMDocument, __construct)
1104 {
1105 	xmlDoc *docp = NULL, *olddoc;
1106 	dom_object *intern;
1107 	char *encoding, *version = NULL;
1108 	size_t encoding_len = 0, version_len = 0;
1109 	int refcount;
1110 
1111 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &version, &version_len, &encoding, &encoding_len) == FAILURE) {
1112 		RETURN_THROWS();
1113 	}
1114 
1115 	docp = xmlNewDoc((xmlChar *) version);
1116 
1117 	if (!docp) {
1118 		php_dom_throw_error(INVALID_STATE_ERR, 1);
1119 		return;
1120 	}
1121 
1122 	if (encoding_len > 0) {
1123 		docp->encoding = (const xmlChar *) xmlStrdup((xmlChar *) encoding);
1124 	}
1125 
1126 	intern = Z_DOMOBJ_P(ZEND_THIS);
1127 	olddoc = (xmlDocPtr) dom_object_get_node(intern);
1128 	if (olddoc != NULL) {
1129 		php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1130 		refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1131 		if (refcount != 0) {
1132 			olddoc->_private = NULL;
1133 		}
1134 	}
1135 	intern->document = NULL;
1136 	if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp) == -1) {
1137 		/* docp is always non-null so php_libxml_increment_doc_ref() never returns -1 */
1138 		ZEND_UNREACHABLE();
1139 	}
1140 	php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)docp, (void *)intern);
1141 }
1142 /* }}} end DOMDocument::__construct */
1143 
_dom_get_valid_file_path(char * source,char * resolved_path,int resolved_path_len)1144 char *_dom_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len ) /* {{{ */
1145 {
1146 	xmlURI *uri;
1147 	xmlChar *escsource;
1148 	char *file_dest;
1149 	int isFileUri = 0;
1150 
1151 	uri = xmlCreateURI();
1152 	if (uri == NULL) {
1153 		return NULL;
1154 	}
1155 	escsource = xmlURIEscapeStr((xmlChar *) source, (xmlChar *) ":");
1156 	xmlParseURIReference(uri, (char *) escsource);
1157 	xmlFree(escsource);
1158 
1159 	if (uri->scheme != NULL) {
1160 		/* absolute file uris - libxml only supports localhost or empty host */
1161 #ifdef PHP_WIN32
1162 		if (strncasecmp(source, "file://",7) == 0 && ':' == source[8]) {
1163 			isFileUri = 1;
1164 			source += 7;
1165 		} else
1166 #endif
1167 		if (strncasecmp(source, "file:///",8) == 0) {
1168 			isFileUri = 1;
1169 #ifdef PHP_WIN32
1170 			source += 8;
1171 #else
1172 			source += 7;
1173 #endif
1174 		} else if (strncasecmp(source, "file://localhost/",17) == 0) {
1175 			isFileUri = 1;
1176 #ifdef PHP_WIN32
1177 			source += 17;
1178 #else
1179 			source += 16;
1180 #endif
1181 		}
1182 	}
1183 
1184 	file_dest = source;
1185 
1186 	if ((uri->scheme == NULL || isFileUri)) {
1187 		/* XXX possible buffer overflow if VCWD_REALPATH does not know size of resolved_path */
1188 		if (!VCWD_REALPATH(source, resolved_path) && !expand_filepath(source, resolved_path)) {
1189 			xmlFreeURI(uri);
1190 			return NULL;
1191 		}
1192 		file_dest = resolved_path;
1193 	}
1194 
1195 	xmlFreeURI(uri);
1196 
1197 	return file_dest;
1198 }
1199 /* }}} */
1200 
dom_document_parser(zval * id,int mode,char * source,size_t source_len,size_t options)1201 static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t source_len, size_t options) /* {{{ */
1202 {
1203 	xmlDocPtr ret;
1204 	xmlParserCtxtPtr ctxt = NULL;
1205 	dom_doc_propsptr doc_props;
1206 	dom_object *intern;
1207 	php_libxml_ref_obj *document = NULL;
1208 	int validate, recover, resolve_externals, keep_blanks, substitute_ent;
1209 	int resolved_path_len;
1210 	int old_error_reporting = 0;
1211 	char *directory=NULL, resolved_path[MAXPATHLEN + 1];
1212 
1213 	if (id != NULL) {
1214 		intern = Z_DOMOBJ_P(id);
1215 		document = intern->document;
1216 	}
1217 
1218 	doc_props = dom_get_doc_props(document);
1219 	validate = doc_props->validateonparse;
1220 	resolve_externals = doc_props->resolveexternals;
1221 	keep_blanks = doc_props->preservewhitespace;
1222 	substitute_ent = doc_props->substituteentities;
1223 	recover = doc_props->recover;
1224 
1225 	if (document == NULL) {
1226 		efree(doc_props);
1227 	}
1228 
1229 	xmlInitParser();
1230 
1231 	if (mode == DOM_LOAD_FILE) {
1232 		char *file_dest;
1233 		if (CHECK_NULL_PATH(source, source_len)) {
1234 			zend_value_error("Path to document must not contain any null bytes");
1235 			return NULL;
1236 		}
1237 		file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1238 		if (file_dest) {
1239 			ctxt = xmlCreateFileParserCtxt(file_dest);
1240 		}
1241 
1242 	} else {
1243 		ctxt = xmlCreateMemoryParserCtxt(source, source_len);
1244 	}
1245 
1246 	if (ctxt == NULL) {
1247 		return(NULL);
1248 	}
1249 
1250 	/* If loading from memory, we need to set the base directory for the document */
1251 	if (mode != DOM_LOAD_FILE) {
1252 #ifdef HAVE_GETCWD
1253 		directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
1254 #elif defined(HAVE_GETWD)
1255 		directory = VCWD_GETWD(resolved_path);
1256 #endif
1257 		if (directory) {
1258 			if(ctxt->directory != NULL) {
1259 				xmlFree((char *) ctxt->directory);
1260 			}
1261 			resolved_path_len = strlen(resolved_path);
1262 			if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) {
1263 				resolved_path[resolved_path_len] = DEFAULT_SLASH;
1264 				resolved_path[++resolved_path_len] = '\0';
1265 			}
1266 			ctxt->directory = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
1267 		}
1268 	}
1269 
1270 	ctxt->vctxt.error = php_libxml_ctx_error;
1271 	ctxt->vctxt.warning = php_libxml_ctx_warning;
1272 
1273 	if (ctxt->sax != NULL) {
1274 		ctxt->sax->error = php_libxml_ctx_error;
1275 		ctxt->sax->warning = php_libxml_ctx_warning;
1276 	}
1277 
1278 	if (validate && ! (options & XML_PARSE_DTDVALID)) {
1279 		options |= XML_PARSE_DTDVALID;
1280 	}
1281 	if (resolve_externals && ! (options & XML_PARSE_DTDATTR)) {
1282 		options |= XML_PARSE_DTDATTR;
1283 	}
1284 	if (substitute_ent && ! (options & XML_PARSE_NOENT)) {
1285 		options |= XML_PARSE_NOENT;
1286 	}
1287 	if (keep_blanks == 0 && ! (options & XML_PARSE_NOBLANKS)) {
1288 		options |= XML_PARSE_NOBLANKS;
1289 	}
1290 
1291 	php_libxml_sanitize_parse_ctxt_options(ctxt);
1292 	xmlCtxtUseOptions(ctxt, options);
1293 
1294 	ctxt->recovery = recover;
1295 	if (recover) {
1296 		old_error_reporting = EG(error_reporting);
1297 		EG(error_reporting) = old_error_reporting | E_WARNING;
1298 	}
1299 
1300 	xmlParseDocument(ctxt);
1301 
1302 	if (ctxt->wellFormed || recover) {
1303 		ret = ctxt->myDoc;
1304 		if (ctxt->recovery) {
1305 			EG(error_reporting) = old_error_reporting;
1306 		}
1307 		/* If loading from memory, set the base reference uri for the document */
1308 		if (ret && ret->URL == NULL && ctxt->directory != NULL) {
1309 			ret->URL = xmlStrdup((xmlChar *) ctxt->directory);
1310 		}
1311 	} else {
1312 		ret = NULL;
1313 		xmlFreeDoc(ctxt->myDoc);
1314 		ctxt->myDoc = NULL;
1315 	}
1316 
1317 	xmlFreeParserCtxt(ctxt);
1318 
1319 	return(ret);
1320 }
1321 /* }}} */
1322 
1323 /* {{{ static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) */
dom_parse_document(INTERNAL_FUNCTION_PARAMETERS,int mode)1324 static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode) {
1325 	zval *id;
1326 	xmlDoc *docp = NULL, *newdoc;
1327 	dom_doc_propsptr doc_prop;
1328 	dom_object *intern;
1329 	char *source;
1330 	size_t source_len;
1331 	int refcount, ret;
1332 	zend_long options = 0;
1333 
1334 	id = getThis();
1335 	if (id != NULL && ! instanceof_function(Z_OBJCE_P(id), dom_document_class_entry)) {
1336 		id = NULL;
1337 	}
1338 
1339 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
1340 		RETURN_THROWS();
1341 	}
1342 
1343 	if (!source_len) {
1344 		zend_argument_value_error(1, "must not be empty");
1345 		RETURN_THROWS();
1346 	}
1347 	if (ZEND_SIZE_T_INT_OVFL(source_len)) {
1348 		php_error_docref(NULL, E_WARNING, "Input string is too long");
1349 		RETURN_FALSE;
1350 	}
1351 	if (ZEND_LONG_EXCEEDS_INT(options)) {
1352 		php_error_docref(NULL, E_WARNING, "Invalid options");
1353 		RETURN_FALSE;
1354 	}
1355 
1356 	newdoc = dom_document_parser(id, mode, source, source_len, options);
1357 
1358 	if (!newdoc)
1359 		RETURN_FALSE;
1360 
1361 	if (id != NULL) {
1362 		intern = Z_DOMOBJ_P(id);
1363 		if (intern != NULL) {
1364 			docp = (xmlDocPtr) dom_object_get_node(intern);
1365 			doc_prop = NULL;
1366 			if (docp != NULL) {
1367 				php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1368 				doc_prop = intern->document->doc_props;
1369 				intern->document->doc_props = NULL;
1370 				refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1371 				if (refcount != 0) {
1372 					docp->_private = NULL;
1373 				}
1374 			}
1375 			intern->document = NULL;
1376 			if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc) == -1) {
1377 				RETURN_FALSE;
1378 			}
1379 			intern->document->doc_props = doc_prop;
1380 		}
1381 
1382 		php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
1383 
1384 		RETURN_TRUE;
1385 	} else {
1386 		DOM_RET_OBJ((xmlNodePtr) newdoc, &ret, NULL);
1387 	}
1388 }
1389 /* }}} end dom_parser_document */
1390 
1391 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-load
1392 Since: DOM Level 3
1393 */
PHP_METHOD(DOMDocument,load)1394 PHP_METHOD(DOMDocument, load)
1395 {
1396 	dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1397 }
1398 /* }}} end dom_document_load */
1399 
1400 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadXML
1401 Since: DOM Level 3
1402 */
PHP_METHOD(DOMDocument,loadXML)1403 PHP_METHOD(DOMDocument, loadXML)
1404 {
1405 	dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1406 }
1407 /* }}} end dom_document_loadxml */
1408 
1409 /* {{{ Convenience method to save to file */
PHP_METHOD(DOMDocument,save)1410 PHP_METHOD(DOMDocument, save)
1411 {
1412 	zval *id;
1413 	xmlDoc *docp;
1414 	size_t file_len = 0;
1415 	int bytes, format, saveempty = 0;
1416 	dom_object *intern;
1417 	dom_doc_propsptr doc_props;
1418 	char *file;
1419 	zend_long options = 0;
1420 
1421 	id = ZEND_THIS;
1422 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &file, &file_len, &options) == FAILURE) {
1423 		RETURN_THROWS();
1424 	}
1425 
1426 	if (file_len == 0) {
1427 		zend_argument_value_error(1, "must not be empty");
1428 		RETURN_THROWS();
1429 	}
1430 
1431 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1432 
1433 	/* encoding handled by property on doc */
1434 
1435 	doc_props = dom_get_doc_props(intern->document);
1436 	format = doc_props->formatoutput;
1437 	if (options & LIBXML_SAVE_NOEMPTYTAG) {
1438 		saveempty = xmlSaveNoEmptyTags;
1439 		xmlSaveNoEmptyTags = 1;
1440 	}
1441 	bytes = xmlSaveFormatFileEnc(file, docp, NULL, format);
1442 	if (options & LIBXML_SAVE_NOEMPTYTAG) {
1443 		xmlSaveNoEmptyTags = saveempty;
1444 	}
1445 	if (bytes == -1) {
1446 		RETURN_FALSE;
1447 	}
1448 	RETURN_LONG(bytes);
1449 }
1450 /* }}} end dom_document_save */
1451 
1452 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML
1453 Since: DOM Level 3
1454 */
PHP_METHOD(DOMDocument,saveXML)1455 PHP_METHOD(DOMDocument, saveXML)
1456 {
1457 	zval *id, *nodep = NULL;
1458 	xmlDoc *docp;
1459 	xmlNode *node;
1460 	xmlBufferPtr buf;
1461 	xmlChar *mem;
1462 	dom_object *intern, *nodeobj;
1463 	dom_doc_propsptr doc_props;
1464 	int size, format, saveempty = 0;
1465 	zend_long options = 0;
1466 
1467 	id = ZEND_THIS;
1468 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!l", &nodep, dom_node_class_entry, &options) == FAILURE) {
1469 		RETURN_THROWS();
1470 	}
1471 
1472 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1473 
1474 	doc_props = dom_get_doc_props(intern->document);
1475 	format = doc_props->formatoutput;
1476 
1477 	if (nodep != NULL) {
1478 		/* Dump contents of Node */
1479 		DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
1480 		if (node->doc != docp) {
1481 			php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
1482 			RETURN_FALSE;
1483 		}
1484 		buf = xmlBufferCreate();
1485 		if (!buf) {
1486 			php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
1487 			RETURN_FALSE;
1488 		}
1489 		if (options & LIBXML_SAVE_NOEMPTYTAG) {
1490 			saveempty = xmlSaveNoEmptyTags;
1491 			xmlSaveNoEmptyTags = 1;
1492 		}
1493 		xmlNodeDump(buf, docp, node, 0, format);
1494 		if (options & LIBXML_SAVE_NOEMPTYTAG) {
1495 			xmlSaveNoEmptyTags = saveempty;
1496 		}
1497 		mem = (xmlChar*) xmlBufferContent(buf);
1498 		if (!mem) {
1499 			xmlBufferFree(buf);
1500 			RETURN_FALSE;
1501 		}
1502 		RETVAL_STRING((char *) mem);
1503 		xmlBufferFree(buf);
1504 	} else {
1505 		if (options & LIBXML_SAVE_NOEMPTYTAG) {
1506 			saveempty = xmlSaveNoEmptyTags;
1507 			xmlSaveNoEmptyTags = 1;
1508 		}
1509 		/* Encoding is handled from the encoding property set on the document */
1510 		xmlDocDumpFormatMemory(docp, &mem, &size, format);
1511 		if (options & LIBXML_SAVE_NOEMPTYTAG) {
1512 			xmlSaveNoEmptyTags = saveempty;
1513 		}
1514 		if (!size || !mem) {
1515 			RETURN_FALSE;
1516 		}
1517 		RETVAL_STRINGL((char *) mem, size);
1518 		xmlFree(mem);
1519 	}
1520 }
1521 /* }}} end dom_document_savexml */
1522 
php_dom_free_xinclude_node(xmlNodePtr cur)1523 static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) /* {{{ */
1524 {
1525 	xmlNodePtr xincnode;
1526 
1527 	xincnode = cur;
1528 	cur = cur->next;
1529 	xmlUnlinkNode(xincnode);
1530 	php_libxml_node_free_resource(xincnode);
1531 
1532 	return cur;
1533 }
1534 /* }}} */
1535 
php_dom_remove_xinclude_nodes(xmlNodePtr cur)1536 static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
1537 {
1538 	while(cur) {
1539 		if (cur->type == XML_XINCLUDE_START) {
1540 			cur = php_dom_free_xinclude_node(cur);
1541 
1542 			/* XML_XINCLUDE_END node will be a sibling of XML_XINCLUDE_START */
1543 			while(cur && cur->type != XML_XINCLUDE_END) {
1544 				/* remove xinclude processing nodes from recursive xincludes */
1545 				if (cur->type == XML_ELEMENT_NODE) {
1546 					   php_dom_remove_xinclude_nodes(cur->children);
1547 				}
1548 				cur = cur->next;
1549 			}
1550 
1551 			if (cur && cur->type == XML_XINCLUDE_END) {
1552 				cur = php_dom_free_xinclude_node(cur);
1553 			}
1554 		} else {
1555 			if (cur->type == XML_ELEMENT_NODE) {
1556 				php_dom_remove_xinclude_nodes(cur->children);
1557 			}
1558 			cur = cur->next;
1559 		}
1560 	}
1561 }
1562 /* }}} */
1563 
1564 /* {{{ Substitutues xincludes in a DomDocument */
PHP_METHOD(DOMDocument,xinclude)1565 PHP_METHOD(DOMDocument, xinclude)
1566 {
1567 	zval *id;
1568 	xmlDoc *docp;
1569 	xmlNodePtr root;
1570 	zend_long flags = 0;
1571 	int err;
1572 	dom_object *intern;
1573 
1574 	id = ZEND_THIS;
1575 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
1576 		RETURN_THROWS();
1577 	}
1578 
1579 	if (ZEND_LONG_EXCEEDS_INT(flags)) {
1580 		php_error_docref(NULL, E_WARNING, "Invalid flags");
1581 		RETURN_FALSE;
1582 	}
1583 
1584 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1585 
1586 	PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
1587 	err = xmlXIncludeProcessFlags(docp, (int)flags);
1588 	PHP_LIBXML_RESTORE_GLOBALS(xinclude);
1589 
1590 	/* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
1591 	are added via xmlXIncludeProcess to mark beginning and ending of xincluded document
1592 	but are not wanted in resulting document - must be done even if err as it could fail after
1593 	having processed some xincludes */
1594 	root = (xmlNodePtr) docp->children;
1595 	while(root && root->type != XML_ELEMENT_NODE && root->type != XML_XINCLUDE_START) {
1596 		root = root->next;
1597 	}
1598 	if (root) {
1599 		php_dom_remove_xinclude_nodes(root);
1600 	}
1601 
1602 	if (err) {
1603 		RETVAL_LONG(err);
1604 	} else {
1605 		RETVAL_FALSE;
1606 	}
1607 
1608 }
1609 /* }}} */
1610 
1611 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,validate)1612 PHP_METHOD(DOMDocument, validate)
1613 {
1614 	zval *id;
1615 	xmlDoc *docp;
1616 	dom_object *intern;
1617 	xmlValidCtxt *cvp;
1618 
1619 	id = ZEND_THIS;
1620 	if (zend_parse_parameters_none() == FAILURE) {
1621 		RETURN_THROWS();
1622 	}
1623 
1624 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1625 
1626 	PHP_LIBXML_SANITIZE_GLOBALS(validate);
1627 	cvp = xmlNewValidCtxt();
1628 
1629 	cvp->userData = NULL;
1630 	cvp->error    = (xmlValidityErrorFunc) php_libxml_error_handler;
1631 	cvp->warning  = (xmlValidityErrorFunc) php_libxml_error_handler;
1632 
1633 	if (xmlValidateDocument(cvp, docp)) {
1634 		RETVAL_TRUE;
1635 	} else {
1636 		RETVAL_FALSE;
1637 	}
1638 	PHP_LIBXML_RESTORE_GLOBALS(validate);
1639 
1640 	xmlFreeValidCtxt(cvp);
1641 
1642 }
1643 /* }}} */
1644 
1645 #ifdef LIBXML_SCHEMAS_ENABLED
_dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1646 static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1647 {
1648 	zval *id;
1649 	xmlDoc *docp;
1650 	dom_object *intern;
1651 	char *source = NULL, *valid_file = NULL;
1652 	size_t source_len = 0;
1653 	int valid_opts = 0;
1654 	zend_long flags = 0;
1655 	xmlSchemaParserCtxtPtr  parser;
1656 	xmlSchemaPtr            sptr;
1657 	xmlSchemaValidCtxtPtr   vptr;
1658 	int                     is_valid;
1659 	char resolved_path[MAXPATHLEN + 1];
1660 
1661 	id = ZEND_THIS;
1662 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &flags) == FAILURE) {
1663 		RETURN_THROWS();
1664 	}
1665 
1666 	if (!source_len) {
1667 		zend_argument_value_error(1, "must not be empty");
1668 		RETURN_THROWS();
1669 	}
1670 
1671 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1672 
1673 	PHP_LIBXML_SANITIZE_GLOBALS(new_parser_ctxt);
1674 
1675 	switch (type) {
1676 	case DOM_LOAD_FILE:
1677 		if (CHECK_NULL_PATH(source, source_len)) {
1678 			PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1679 			zend_argument_value_error(1, "must not contain any null bytes");
1680 			RETURN_THROWS();
1681 		}
1682 		valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1683 		if (!valid_file) {
1684 			PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1685 			php_error_docref(NULL, E_WARNING, "Invalid Schema file source");
1686 			RETURN_FALSE;
1687 		}
1688 		parser = xmlSchemaNewParserCtxt(valid_file);
1689 		break;
1690 	case DOM_LOAD_STRING:
1691 		parser = xmlSchemaNewMemParserCtxt(source, source_len);
1692 		/* If loading from memory, we need to set the base directory for the document
1693 		   but it is not apparent how to do that for schema's */
1694 		break;
1695 	default:
1696 		return;
1697 	}
1698 
1699 	xmlSchemaSetParserErrors(parser,
1700 		(xmlSchemaValidityErrorFunc) php_libxml_error_handler,
1701 		(xmlSchemaValidityWarningFunc) php_libxml_error_handler,
1702 		parser);
1703 	sptr = xmlSchemaParse(parser);
1704 	xmlSchemaFreeParserCtxt(parser);
1705 	PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1706 	if (!sptr) {
1707 		if (!EG(exception)) {
1708 			php_error_docref(NULL, E_WARNING, "Invalid Schema");
1709 		}
1710 		RETURN_FALSE;
1711 	}
1712 
1713 	docp = (xmlDocPtr) dom_object_get_node(intern);
1714 
1715 	vptr = xmlSchemaNewValidCtxt(sptr);
1716 	if (!vptr) {
1717 		xmlSchemaFree(sptr);
1718 		zend_throw_error(NULL, "Invalid Schema Validation Context");
1719 		RETURN_THROWS();
1720 	}
1721 
1722 	if (flags & XML_SCHEMA_VAL_VC_I_CREATE) {
1723 		valid_opts |= XML_SCHEMA_VAL_VC_I_CREATE;
1724 	}
1725 
1726 	PHP_LIBXML_SANITIZE_GLOBALS(validate);
1727 	xmlSchemaSetValidOptions(vptr, valid_opts);
1728 	xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
1729 	is_valid = xmlSchemaValidateDoc(vptr, docp);
1730 	xmlSchemaFree(sptr);
1731 	xmlSchemaFreeValidCtxt(vptr);
1732 	PHP_LIBXML_RESTORE_GLOBALS(validate);
1733 
1734 	if (is_valid == 0) {
1735 		RETURN_TRUE;
1736 	} else {
1737 		RETURN_FALSE;
1738 	}
1739 }
1740 /* }}} */
1741 
1742 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidate)1743 PHP_METHOD(DOMDocument, schemaValidate)
1744 {
1745 	_dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1746 }
1747 /* }}} end dom_document_schema_validate_file */
1748 
1749 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidateSource)1750 PHP_METHOD(DOMDocument, schemaValidateSource)
1751 {
1752 	_dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1753 }
1754 /* }}} end dom_document_schema_validate */
1755 
_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1756 static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1757 {
1758 	zval *id;
1759 	xmlDoc *docp;
1760 	dom_object *intern;
1761 	char *source = NULL, *valid_file = NULL;
1762 	size_t source_len = 0;
1763 	xmlRelaxNGParserCtxtPtr parser;
1764 	xmlRelaxNGPtr           sptr;
1765 	xmlRelaxNGValidCtxtPtr  vptr;
1766 	int                     is_valid;
1767 	char resolved_path[MAXPATHLEN + 1];
1768 
1769 	id = ZEND_THIS;
1770 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &source, &source_len) == FAILURE) {
1771 		RETURN_THROWS();
1772 	}
1773 
1774 	if (!source_len) {
1775 		zend_argument_value_error(1, "must not be empty");
1776 		RETURN_THROWS();
1777 	}
1778 
1779 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1780 
1781 	switch (type) {
1782 	case DOM_LOAD_FILE:
1783 		if (CHECK_NULL_PATH(source, source_len)) {
1784 			zend_argument_value_error(1, "must not contain any null bytes");
1785 			RETURN_THROWS();
1786 		}
1787 		valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1788 		if (!valid_file) {
1789 			php_error_docref(NULL, E_WARNING, "Invalid RelaxNG file source");
1790 			RETURN_FALSE;
1791 		}
1792 		parser = xmlRelaxNGNewParserCtxt(valid_file);
1793 		break;
1794 	case DOM_LOAD_STRING:
1795 		parser = xmlRelaxNGNewMemParserCtxt(source, source_len);
1796 		/* If loading from memory, we need to set the base directory for the document
1797 		   but it is not apparent how to do that for schema's */
1798 		break;
1799 	default:
1800 		return;
1801 	}
1802 
1803 	PHP_LIBXML_SANITIZE_GLOBALS(parse);
1804 	xmlRelaxNGSetParserErrors(parser,
1805 		(xmlRelaxNGValidityErrorFunc) php_libxml_error_handler,
1806 		(xmlRelaxNGValidityWarningFunc) php_libxml_error_handler,
1807 		parser);
1808 	sptr = xmlRelaxNGParse(parser);
1809 	xmlRelaxNGFreeParserCtxt(parser);
1810 	PHP_LIBXML_RESTORE_GLOBALS(parse);
1811 	if (!sptr) {
1812 		php_error_docref(NULL, E_WARNING, "Invalid RelaxNG");
1813 		RETURN_FALSE;
1814 	}
1815 
1816 	docp = (xmlDocPtr) dom_object_get_node(intern);
1817 
1818 	vptr = xmlRelaxNGNewValidCtxt(sptr);
1819 	if (!vptr) {
1820 		xmlRelaxNGFree(sptr);
1821 		zend_throw_error(NULL, "Invalid RelaxNG Validation Context");
1822 		RETURN_THROWS();
1823 	}
1824 
1825 	xmlRelaxNGSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
1826 	is_valid = xmlRelaxNGValidateDoc(vptr, docp);
1827 	xmlRelaxNGFree(sptr);
1828 	xmlRelaxNGFreeValidCtxt(vptr);
1829 
1830 	if (is_valid == 0) {
1831 		RETURN_TRUE;
1832 	} else {
1833 		RETURN_FALSE;
1834 	}
1835 }
1836 /* }}} */
1837 
1838 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidate)1839 PHP_METHOD(DOMDocument, relaxNGValidate)
1840 {
1841 	_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1842 }
1843 /* }}} end dom_document_relaxNG_validate_file */
1844 
1845 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidateSource)1846 PHP_METHOD(DOMDocument, relaxNGValidateSource)
1847 {
1848 	_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1849 }
1850 /* }}} end dom_document_relaxNG_validate_xml */
1851 
1852 #endif
1853 
1854 #ifdef LIBXML_HTML_ENABLED
1855 
dom_load_html(INTERNAL_FUNCTION_PARAMETERS,int mode)1856 static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
1857 {
1858 	zval *id;
1859 	xmlDoc *docp = NULL, *newdoc;
1860 	dom_object *intern;
1861 	dom_doc_propsptr doc_prop;
1862 	char *source;
1863 	size_t source_len;
1864 	int refcount, ret;
1865 	zend_long options = 0;
1866 	htmlParserCtxtPtr ctxt;
1867 
1868 	id = getThis();
1869 
1870 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
1871 		RETURN_THROWS();
1872 	}
1873 
1874 	if (!source_len) {
1875 		zend_argument_value_error(1, "must not be empty");
1876 		RETURN_THROWS();
1877 	}
1878 
1879 	if (ZEND_LONG_EXCEEDS_INT(options)) {
1880 		php_error_docref(NULL, E_WARNING, "Invalid options");
1881 		RETURN_FALSE;
1882 	}
1883 
1884 	if (mode == DOM_LOAD_FILE) {
1885 		if (CHECK_NULL_PATH(source, source_len)) {
1886 			zend_argument_value_error(1, "must not contain any null bytes");
1887 			RETURN_THROWS();
1888 		}
1889 		ctxt = htmlCreateFileParserCtxt(source, NULL);
1890 	} else {
1891 		if (ZEND_SIZE_T_INT_OVFL(source_len)) {
1892 			php_error_docref(NULL, E_WARNING, "Input string is too long");
1893 			RETURN_FALSE;
1894 		}
1895 		ctxt = htmlCreateMemoryParserCtxt(source, (int)source_len);
1896 	}
1897 
1898 	if (!ctxt) {
1899 		RETURN_FALSE;
1900 	}
1901 
1902 
1903 	ctxt->vctxt.error = php_libxml_ctx_error;
1904 	ctxt->vctxt.warning = php_libxml_ctx_warning;
1905 	if (ctxt->sax != NULL) {
1906 		ctxt->sax->error = php_libxml_ctx_error;
1907 		ctxt->sax->warning = php_libxml_ctx_warning;
1908 	}
1909 	php_libxml_sanitize_parse_ctxt_options(ctxt);
1910 	if (options) {
1911 		htmlCtxtUseOptions(ctxt, (int)options);
1912 	}
1913 	htmlParseDocument(ctxt);
1914 	newdoc = ctxt->myDoc;
1915 	htmlFreeParserCtxt(ctxt);
1916 
1917 	if (!newdoc)
1918 		RETURN_FALSE;
1919 
1920 	if (id != NULL && instanceof_function(Z_OBJCE_P(id), dom_document_class_entry)) {
1921 		intern = Z_DOMOBJ_P(id);
1922 		if (intern != NULL) {
1923 			docp = (xmlDocPtr) dom_object_get_node(intern);
1924 			doc_prop = NULL;
1925 			if (docp != NULL) {
1926 				php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1927 				doc_prop = intern->document->doc_props;
1928 				intern->document->doc_props = NULL;
1929 				refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1930 				if (refcount != 0) {
1931 					docp->_private = NULL;
1932 				}
1933 			}
1934 			intern->document = NULL;
1935 			if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc) == -1) {
1936 				RETURN_FALSE;
1937 			}
1938 			intern->document->doc_props = doc_prop;
1939 		}
1940 
1941 		php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
1942 
1943 		RETURN_TRUE;
1944 	} else {
1945 		DOM_RET_OBJ((xmlNodePtr) newdoc, &ret, NULL);
1946 	}
1947 }
1948 /* }}} */
1949 
1950 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTMLFile)1951 PHP_METHOD(DOMDocument, loadHTMLFile)
1952 {
1953 	dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1954 }
1955 /* }}} end dom_document_load_html_file */
1956 
1957 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTML)1958 PHP_METHOD(DOMDocument, loadHTML)
1959 {
1960 	dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1961 }
1962 /* }}} end dom_document_load_html */
1963 
1964 /* {{{ Convenience method to save to file as html */
PHP_METHOD(DOMDocument,saveHTMLFile)1965 PHP_METHOD(DOMDocument, saveHTMLFile)
1966 {
1967 	zval *id;
1968 	xmlDoc *docp;
1969 	size_t file_len;
1970 	int bytes, format;
1971 	dom_object *intern;
1972 	dom_doc_propsptr doc_props;
1973 	char *file;
1974 	const char *encoding;
1975 
1976 	id = ZEND_THIS;
1977 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &file, &file_len) == FAILURE) {
1978 		RETURN_THROWS();
1979 	}
1980 
1981 	if (file_len == 0) {
1982 		zend_argument_value_error(1, "must not be empty");
1983 		RETURN_THROWS();
1984 	}
1985 
1986 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1987 
1988 
1989 	encoding = (const char *) htmlGetMetaEncoding(docp);
1990 
1991 	doc_props = dom_get_doc_props(intern->document);
1992 	format = doc_props->formatoutput;
1993 	bytes = htmlSaveFileFormat(file, docp, encoding, format);
1994 
1995 	if (bytes == -1) {
1996 		RETURN_FALSE;
1997 	}
1998 	RETURN_LONG(bytes);
1999 }
2000 /* }}} end dom_document_save_html_file */
2001 
2002 /* {{{ Convenience method to output as html */
PHP_METHOD(DOMDocument,saveHTML)2003 PHP_METHOD(DOMDocument, saveHTML)
2004 {
2005 	zval *id, *nodep = NULL;
2006 	xmlDoc *docp;
2007 	xmlNode *node;
2008 	xmlOutputBufferPtr outBuf;
2009 	xmlBufferPtr buf;
2010 	dom_object *intern, *nodeobj;
2011 	xmlChar *mem = NULL;
2012 	int format;
2013 	dom_doc_propsptr doc_props;
2014 
2015 	id = ZEND_THIS;
2016 	if (zend_parse_parameters(ZEND_NUM_ARGS(),
2017 		"|O!", &nodep, dom_node_class_entry)
2018 		== FAILURE) {
2019 		RETURN_THROWS();
2020 	}
2021 
2022 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2023 
2024 	doc_props = dom_get_doc_props(intern->document);
2025 	format = doc_props->formatoutput;
2026 
2027 	if (nodep != NULL) {
2028 		/* Dump contents of Node */
2029 		DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
2030 		if (node->doc != docp) {
2031 			php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
2032 			RETURN_FALSE;
2033 		}
2034 
2035 		buf = xmlBufferCreate();
2036 		if (!buf) {
2037 			php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
2038 			RETURN_FALSE;
2039 		}
2040 		outBuf = xmlOutputBufferCreateBuffer(buf, NULL);
2041 		if (!outBuf) {
2042 			xmlBufferFree(buf);
2043 			php_error_docref(NULL, E_WARNING, "Could not fetch output buffer");
2044 			RETURN_FALSE;
2045 		}
2046 
2047 		if (node->type == XML_DOCUMENT_FRAG_NODE) {
2048 			for (node = node->children; node; node = node->next) {
2049 				htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2050 				if (outBuf->error) {
2051 					break;
2052 				}
2053 			}
2054 		} else {
2055 			htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2056 		}
2057 		if (!outBuf->error) {
2058 			xmlOutputBufferFlush(outBuf);
2059 			mem = (xmlChar*) xmlBufferContent(buf);
2060 			if (!mem) {
2061 				RETVAL_FALSE;
2062 			} else {
2063 				int size = xmlBufferLength(buf);
2064 				RETVAL_STRINGL((const char*) mem, size);
2065 			}
2066 		} else {
2067 			php_error_docref(NULL, E_WARNING, "Error dumping HTML node");
2068 			RETVAL_FALSE;
2069 		}
2070 		xmlOutputBufferClose(outBuf);
2071 		xmlBufferFree(buf);
2072 	} else {
2073 		int size = 0;
2074 		htmlDocDumpMemoryFormat(docp, &mem, &size, format);
2075 		if (!size || !mem) {
2076 			RETVAL_FALSE;
2077 		} else {
2078 			RETVAL_STRINGL((const char*) mem, size);
2079 		}
2080 		if (mem)
2081 			xmlFree(mem);
2082 	}
2083 
2084 }
2085 /* }}} end dom_document_save_html */
2086 
2087 #endif  /* defined(LIBXML_HTML_ENABLED) */
2088 
2089 /* {{{ Register extended class used to create base node type */
PHP_METHOD(DOMDocument,registerNodeClass)2090 PHP_METHOD(DOMDocument, registerNodeClass)
2091 {
2092 	zval *id;
2093 	xmlDoc *docp;
2094 	zend_class_entry *basece = dom_node_class_entry, *ce = NULL;
2095 	dom_object *intern;
2096 
2097 	id = ZEND_THIS;
2098 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "CC!", &basece, &ce) == FAILURE) {
2099 		RETURN_THROWS();
2100 	}
2101 
2102 	if (ce == NULL || instanceof_function(ce, basece)) {
2103 		if (UNEXPECTED(ce != NULL && (ce->ce_flags & ZEND_ACC_ABSTRACT))) {
2104 			zend_argument_value_error(2, "must not be an abstract class");
2105 			RETURN_THROWS();
2106 		}
2107 		DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2108 		dom_set_doc_classmap(intern->document, basece, ce);
2109 		RETURN_TRUE;
2110 	}
2111 
2112 	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));
2113 }
2114 /* }}} */
2115 
2116 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-append
2117 Since: DOM Living Standard (DOM4)
2118 */
PHP_METHOD(DOMDocument,append)2119 PHP_METHOD(DOMDocument, append)
2120 {
2121 	int argc = 0;
2122 	zval *args, *id;
2123 	dom_object *intern;
2124 	xmlNode *context;
2125 
2126 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
2127 		RETURN_THROWS();
2128 	}
2129 
2130 	id = ZEND_THIS;
2131 	DOM_GET_OBJ(context, id, xmlNodePtr, intern);
2132 
2133 	dom_parent_node_append(intern, args, argc);
2134 }
2135 /* }}} */
2136 
2137 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
2138 Since: DOM Living Standard (DOM4)
2139 */
PHP_METHOD(DOMDocument,prepend)2140 PHP_METHOD(DOMDocument, prepend)
2141 {
2142 	int argc = 0;
2143 	zval *args, *id;
2144 	dom_object *intern;
2145 	xmlNode *context;
2146 
2147 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
2148 		RETURN_THROWS();
2149 	}
2150 
2151 	id = ZEND_THIS;
2152 	DOM_GET_OBJ(context, id, xmlNodePtr, intern);
2153 
2154 	dom_parent_node_prepend(intern, args, argc);
2155 }
2156 /* }}} */
2157 
2158 #endif  /* HAVE_LIBXML && HAVE_DOM */
2159