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