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 "private_data.h"
27 #include "xml_serializer.h"
28 #include "internal_helpers.h"
29 #include "dom_properties.h"
30 #include <libxml/SAX.h>
31 #ifdef LIBXML_SCHEMAS_ENABLED
32 #include <libxml/relaxng.h>
33 #include <libxml/xmlschemas.h>
34 #endif
35
36 /*
37 * class DOMDocument extends DOMNode
38 *
39 * URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-i-Document
40 * Since:
41 */
42
43 /* {{{ docType DOMDocumentType
44 readonly=yes
45 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-B63ED1A31
46 Since:
47 */
dom_document_doctype_read(dom_object * obj,zval * retval)48 zend_result dom_document_doctype_read(dom_object *obj, zval *retval)
49 {
50 DOM_PROP_NODE(xmlDocPtr, docp, obj);
51
52 xmlDtdPtr dtdptr = xmlGetIntSubset(docp);
53
54 php_dom_create_nullable_object((xmlNodePtr) dtdptr, retval, obj);
55 return SUCCESS;
56 }
57
58 /* }}} */
59
60 /* {{{ implementation DOMImplementation
61 readonly=yes
62 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1B793EBA
63 Since:
64 */
dom_document_implementation_read(dom_object * obj,zval * retval)65 zend_result dom_document_implementation_read(dom_object *obj, zval *retval)
66 {
67 php_dom_create_implementation(retval, false);
68 return SUCCESS;
69 }
70 /* }}} */
71
72 /* {{{ documentElement DOMElement
73 readonly=yes
74 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-87CD092
75 Since:
76 */
dom_document_document_element_read(dom_object * obj,zval * retval)77 zend_result dom_document_document_element_read(dom_object *obj, zval *retval)
78 {
79 DOM_PROP_NODE(xmlDocPtr, docp, obj);
80
81 xmlNodePtr root = xmlDocGetRootElement(docp);
82
83 php_dom_create_nullable_object(root, retval, obj);
84 return SUCCESS;
85 }
86
87 /* }}} */
88
89 /* {{{ encoding string
90 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-encoding
91 Since: DOM Level 3
92 */
dom_document_encoding_read(dom_object * obj,zval * retval)93 zend_result dom_document_encoding_read(dom_object *obj, zval *retval)
94 {
95 DOM_PROP_NODE(xmlDocPtr, docp, obj);
96
97 const char *encoding = (const char *) docp->encoding;
98
99 if (encoding != NULL) {
100 ZVAL_STRING(retval, encoding);
101 } else {
102 ZVAL_NULL(retval);
103 }
104
105 return SUCCESS;
106 }
107
dom_document_actual_encoding_read(dom_object * obj,zval * retval)108 zend_result dom_document_actual_encoding_read(dom_object *obj, zval *retval)
109 {
110 PHP_DOM_DEPRECATED_PROPERTY("Property DOMDocument::$actualEncoding is deprecated");
111
112 return dom_document_encoding_read(obj, retval);
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_ASSERT(Z_TYPE_P(newval) == IS_TRUE || Z_TYPE_P(newval) == IS_FALSE);
166 docp->standalone = Z_TYPE_P(newval) == IS_TRUE;
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 PHP_DOM_DEPRECATED_PROPERTY("Property DOMDocument::$config is deprecated");
420
421 ZVAL_NULL(retval);
422 return SUCCESS;
423 }
424
425 /* }}} */
426
427 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-2141741547
428 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createelement
429 Since:
430 */
PHP_METHOD(DOMDocument,createElement)431 PHP_METHOD(DOMDocument, createElement)
432 {
433 xmlDocPtr docp;
434 dom_object *intern;
435 size_t value_len;
436 char *value = NULL;
437 zend_string *name;
438
439 ZEND_PARSE_PARAMETERS_START(1, 2)
440 Z_PARAM_STR(name)
441 Z_PARAM_OPTIONAL
442 Z_PARAM_STRING(value, value_len)
443 ZEND_PARSE_PARAMETERS_END();
444
445 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
446
447 if (xmlValidateName(BAD_CAST ZSTR_VAL(name), 0) != 0) {
448 php_dom_throw_error(INVALID_CHARACTER_ERR, dom_get_strict_error(intern->document));
449 RETURN_FALSE;
450 }
451
452 xmlNodePtr node = xmlNewDocNode(docp, NULL, BAD_CAST ZSTR_VAL(name), BAD_CAST value);
453
454 if (!node) {
455 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
456 RETURN_THROWS();
457 }
458
459 DOM_RET_OBJ(node, intern);
460 }
461
PHP_METHOD(Dom_Document,createElement)462 PHP_METHOD(Dom_Document, createElement)
463 {
464 xmlNode *node;
465 xmlDocPtr docp;
466 dom_object *intern;
467 zend_string *name;
468
469 ZEND_PARSE_PARAMETERS_START(1, 1)
470 Z_PARAM_STR(name)
471 ZEND_PARSE_PARAMETERS_END();
472
473 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
474
475 if (xmlValidateName(BAD_CAST ZSTR_VAL(name), 0) != 0) {
476 php_dom_throw_error(INVALID_CHARACTER_ERR, /* strict */ true);
477 RETURN_THROWS();
478 }
479
480 if (docp->type == XML_HTML_DOCUMENT_NODE) {
481 php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
482 char *lower = zend_str_tolower_dup_ex(ZSTR_VAL(name), ZSTR_LEN(name));
483 node = xmlNewDocRawNode(docp, php_dom_libxml_ns_mapper_ensure_html_ns(ns_mapper), BAD_CAST (lower ? lower : ZSTR_VAL(name)), NULL);
484 efree(lower);
485 } else {
486 node = xmlNewDocNode(docp, NULL, BAD_CAST ZSTR_VAL(name), NULL);
487 }
488
489 if (!node) {
490 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
491 RETURN_THROWS();
492 }
493
494 DOM_RET_OBJ(node, intern);
495 }
496 /* }}} end dom_document_create_element */
497
498 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-35CB04B5
499 Since:
500 */
PHP_METHOD(DOMDocument,createDocumentFragment)501 PHP_METHOD(DOMDocument, createDocumentFragment)
502 {
503 zval *id;
504 xmlNode *node;
505 xmlDocPtr docp;
506 dom_object *intern;
507
508 id = ZEND_THIS;
509 if (zend_parse_parameters_none() == FAILURE) {
510 RETURN_THROWS();
511 }
512
513 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
514
515 node = xmlNewDocFragment(docp);
516 if (!node) {
517 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
518 RETURN_THROWS();
519 }
520
521 DOM_RET_OBJ(node, intern);
522 }
523 /* }}} end dom_document_create_document_fragment */
524
525 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1975348127
526 Since:
527 */
PHP_METHOD(DOMDocument,createTextNode)528 PHP_METHOD(DOMDocument, createTextNode)
529 {
530 xmlNode *node;
531 xmlDocPtr docp;
532 size_t value_len;
533 dom_object *intern;
534 char *value;
535
536 ZEND_PARSE_PARAMETERS_START(1, 1)
537 Z_PARAM_STRING(value, value_len)
538 ZEND_PARSE_PARAMETERS_END();
539
540 DOM_GET_OBJ(docp, ZEND_THIS, 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 /* If there is no root, the namespace cannot be attached to it, so we have to attach it to the old list. */
792 if (nsptr != NULL && root == NULL) {
793 php_libxml_set_old_ns(nodep->doc, nsptr);
794 }
795 }
796 retnodep->ns = nsptr;
797 }
798 }
799
800 DOM_RET_OBJ(retnodep, intern);
801 }
802
dom_modern_document_import_node(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * node_ce)803 static void dom_modern_document_import_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce)
804 {
805 zval *node;
806 xmlDocPtr docp;
807 xmlNodePtr nodep, retnodep;
808 dom_object *intern, *nodeobj;
809 bool recursive = 0;
810
811 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &node, node_ce, &recursive) != SUCCESS) {
812 RETURN_THROWS();
813 }
814
815 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
816
817 DOM_GET_OBJ(nodep, node, xmlNodePtr, nodeobj);
818
819 if (nodep->type == XML_HTML_DOCUMENT_NODE || nodep->type == XML_DOCUMENT_NODE) {
820 php_dom_throw_error(NOT_SUPPORTED_ERR, /* strict */ true);
821 RETURN_THROWS();
822 }
823
824 if (nodep->doc == docp) {
825 retnodep = nodep;
826 } else {
827 retnodep = dom_clone_node(php_dom_get_ns_mapper(intern), nodep, docp, recursive);
828 if (!retnodep) {
829 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
830 RETURN_THROWS();
831 }
832 }
833
834 DOM_RET_OBJ(retnodep, intern);
835 }
836
PHP_METHOD(Dom_Document,importNode)837 PHP_METHOD(Dom_Document, importNode)
838 {
839 dom_modern_document_import_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry);
840 }
841 /* }}} end dom_document_import_node */
842
PHP_METHOD(Dom_Document,importLegacyNode)843 PHP_METHOD(Dom_Document, importLegacyNode)
844 {
845 dom_modern_document_import_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry);
846 }
847
848 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNS
849 Modern spec URL: https://dom.spec.whatwg.org/#internal-createelementns-steps
850 Since: DOM Level 2
851 */
PHP_METHOD(DOMDocument,createElementNS)852 PHP_METHOD(DOMDocument, createElementNS)
853 {
854 xmlDocPtr docp;
855 xmlNodePtr nodep = NULL;
856 size_t value_len = 0;
857 char *value = NULL;
858 int errorcode;
859 dom_object *intern;
860 zend_string *name = NULL, *uri;
861
862 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!S|s", &uri, &name, &value, &value_len) == FAILURE) {
863 RETURN_THROWS();
864 }
865
866 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
867
868 char *localname = NULL, *prefix = NULL;
869 errorcode = dom_check_qname(ZSTR_VAL(name), &localname, &prefix, uri ? ZSTR_LEN(uri) : 0, ZSTR_LEN(name));
870
871 if (errorcode == 0) {
872 if (xmlValidateName(BAD_CAST localname, 0) == 0) {
873 nodep = xmlNewDocNode(docp, NULL, BAD_CAST localname, BAD_CAST value);
874 if (UNEXPECTED(nodep == NULL)) {
875 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
876 RETURN_THROWS();
877 }
878
879 if (uri != NULL) {
880 xmlNsPtr nsptr = xmlSearchNsByHref(nodep->doc, nodep, BAD_CAST ZSTR_VAL(uri));
881 if (nsptr == NULL) {
882 nsptr = dom_get_ns(nodep, ZSTR_VAL(uri), &errorcode, prefix);
883 }
884 nodep->ns = nsptr;
885 }
886 } else {
887 errorcode = INVALID_CHARACTER_ERR;
888 }
889 }
890
891 xmlFree(localname);
892 xmlFree(prefix);
893
894 if (errorcode != 0) {
895 xmlFreeNode(nodep);
896 php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
897 RETURN_FALSE;
898 }
899
900 DOM_RET_OBJ(nodep, intern);
901 }
902
PHP_METHOD(Dom_Document,createElementNS)903 PHP_METHOD(Dom_Document, createElementNS)
904 {
905 xmlDocPtr docp;
906 dom_object *intern;
907 zend_string *name, *uri;
908
909 ZEND_PARSE_PARAMETERS_START(2, 2)
910 Z_PARAM_STR_OR_NULL(uri)
911 Z_PARAM_STR(name)
912 ZEND_PARSE_PARAMETERS_END();
913
914 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
915
916 xmlChar *localname = NULL, *prefix = NULL;
917 int errorcode = dom_validate_and_extract(uri, name, &localname, &prefix);
918
919 if (errorcode == 0) {
920 php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
921 xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri);
922
923 /* Try to create the node with the local name interned. */
924 const xmlChar *interned_localname = xmlDictLookup(docp->dict, localname, -1);
925 xmlNodePtr nodep;
926 if (interned_localname == NULL) {
927 nodep = xmlNewDocNodeEatName(docp, ns, localname, NULL);
928 } else {
929 xmlFree(localname);
930 nodep = xmlNewDocNodeEatName(docp, ns, BAD_CAST interned_localname, NULL);
931 }
932
933 if (UNEXPECTED(nodep == NULL)) {
934 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
935 } else {
936 DOM_RET_OBJ(nodep, intern);
937 }
938 } else {
939 php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
940 xmlFree(localname);
941 }
942
943 xmlFree(prefix);
944 }
945 /* }}} end dom_document_create_element_ns */
946
947 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNS
948 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-createattributens
949 Since: DOM Level 2
950 */
PHP_METHOD(DOMDocument,createAttributeNS)951 PHP_METHOD(DOMDocument, createAttributeNS)
952 {
953 zval *id;
954 xmlDocPtr docp;
955 xmlNodePtr nodep = NULL, root;
956 xmlNsPtr nsptr;
957 zend_string *name, *uri;
958 xmlChar *localname = NULL, *prefix = NULL;
959 dom_object *intern;
960 int errorcode;
961
962 id = ZEND_THIS;
963 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!S", &uri, &name) == FAILURE) {
964 RETURN_THROWS();
965 }
966
967 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
968
969 root = xmlDocGetRootElement(docp);
970 if (root != NULL || php_dom_follow_spec_intern(intern)) {
971 errorcode = dom_validate_and_extract(uri, name, &localname, &prefix);
972 if (UNEXPECTED(errorcode != 0)) {
973 if (!php_dom_follow_spec_intern(intern)) {
974 /* legacy behaviour */
975 errorcode = NAMESPACE_ERR;
976 }
977 goto error;
978 }
979
980 nodep = (xmlNodePtr) xmlNewDocProp(docp, localname, NULL);
981 if (UNEXPECTED(nodep == NULL)) {
982 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
983 RETURN_THROWS();
984 }
985
986 if (uri != NULL && ZSTR_LEN(uri) > 0) {
987 if (php_dom_follow_spec_intern(intern)) {
988 php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
989 nsptr = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri);
990 } else {
991 nsptr = xmlSearchNsByHref(docp, root, BAD_CAST ZSTR_VAL(uri));
992
993 if (zend_string_equals_literal(name, "xmlns") || xmlStrEqual(BAD_CAST prefix, BAD_CAST "xml")) {
994 if (nsptr == NULL) {
995 nsptr = xmlNewNs(NULL, BAD_CAST ZSTR_VAL(uri), BAD_CAST prefix);
996 php_libxml_set_old_ns(docp, nsptr);
997 }
998 } else {
999 if (nsptr == NULL || nsptr->prefix == NULL) {
1000 nsptr = dom_get_ns_unchecked(root, ZSTR_VAL(uri), prefix ? (char *) prefix : "default");
1001 if (UNEXPECTED(nsptr == NULL)) {
1002 errorcode = NAMESPACE_ERR;
1003 }
1004 }
1005 }
1006 }
1007 nodep->ns = nsptr;
1008 }
1009 } else {
1010 php_error_docref(NULL, E_WARNING, "Document Missing Root Element");
1011 RETURN_FALSE;
1012 }
1013
1014 error:
1015 xmlFree(localname);
1016 xmlFree(prefix);
1017
1018 if (errorcode != 0) {
1019 xmlFreeProp((xmlAttrPtr) nodep);
1020 php_dom_throw_error(errorcode, dom_get_strict_error(intern->document));
1021 RETURN_FALSE;
1022 }
1023
1024 DOM_RET_OBJ(nodep, intern);
1025 }
1026 /* }}} end dom_document_create_attribute_ns */
1027
1028 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBId
1029 Since: DOM Level 2
1030 */
PHP_METHOD(DOMDocument,getElementById)1031 PHP_METHOD(DOMDocument, getElementById)
1032 {
1033 xmlDocPtr docp;
1034 size_t idname_len;
1035 dom_object *intern;
1036 char *idname;
1037
1038 ZEND_PARSE_PARAMETERS_START(1, 1)
1039 Z_PARAM_STRING(idname, idname_len)
1040 ZEND_PARSE_PARAMETERS_END();
1041
1042 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
1043
1044 /* If the document has not been manipulated yet, the ID cache will be in sync and we can trust its result.
1045 * This check mainly exists because a lot of times people just query a document without modifying it,
1046 * and we can allow quick access to IDs in that case. */
1047 if (!dom_is_document_cache_modified_since_parsing(intern->document)) {
1048 const xmlAttr *attrp = xmlGetID(docp, BAD_CAST idname);
1049 if (attrp && attrp->parent) {
1050 DOM_RET_OBJ(attrp->parent, intern);
1051 }
1052 } else {
1053 /* From the moment an ID is created, libxml2's behaviour is to cache that element, even
1054 * if that element is not yet attached to the document. Similarly, only upon destruction of
1055 * the element the ID is actually removed by libxml2. Since libxml2 has such behaviour deeply
1056 * ingrained in the library, and uses the cache for various purposes, it seems like a bad
1057 * idea and lost cause to fight it. */
1058
1059 const xmlNode *base = (const xmlNode *) docp;
1060 const xmlNode *node = base->children;
1061 while (node != NULL) {
1062 if (node->type == XML_ELEMENT_NODE) {
1063 for (const xmlAttr *attr = node->properties; attr != NULL; attr = attr->next) {
1064 if (attr->atype == XML_ATTRIBUTE_ID && dom_compare_value(attr, BAD_CAST idname)) {
1065 DOM_RET_OBJ((xmlNodePtr) node, intern);
1066 return;
1067 }
1068 }
1069 }
1070
1071 node = php_dom_next_in_tree_order(node, base);
1072 }
1073 }
1074 }
1075 /* }}} end dom_document_get_element_by_id */
1076
php_dom_transfer_document_ref_single_node(xmlNodePtr node,php_libxml_ref_obj * new_document)1077 static zend_always_inline void php_dom_transfer_document_ref_single_node(xmlNodePtr node, php_libxml_ref_obj *new_document)
1078 {
1079 php_libxml_node_ptr *iteration_object_ptr = node->_private;
1080 if (iteration_object_ptr) {
1081 php_libxml_node_object *iteration_object = iteration_object_ptr->_private;
1082 ZEND_ASSERT(iteration_object != NULL);
1083 /* Must increase refcount first because we could be the last reference holder, and the document may be equal. */
1084 new_document->refcount++;
1085 php_libxml_decrement_doc_ref(iteration_object);
1086 iteration_object->document = new_document;
1087 }
1088 }
1089
php_dom_transfer_document_ref(xmlNodePtr node,php_libxml_ref_obj * new_document)1090 static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *new_document)
1091 {
1092 if (node->children) {
1093 php_dom_transfer_document_ref(node->children, new_document);
1094 }
1095
1096 while (node) {
1097 if (node->type == XML_ELEMENT_NODE) {
1098 for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
1099 php_dom_transfer_document_ref_single_node((xmlNodePtr) attr, new_document);
1100 }
1101 }
1102
1103 php_dom_transfer_document_ref_single_node(node, new_document);
1104 node = node->next;
1105 }
1106 }
1107
1108 /* Workaround for bug that was fixed in https://github.com/GNOME/libxml2/commit/4bc3ebf3eaba352fbbce2ef70ad00a3c7752478a */
1109 #if LIBXML_VERSION < 21000
libxml_copy_dicted_string(xmlDictPtr src_dict,xmlDictPtr dst_dict,xmlChar * str)1110 static xmlChar *libxml_copy_dicted_string(xmlDictPtr src_dict, xmlDictPtr dst_dict, xmlChar *str)
1111 {
1112 if (str == NULL) {
1113 return NULL;
1114 }
1115 if (xmlDictOwns(src_dict, str) == 1) {
1116 if (dst_dict == NULL) {
1117 return xmlStrdup(str);
1118 }
1119 return BAD_CAST xmlDictLookup(dst_dict, str, -1);
1120 }
1121 return str;
1122 }
1123
libxml_fixup_name_and_content(xmlDocPtr src_doc,xmlDocPtr dst_doc,xmlNodePtr node)1124 static void libxml_fixup_name_and_content(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
1125 {
1126 if (src_doc != NULL && dst_doc != src_doc && src_doc->dict != NULL) {
1127 node->name = libxml_copy_dicted_string(src_doc->dict, dst_doc->dict, BAD_CAST node->name);
1128 node->content = libxml_copy_dicted_string(src_doc->dict, NULL, node->content);
1129 }
1130 }
1131
libxml_fixup_name_and_content_element(xmlDocPtr src_doc,xmlDocPtr dst_doc,xmlNodePtr node)1132 static void libxml_fixup_name_and_content_element(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
1133 {
1134 libxml_fixup_name_and_content(src_doc, dst_doc, node);
1135 for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
1136 libxml_fixup_name_and_content(src_doc, dst_doc, (xmlNodePtr) attr);
1137 }
1138
1139 for (xmlNodePtr child = node->children; child != NULL; child = child->next) {
1140 libxml_fixup_name_and_content_element(src_doc, dst_doc, child);
1141 }
1142 }
1143 #endif
1144
php_dom_adopt_node(xmlNodePtr nodep,dom_object * dom_object_new_document,xmlDocPtr new_document)1145 bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document)
1146 {
1147 xmlDocPtr original_document = nodep->doc;
1148 php_libxml_invalidate_node_list_cache_from_doc(original_document);
1149 if (nodep->doc != new_document) {
1150 php_libxml_invalidate_node_list_cache(dom_object_new_document->document);
1151
1152 /* Note for ATTRIBUTE_NODE: specified is always true in ext/dom,
1153 * and since this unlink it; the owner element will be unset (i.e. parentNode). */
1154 if (php_dom_follow_spec_intern(dom_object_new_document)) {
1155 xmlUnlinkNode(nodep);
1156 xmlSetTreeDoc(nodep, new_document);
1157 php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(dom_object_new_document);
1158 php_dom_libxml_reconcile_modern(ns_mapper, nodep);
1159 #if LIBXML_VERSION < 21000
1160 libxml_fixup_name_and_content_element(original_document, new_document, nodep);
1161 #endif
1162 } else {
1163 int ret = xmlDOMWrapAdoptNode(NULL, original_document, nodep, new_document, NULL, /* options, unused */ 0);
1164 if (UNEXPECTED(ret != 0)) {
1165 return false;
1166 }
1167 }
1168
1169 php_dom_transfer_document_ref(nodep, dom_object_new_document->document);
1170 } else {
1171 xmlUnlinkNode(nodep);
1172 }
1173 return true;
1174 }
1175
1176 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNode
1177 Since: DOM Level 3
1178 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-adoptnode
1179 */
dom_document_adopt_node(INTERNAL_FUNCTION_PARAMETERS,bool modern)1180 static void dom_document_adopt_node(INTERNAL_FUNCTION_PARAMETERS, bool modern)
1181 {
1182 zval *node_zval;
1183 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, dom_get_node_ce(modern)) == FAILURE) {
1184 RETURN_THROWS();
1185 }
1186
1187 xmlNodePtr nodep;
1188 dom_object *dom_object_nodep;
1189 DOM_GET_OBJ(nodep, node_zval, xmlNodePtr, dom_object_nodep);
1190
1191 if (UNEXPECTED(nodep->type == XML_DOCUMENT_NODE
1192 || nodep->type == XML_HTML_DOCUMENT_NODE
1193 || nodep->type == XML_DOCUMENT_TYPE_NODE
1194 || nodep->type == XML_DTD_NODE
1195 || nodep->type == XML_ENTITY_NODE
1196 || nodep->type == XML_NOTATION_NODE)) {
1197 php_dom_throw_error(NOT_SUPPORTED_ERR, dom_get_strict_error(dom_object_nodep->document));
1198 RETURN_FALSE;
1199 }
1200
1201 xmlDocPtr new_document;
1202 dom_object *dom_object_new_document;
1203 zval *new_document_zval = ZEND_THIS;
1204 DOM_GET_OBJ(new_document, new_document_zval, xmlDocPtr, dom_object_new_document);
1205
1206 if (!php_dom_adopt_node(nodep, dom_object_new_document, new_document)) {
1207 if (modern) {
1208 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
1209 RETURN_THROWS();
1210 }
1211 RETURN_FALSE;
1212 }
1213
1214 RETURN_OBJ_COPY(&dom_object_nodep->std);
1215 }
1216
PHP_METHOD(DOMDocument,adoptNode)1217 PHP_METHOD(DOMDocument, adoptNode)
1218 {
1219 dom_document_adopt_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1220 }
1221
PHP_METHOD(Dom_Document,adoptNode)1222 PHP_METHOD(Dom_Document, adoptNode)
1223 {
1224 dom_document_adopt_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1225 }
1226 /* }}} end dom_document_adopt_node */
1227
1228 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocument
1229 Since: DOM Level 3
1230 */
PHP_METHOD(DOMDocument,normalizeDocument)1231 PHP_METHOD(DOMDocument, normalizeDocument)
1232 {
1233 zval *id;
1234 xmlDocPtr docp;
1235 dom_object *intern;
1236
1237 id = ZEND_THIS;
1238 if (zend_parse_parameters_none() == FAILURE) {
1239 RETURN_THROWS();
1240 }
1241
1242 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1243
1244 php_dom_normalize_legacy((xmlNodePtr) docp);
1245 }
1246 /* }}} end dom_document_normalize_document */
1247
1248 /* {{{ */
PHP_METHOD(DOMDocument,__construct)1249 PHP_METHOD(DOMDocument, __construct)
1250 {
1251 xmlDoc *docp = NULL, *olddoc;
1252 dom_object *intern;
1253 char *encoding, *version = NULL;
1254 size_t encoding_len = 0, version_len = 0;
1255 unsigned int refcount;
1256
1257 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &version, &version_len, &encoding, &encoding_len) == FAILURE) {
1258 RETURN_THROWS();
1259 }
1260
1261 docp = xmlNewDoc(BAD_CAST version);
1262
1263 if (!docp) {
1264 php_dom_throw_error(INVALID_STATE_ERR, true);
1265 return;
1266 }
1267
1268 if (encoding_len > 0) {
1269 docp->encoding = (const xmlChar *) xmlStrdup(BAD_CAST encoding);
1270 }
1271
1272 intern = Z_DOMOBJ_P(ZEND_THIS);
1273 olddoc = (xmlDocPtr) dom_object_get_node(intern);
1274 if (olddoc != NULL) {
1275 php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1276 refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1277 if (refcount != 0) {
1278 olddoc->_private = NULL;
1279 }
1280 }
1281 intern->document = NULL;
1282 if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp) == -1) {
1283 /* docp is always non-null so php_libxml_increment_doc_ref() never returns -1 */
1284 ZEND_UNREACHABLE();
1285 }
1286 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)docp, (void *)intern);
1287 }
1288 /* }}} end DOMDocument::__construct */
1289
dom_get_valid_file_path(const char * source,char * resolved_path,int resolved_path_len)1290 const char *dom_get_valid_file_path(const char *source, char *resolved_path, int resolved_path_len ) /* {{{ */
1291 {
1292 xmlURI *uri;
1293 xmlChar *escsource;
1294 const char *file_dest;
1295 int isFileUri = 0;
1296
1297 uri = xmlCreateURI();
1298 if (uri == NULL) {
1299 return NULL;
1300 }
1301 escsource = xmlURIEscapeStr(BAD_CAST source, BAD_CAST ":");
1302 xmlParseURIReference(uri, (char *) escsource);
1303 xmlFree(escsource);
1304
1305 if (uri->scheme != NULL) {
1306 /* absolute file uris - libxml only supports localhost or empty host */
1307 #ifdef PHP_WIN32
1308 if (strncasecmp(source, "file://",7) == 0 && ':' == source[8]) {
1309 isFileUri = 1;
1310 source += 7;
1311 } else
1312 #endif
1313 if (strncasecmp(source, "file:///",8) == 0) {
1314 isFileUri = 1;
1315 #ifdef PHP_WIN32
1316 source += 8;
1317 #else
1318 source += 7;
1319 #endif
1320 } else if (strncasecmp(source, "file://localhost/",17) == 0) {
1321 isFileUri = 1;
1322 #ifdef PHP_WIN32
1323 source += 17;
1324 #else
1325 source += 16;
1326 #endif
1327 }
1328 }
1329
1330 file_dest = source;
1331
1332 if ((uri->scheme == NULL || isFileUri)) {
1333 /* XXX possible buffer overflow if VCWD_REALPATH does not know size of resolved_path */
1334 if (!VCWD_REALPATH(source, resolved_path) && !expand_filepath(source, resolved_path)) {
1335 xmlFreeURI(uri);
1336 return NULL;
1337 }
1338 file_dest = resolved_path;
1339 }
1340
1341 xmlFreeURI(uri);
1342
1343 return file_dest;
1344 }
1345 /* }}} */
1346
dom_document_parser(zval * id,dom_load_mode mode,const char * source,size_t source_len,size_t options,xmlCharEncodingHandlerPtr encoding)1347 xmlDocPtr dom_document_parser(zval *id, dom_load_mode mode, const char *source, size_t source_len, size_t options, xmlCharEncodingHandlerPtr encoding) /* {{{ */
1348 {
1349 xmlDocPtr ret;
1350 xmlParserCtxtPtr ctxt = NULL;
1351 int validate, recover, resolve_externals, keep_blanks, substitute_ent;
1352 int resolved_path_len;
1353 int old_error_reporting = 0;
1354 char *directory=NULL, resolved_path[MAXPATHLEN + 1];
1355
1356 libxml_doc_props const* doc_props;
1357 if (id == NULL) {
1358 doc_props = dom_get_doc_props_read_only(NULL);
1359 } else {
1360 dom_object *intern = Z_DOMOBJ_P(id);
1361 php_libxml_ref_obj *document = intern->document;
1362 doc_props = dom_get_doc_props_read_only(document);
1363 }
1364 validate = doc_props->validateonparse;
1365 resolve_externals = doc_props->resolveexternals;
1366 keep_blanks = doc_props->preservewhitespace;
1367 substitute_ent = doc_props->substituteentities;
1368 recover = doc_props->recover || (options & XML_PARSE_RECOVER) == XML_PARSE_RECOVER;
1369
1370 xmlInitParser();
1371
1372 if (mode == DOM_LOAD_FILE) {
1373 if (CHECK_NULL_PATH(source, source_len)) {
1374 zend_argument_value_error(1, "must not contain any null bytes");
1375 return NULL;
1376 }
1377 const char *file_dest = dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1378 if (file_dest) {
1379 ctxt = xmlCreateFileParserCtxt(file_dest);
1380 }
1381 } else {
1382 ctxt = xmlCreateMemoryParserCtxt(source, source_len);
1383 }
1384
1385 if (ctxt == NULL) {
1386 return(NULL);
1387 }
1388
1389 if (encoding != NULL) {
1390 /* Note: libxml 2.12+ doesn't handle NULL encoding well. */
1391 (void) xmlSwitchToEncoding(ctxt, encoding);
1392 }
1393
1394 /* If loading from memory, we need to set the base directory for the document */
1395 if (mode != DOM_LOAD_FILE) {
1396 #ifdef HAVE_GETCWD
1397 directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
1398 #elif defined(HAVE_GETWD)
1399 directory = VCWD_GETWD(resolved_path);
1400 #endif
1401 if (directory) {
1402 if(ctxt->directory != NULL) {
1403 xmlFree((char *) ctxt->directory);
1404 }
1405 resolved_path_len = strlen(resolved_path);
1406 if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) {
1407 resolved_path[resolved_path_len] = DEFAULT_SLASH;
1408 resolved_path[++resolved_path_len] = '\0';
1409 }
1410 ctxt->directory = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
1411 }
1412 }
1413
1414 ctxt->vctxt.error = php_libxml_ctx_error;
1415 ctxt->vctxt.warning = php_libxml_ctx_warning;
1416
1417 if (ctxt->sax != NULL) {
1418 ctxt->sax->error = php_libxml_ctx_error;
1419 ctxt->sax->warning = php_libxml_ctx_warning;
1420 }
1421
1422 if (validate && ! (options & XML_PARSE_DTDVALID)) {
1423 options |= XML_PARSE_DTDVALID;
1424 }
1425 if (resolve_externals && ! (options & XML_PARSE_DTDATTR)) {
1426 options |= XML_PARSE_DTDATTR;
1427 }
1428 if (substitute_ent && ! (options & XML_PARSE_NOENT)) {
1429 options |= XML_PARSE_NOENT;
1430 }
1431 if (keep_blanks == 0 && ! (options & XML_PARSE_NOBLANKS)) {
1432 options |= XML_PARSE_NOBLANKS;
1433 }
1434 if (recover) {
1435 options |= XML_PARSE_RECOVER;
1436 }
1437
1438 php_libxml_sanitize_parse_ctxt_options(ctxt);
1439 xmlCtxtUseOptions(ctxt, options);
1440
1441 if (recover) {
1442 old_error_reporting = EG(error_reporting);
1443 EG(error_reporting) = old_error_reporting | E_WARNING;
1444 }
1445
1446 xmlParseDocument(ctxt);
1447
1448 if (ctxt->wellFormed || recover) {
1449 ret = ctxt->myDoc;
1450 if (recover) {
1451 EG(error_reporting) = old_error_reporting;
1452 }
1453 /* If loading from memory, set the base reference uri for the document */
1454 if (ret && ret->URL == NULL && ctxt->directory != NULL) {
1455 ret->URL = xmlStrdup(BAD_CAST ctxt->directory);
1456 }
1457 } else {
1458 ret = DOM_DOCUMENT_MALFORMED;
1459 xmlFreeDoc(ctxt->myDoc);
1460 ctxt->myDoc = NULL;
1461 }
1462
1463 xmlFreeParserCtxt(ctxt);
1464
1465 return(ret);
1466 }
1467 /* }}} */
1468
php_dom_finish_loading_document(zval * this,zval * return_value,xmlDocPtr newdoc)1469 static void php_dom_finish_loading_document(zval *this, zval *return_value, xmlDocPtr newdoc)
1470 {
1471 if (!newdoc)
1472 RETURN_FALSE;
1473
1474 dom_object *intern = Z_DOMOBJ_P(this);
1475 size_t old_modification_nr = 0;
1476 if (intern != NULL) {
1477 php_libxml_class_type class_type = PHP_LIBXML_CLASS_LEGACY;
1478 xmlDocPtr docp = (xmlDocPtr) dom_object_get_node(intern);
1479 dom_doc_propsptr doc_prop = NULL;
1480 if (docp != NULL) {
1481 const php_libxml_ref_obj *doc_ptr = intern->document;
1482 ZEND_ASSERT(doc_ptr != NULL); /* Must exist, we have a document */
1483 class_type = doc_ptr->class_type;
1484 old_modification_nr = doc_ptr->cache_tag.modification_nr;
1485 php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1486 doc_prop = intern->document->doc_props;
1487 intern->document->doc_props = NULL;
1488 unsigned int refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1489 if (refcount != 0) {
1490 docp->_private = NULL;
1491 }
1492 }
1493 intern->document = NULL;
1494 if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc) == -1) {
1495 RETURN_FALSE;
1496 }
1497 intern->document->doc_props = doc_prop;
1498 intern->document->class_type = class_type;
1499 }
1500
1501 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
1502 /* Since iterators should invalidate, we need to start the modification number from the old counter */
1503 if (old_modification_nr != 0) {
1504 intern->document->cache_tag.modification_nr = old_modification_nr;
1505 php_libxml_invalidate_node_list_cache(intern->document);
1506 }
1507
1508 RETURN_TRUE;
1509 }
1510
dom_parse_document(INTERNAL_FUNCTION_PARAMETERS,int mode)1511 static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode)
1512 {
1513 char *source;
1514 size_t source_len;
1515 zend_long options = 0;
1516
1517 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
1518 RETURN_THROWS();
1519 }
1520
1521 if (!source_len) {
1522 zend_argument_must_not_be_empty_error(1);
1523 RETURN_THROWS();
1524 }
1525 if (ZEND_SIZE_T_INT_OVFL(source_len)) {
1526 php_error_docref(NULL, E_WARNING, "Input string is too long");
1527 RETURN_FALSE;
1528 }
1529 if (ZEND_LONG_EXCEEDS_INT(options)) {
1530 php_error_docref(NULL, E_WARNING, "Invalid options");
1531 RETURN_FALSE;
1532 }
1533
1534 xmlDocPtr newdoc = dom_document_parser(ZEND_THIS, mode, source, source_len, options, NULL);
1535 if (newdoc == DOM_DOCUMENT_MALFORMED) {
1536 newdoc = NULL;
1537 }
1538 php_dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
1539 }
1540
1541 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-load
1542 Since: DOM Level 3
1543 */
PHP_METHOD(DOMDocument,load)1544 PHP_METHOD(DOMDocument, load)
1545 {
1546 dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1547 }
1548 /* }}} end dom_document_load */
1549
1550 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadXML
1551 Since: DOM Level 3
1552 */
PHP_METHOD(DOMDocument,loadXML)1553 PHP_METHOD(DOMDocument, loadXML)
1554 {
1555 dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1556 }
1557 /* }}} end dom_document_loadxml */
1558
1559 /* {{{ Convenience method to save to file */
PHP_METHOD(DOMDocument,save)1560 PHP_METHOD(DOMDocument, save)
1561 {
1562 zval *id;
1563 xmlDoc *docp;
1564 size_t file_len = 0;
1565 int saveempty = 0;
1566 dom_object *intern;
1567 char *file;
1568 zend_long options = 0;
1569
1570 id = ZEND_THIS;
1571 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &file, &file_len, &options) == FAILURE) {
1572 RETURN_THROWS();
1573 }
1574
1575 if (file_len == 0) {
1576 zend_argument_must_not_be_empty_error(1);
1577 RETURN_THROWS();
1578 }
1579
1580 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1581
1582 /* encoding handled by property on doc */
1583
1584 libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
1585 bool format = doc_props->formatoutput;
1586 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1587 saveempty = xmlSaveNoEmptyTags;
1588 xmlSaveNoEmptyTags = 1;
1589 }
1590 zend_long bytes = intern->document->handlers->dump_doc_to_file(file, docp, format, (const char *) docp->encoding);
1591 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1592 xmlSaveNoEmptyTags = saveempty;
1593 }
1594 if (bytes == -1) {
1595 RETURN_FALSE;
1596 }
1597 RETURN_LONG(bytes);
1598 }
1599 /* }}} end dom_document_save */
1600
1601 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML
1602 Since: DOM Level 3
1603 */
dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * node_ce)1604 static void dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce)
1605 {
1606 zval *nodep = NULL;
1607 xmlDoc *docp;
1608 xmlNode *node;
1609 dom_object *intern, *nodeobj;
1610 int old_xml_save_no_empty_tags;
1611 zend_long options = 0;
1612
1613 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!l", &nodep, node_ce, &options) != SUCCESS) {
1614 RETURN_THROWS();
1615 }
1616
1617 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
1618
1619 libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
1620 bool format = doc_props->formatoutput;
1621
1622 zend_string *res;
1623 if (nodep != NULL) {
1624 /* Dump contents of Node */
1625 DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
1626 if (node->doc != docp) {
1627 php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
1628 RETURN_FALSE;
1629 }
1630
1631 /* Save libxml2 global, override its value, and restore after saving (don't move me or risk breaking the state
1632 * w.r.t. the implicit return in DOM_GET_OBJ). */
1633 old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
1634 xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
1635 res = intern->document->handlers->dump_node_to_str(docp, node, format, (const char *) docp->encoding);
1636 xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
1637 } else {
1638 int converted_options = XML_SAVE_AS_XML;
1639 if (options & XML_SAVE_NO_DECL) {
1640 converted_options |= XML_SAVE_NO_DECL;
1641 }
1642 if (format) {
1643 converted_options |= XML_SAVE_FORMAT;
1644 }
1645
1646 /* Save libxml2 global, override its value, and restore after saving. */
1647 old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
1648 xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
1649 res = intern->document->handlers->dump_doc_to_str(docp, converted_options, (const char *) docp->encoding);
1650 xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
1651 }
1652
1653 if (!res) {
1654 php_error_docref(NULL, E_WARNING, "Could not save document");
1655 RETURN_FALSE;
1656 } else {
1657 RETURN_NEW_STR(res);
1658 }
1659 }
1660
PHP_METHOD(DOMDocument,saveXML)1661 PHP_METHOD(DOMDocument, saveXML)
1662 {
1663 dom_document_save_xml(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry);
1664 }
1665
PHP_METHOD(Dom_XMLDocument,saveXml)1666 PHP_METHOD(Dom_XMLDocument, saveXml)
1667 {
1668 dom_document_save_xml(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry);
1669 }
1670 /* }}} end dom_document_savexml */
1671
php_dom_free_xinclude_node(xmlNodePtr cur)1672 static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) /* {{{ */
1673 {
1674 xmlNodePtr xincnode;
1675
1676 xincnode = cur;
1677 cur = cur->next;
1678 xmlUnlinkNode(xincnode);
1679 php_libxml_node_free_resource(xincnode);
1680
1681 return cur;
1682 }
1683 /* }}} */
1684
php_dom_remove_xinclude_nodes(xmlNodePtr cur)1685 static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
1686 {
1687 while(cur) {
1688 if (cur->type == XML_XINCLUDE_START) {
1689 cur = php_dom_free_xinclude_node(cur);
1690
1691 /* XML_XINCLUDE_END node will be a sibling of XML_XINCLUDE_START */
1692 while(cur && cur->type != XML_XINCLUDE_END) {
1693 /* remove xinclude processing nodes from recursive xincludes */
1694 if (cur->type == XML_ELEMENT_NODE) {
1695 php_dom_remove_xinclude_nodes(cur->children);
1696 }
1697 cur = cur->next;
1698 }
1699
1700 if (cur && cur->type == XML_XINCLUDE_END) {
1701 cur = php_dom_free_xinclude_node(cur);
1702 }
1703 } else {
1704 if (cur->type == XML_ELEMENT_NODE) {
1705 php_dom_remove_xinclude_nodes(cur->children);
1706 }
1707 cur = cur->next;
1708 }
1709 }
1710 }
1711 /* }}} */
1712
dom_xinclude_strip_references(xmlNodePtr basep)1713 static void dom_xinclude_strip_references(xmlNodePtr basep)
1714 {
1715 php_libxml_node_free_resource(basep);
1716
1717 xmlNodePtr current = basep->children;
1718
1719 while (current) {
1720 php_libxml_node_free_resource(current);
1721 current = php_dom_next_in_tree_order(current, basep);
1722 }
1723 }
1724
1725 /* See GH-14702.
1726 * We have to remove userland references to xinclude fallback nodes because libxml2 will make clones of these
1727 * and remove the original nodes. If the originals are removed while there are still userland references
1728 * this will cause memory corruption. */
dom_xinclude_strip_fallback_references(const xmlNode * basep)1729 static void dom_xinclude_strip_fallback_references(const xmlNode *basep)
1730 {
1731 xmlNodePtr current = basep->children;
1732
1733 while (current) {
1734 if (current->type == XML_ELEMENT_NODE && current->ns != NULL && current->_private != NULL
1735 && xmlStrEqual(current->name, XINCLUDE_FALLBACK)
1736 && (xmlStrEqual(current->ns->href, XINCLUDE_NS) || xmlStrEqual(current->ns->href, XINCLUDE_OLD_NS))) {
1737 dom_xinclude_strip_references(current);
1738 }
1739
1740 current = php_dom_next_in_tree_order(current, basep);
1741 }
1742 }
1743
dom_perform_xinclude(xmlDocPtr docp,dom_object * intern,zend_long flags)1744 static int dom_perform_xinclude(xmlDocPtr docp, dom_object *intern, zend_long flags)
1745 {
1746 dom_xinclude_strip_fallback_references((const xmlNode *) docp);
1747
1748 PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
1749 int err = xmlXIncludeProcessFlags(docp, (int)flags);
1750 PHP_LIBXML_RESTORE_GLOBALS(xinclude);
1751
1752 /* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
1753 are added via xmlXIncludeProcess to mark beginning and ending of xincluded document
1754 but are not wanted in resulting document - must be done even if err as it could fail after
1755 having processed some xincludes */
1756 xmlNodePtr root = docp->children;
1757 while (root && root->type != XML_ELEMENT_NODE && root->type != XML_XINCLUDE_START) {
1758 root = root->next;
1759 }
1760 if (root) {
1761 php_dom_remove_xinclude_nodes(root);
1762 }
1763
1764 php_libxml_invalidate_node_list_cache(intern->document);
1765
1766 return err;
1767 }
1768
1769 /* {{{ Substitutues xincludes in a DomDocument */
PHP_METHOD(DOMDocument,xinclude)1770 PHP_METHOD(DOMDocument, xinclude)
1771 {
1772 xmlDoc *docp;
1773 zend_long flags = 0;
1774 dom_object *intern;
1775
1776 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
1777 RETURN_THROWS();
1778 }
1779
1780 if (ZEND_LONG_EXCEEDS_INT(flags)) {
1781 php_error_docref(NULL, E_WARNING, "Invalid flags");
1782 RETURN_FALSE;
1783 }
1784
1785 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
1786
1787 int err = dom_perform_xinclude(docp, intern, flags);
1788
1789 if (err) {
1790 RETVAL_LONG(err);
1791 } else {
1792 RETVAL_FALSE;
1793 }
1794 }
1795
PHP_METHOD(Dom_XMLDocument,xinclude)1796 PHP_METHOD(Dom_XMLDocument, xinclude)
1797 {
1798 xmlDoc *docp;
1799 zend_long flags = 0;
1800 dom_object *intern;
1801
1802 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
1803 RETURN_THROWS();
1804 }
1805
1806 if (ZEND_LONG_EXCEEDS_INT(flags)) {
1807 zend_argument_value_error(1, "is too large");
1808 RETURN_THROWS();
1809 }
1810
1811 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
1812
1813 int err = dom_perform_xinclude(docp, intern, flags);
1814
1815 if (err < 0) {
1816 php_dom_throw_error(INVALID_MODIFICATION_ERR, /* strict */ true);
1817 } else {
1818 RETURN_LONG(err);
1819 }
1820 }
1821 /* }}} */
1822
1823 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,validate)1824 PHP_METHOD(DOMDocument, validate)
1825 {
1826 zval *id;
1827 xmlDoc *docp;
1828 dom_object *intern;
1829 xmlValidCtxt *cvp;
1830
1831 id = ZEND_THIS;
1832 if (zend_parse_parameters_none() == FAILURE) {
1833 RETURN_THROWS();
1834 }
1835
1836 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1837
1838 PHP_LIBXML_SANITIZE_GLOBALS(validate);
1839 cvp = xmlNewValidCtxt();
1840
1841 cvp->userData = NULL;
1842 cvp->error = (xmlValidityErrorFunc) php_libxml_error_handler;
1843 cvp->warning = (xmlValidityErrorFunc) php_libxml_error_handler;
1844
1845 if (xmlValidateDocument(cvp, docp)) {
1846 RETVAL_TRUE;
1847 } else {
1848 RETVAL_FALSE;
1849 }
1850 PHP_LIBXML_RESTORE_GLOBALS(validate);
1851
1852 xmlFreeValidCtxt(cvp);
1853
1854 }
1855 /* }}} */
1856
1857 #ifdef LIBXML_SCHEMAS_ENABLED
dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1858 static void dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1859 {
1860 zval *id;
1861 xmlDoc *docp;
1862 dom_object *intern;
1863 char *source = NULL;
1864 const char *valid_file = NULL;
1865 size_t source_len = 0;
1866 int valid_opts = 0;
1867 zend_long flags = 0;
1868 xmlSchemaParserCtxtPtr parser;
1869 xmlSchemaPtr sptr;
1870 xmlSchemaValidCtxtPtr vptr;
1871 int is_valid;
1872 char resolved_path[MAXPATHLEN + 1];
1873
1874 id = ZEND_THIS;
1875 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &flags) == FAILURE) {
1876 RETURN_THROWS();
1877 }
1878
1879 if (!source_len) {
1880 zend_argument_must_not_be_empty_error(1);
1881 RETURN_THROWS();
1882 }
1883
1884 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1885
1886 PHP_LIBXML_SANITIZE_GLOBALS(new_parser_ctxt);
1887
1888 switch (type) {
1889 case DOM_LOAD_FILE:
1890 if (CHECK_NULL_PATH(source, source_len)) {
1891 PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1892 zend_argument_value_error(1, "must not contain any null bytes");
1893 RETURN_THROWS();
1894 }
1895 valid_file = dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1896 if (!valid_file) {
1897 PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1898 php_error_docref(NULL, E_WARNING, "Invalid Schema file source");
1899 RETURN_FALSE;
1900 }
1901 parser = xmlSchemaNewParserCtxt(valid_file);
1902 break;
1903 case DOM_LOAD_STRING:
1904 parser = xmlSchemaNewMemParserCtxt(source, source_len);
1905 /* If loading from memory, we need to set the base directory for the document
1906 but it is not apparent how to do that for schema's */
1907 break;
1908 default:
1909 return;
1910 }
1911
1912 xmlSchemaSetParserErrors(parser,
1913 (xmlSchemaValidityErrorFunc) php_libxml_error_handler,
1914 (xmlSchemaValidityWarningFunc) php_libxml_error_handler,
1915 parser);
1916 sptr = xmlSchemaParse(parser);
1917 xmlSchemaFreeParserCtxt(parser);
1918 PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1919 if (!sptr) {
1920 if (!EG(exception)) {
1921 php_error_docref(NULL, E_WARNING, "Invalid Schema");
1922 }
1923 RETURN_FALSE;
1924 }
1925
1926 docp = (xmlDocPtr) dom_object_get_node(intern);
1927
1928 vptr = xmlSchemaNewValidCtxt(sptr);
1929 if (!vptr) {
1930 xmlSchemaFree(sptr);
1931 zend_throw_error(NULL, "Invalid Schema Validation Context");
1932 RETURN_THROWS();
1933 }
1934
1935 if (flags & XML_SCHEMA_VAL_VC_I_CREATE) {
1936 valid_opts |= XML_SCHEMA_VAL_VC_I_CREATE;
1937 }
1938
1939 PHP_LIBXML_SANITIZE_GLOBALS(validate);
1940 xmlSchemaSetValidOptions(vptr, valid_opts);
1941 xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
1942 is_valid = xmlSchemaValidateDoc(vptr, docp);
1943 xmlSchemaFree(sptr);
1944 xmlSchemaFreeValidCtxt(vptr);
1945 PHP_LIBXML_RESTORE_GLOBALS(validate);
1946
1947 if (is_valid == 0) {
1948 RETURN_TRUE;
1949 } else {
1950 RETURN_FALSE;
1951 }
1952 }
1953 /* }}} */
1954
1955 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidate)1956 PHP_METHOD(DOMDocument, schemaValidate)
1957 {
1958 dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1959 }
1960 /* }}} end dom_document_schema_validate_file */
1961
1962 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidateSource)1963 PHP_METHOD(DOMDocument, schemaValidateSource)
1964 {
1965 dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1966 }
1967 /* }}} end dom_document_schema_validate */
1968
dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1969 static void dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1970 {
1971 zval *id;
1972 xmlDoc *docp;
1973 dom_object *intern;
1974 char *source = NULL;
1975 const char *valid_file = NULL;
1976 size_t source_len = 0;
1977 xmlRelaxNGParserCtxtPtr parser;
1978 xmlRelaxNGPtr sptr;
1979 xmlRelaxNGValidCtxtPtr vptr;
1980 int is_valid;
1981 char resolved_path[MAXPATHLEN + 1];
1982
1983 id = ZEND_THIS;
1984 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &source, &source_len) == FAILURE) {
1985 RETURN_THROWS();
1986 }
1987
1988 if (!source_len) {
1989 zend_argument_must_not_be_empty_error(1);
1990 RETURN_THROWS();
1991 }
1992
1993 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1994
1995 switch (type) {
1996 case DOM_LOAD_FILE:
1997 if (CHECK_NULL_PATH(source, source_len)) {
1998 zend_argument_value_error(1, "must not contain any null bytes");
1999 RETURN_THROWS();
2000 }
2001 valid_file = dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
2002 if (!valid_file) {
2003 php_error_docref(NULL, E_WARNING, "Invalid RelaxNG file source");
2004 RETURN_FALSE;
2005 }
2006 parser = xmlRelaxNGNewParserCtxt(valid_file);
2007 break;
2008 case DOM_LOAD_STRING:
2009 parser = xmlRelaxNGNewMemParserCtxt(source, source_len);
2010 /* If loading from memory, we need to set the base directory for the document
2011 but it is not apparent how to do that for schema's */
2012 break;
2013 default:
2014 return;
2015 }
2016
2017 PHP_LIBXML_SANITIZE_GLOBALS(parse);
2018 xmlRelaxNGSetParserErrors(parser,
2019 (xmlRelaxNGValidityErrorFunc) php_libxml_error_handler,
2020 (xmlRelaxNGValidityWarningFunc) php_libxml_error_handler,
2021 parser);
2022 sptr = xmlRelaxNGParse(parser);
2023 xmlRelaxNGFreeParserCtxt(parser);
2024 PHP_LIBXML_RESTORE_GLOBALS(parse);
2025 if (!sptr) {
2026 php_error_docref(NULL, E_WARNING, "Invalid RelaxNG");
2027 RETURN_FALSE;
2028 }
2029
2030 docp = (xmlDocPtr) dom_object_get_node(intern);
2031
2032 vptr = xmlRelaxNGNewValidCtxt(sptr);
2033 if (!vptr) {
2034 xmlRelaxNGFree(sptr);
2035 zend_throw_error(NULL, "Invalid RelaxNG Validation Context");
2036 RETURN_THROWS();
2037 }
2038
2039 xmlRelaxNGSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
2040 is_valid = xmlRelaxNGValidateDoc(vptr, docp);
2041 xmlRelaxNGFree(sptr);
2042 xmlRelaxNGFreeValidCtxt(vptr);
2043
2044 if (is_valid == 0) {
2045 RETURN_TRUE;
2046 } else {
2047 RETURN_FALSE;
2048 }
2049 }
2050 /* }}} */
2051
2052 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidate)2053 PHP_METHOD(DOMDocument, relaxNGValidate)
2054 {
2055 dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
2056 }
2057 /* }}} end dom_document_relaxNG_validate_file */
2058
2059 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidateSource)2060 PHP_METHOD(DOMDocument, relaxNGValidateSource)
2061 {
2062 dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
2063 }
2064 /* }}} end dom_document_relaxNG_validate_xml */
2065
2066 #endif
2067
2068 #ifdef LIBXML_HTML_ENABLED
2069
dom_load_html(INTERNAL_FUNCTION_PARAMETERS,int mode)2070 static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
2071 {
2072 char *source;
2073 size_t source_len;
2074 zend_long options = 0;
2075 htmlParserCtxtPtr ctxt;
2076
2077 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
2078 RETURN_THROWS();
2079 }
2080
2081 if (!source_len) {
2082 zend_argument_must_not_be_empty_error(1);
2083 RETURN_THROWS();
2084 }
2085
2086 if (ZEND_LONG_EXCEEDS_INT(options)) {
2087 php_error_docref(NULL, E_WARNING, "Invalid options");
2088 RETURN_FALSE;
2089 }
2090
2091 if (mode == DOM_LOAD_FILE) {
2092 if (CHECK_NULL_PATH(source, source_len)) {
2093 zend_argument_value_error(1, "must not contain any null bytes");
2094 RETURN_THROWS();
2095 }
2096 ctxt = htmlCreateFileParserCtxt(source, NULL);
2097 } else {
2098 if (ZEND_SIZE_T_INT_OVFL(source_len)) {
2099 php_error_docref(NULL, E_WARNING, "Input string is too long");
2100 RETURN_FALSE;
2101 }
2102 ctxt = htmlCreateMemoryParserCtxt(source, (int)source_len);
2103 }
2104
2105 if (!ctxt) {
2106 RETURN_FALSE;
2107 }
2108
2109
2110 ctxt->vctxt.error = php_libxml_ctx_error;
2111 ctxt->vctxt.warning = php_libxml_ctx_warning;
2112 if (ctxt->sax != NULL) {
2113 ctxt->sax->error = php_libxml_ctx_error;
2114 ctxt->sax->warning = php_libxml_ctx_warning;
2115 }
2116 php_libxml_sanitize_parse_ctxt_options(ctxt);
2117 if (options) {
2118 htmlCtxtUseOptions(ctxt, (int)options);
2119 }
2120 htmlParseDocument(ctxt);
2121 xmlDocPtr newdoc = ctxt->myDoc;
2122 htmlFreeParserCtxt(ctxt);
2123
2124 php_dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
2125 }
2126 /* }}} */
2127
2128 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTMLFile)2129 PHP_METHOD(DOMDocument, loadHTMLFile)
2130 {
2131 dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
2132 }
2133 /* }}} end dom_document_load_html_file */
2134
2135 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTML)2136 PHP_METHOD(DOMDocument, loadHTML)
2137 {
2138 dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
2139 }
2140 /* }}} end dom_document_load_html */
2141
2142 /* {{{ Convenience method to save to file as html */
PHP_METHOD(DOMDocument,saveHTMLFile)2143 PHP_METHOD(DOMDocument, saveHTMLFile)
2144 {
2145 zval *id;
2146 xmlDoc *docp;
2147 size_t file_len;
2148 int bytes, format;
2149 dom_object *intern;
2150 char *file;
2151 const char *encoding;
2152
2153 id = ZEND_THIS;
2154 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &file, &file_len) == FAILURE) {
2155 RETURN_THROWS();
2156 }
2157
2158 if (file_len == 0) {
2159 zend_argument_must_not_be_empty_error(1);
2160 RETURN_THROWS();
2161 }
2162
2163 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2164
2165
2166 encoding = (const char *) htmlGetMetaEncoding(docp);
2167
2168 libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
2169 format = doc_props->formatoutput;
2170 bytes = htmlSaveFileFormat(file, docp, encoding, format);
2171
2172 if (bytes == -1) {
2173 RETURN_FALSE;
2174 }
2175 RETURN_LONG(bytes);
2176 }
2177 /* }}} end dom_document_save_html_file */
2178
2179 /* {{{ Convenience method to output as html */
PHP_METHOD(DOMDocument,saveHTML)2180 PHP_METHOD(DOMDocument, saveHTML)
2181 {
2182 zval *id, *nodep = NULL;
2183 xmlDoc *docp;
2184 xmlNode *node;
2185 xmlOutputBufferPtr outBuf;
2186 xmlBufferPtr buf;
2187 dom_object *intern, *nodeobj;
2188 int format;
2189
2190 id = ZEND_THIS;
2191 if (zend_parse_parameters(ZEND_NUM_ARGS(),
2192 "|O!", &nodep, dom_node_class_entry)
2193 == FAILURE) {
2194 RETURN_THROWS();
2195 }
2196
2197 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2198
2199 libxml_doc_props const* doc_props = dom_get_doc_props(intern->document);
2200 format = doc_props->formatoutput;
2201
2202 if (nodep != NULL) {
2203 /* Dump contents of Node */
2204 DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
2205 if (node->doc != docp) {
2206 php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
2207 RETURN_FALSE;
2208 }
2209
2210 buf = xmlBufferCreate();
2211 if (!buf) {
2212 php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
2213 RETURN_FALSE;
2214 }
2215 outBuf = xmlOutputBufferCreateBuffer(buf, NULL);
2216 if (!outBuf) {
2217 xmlBufferFree(buf);
2218 php_error_docref(NULL, E_WARNING, "Could not fetch output buffer");
2219 RETURN_FALSE;
2220 }
2221
2222 if (node->type == XML_DOCUMENT_FRAG_NODE) {
2223 for (node = node->children; node; node = node->next) {
2224 htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2225 if (outBuf->error) {
2226 break;
2227 }
2228 }
2229 } else {
2230 htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2231 }
2232 if (!outBuf->error) {
2233 xmlOutputBufferFlush(outBuf);
2234 const xmlChar *mem = xmlBufferContent(buf);
2235 if (!mem) {
2236 RETVAL_FALSE;
2237 } else {
2238 int size = xmlBufferLength(buf);
2239 RETVAL_STRINGL((const char*) mem, size);
2240 }
2241 } else {
2242 php_error_docref(NULL, E_WARNING, "Error dumping HTML node");
2243 RETVAL_FALSE;
2244 }
2245 xmlOutputBufferClose(outBuf);
2246 xmlBufferFree(buf);
2247 } else {
2248 xmlChar *mem = NULL;
2249 int size = 0;
2250 htmlDocDumpMemoryFormat(docp, &mem, &size, format);
2251 if (!size || !mem) {
2252 RETVAL_FALSE;
2253 } else {
2254 RETVAL_STRINGL((const char*) mem, size);
2255 }
2256 xmlFree(mem);
2257 }
2258
2259 }
2260 /* }}} end dom_document_save_html */
2261
2262 #endif /* defined(LIBXML_HTML_ENABLED) */
2263
2264 /* {{{ Register extended class used to create base node type */
dom_document_register_node_class(INTERNAL_FUNCTION_PARAMETERS,bool modern)2265 static void dom_document_register_node_class(INTERNAL_FUNCTION_PARAMETERS, bool modern)
2266 {
2267 zend_class_entry *basece = dom_get_node_ce(modern), *ce = NULL;
2268 dom_object *intern;
2269
2270 if (zend_parse_parameters(ZEND_NUM_ARGS(), "CC!", &basece, &ce) == FAILURE) {
2271 RETURN_THROWS();
2272 }
2273
2274 if (basece->ce_flags & ZEND_ACC_ABSTRACT) {
2275 zend_argument_value_error(1, "must not be an abstract class");
2276 RETURN_THROWS();
2277 }
2278
2279 if (ce == NULL || instanceof_function(ce, basece)) {
2280 if (UNEXPECTED(ce != NULL && (ce->ce_flags & ZEND_ACC_ABSTRACT))) {
2281 zend_argument_value_error(2, "must not be an abstract class");
2282 RETURN_THROWS();
2283 }
2284 DOM_GET_THIS_INTERN(intern);
2285 dom_set_doc_classmap(intern->document, basece, ce);
2286 if (!modern) {
2287 RETVAL_TRUE;
2288 }
2289 return;
2290 }
2291
2292 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));
2293 RETURN_THROWS();
2294 }
2295
PHP_METHOD(DOMDocument,registerNodeClass)2296 PHP_METHOD(DOMDocument, registerNodeClass)
2297 {
2298 dom_document_register_node_class(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
2299 }
2300
PHP_METHOD(Dom_Document,registerNodeClass)2301 PHP_METHOD(Dom_Document, registerNodeClass)
2302 {
2303 dom_document_register_node_class(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
2304 }
2305 /* }}} */
2306
2307 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
2308 Since:
2309 */
PHP_METHOD(DOMDocument,replaceChildren)2310 PHP_METHOD(DOMDocument, replaceChildren)
2311 {
2312 uint32_t argc = 0;
2313 zval *args;
2314 dom_object *intern;
2315
2316 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
2317 RETURN_THROWS();
2318 }
2319
2320 DOM_GET_THIS_INTERN(intern);
2321
2322 dom_parent_node_replace_children(intern, args, argc);
2323 }
2324 /* }}} */
2325
2326 #endif /* HAVE_LIBXML && HAVE_DOM */
2327