xref: /php-src/ext/dom/document.c (revision 8dc2391b)
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 	xmlNode *node;
529 	xmlDocPtr docp;
530 	size_t value_len;
531 	dom_object *intern;
532 	char *value;
533 
534 	ZEND_PARSE_PARAMETERS_START(1, 1)
535 		Z_PARAM_STRING(value, value_len)
536 	ZEND_PARSE_PARAMETERS_END();
537 
538 	DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
539 
540 	node = xmlNewDocText(docp, BAD_CAST value);
541 	if (!node) {
542 		php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
543 		RETURN_THROWS();
544 	}
545 
546 	DOM_RET_OBJ(node, intern);
547 }
548 /* }}} end dom_document_create_text_node */
549 
550 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1334481328
551 Since:
552 */
PHP_METHOD(DOMDocument,createComment)553 PHP_METHOD(DOMDocument, createComment)
554 {
555 	zval *id;
556 	xmlNode *node;
557 	xmlDocPtr docp;
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 = xmlNewDocComment(docp, BAD_CAST value);
570 	if (!node) {
571 		php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
572 		RETURN_THROWS();
573 	}
574 
575 	DOM_RET_OBJ(node, intern);
576 }
577 /* }}} end dom_document_create_comment */
578 
579 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D26C0AF8
580 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createcdatasection
581 Since:
582 */
PHP_METHOD(DOMDocument,createCDATASection)583 PHP_METHOD(DOMDocument, createCDATASection)
584 {
585 	zval *id;
586 	xmlNode *node;
587 	xmlDocPtr docp;
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 	if (php_dom_follow_spec_intern(intern)) {
600 		if (docp->type == XML_HTML_DOCUMENT_NODE) {
601 			php_dom_throw_error_with_message(NOT_SUPPORTED_ERR, "This operation is not supported for HTML documents", /* strict */ true);
602 			RETURN_THROWS();
603 		}
604 
605 		if (zend_memnstr(value, "]]>", strlen("]]>"), value + value_len) != NULL) {
606 			php_dom_throw_error_with_message(INVALID_CHARACTER_ERR, "Invalid character sequence \"]]>\" in CDATA section", /* strict */ true);
607 			RETURN_THROWS();
608 		}
609 	}
610 
611 	node = xmlNewCDataBlock(docp, BAD_CAST value, value_len);
612 	if (!node) {
613 		php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
614 		RETURN_THROWS();
615 	}
616 
617 	DOM_RET_OBJ(node, intern);
618 }
619 /* }}} end dom_document_create_cdatasection */
620 
621 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-135944439
622 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createprocessinginstruction
623 Since:
624 */
dom_document_create_processing_instruction(INTERNAL_FUNCTION_PARAMETERS,bool modern)625 static void dom_document_create_processing_instruction(INTERNAL_FUNCTION_PARAMETERS, bool modern)
626 {
627 	xmlNode *node;
628 	xmlDocPtr docp;
629 	size_t value_len, name_len = 0;
630 	dom_object *intern;
631 	char *name, *value = NULL;
632 
633 	if (zend_parse_parameters(ZEND_NUM_ARGS(), modern ? "ss" : "s|s", &name, &name_len, &value, &value_len) != SUCCESS) {
634 		RETURN_THROWS();
635 	}
636 
637 	DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
638 
639 	if (xmlValidateName(BAD_CAST name, 0) != 0) {
640 		php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
641 		RETURN_FALSE;
642 	}
643 
644 	if (modern) {
645 		if (value != NULL && zend_memnstr(value, "?>", strlen("?>"), value + value_len) != NULL) {
646 			php_dom_throw_error_with_message(INVALID_CHARACTER_ERR, "Invalid character sequence \"?>\" in processing instruction", /* strict */ true);
647 			RETURN_THROWS();
648 		}
649 	}
650 
651 	node = xmlNewDocPI(docp, BAD_CAST name, BAD_CAST value);
652 	if (!node) {
653 		php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
654 		RETURN_THROWS();
655 	}
656 
657 	DOM_RET_OBJ(node, intern);
658 }
659 
PHP_METHOD(DOMDocument,createProcessingInstruction)660 PHP_METHOD(DOMDocument, createProcessingInstruction)
661 {
662 	dom_document_create_processing_instruction(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
663 }
664 
PHP_METHOD(Dom_Document,createProcessingInstruction)665 PHP_METHOD(Dom_Document, createProcessingInstruction)
666 {
667 	dom_document_create_processing_instruction(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
668 }
669 /* }}} end dom_document_create_processing_instruction */
670 
671 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1084891198
672 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createattribute
673 Since:
674 */
PHP_METHOD(DOMDocument,createAttribute)675 PHP_METHOD(DOMDocument, createAttribute)
676 {
677 	zval *id;
678 	xmlAttrPtr node;
679 	xmlDocPtr docp;
680 	dom_object *intern;
681 	zend_string *name;
682 
683 	id = ZEND_THIS;
684 	ZEND_PARSE_PARAMETERS_START(1, 1)
685 		Z_PARAM_PATH_STR(name)
686 	ZEND_PARSE_PARAMETERS_END();
687 
688 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
689 
690 	if (xmlValidateName(BAD_CAST ZSTR_VAL(name), 0) != 0) {
691 		php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
692 		RETURN_FALSE;
693 	}
694 
695 	if (docp->type == XML_HTML_DOCUMENT_NODE && php_dom_follow_spec_intern(intern)) {
696 		char *lower = zend_str_tolower_dup_ex(ZSTR_VAL(name), ZSTR_LEN(name));
697 		node = xmlNewDocProp(docp, BAD_CAST (lower ? lower : ZSTR_VAL(name)), NULL);
698 		efree(lower);
699 	} else {
700 		node = xmlNewDocProp(docp, BAD_CAST ZSTR_VAL(name), NULL);
701 	}
702 
703 	if (!node) {
704 		php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
705 		RETURN_THROWS();
706 	}
707 
708 	DOM_RET_OBJ((xmlNodePtr) node, intern);
709 
710 }
711 /* }}} end dom_document_create_attribute */
712 
713 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-392B75AE
714 Since:
715 */
PHP_METHOD(DOMDocument,createEntityReference)716 PHP_METHOD(DOMDocument, createEntityReference)
717 {
718 	zval *id;
719 	xmlNode *node;
720 	xmlDocPtr docp = NULL;
721 	dom_object *intern;
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(BAD_CAST 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, BAD_CAST 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, 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-Document-importNode
748 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-importnode
749 Since: DOM Level 2
750 */
PHP_METHOD(DOMDocument,importNode)751 PHP_METHOD(DOMDocument, importNode)
752 {
753 	zval *node;
754 	xmlDocPtr docp;
755 	xmlNodePtr nodep, retnodep;
756 	dom_object *intern, *nodeobj;
757 	bool recursive = 0;
758 
759 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &node, dom_node_class_entry, &recursive) == FAILURE) {
760 		RETURN_THROWS();
761 	}
762 
763 	DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
764 
765 	DOM_GET_OBJ(nodep, node, xmlNodePtr, nodeobj);
766 
767 	if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE) {
768 		php_error_docref(NULL, E_WARNING, "Cannot import: Node Type Not Supported");
769 		RETURN_FALSE;
770 	}
771 
772 	if (nodep->doc == docp) {
773 		retnodep = nodep;
774 	} else {
775 		retnodep = dom_clone_node(NULL, nodep, docp, recursive);
776 		if (!retnodep) {
777 			RETURN_FALSE;
778 		}
779 
780 		if (retnodep->type == XML_ATTRIBUTE_NODE && nodep->ns != NULL && retnodep->ns == NULL) {
781 			xmlNsPtr nsptr = NULL;
782 			xmlNodePtr root = xmlDocGetRootElement(docp);
783 
784 			nsptr = xmlSearchNsByHref (nodep->doc, root, nodep->ns->href);
785 			if (nsptr == NULL || nsptr->prefix == NULL) {
786 				int errorcode;
787 				nsptr = dom_get_ns(root, (char *) nodep->ns->href, &errorcode, (char *) nodep->ns->prefix);
788 
789 				/* If there is no root, the namespace cannot be attached to it, so we have to attach it to the old list. */
790 				if (nsptr != NULL && root == NULL) {
791 					php_libxml_set_old_ns(nodep->doc, nsptr);
792 				}
793 			}
794 			retnodep->ns = nsptr;
795 		}
796 	}
797 
798 	DOM_RET_OBJ(retnodep, intern);
799 }
800 
dom_modern_document_import_node(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * node_ce)801 static void dom_modern_document_import_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce)
802 {
803 	zval *node;
804 	xmlDocPtr docp;
805 	xmlNodePtr nodep, retnodep;
806 	dom_object *intern, *nodeobj;
807 	bool recursive = 0;
808 
809 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &node, node_ce, &recursive) != SUCCESS) {
810 		RETURN_THROWS();
811 	}
812 
813 	DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
814 
815 	DOM_GET_OBJ(nodep, node, xmlNodePtr, nodeobj);
816 
817 	if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE) {
818 		php_dom_throw_error(NOT_SUPPORTED_ERR, /* strict */ true);
819 		RETURN_THROWS();
820 	}
821 
822 	if (nodep->doc == docp) {
823 		retnodep = nodep;
824 	} else {
825 		retnodep = dom_clone_node(php_dom_get_ns_mapper(intern), nodep, docp, recursive);
826 		if (!retnodep) {
827 			php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
828 			RETURN_THROWS();
829 		}
830 	}
831 
832 	DOM_RET_OBJ(retnodep, intern);
833 }
834 
PHP_METHOD(Dom_Document,importNode)835 PHP_METHOD(Dom_Document, importNode)
836 {
837 	dom_modern_document_import_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry);
838 }
839 /* }}} end dom_document_import_node */
840 
PHP_METHOD(Dom_Document,importLegacyNode)841 PHP_METHOD(Dom_Document, importLegacyNode)
842 {
843 	dom_modern_document_import_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry);
844 }
845 
846 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS
847 Modern spec URL: https://dom.spec.whatwg.org/#internal-createelementns-steps
848 Since: DOM Level 2
849 */
PHP_METHOD(DOMDocument,createElementNS)850 PHP_METHOD(DOMDocument, createElementNS)
851 {
852 	xmlDocPtr docp;
853 	xmlNodePtr nodep = NULL;
854 	size_t value_len = 0;
855 	char *value = NULL;
856 	int errorcode;
857 	dom_object *intern;
858 	zend_string *name = NULL, *uri;
859 
860 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!S|s", &uri, &name, &value, &value_len) == FAILURE) {
861 		RETURN_THROWS();
862 	}
863 
864 	DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
865 
866 	char *localname = NULL, *prefix = NULL;
867 	errorcode = dom_check_qname(ZSTR_VAL(name), &localname, &prefix, uri ? ZSTR_LEN(uri) : 0, ZSTR_LEN(name));
868 
869 	if (errorcode == 0) {
870 		if (xmlValidateName(BAD_CAST localname, 0) == 0) {
871 			nodep = xmlNewDocNode(docp, NULL, BAD_CAST localname, BAD_CAST value);
872 			if (UNEXPECTED(nodep == NULL)) {
873 				php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
874 				RETURN_THROWS();
875 			}
876 
877 			if (uri != NULL) {
878 				xmlNsPtr nsptr = xmlSearchNsByHref(nodep->doc, nodep, BAD_CAST ZSTR_VAL(uri));
879 				if (nsptr == NULL) {
880 					nsptr = dom_get_ns(nodep, ZSTR_VAL(uri), &errorcode, prefix);
881 				}
882 				nodep->ns = nsptr;
883 			}
884 		} else {
885 			errorcode = INVALID_CHARACTER_ERR;
886 		}
887 	}
888 
889 	xmlFree(localname);
890 	xmlFree(prefix);
891 
892 	if (errorcode != 0) {
893 		xmlFreeNode(nodep);
894 		php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
895 		RETURN_FALSE;
896 	}
897 
898 	DOM_RET_OBJ(nodep, intern);
899 }
900 
PHP_METHOD(Dom_Document,createElementNS)901 PHP_METHOD(Dom_Document, createElementNS)
902 {
903 	xmlDocPtr docp;
904 	dom_object *intern;
905 	zend_string *name, *uri;
906 
907 	ZEND_PARSE_PARAMETERS_START(2, 2)
908 		Z_PARAM_STR_OR_NULL(uri)
909 		Z_PARAM_STR(name)
910 	ZEND_PARSE_PARAMETERS_END();
911 
912 	DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
913 
914 	xmlChar *localname = NULL, *prefix = NULL;
915 	int errorcode = dom_validate_and_extract(uri, name, &localname, &prefix);
916 
917 	if (errorcode == 0) {
918 		php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
919 		xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri);
920 
921 		/* Try to create the node with the local name interned. */
922 		const xmlChar *interned_localname = xmlDictLookup(docp->dict, localname, -1);
923 		xmlNodePtr nodep;
924 		if (interned_localname == NULL) {
925 			nodep = xmlNewDocNodeEatName(docp, ns, localname, NULL);
926 		} else {
927 			xmlFree(localname);
928 			nodep = xmlNewDocNodeEatName(docp, ns, BAD_CAST interned_localname, NULL);
929 		}
930 
931 		if (UNEXPECTED(nodep == NULL)) {
932 			php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
933 		} else {
934 			DOM_RET_OBJ(nodep, intern);
935 		}
936 	} else {
937 		php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
938 		xmlFree(localname);
939 	}
940 
941 	xmlFree(prefix);
942 }
943 /* }}} end dom_document_create_element_ns */
944 
945 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNS
946 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createattributens
947 Since: DOM Level 2
948 */
PHP_METHOD(DOMDocument,createAttributeNS)949 PHP_METHOD(DOMDocument, createAttributeNS)
950 {
951 	zval *id;
952 	xmlDocPtr docp;
953 	xmlNodePtr nodep = NULL, root;
954 	xmlNsPtr nsptr;
955 	zend_string *name, *uri;
956 	xmlChar *localname = NULL, *prefix = NULL;
957 	dom_object *intern;
958 	int errorcode;
959 
960 	id = ZEND_THIS;
961 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!S", &uri, &name) == FAILURE) {
962 		RETURN_THROWS();
963 	}
964 
965 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
966 
967 	root = xmlDocGetRootElement(docp);
968 	if (root != NULL || php_dom_follow_spec_intern(intern)) {
969 		errorcode = dom_validate_and_extract(uri, name, &localname, &prefix);
970 		if (UNEXPECTED(errorcode != 0)) {
971 			if (!php_dom_follow_spec_intern(intern)) {
972 				/* legacy behaviour */
973 				errorcode = NAMESPACE_ERR;
974 			}
975 			goto error;
976 		}
977 
978 		nodep = (xmlNodePtr) xmlNewDocProp(docp, localname, NULL);
979 		if (UNEXPECTED(nodep == NULL)) {
980 			php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
981 			RETURN_THROWS();
982 		}
983 
984 		if (uri != NULL && ZSTR_LEN(uri) > 0) {
985 			if (php_dom_follow_spec_intern(intern)) {
986 				php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
987 				nsptr = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri);
988 			} else {
989 				nsptr = xmlSearchNsByHref(docp, root, BAD_CAST ZSTR_VAL(uri));
990 
991 				if (zend_string_equals_literal(name, "xmlns") || xmlStrEqual(BAD_CAST prefix, BAD_CAST "xml")) {
992 					if (nsptr == NULL) {
993 						nsptr = xmlNewNs(NULL, BAD_CAST ZSTR_VAL(uri), BAD_CAST prefix);
994 						php_libxml_set_old_ns(docp, nsptr);
995 					}
996 				} else {
997 					if (nsptr == NULL || nsptr->prefix == NULL) {
998 						nsptr = dom_get_ns_unchecked(root, ZSTR_VAL(uri), prefix ? (char *) prefix : "default");
999 						if (UNEXPECTED(nsptr == NULL)) {
1000 							errorcode = NAMESPACE_ERR;
1001 						}
1002 					}
1003 				}
1004 			}
1005 			nodep->ns = nsptr;
1006 		}
1007 	} else {
1008 		php_error_docref(NULL, E_WARNING, "Document Missing Root Element");
1009 		RETURN_FALSE;
1010 	}
1011 
1012 error:
1013 	xmlFree(localname);
1014 	xmlFree(prefix);
1015 
1016 	if (errorcode != 0) {
1017 		xmlFreeProp((xmlAttrPtr) nodep);
1018 		php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
1019 		RETURN_FALSE;
1020 	}
1021 
1022 	DOM_RET_OBJ(nodep, intern);
1023 }
1024 /* }}} end dom_document_create_attribute_ns */
1025 
1026 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBId
1027 Since: DOM Level 2
1028 */
PHP_METHOD(DOMDocument,getElementById)1029 PHP_METHOD(DOMDocument, getElementById)
1030 {
1031 	xmlDocPtr docp;
1032 	size_t idname_len;
1033 	dom_object *intern;
1034 	char *idname;
1035 
1036 	ZEND_PARSE_PARAMETERS_START(1, 1)
1037 		Z_PARAM_STRING(idname, idname_len)
1038 	ZEND_PARSE_PARAMETERS_END();
1039 
1040 	DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
1041 
1042 	/* If the document has not been manipulated yet, the ID cache will be in sync and we can trust its result.
1043 	 * This check mainly exists because a lot of times people just query a document without modifying it,
1044 	 * and we can allow quick access to IDs in that case. */
1045 	if (!dom_is_document_cache_modified_since_parsing(intern->document)) {
1046 		const xmlAttr *attrp = xmlGetID(docp, BAD_CAST idname);
1047 		if (attrp && attrp->parent) {
1048 			DOM_RET_OBJ(attrp->parent, intern);
1049 		}
1050 	} else {
1051 		/* From the moment an ID is created, libxml2's behaviour is to cache that element, even
1052 		 * if that element is not yet attached to the document. Similarly, only upon destruction of
1053 		 * the element the ID is actually removed by libxml2. Since libxml2 has such behaviour deeply
1054 		 * ingrained in the library, and uses the cache for various purposes, it seems like a bad
1055 		 * idea and lost cause to fight it. */
1056 
1057 		const xmlNode *base = (const xmlNode *) docp;
1058 		const xmlNode *node = base->children;
1059 		while (node != NULL) {
1060 			if (node->type == XML_ELEMENT_NODE) {
1061 				for (const xmlAttr *attr = node->properties; attr != NULL; attr = attr->next) {
1062 					if (attr->atype == XML_ATTRIBUTE_ID && dom_compare_value(attr, BAD_CAST idname)) {
1063 						DOM_RET_OBJ((xmlNodePtr) node, intern);
1064 						return;
1065 					}
1066 				}
1067 			}
1068 
1069 			node = php_dom_next_in_tree_order(node, base);
1070 		}
1071 	}
1072 }
1073 /* }}} end dom_document_get_element_by_id */
1074 
php_dom_transfer_document_ref_single_node(xmlNodePtr node,php_libxml_ref_obj * new_document)1075 static zend_always_inline void php_dom_transfer_document_ref_single_node(xmlNodePtr node, php_libxml_ref_obj *new_document)
1076 {
1077 	php_libxml_node_ptr *iteration_object_ptr = node->_private;
1078 	if (iteration_object_ptr) {
1079 		php_libxml_node_object *iteration_object = iteration_object_ptr->_private;
1080 		ZEND_ASSERT(iteration_object != NULL);
1081 		/* Must increase refcount first because we could be the last reference holder, and the document may be equal. */
1082 		new_document->refcount++;
1083 		php_libxml_decrement_doc_ref(iteration_object);
1084 		iteration_object->document = new_document;
1085 	}
1086 }
1087 
php_dom_transfer_document_ref(xmlNodePtr node,php_libxml_ref_obj * new_document)1088 static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *new_document)
1089 {
1090 	if (node->children) {
1091 		php_dom_transfer_document_ref(node->children, new_document);
1092 	}
1093 
1094 	while (node) {
1095 		if (node->type == XML_ELEMENT_NODE) {
1096 			for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
1097 				php_dom_transfer_document_ref_single_node((xmlNodePtr) attr, new_document);
1098 			}
1099 		}
1100 
1101 		php_dom_transfer_document_ref_single_node(node, new_document);
1102 		node = node->next;
1103 	}
1104 }
1105 
1106 /* Workaround for bug that was fixed in https://github.com/GNOME/libxml2/commit/4bc3ebf3eaba352fbbce2ef70ad00a3c7752478a */
1107 #if LIBXML_VERSION < 21000
libxml_copy_dicted_string(xmlDictPtr src_dict,xmlDictPtr dst_dict,xmlChar * str)1108 static xmlChar *libxml_copy_dicted_string(xmlDictPtr src_dict, xmlDictPtr dst_dict, xmlChar *str)
1109 {
1110 	if (str == NULL) {
1111 		return NULL;
1112 	}
1113 	if (xmlDictOwns(src_dict, str) == 1) {
1114 		if (dst_dict == NULL) {
1115 			return xmlStrdup(str);
1116 		}
1117 		return BAD_CAST xmlDictLookup(dst_dict, str, -1);
1118 	}
1119 	return str;
1120 }
1121 
libxml_fixup_name_and_content(xmlDocPtr src_doc,xmlDocPtr dst_doc,xmlNodePtr node)1122 static void libxml_fixup_name_and_content(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
1123 {
1124 	if (src_doc != NULL && dst_doc != src_doc && src_doc->dict != NULL) {
1125 		node->name = libxml_copy_dicted_string(src_doc->dict, dst_doc->dict, BAD_CAST node->name);
1126 		node->content = libxml_copy_dicted_string(src_doc->dict, NULL, node->content);
1127 	}
1128 }
1129 
libxml_fixup_name_and_content_element(xmlDocPtr src_doc,xmlDocPtr dst_doc,xmlNodePtr node)1130 static void libxml_fixup_name_and_content_element(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
1131 {
1132 	libxml_fixup_name_and_content(src_doc, dst_doc, node);
1133 	for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
1134 		libxml_fixup_name_and_content(src_doc, dst_doc, (xmlNodePtr) attr);
1135 	}
1136 
1137 	for (xmlNodePtr child = node->children; child != NULL; child = child->next) {
1138 		libxml_fixup_name_and_content_element(src_doc, dst_doc, child);
1139 	}
1140 }
1141 #endif
1142 
php_dom_adopt_node(xmlNodePtr nodep,dom_object * dom_object_new_document,xmlDocPtr new_document)1143 bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document)
1144 {
1145 	xmlDocPtr original_document = nodep->doc;
1146 	php_libxml_invalidate_node_list_cache_from_doc(original_document);
1147 	if (nodep->doc != new_document) {
1148 		php_libxml_invalidate_node_list_cache(dom_object_new_document->document);
1149 
1150 		/* Note for ATTRIBUTE_NODE: specified is always true in ext/dom,
1151 		 * and since this unlink it; the owner element will be unset (i.e. parentNode). */
1152 		if (php_dom_follow_spec_intern(dom_object_new_document)) {
1153 			xmlUnlinkNode(nodep);
1154 			xmlSetTreeDoc(nodep, new_document);
1155 			php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(dom_object_new_document);
1156 			php_dom_libxml_reconcile_modern(ns_mapper, nodep);
1157 #if LIBXML_VERSION < 21000
1158 			libxml_fixup_name_and_content_element(original_document, new_document, nodep);
1159 #endif
1160 		} else {
1161 			int ret = xmlDOMWrapAdoptNode(NULL, original_document, nodep, new_document, NULL, /* options, unused */ 0);
1162 			if (UNEXPECTED(ret != 0)) {
1163 				return false;
1164 			}
1165 		}
1166 
1167 		php_dom_transfer_document_ref(nodep, dom_object_new_document->document);
1168 	} else {
1169 		xmlUnlinkNode(nodep);
1170 	}
1171 	return true;
1172 }
1173 
1174 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNode
1175 Since: DOM Level 3
1176 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-adoptnode
1177 */
dom_document_adopt_node(INTERNAL_FUNCTION_PARAMETERS,bool modern)1178 static void dom_document_adopt_node(INTERNAL_FUNCTION_PARAMETERS, bool modern)
1179 {
1180 	zval *node_zval;
1181 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, dom_get_node_ce(modern)) == FAILURE) {
1182 		RETURN_THROWS();
1183 	}
1184 
1185 	xmlNodePtr nodep;
1186 	dom_object *dom_object_nodep;
1187 	DOM_GET_OBJ(nodep, node_zval, xmlNodePtr, dom_object_nodep);
1188 
1189 	if (UNEXPECTED(nodep->type == XML_DOCUMENT_NODE
1190 		|| nodep->type == XML_HTML_DOCUMENT_NODE
1191 		|| nodep->type == XML_DOCUMENT_TYPE_NODE
1192 		|| nodep->type == XML_DTD_NODE
1193 		|| nodep->type == XML_ENTITY_NODE
1194 		|| nodep->type == XML_NOTATION_NODE)) {
1195 		php_dom_throw_error(NOT_SUPPORTED_ERR, dom_get_strict_error(dom_object_nodep->document));
1196 		RETURN_FALSE;
1197 	}
1198 
1199 	xmlDocPtr new_document;
1200 	dom_object *dom_object_new_document;
1201 	zval *new_document_zval = ZEND_THIS;
1202 	DOM_GET_OBJ(new_document, new_document_zval, xmlDocPtr, dom_object_new_document);
1203 
1204 	if (!php_dom_adopt_node(nodep, dom_object_new_document, new_document)) {
1205 		if (modern) {
1206 			php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
1207 			RETURN_THROWS();
1208 		}
1209 		RETURN_FALSE;
1210 	}
1211 
1212 	RETURN_OBJ_COPY(&dom_object_nodep->std);
1213 }
1214 
PHP_METHOD(DOMDocument,adoptNode)1215 PHP_METHOD(DOMDocument, adoptNode)
1216 {
1217 	dom_document_adopt_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1218 }
1219 
PHP_METHOD(Dom_Document,adoptNode)1220 PHP_METHOD(Dom_Document, adoptNode)
1221 {
1222 	dom_document_adopt_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1223 }
1224 /* }}} end dom_document_adopt_node */
1225 
1226 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocument
1227 Since: DOM Level 3
1228 */
PHP_METHOD(DOMDocument,normalizeDocument)1229 PHP_METHOD(DOMDocument, normalizeDocument)
1230 {
1231 	zval *id;
1232 	xmlDocPtr docp;
1233 	dom_object *intern;
1234 
1235 	id = ZEND_THIS;
1236 	if (zend_parse_parameters_none() == FAILURE) {
1237 		RETURN_THROWS();
1238 	}
1239 
1240 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1241 
1242 	php_dom_normalize_legacy((xmlNodePtr) docp);
1243 }
1244 /* }}} end dom_document_normalize_document */
1245 
1246 /* {{{ */
PHP_METHOD(DOMDocument,__construct)1247 PHP_METHOD(DOMDocument, __construct)
1248 {
1249 	xmlDoc *docp = NULL, *olddoc;
1250 	dom_object *intern;
1251 	char *encoding, *version = NULL;
1252 	size_t encoding_len = 0, version_len = 0;
1253 	int refcount;
1254 
1255 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &version, &version_len, &encoding, &encoding_len) == FAILURE) {
1256 		RETURN_THROWS();
1257 	}
1258 
1259 	docp = xmlNewDoc(BAD_CAST version);
1260 
1261 	if (!docp) {
1262 		php_dom_throw_error(INVALID_STATE_ERR, true);
1263 		return;
1264 	}
1265 
1266 	if (encoding_len > 0) {
1267 		docp->encoding = (const xmlChar *) xmlStrdup(BAD_CAST encoding);
1268 	}
1269 
1270 	intern = Z_DOMOBJ_P(ZEND_THIS);
1271 	olddoc = (xmlDocPtr) dom_object_get_node(intern);
1272 	if (olddoc != NULL) {
1273 		php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1274 		refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1275 		if (refcount != 0) {
1276 			olddoc->_private = NULL;
1277 		}
1278 	}
1279 	intern->document = NULL;
1280 	if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp) == -1) {
1281 		/* docp is always non-null so php_libxml_increment_doc_ref() never returns -1 */
1282 		ZEND_UNREACHABLE();
1283 	}
1284 	php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)docp, (void *)intern);
1285 }
1286 /* }}} end DOMDocument::__construct */
1287 
_dom_get_valid_file_path(const char * source,char * resolved_path,int resolved_path_len)1288 const char *_dom_get_valid_file_path(const char *source, char *resolved_path, int resolved_path_len ) /* {{{ */
1289 {
1290 	xmlURI *uri;
1291 	xmlChar *escsource;
1292 	const char *file_dest;
1293 	int isFileUri = 0;
1294 
1295 	uri = xmlCreateURI();
1296 	if (uri == NULL) {
1297 		return NULL;
1298 	}
1299 	escsource = xmlURIEscapeStr(BAD_CAST source, BAD_CAST ":");
1300 	xmlParseURIReference(uri, (char *) escsource);
1301 	xmlFree(escsource);
1302 
1303 	if (uri->scheme != NULL) {
1304 		/* absolute file uris - libxml only supports localhost or empty host */
1305 #ifdef PHP_WIN32
1306 		if (strncasecmp(source, "file://",7) == 0 && ':' == source[8]) {
1307 			isFileUri = 1;
1308 			source += 7;
1309 		} else
1310 #endif
1311 		if (strncasecmp(source, "file:///",8) == 0) {
1312 			isFileUri = 1;
1313 #ifdef PHP_WIN32
1314 			source += 8;
1315 #else
1316 			source += 7;
1317 #endif
1318 		} else if (strncasecmp(source, "file://localhost/",17) == 0) {
1319 			isFileUri = 1;
1320 #ifdef PHP_WIN32
1321 			source += 17;
1322 #else
1323 			source += 16;
1324 #endif
1325 		}
1326 	}
1327 
1328 	file_dest = source;
1329 
1330 	if ((uri->scheme == NULL || isFileUri)) {
1331 		/* XXX possible buffer overflow if VCWD_REALPATH does not know size of resolved_path */
1332 		if (!VCWD_REALPATH(source, resolved_path) && !expand_filepath(source, resolved_path)) {
1333 			xmlFreeURI(uri);
1334 			return NULL;
1335 		}
1336 		file_dest = resolved_path;
1337 	}
1338 
1339 	xmlFreeURI(uri);
1340 
1341 	return file_dest;
1342 }
1343 /* }}} */
1344 
dom_document_parser(zval * id,dom_load_mode mode,const char * source,size_t source_len,size_t options,xmlCharEncodingHandlerPtr encoding)1345 xmlDocPtr dom_document_parser(zval *id, dom_load_mode mode, const char *source, size_t source_len, size_t options, xmlCharEncodingHandlerPtr encoding) /* {{{ */
1346 {
1347 	xmlDocPtr ret;
1348 	xmlParserCtxtPtr ctxt = NULL;
1349 	int validate, recover, resolve_externals, keep_blanks, substitute_ent;
1350 	int resolved_path_len;
1351 	int old_error_reporting = 0;
1352 	char *directory=NULL, resolved_path[MAXPATHLEN + 1];
1353 
1354 	libxml_doc_props const* doc_props;
1355 	if (id == NULL) {
1356 		doc_props = dom_get_doc_props_read_only(NULL);
1357 	} else {
1358 		dom_object *intern = Z_DOMOBJ_P(id);
1359 		php_libxml_ref_obj *document = intern->document;
1360 		doc_props = dom_get_doc_props_read_only(document);
1361 	}
1362 	validate = doc_props->validateonparse;
1363 	resolve_externals = doc_props->resolveexternals;
1364 	keep_blanks = doc_props->preservewhitespace;
1365 	substitute_ent = doc_props->substituteentities;
1366 	recover = doc_props->recover || (options & XML_PARSE_RECOVER) == XML_PARSE_RECOVER;
1367 
1368 	xmlInitParser();
1369 
1370 	if (mode == DOM_LOAD_FILE) {
1371 		if (CHECK_NULL_PATH(source, source_len)) {
1372 			zend_argument_value_error(1, "must not contain any null bytes");
1373 			return NULL;
1374 		}
1375 		const char *file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1376 		if (file_dest) {
1377 			ctxt = xmlCreateFileParserCtxt(file_dest);
1378 		}
1379 	} else {
1380 		ctxt = xmlCreateMemoryParserCtxt(source, source_len);
1381 	}
1382 
1383 	if (ctxt == NULL) {
1384 		return(NULL);
1385 	}
1386 
1387 	if (encoding != NULL) {
1388 		/* Note: libxml 2.12+ doesn't handle NULL encoding well. */
1389 		(void) xmlSwitchToEncoding(ctxt, encoding);
1390 	}
1391 
1392 	/* If loading from memory, we need to set the base directory for the document */
1393 	if (mode != DOM_LOAD_FILE) {
1394 #ifdef HAVE_GETCWD
1395 		directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
1396 #elif defined(HAVE_GETWD)
1397 		directory = VCWD_GETWD(resolved_path);
1398 #endif
1399 		if (directory) {
1400 			if(ctxt->directory != NULL) {
1401 				xmlFree((char *) ctxt->directory);
1402 			}
1403 			resolved_path_len = strlen(resolved_path);
1404 			if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) {
1405 				resolved_path[resolved_path_len] = DEFAULT_SLASH;
1406 				resolved_path[++resolved_path_len] = '\0';
1407 			}
1408 			ctxt->directory = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
1409 		}
1410 	}
1411 
1412 	ctxt->vctxt.error = php_libxml_ctx_error;
1413 	ctxt->vctxt.warning = php_libxml_ctx_warning;
1414 
1415 	if (ctxt->sax != NULL) {
1416 		ctxt->sax->error = php_libxml_ctx_error;
1417 		ctxt->sax->warning = php_libxml_ctx_warning;
1418 	}
1419 
1420 	if (validate && ! (options & XML_PARSE_DTDVALID)) {
1421 		options |= XML_PARSE_DTDVALID;
1422 	}
1423 	if (resolve_externals && ! (options & XML_PARSE_DTDATTR)) {
1424 		options |= XML_PARSE_DTDATTR;
1425 	}
1426 	if (substitute_ent && ! (options & XML_PARSE_NOENT)) {
1427 		options |= XML_PARSE_NOENT;
1428 	}
1429 	if (keep_blanks == 0 && ! (options & XML_PARSE_NOBLANKS)) {
1430 		options |= XML_PARSE_NOBLANKS;
1431 	}
1432 
1433 	php_libxml_sanitize_parse_ctxt_options(ctxt);
1434 	xmlCtxtUseOptions(ctxt, options);
1435 
1436 	ctxt->recovery = recover;
1437 	if (recover) {
1438 		old_error_reporting = EG(error_reporting);
1439 		EG(error_reporting) = old_error_reporting | E_WARNING;
1440 	}
1441 
1442 	xmlParseDocument(ctxt);
1443 
1444 	if (ctxt->wellFormed || recover) {
1445 		ret = ctxt->myDoc;
1446 		if (ctxt->recovery) {
1447 			EG(error_reporting) = old_error_reporting;
1448 		}
1449 		/* If loading from memory, set the base reference uri for the document */
1450 		if (ret && ret->URL == NULL && ctxt->directory != NULL) {
1451 			ret->URL = xmlStrdup(BAD_CAST ctxt->directory);
1452 		}
1453 	} else {
1454 		ret = DOM_DOCUMENT_MALFORMED;
1455 		xmlFreeDoc(ctxt->myDoc);
1456 		ctxt->myDoc = NULL;
1457 	}
1458 
1459 	xmlFreeParserCtxt(ctxt);
1460 
1461 	return(ret);
1462 }
1463 /* }}} */
1464 
php_dom_finish_loading_document(zval * this,zval * return_value,xmlDocPtr newdoc)1465 static void php_dom_finish_loading_document(zval *this, zval *return_value, xmlDocPtr newdoc)
1466 {
1467 	if (!newdoc)
1468 		RETURN_FALSE;
1469 
1470 	dom_object *intern = Z_DOMOBJ_P(this);
1471 	size_t old_modification_nr = 0;
1472 	if (intern != NULL) {
1473 		php_libxml_class_type class_type = PHP_LIBXML_CLASS_LEGACY;
1474 		xmlDocPtr docp = (xmlDocPtr) dom_object_get_node(intern);
1475 		dom_doc_propsptr doc_prop = NULL;
1476 		if (docp != NULL) {
1477 			const php_libxml_ref_obj *doc_ptr = intern->document;
1478 			ZEND_ASSERT(doc_ptr != NULL); /* Must exist, we have a document */
1479 			class_type = doc_ptr->class_type;
1480 			old_modification_nr = doc_ptr->cache_tag.modification_nr;
1481 			php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1482 			doc_prop = intern->document->doc_props;
1483 			intern->document->doc_props = NULL;
1484 			int refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1485 			if (refcount != 0) {
1486 				docp->_private = NULL;
1487 			}
1488 		}
1489 		intern->document = NULL;
1490 		if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc) == -1) {
1491 			RETURN_FALSE;
1492 		}
1493 		intern->document->doc_props = doc_prop;
1494 		intern->document->class_type = class_type;
1495 	}
1496 
1497 	php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
1498 	/* Since iterators should invalidate, we need to start the modification number from the old counter */
1499 	if (old_modification_nr != 0) {
1500 		intern->document->cache_tag.modification_nr = old_modification_nr;
1501 		php_libxml_invalidate_node_list_cache(intern->document);
1502 	}
1503 
1504 	RETURN_TRUE;
1505 }
1506 
dom_parse_document(INTERNAL_FUNCTION_PARAMETERS,int mode)1507 static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode)
1508 {
1509 	char *source;
1510 	size_t source_len;
1511 	zend_long options = 0;
1512 
1513 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
1514 		RETURN_THROWS();
1515 	}
1516 
1517 	if (!source_len) {
1518 		zend_argument_value_error(1, "must not be empty");
1519 		RETURN_THROWS();
1520 	}
1521 	if (ZEND_SIZE_T_INT_OVFL(source_len)) {
1522 		php_error_docref(NULL, E_WARNING, "Input string is too long");
1523 		RETURN_FALSE;
1524 	}
1525 	if (ZEND_LONG_EXCEEDS_INT(options)) {
1526 		php_error_docref(NULL, E_WARNING, "Invalid options");
1527 		RETURN_FALSE;
1528 	}
1529 
1530 	xmlDocPtr newdoc = dom_document_parser(ZEND_THIS, mode, source, source_len, options, NULL);
1531 	if (newdoc == DOM_DOCUMENT_MALFORMED) {
1532 		newdoc = NULL;
1533 	}
1534 	php_dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
1535 }
1536 
1537 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-load
1538 Since: DOM Level 3
1539 */
PHP_METHOD(DOMDocument,load)1540 PHP_METHOD(DOMDocument, load)
1541 {
1542 	dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1543 }
1544 /* }}} end dom_document_load */
1545 
1546 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadXML
1547 Since: DOM Level 3
1548 */
PHP_METHOD(DOMDocument,loadXML)1549 PHP_METHOD(DOMDocument, loadXML)
1550 {
1551 	dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1552 }
1553 /* }}} end dom_document_loadxml */
1554 
1555 /* {{{ Convenience method to save to file */
PHP_METHOD(DOMDocument,save)1556 PHP_METHOD(DOMDocument, save)
1557 {
1558 	zval *id;
1559 	xmlDoc *docp;
1560 	size_t file_len = 0;
1561 	int saveempty = 0;
1562 	dom_object *intern;
1563 	char *file;
1564 	zend_long options = 0;
1565 
1566 	id = ZEND_THIS;
1567 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &file, &file_len, &options) == FAILURE) {
1568 		RETURN_THROWS();
1569 	}
1570 
1571 	if (file_len == 0) {
1572 		zend_argument_value_error(1, "must not be empty");
1573 		RETURN_THROWS();
1574 	}
1575 
1576 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1577 
1578 	/* encoding handled by property on doc */
1579 
1580 	libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
1581 	bool format = doc_props->formatoutput;
1582 	if (options & LIBXML_SAVE_NOEMPTYTAG) {
1583 		saveempty = xmlSaveNoEmptyTags;
1584 		xmlSaveNoEmptyTags = 1;
1585 	}
1586 	zend_long bytes = intern->document->handlers->dump_doc_to_file(file, docp, format, (const char *) docp->encoding);
1587 	if (options & LIBXML_SAVE_NOEMPTYTAG) {
1588 		xmlSaveNoEmptyTags = saveempty;
1589 	}
1590 	if (bytes == -1) {
1591 		RETURN_FALSE;
1592 	}
1593 	RETURN_LONG(bytes);
1594 }
1595 /* }}} end dom_document_save */
1596 
1597 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML
1598 Since: DOM Level 3
1599 */
dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * node_ce)1600 static void dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce)
1601 {
1602 	zval *nodep = NULL;
1603 	xmlDoc *docp;
1604 	xmlNode *node;
1605 	dom_object *intern, *nodeobj;
1606 	int old_xml_save_no_empty_tags;
1607 	zend_long options = 0;
1608 
1609 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!l", &nodep, node_ce, &options) != SUCCESS) {
1610 		RETURN_THROWS();
1611 	}
1612 
1613 	DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
1614 
1615 	libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
1616 	bool format = doc_props->formatoutput;
1617 
1618 	zend_string *res;
1619 	if (nodep != NULL) {
1620 		/* Dump contents of Node */
1621 		DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
1622 		if (node->doc != docp) {
1623 			php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
1624 			RETURN_FALSE;
1625 		}
1626 
1627 		/* Save libxml2 global, override its value, and restore after saving (don't move me or risk breaking the state
1628 		 * w.r.t. the implicit return in DOM_GET_OBJ). */
1629 		old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
1630 		xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
1631 		res = intern->document->handlers->dump_node_to_str(docp, node, format, (const char *) docp->encoding);
1632 		xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
1633 	} else {
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 
1642 		/* Save libxml2 global, override its value, and restore after saving. */
1643 		old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
1644 		xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
1645 		res = intern->document->handlers->dump_doc_to_str(docp, converted_options, (const char *) docp->encoding);
1646 		xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
1647 	}
1648 
1649 	if (!res) {
1650 		php_error_docref(NULL, E_WARNING, "Could not save document");
1651 		RETURN_FALSE;
1652 	} else {
1653 		RETURN_NEW_STR(res);
1654 	}
1655 }
1656 
PHP_METHOD(DOMDocument,saveXML)1657 PHP_METHOD(DOMDocument, saveXML)
1658 {
1659 	dom_document_save_xml(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry);
1660 }
1661 
PHP_METHOD(Dom_XMLDocument,saveXml)1662 PHP_METHOD(Dom_XMLDocument, saveXml)
1663 {
1664 	dom_document_save_xml(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry);
1665 }
1666 /* }}} end dom_document_savexml */
1667 
php_dom_free_xinclude_node(xmlNodePtr cur)1668 static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) /* {{{ */
1669 {
1670 	xmlNodePtr xincnode;
1671 
1672 	xincnode = cur;
1673 	cur = cur->next;
1674 	xmlUnlinkNode(xincnode);
1675 	php_libxml_node_free_resource(xincnode);
1676 
1677 	return cur;
1678 }
1679 /* }}} */
1680 
php_dom_remove_xinclude_nodes(xmlNodePtr cur)1681 static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
1682 {
1683 	while(cur) {
1684 		if (cur->type == XML_XINCLUDE_START) {
1685 			cur = php_dom_free_xinclude_node(cur);
1686 
1687 			/* XML_XINCLUDE_END node will be a sibling of XML_XINCLUDE_START */
1688 			while(cur && cur->type != XML_XINCLUDE_END) {
1689 				/* remove xinclude processing nodes from recursive xincludes */
1690 				if (cur->type == XML_ELEMENT_NODE) {
1691 					   php_dom_remove_xinclude_nodes(cur->children);
1692 				}
1693 				cur = cur->next;
1694 			}
1695 
1696 			if (cur && cur->type == XML_XINCLUDE_END) {
1697 				cur = php_dom_free_xinclude_node(cur);
1698 			}
1699 		} else {
1700 			if (cur->type == XML_ELEMENT_NODE) {
1701 				php_dom_remove_xinclude_nodes(cur->children);
1702 			}
1703 			cur = cur->next;
1704 		}
1705 	}
1706 }
1707 /* }}} */
1708 
1709 /* {{{ Substitutues xincludes in a DomDocument */
PHP_METHOD(DOMDocument,xinclude)1710 PHP_METHOD(DOMDocument, xinclude)
1711 {
1712 	zval *id;
1713 	xmlDoc *docp;
1714 	xmlNodePtr root;
1715 	zend_long flags = 0;
1716 	int err;
1717 	dom_object *intern;
1718 
1719 	id = ZEND_THIS;
1720 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
1721 		RETURN_THROWS();
1722 	}
1723 
1724 	if (ZEND_LONG_EXCEEDS_INT(flags)) {
1725 		php_error_docref(NULL, E_WARNING, "Invalid flags");
1726 		RETURN_FALSE;
1727 	}
1728 
1729 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1730 
1731 	PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
1732 	err = xmlXIncludeProcessFlags(docp, (int)flags);
1733 	PHP_LIBXML_RESTORE_GLOBALS(xinclude);
1734 
1735 	/* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
1736 	are added via xmlXIncludeProcess to mark beginning and ending of xincluded document
1737 	but are not wanted in resulting document - must be done even if err as it could fail after
1738 	having processed some xincludes */
1739 	root = (xmlNodePtr) docp->children;
1740 	while(root && root->type != XML_ELEMENT_NODE && root->type != XML_XINCLUDE_START) {
1741 		root = root->next;
1742 	}
1743 	if (root) {
1744 		php_dom_remove_xinclude_nodes(root);
1745 	}
1746 
1747 	php_libxml_invalidate_node_list_cache(intern->document);
1748 
1749 	if (err) {
1750 		RETVAL_LONG(err);
1751 	} else {
1752 		RETVAL_FALSE;
1753 	}
1754 }
1755 /* }}} */
1756 
1757 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,validate)1758 PHP_METHOD(DOMDocument, validate)
1759 {
1760 	zval *id;
1761 	xmlDoc *docp;
1762 	dom_object *intern;
1763 	xmlValidCtxt *cvp;
1764 
1765 	id = ZEND_THIS;
1766 	if (zend_parse_parameters_none() == FAILURE) {
1767 		RETURN_THROWS();
1768 	}
1769 
1770 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1771 
1772 	PHP_LIBXML_SANITIZE_GLOBALS(validate);
1773 	cvp = xmlNewValidCtxt();
1774 
1775 	cvp->userData = NULL;
1776 	cvp->error    = (xmlValidityErrorFunc) php_libxml_error_handler;
1777 	cvp->warning  = (xmlValidityErrorFunc) php_libxml_error_handler;
1778 
1779 	if (xmlValidateDocument(cvp, docp)) {
1780 		RETVAL_TRUE;
1781 	} else {
1782 		RETVAL_FALSE;
1783 	}
1784 	PHP_LIBXML_RESTORE_GLOBALS(validate);
1785 
1786 	xmlFreeValidCtxt(cvp);
1787 
1788 }
1789 /* }}} */
1790 
1791 #ifdef LIBXML_SCHEMAS_ENABLED
_dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1792 static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1793 {
1794 	zval *id;
1795 	xmlDoc *docp;
1796 	dom_object *intern;
1797 	char *source = NULL;
1798 	const char *valid_file = NULL;
1799 	size_t source_len = 0;
1800 	int valid_opts = 0;
1801 	zend_long flags = 0;
1802 	xmlSchemaParserCtxtPtr  parser;
1803 	xmlSchemaPtr            sptr;
1804 	xmlSchemaValidCtxtPtr   vptr;
1805 	int                     is_valid;
1806 	char resolved_path[MAXPATHLEN + 1];
1807 
1808 	id = ZEND_THIS;
1809 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &flags) == FAILURE) {
1810 		RETURN_THROWS();
1811 	}
1812 
1813 	if (!source_len) {
1814 		zend_argument_value_error(1, "must not be empty");
1815 		RETURN_THROWS();
1816 	}
1817 
1818 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1819 
1820 	PHP_LIBXML_SANITIZE_GLOBALS(new_parser_ctxt);
1821 
1822 	switch (type) {
1823 	case DOM_LOAD_FILE:
1824 		if (CHECK_NULL_PATH(source, source_len)) {
1825 			PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1826 			zend_argument_value_error(1, "must not contain any null bytes");
1827 			RETURN_THROWS();
1828 		}
1829 		valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1830 		if (!valid_file) {
1831 			PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1832 			php_error_docref(NULL, E_WARNING, "Invalid Schema file source");
1833 			RETURN_FALSE;
1834 		}
1835 		parser = xmlSchemaNewParserCtxt(valid_file);
1836 		break;
1837 	case DOM_LOAD_STRING:
1838 		parser = xmlSchemaNewMemParserCtxt(source, source_len);
1839 		/* If loading from memory, we need to set the base directory for the document
1840 		   but it is not apparent how to do that for schema's */
1841 		break;
1842 	default:
1843 		return;
1844 	}
1845 
1846 	xmlSchemaSetParserErrors(parser,
1847 		(xmlSchemaValidityErrorFunc) php_libxml_error_handler,
1848 		(xmlSchemaValidityWarningFunc) php_libxml_error_handler,
1849 		parser);
1850 	sptr = xmlSchemaParse(parser);
1851 	xmlSchemaFreeParserCtxt(parser);
1852 	PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1853 	if (!sptr) {
1854 		if (!EG(exception)) {
1855 			php_error_docref(NULL, E_WARNING, "Invalid Schema");
1856 		}
1857 		RETURN_FALSE;
1858 	}
1859 
1860 	docp = (xmlDocPtr) dom_object_get_node(intern);
1861 
1862 	vptr = xmlSchemaNewValidCtxt(sptr);
1863 	if (!vptr) {
1864 		xmlSchemaFree(sptr);
1865 		zend_throw_error(NULL, "Invalid Schema Validation Context");
1866 		RETURN_THROWS();
1867 	}
1868 
1869 	if (flags & XML_SCHEMA_VAL_VC_I_CREATE) {
1870 		valid_opts |= XML_SCHEMA_VAL_VC_I_CREATE;
1871 	}
1872 
1873 	PHP_LIBXML_SANITIZE_GLOBALS(validate);
1874 	xmlSchemaSetValidOptions(vptr, valid_opts);
1875 	xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
1876 	is_valid = xmlSchemaValidateDoc(vptr, docp);
1877 	xmlSchemaFree(sptr);
1878 	xmlSchemaFreeValidCtxt(vptr);
1879 	PHP_LIBXML_RESTORE_GLOBALS(validate);
1880 
1881 	if (is_valid == 0) {
1882 		RETURN_TRUE;
1883 	} else {
1884 		RETURN_FALSE;
1885 	}
1886 }
1887 /* }}} */
1888 
1889 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidate)1890 PHP_METHOD(DOMDocument, schemaValidate)
1891 {
1892 	_dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1893 }
1894 /* }}} end dom_document_schema_validate_file */
1895 
1896 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidateSource)1897 PHP_METHOD(DOMDocument, schemaValidateSource)
1898 {
1899 	_dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1900 }
1901 /* }}} end dom_document_schema_validate */
1902 
_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1903 static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1904 {
1905 	zval *id;
1906 	xmlDoc *docp;
1907 	dom_object *intern;
1908 	char *source = NULL;
1909 	const char *valid_file = NULL;
1910 	size_t source_len = 0;
1911 	xmlRelaxNGParserCtxtPtr parser;
1912 	xmlRelaxNGPtr           sptr;
1913 	xmlRelaxNGValidCtxtPtr  vptr;
1914 	int                     is_valid;
1915 	char resolved_path[MAXPATHLEN + 1];
1916 
1917 	id = ZEND_THIS;
1918 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &source, &source_len) == FAILURE) {
1919 		RETURN_THROWS();
1920 	}
1921 
1922 	if (!source_len) {
1923 		zend_argument_value_error(1, "must not be empty");
1924 		RETURN_THROWS();
1925 	}
1926 
1927 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1928 
1929 	switch (type) {
1930 	case DOM_LOAD_FILE:
1931 		if (CHECK_NULL_PATH(source, source_len)) {
1932 			zend_argument_value_error(1, "must not contain any null bytes");
1933 			RETURN_THROWS();
1934 		}
1935 		valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1936 		if (!valid_file) {
1937 			php_error_docref(NULL, E_WARNING, "Invalid RelaxNG file source");
1938 			RETURN_FALSE;
1939 		}
1940 		parser = xmlRelaxNGNewParserCtxt(valid_file);
1941 		break;
1942 	case DOM_LOAD_STRING:
1943 		parser = xmlRelaxNGNewMemParserCtxt(source, source_len);
1944 		/* If loading from memory, we need to set the base directory for the document
1945 		   but it is not apparent how to do that for schema's */
1946 		break;
1947 	default:
1948 		return;
1949 	}
1950 
1951 	PHP_LIBXML_SANITIZE_GLOBALS(parse);
1952 	xmlRelaxNGSetParserErrors(parser,
1953 		(xmlRelaxNGValidityErrorFunc) php_libxml_error_handler,
1954 		(xmlRelaxNGValidityWarningFunc) php_libxml_error_handler,
1955 		parser);
1956 	sptr = xmlRelaxNGParse(parser);
1957 	xmlRelaxNGFreeParserCtxt(parser);
1958 	PHP_LIBXML_RESTORE_GLOBALS(parse);
1959 	if (!sptr) {
1960 		php_error_docref(NULL, E_WARNING, "Invalid RelaxNG");
1961 		RETURN_FALSE;
1962 	}
1963 
1964 	docp = (xmlDocPtr) dom_object_get_node(intern);
1965 
1966 	vptr = xmlRelaxNGNewValidCtxt(sptr);
1967 	if (!vptr) {
1968 		xmlRelaxNGFree(sptr);
1969 		zend_throw_error(NULL, "Invalid RelaxNG Validation Context");
1970 		RETURN_THROWS();
1971 	}
1972 
1973 	xmlRelaxNGSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
1974 	is_valid = xmlRelaxNGValidateDoc(vptr, docp);
1975 	xmlRelaxNGFree(sptr);
1976 	xmlRelaxNGFreeValidCtxt(vptr);
1977 
1978 	if (is_valid == 0) {
1979 		RETURN_TRUE;
1980 	} else {
1981 		RETURN_FALSE;
1982 	}
1983 }
1984 /* }}} */
1985 
1986 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidate)1987 PHP_METHOD(DOMDocument, relaxNGValidate)
1988 {
1989 	_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1990 }
1991 /* }}} end dom_document_relaxNG_validate_file */
1992 
1993 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidateSource)1994 PHP_METHOD(DOMDocument, relaxNGValidateSource)
1995 {
1996 	_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1997 }
1998 /* }}} end dom_document_relaxNG_validate_xml */
1999 
2000 #endif
2001 
2002 #ifdef LIBXML_HTML_ENABLED
2003 
dom_load_html(INTERNAL_FUNCTION_PARAMETERS,int mode)2004 static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
2005 {
2006 	char *source;
2007 	size_t source_len;
2008 	zend_long options = 0;
2009 	htmlParserCtxtPtr ctxt;
2010 
2011 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
2012 		RETURN_THROWS();
2013 	}
2014 
2015 	if (!source_len) {
2016 		zend_argument_value_error(1, "must not be empty");
2017 		RETURN_THROWS();
2018 	}
2019 
2020 	if (ZEND_LONG_EXCEEDS_INT(options)) {
2021 		php_error_docref(NULL, E_WARNING, "Invalid options");
2022 		RETURN_FALSE;
2023 	}
2024 
2025 	if (mode == DOM_LOAD_FILE) {
2026 		if (CHECK_NULL_PATH(source, source_len)) {
2027 			zend_argument_value_error(1, "must not contain any null bytes");
2028 			RETURN_THROWS();
2029 		}
2030 		ctxt = htmlCreateFileParserCtxt(source, NULL);
2031 	} else {
2032 		if (ZEND_SIZE_T_INT_OVFL(source_len)) {
2033 			php_error_docref(NULL, E_WARNING, "Input string is too long");
2034 			RETURN_FALSE;
2035 		}
2036 		ctxt = htmlCreateMemoryParserCtxt(source, (int)source_len);
2037 	}
2038 
2039 	if (!ctxt) {
2040 		RETURN_FALSE;
2041 	}
2042 
2043 
2044 	ctxt->vctxt.error = php_libxml_ctx_error;
2045 	ctxt->vctxt.warning = php_libxml_ctx_warning;
2046 	if (ctxt->sax != NULL) {
2047 		ctxt->sax->error = php_libxml_ctx_error;
2048 		ctxt->sax->warning = php_libxml_ctx_warning;
2049 	}
2050 	php_libxml_sanitize_parse_ctxt_options(ctxt);
2051 	if (options) {
2052 		htmlCtxtUseOptions(ctxt, (int)options);
2053 	}
2054 	htmlParseDocument(ctxt);
2055 	xmlDocPtr newdoc = ctxt->myDoc;
2056 	htmlFreeParserCtxt(ctxt);
2057 
2058 	php_dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
2059 }
2060 /* }}} */
2061 
2062 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTMLFile)2063 PHP_METHOD(DOMDocument, loadHTMLFile)
2064 {
2065 	dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
2066 }
2067 /* }}} end dom_document_load_html_file */
2068 
2069 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTML)2070 PHP_METHOD(DOMDocument, loadHTML)
2071 {
2072 	dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
2073 }
2074 /* }}} end dom_document_load_html */
2075 
2076 /* {{{ Convenience method to save to file as html */
PHP_METHOD(DOMDocument,saveHTMLFile)2077 PHP_METHOD(DOMDocument, saveHTMLFile)
2078 {
2079 	zval *id;
2080 	xmlDoc *docp;
2081 	size_t file_len;
2082 	int bytes, format;
2083 	dom_object *intern;
2084 	char *file;
2085 	const char *encoding;
2086 
2087 	id = ZEND_THIS;
2088 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &file, &file_len) == FAILURE) {
2089 		RETURN_THROWS();
2090 	}
2091 
2092 	if (file_len == 0) {
2093 		zend_argument_value_error(1, "must not be empty");
2094 		RETURN_THROWS();
2095 	}
2096 
2097 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2098 
2099 
2100 	encoding = (const char *) htmlGetMetaEncoding(docp);
2101 
2102 	libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
2103 	format = doc_props->formatoutput;
2104 	bytes = htmlSaveFileFormat(file, docp, encoding, format);
2105 
2106 	if (bytes == -1) {
2107 		RETURN_FALSE;
2108 	}
2109 	RETURN_LONG(bytes);
2110 }
2111 /* }}} end dom_document_save_html_file */
2112 
2113 /* {{{ Convenience method to output as html */
PHP_METHOD(DOMDocument,saveHTML)2114 PHP_METHOD(DOMDocument, saveHTML)
2115 {
2116 	zval *id, *nodep = NULL;
2117 	xmlDoc *docp;
2118 	xmlNode *node;
2119 	xmlOutputBufferPtr outBuf;
2120 	xmlBufferPtr buf;
2121 	dom_object *intern, *nodeobj;
2122 	int format;
2123 
2124 	id = ZEND_THIS;
2125 	if (zend_parse_parameters(ZEND_NUM_ARGS(),
2126 		"|O!", &nodep, dom_node_class_entry)
2127 		== FAILURE) {
2128 		RETURN_THROWS();
2129 	}
2130 
2131 	DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2132 
2133 	libxml_doc_props const* doc_props = dom_get_doc_props(intern->document);
2134 	format = doc_props->formatoutput;
2135 
2136 	if (nodep != NULL) {
2137 		/* Dump contents of Node */
2138 		DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
2139 		if (node->doc != docp) {
2140 			php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
2141 			RETURN_FALSE;
2142 		}
2143 
2144 		buf = xmlBufferCreate();
2145 		if (!buf) {
2146 			php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
2147 			RETURN_FALSE;
2148 		}
2149 		outBuf = xmlOutputBufferCreateBuffer(buf, NULL);
2150 		if (!outBuf) {
2151 			xmlBufferFree(buf);
2152 			php_error_docref(NULL, E_WARNING, "Could not fetch output buffer");
2153 			RETURN_FALSE;
2154 		}
2155 
2156 		if (node->type == XML_DOCUMENT_FRAG_NODE) {
2157 			for (node = node->children; node; node = node->next) {
2158 				htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2159 				if (outBuf->error) {
2160 					break;
2161 				}
2162 			}
2163 		} else {
2164 			htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2165 		}
2166 		if (!outBuf->error) {
2167 			xmlOutputBufferFlush(outBuf);
2168 			const xmlChar *mem = xmlBufferContent(buf);
2169 			if (!mem) {
2170 				RETVAL_FALSE;
2171 			} else {
2172 				int size = xmlBufferLength(buf);
2173 				RETVAL_STRINGL((const char*) mem, size);
2174 			}
2175 		} else {
2176 			php_error_docref(NULL, E_WARNING, "Error dumping HTML node");
2177 			RETVAL_FALSE;
2178 		}
2179 		xmlOutputBufferClose(outBuf);
2180 		xmlBufferFree(buf);
2181 	} else {
2182 		xmlChar *mem = NULL;
2183 		int size = 0;
2184 		htmlDocDumpMemoryFormat(docp, &mem, &size, format);
2185 		if (!size || !mem) {
2186 			RETVAL_FALSE;
2187 		} else {
2188 			RETVAL_STRINGL((const char*) mem, size);
2189 		}
2190 		xmlFree(mem);
2191 	}
2192 
2193 }
2194 /* }}} end dom_document_save_html */
2195 
2196 #endif  /* defined(LIBXML_HTML_ENABLED) */
2197 
2198 /* {{{ Register extended class used to create base node type */
dom_document_register_node_class(INTERNAL_FUNCTION_PARAMETERS,bool modern)2199 static void dom_document_register_node_class(INTERNAL_FUNCTION_PARAMETERS, bool modern)
2200 {
2201 	zend_class_entry *basece = dom_get_node_ce(modern), *ce = NULL;
2202 	dom_object *intern;
2203 
2204 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "CC!", &basece, &ce) == FAILURE) {
2205 		RETURN_THROWS();
2206 	}
2207 
2208 	if (basece->ce_flags & ZEND_ACC_ABSTRACT) {
2209 		zend_argument_value_error(1, "must not be an abstract class");
2210 		RETURN_THROWS();
2211 	}
2212 
2213 	if (ce == NULL || instanceof_function(ce, basece)) {
2214 		if (UNEXPECTED(ce != NULL && (ce->ce_flags & ZEND_ACC_ABSTRACT))) {
2215 			zend_argument_value_error(2, "must not be an abstract class");
2216 			RETURN_THROWS();
2217 		}
2218 		DOM_GET_THIS_INTERN(intern);
2219 		dom_set_doc_classmap(intern->document, basece, ce);
2220 		if (!modern) {
2221 			RETVAL_TRUE;
2222 		}
2223 		return;
2224 	}
2225 
2226 	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));
2227 	RETURN_THROWS();
2228 }
2229 
PHP_METHOD(DOMDocument,registerNodeClass)2230 PHP_METHOD(DOMDocument, registerNodeClass)
2231 {
2232 	dom_document_register_node_class(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
2233 }
2234 
PHP_METHOD(Dom_Document,registerNodeClass)2235 PHP_METHOD(Dom_Document, registerNodeClass)
2236 {
2237 	dom_document_register_node_class(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
2238 }
2239 /* }}} */
2240 
2241 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
2242 Since:
2243 */
PHP_METHOD(DOMDocument,replaceChildren)2244 PHP_METHOD(DOMDocument, replaceChildren)
2245 {
2246 	uint32_t argc = 0;
2247 	zval *args;
2248 	dom_object *intern;
2249 
2250 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
2251 		RETURN_THROWS();
2252 	}
2253 
2254 	DOM_GET_THIS_INTERN(intern);
2255 
2256 	dom_parent_node_replace_children(intern, args, argc);
2257 }
2258 /* }}} */
2259 
2260 #endif  /* HAVE_LIBXML && HAVE_DOM */
2261