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