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 zval *id;
1032 xmlDocPtr docp;
1033 xmlAttrPtr attrp;
1034 size_t idname_len;
1035 dom_object *intern;
1036 char *idname;
1037
1038 id = ZEND_THIS;
1039 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &idname, &idname_len) == FAILURE) {
1040 RETURN_THROWS();
1041 }
1042
1043 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1044
1045 attrp = xmlGetID(docp, BAD_CAST idname);
1046
1047 /* From the moment an ID is created, libxml2's behaviour is to cache that element, even
1048 * if that element is not yet attached to the document. Similarly, only upon destruction of
1049 * the element the ID is actually removed by libxml2. Since libxml2 has such behaviour deeply
1050 * ingrained in the library, and uses the cache for various purposes, it seems like a bad
1051 * idea and lost cause to fight it. Instead, we'll simply walk the tree upwards to check
1052 * if the node is attached to the document. */
1053 if (attrp && attrp->parent && php_dom_is_node_connected(attrp->parent)) {
1054 DOM_RET_OBJ((xmlNodePtr) attrp->parent, intern);
1055 } else {
1056 RETVAL_NULL();
1057 }
1058
1059 }
1060 /* }}} end dom_document_get_element_by_id */
1061
php_dom_transfer_document_ref_single_node(xmlNodePtr node,php_libxml_ref_obj * new_document)1062 static zend_always_inline void php_dom_transfer_document_ref_single_node(xmlNodePtr node, php_libxml_ref_obj *new_document)
1063 {
1064 php_libxml_node_ptr *iteration_object_ptr = node->_private;
1065 if (iteration_object_ptr) {
1066 php_libxml_node_object *iteration_object = iteration_object_ptr->_private;
1067 ZEND_ASSERT(iteration_object != NULL);
1068 /* Must increase refcount first because we could be the last reference holder, and the document may be equal. */
1069 new_document->refcount++;
1070 php_libxml_decrement_doc_ref(iteration_object);
1071 iteration_object->document = new_document;
1072 }
1073 }
1074
php_dom_transfer_document_ref(xmlNodePtr node,php_libxml_ref_obj * new_document)1075 static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *new_document)
1076 {
1077 if (node->children) {
1078 php_dom_transfer_document_ref(node->children, new_document);
1079 }
1080
1081 while (node) {
1082 if (node->type == XML_ELEMENT_NODE) {
1083 for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
1084 php_dom_transfer_document_ref_single_node((xmlNodePtr) attr, new_document);
1085 }
1086 }
1087
1088 php_dom_transfer_document_ref_single_node(node, new_document);
1089 node = node->next;
1090 }
1091 }
1092
1093 /* Workaround for bug that was fixed in https://github.com/GNOME/libxml2/commit/4bc3ebf3eaba352fbbce2ef70ad00a3c7752478a */
1094 #if LIBXML_VERSION < 21000
libxml_copy_dicted_string(xmlDictPtr src_dict,xmlDictPtr dst_dict,xmlChar * str)1095 static xmlChar *libxml_copy_dicted_string(xmlDictPtr src_dict, xmlDictPtr dst_dict, xmlChar *str)
1096 {
1097 if (str == NULL) {
1098 return NULL;
1099 }
1100 if (xmlDictOwns(src_dict, str) == 1) {
1101 if (dst_dict == NULL) {
1102 return xmlStrdup(str);
1103 }
1104 return BAD_CAST xmlDictLookup(dst_dict, str, -1);
1105 }
1106 return str;
1107 }
1108
libxml_fixup_name_and_content(xmlDocPtr src_doc,xmlDocPtr dst_doc,xmlNodePtr node)1109 static void libxml_fixup_name_and_content(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
1110 {
1111 if (src_doc != NULL && dst_doc != src_doc && src_doc->dict != NULL) {
1112 node->name = libxml_copy_dicted_string(src_doc->dict, dst_doc->dict, BAD_CAST node->name);
1113 node->content = libxml_copy_dicted_string(src_doc->dict, NULL, node->content);
1114 }
1115 }
1116
libxml_fixup_name_and_content_element(xmlDocPtr src_doc,xmlDocPtr dst_doc,xmlNodePtr node)1117 static void libxml_fixup_name_and_content_element(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
1118 {
1119 libxml_fixup_name_and_content(src_doc, dst_doc, node);
1120 for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
1121 libxml_fixup_name_and_content(src_doc, dst_doc, (xmlNodePtr) attr);
1122 }
1123
1124 for (xmlNodePtr child = node->children; child != NULL; child = child->next) {
1125 libxml_fixup_name_and_content_element(src_doc, dst_doc, child);
1126 }
1127 }
1128 #endif
1129
php_dom_adopt_node(xmlNodePtr nodep,dom_object * dom_object_new_document,xmlDocPtr new_document)1130 bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document)
1131 {
1132 xmlDocPtr original_document = nodep->doc;
1133 php_libxml_invalidate_node_list_cache_from_doc(original_document);
1134 if (nodep->doc != new_document) {
1135 php_libxml_invalidate_node_list_cache(dom_object_new_document->document);
1136
1137 /* Note for ATTRIBUTE_NODE: specified is always true in ext/dom,
1138 * and since this unlink it; the owner element will be unset (i.e. parentNode). */
1139 if (php_dom_follow_spec_intern(dom_object_new_document)) {
1140 xmlUnlinkNode(nodep);
1141 xmlSetTreeDoc(nodep, new_document);
1142 php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(dom_object_new_document);
1143 php_dom_libxml_reconcile_modern(ns_mapper, nodep);
1144 #if LIBXML_VERSION < 21000
1145 libxml_fixup_name_and_content_element(original_document, new_document, nodep);
1146 #endif
1147 } else {
1148 int ret = xmlDOMWrapAdoptNode(NULL, original_document, nodep, new_document, NULL, /* options, unused */ 0);
1149 if (UNEXPECTED(ret != 0)) {
1150 return false;
1151 }
1152 }
1153
1154 php_dom_transfer_document_ref(nodep, dom_object_new_document->document);
1155 } else {
1156 xmlUnlinkNode(nodep);
1157 }
1158 return true;
1159 }
1160
1161 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNode
1162 Since: DOM Level 3
1163 Modern spec URL: https://dom.spec.whatwg.org/#dom-document-adoptnode
1164 */
dom_document_adopt_node(INTERNAL_FUNCTION_PARAMETERS,bool modern)1165 static void dom_document_adopt_node(INTERNAL_FUNCTION_PARAMETERS, bool modern)
1166 {
1167 zval *node_zval;
1168 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, dom_get_node_ce(modern)) == FAILURE) {
1169 RETURN_THROWS();
1170 }
1171
1172 xmlNodePtr nodep;
1173 dom_object *dom_object_nodep;
1174 DOM_GET_OBJ(nodep, node_zval, xmlNodePtr, dom_object_nodep);
1175
1176 if (UNEXPECTED(nodep->type == XML_DOCUMENT_NODE
1177 || nodep->type == XML_HTML_DOCUMENT_NODE
1178 || nodep->type == XML_DOCUMENT_TYPE_NODE
1179 || nodep->type == XML_DTD_NODE
1180 || nodep->type == XML_ENTITY_NODE
1181 || nodep->type == XML_NOTATION_NODE)) {
1182 php_dom_throw_error(NOT_SUPPORTED_ERR, dom_get_strict_error(dom_object_nodep->document));
1183 RETURN_FALSE;
1184 }
1185
1186 xmlDocPtr new_document;
1187 dom_object *dom_object_new_document;
1188 zval *new_document_zval = ZEND_THIS;
1189 DOM_GET_OBJ(new_document, new_document_zval, xmlDocPtr, dom_object_new_document);
1190
1191 if (!php_dom_adopt_node(nodep, dom_object_new_document, new_document)) {
1192 if (modern) {
1193 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
1194 RETURN_THROWS();
1195 }
1196 RETURN_FALSE;
1197 }
1198
1199 RETURN_OBJ_COPY(&dom_object_nodep->std);
1200 }
1201
PHP_METHOD(DOMDocument,adoptNode)1202 PHP_METHOD(DOMDocument, adoptNode)
1203 {
1204 dom_document_adopt_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1205 }
1206
PHP_METHOD(Dom_Document,adoptNode)1207 PHP_METHOD(Dom_Document, adoptNode)
1208 {
1209 dom_document_adopt_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1210 }
1211 /* }}} end dom_document_adopt_node */
1212
1213 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocument
1214 Since: DOM Level 3
1215 */
PHP_METHOD(DOMDocument,normalizeDocument)1216 PHP_METHOD(DOMDocument, normalizeDocument)
1217 {
1218 zval *id;
1219 xmlDocPtr docp;
1220 dom_object *intern;
1221
1222 id = ZEND_THIS;
1223 if (zend_parse_parameters_none() == FAILURE) {
1224 RETURN_THROWS();
1225 }
1226
1227 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1228
1229 php_dom_normalize_legacy((xmlNodePtr) docp);
1230 }
1231 /* }}} end dom_document_normalize_document */
1232
1233 /* {{{ */
PHP_METHOD(DOMDocument,__construct)1234 PHP_METHOD(DOMDocument, __construct)
1235 {
1236 xmlDoc *docp = NULL, *olddoc;
1237 dom_object *intern;
1238 char *encoding, *version = NULL;
1239 size_t encoding_len = 0, version_len = 0;
1240 int refcount;
1241
1242 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &version, &version_len, &encoding, &encoding_len) == FAILURE) {
1243 RETURN_THROWS();
1244 }
1245
1246 docp = xmlNewDoc(BAD_CAST version);
1247
1248 if (!docp) {
1249 php_dom_throw_error(INVALID_STATE_ERR, true);
1250 return;
1251 }
1252
1253 if (encoding_len > 0) {
1254 docp->encoding = (const xmlChar *) xmlStrdup(BAD_CAST encoding);
1255 }
1256
1257 intern = Z_DOMOBJ_P(ZEND_THIS);
1258 olddoc = (xmlDocPtr) dom_object_get_node(intern);
1259 if (olddoc != NULL) {
1260 php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1261 refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1262 if (refcount != 0) {
1263 olddoc->_private = NULL;
1264 }
1265 }
1266 intern->document = NULL;
1267 if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp) == -1) {
1268 /* docp is always non-null so php_libxml_increment_doc_ref() never returns -1 */
1269 ZEND_UNREACHABLE();
1270 }
1271 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)docp, (void *)intern);
1272 }
1273 /* }}} end DOMDocument::__construct */
1274
_dom_get_valid_file_path(const char * source,char * resolved_path,int resolved_path_len)1275 const char *_dom_get_valid_file_path(const char *source, char *resolved_path, int resolved_path_len ) /* {{{ */
1276 {
1277 xmlURI *uri;
1278 xmlChar *escsource;
1279 const char *file_dest;
1280 int isFileUri = 0;
1281
1282 uri = xmlCreateURI();
1283 if (uri == NULL) {
1284 return NULL;
1285 }
1286 escsource = xmlURIEscapeStr(BAD_CAST source, BAD_CAST ":");
1287 xmlParseURIReference(uri, (char *) escsource);
1288 xmlFree(escsource);
1289
1290 if (uri->scheme != NULL) {
1291 /* absolute file uris - libxml only supports localhost or empty host */
1292 #ifdef PHP_WIN32
1293 if (strncasecmp(source, "file://",7) == 0 && ':' == source[8]) {
1294 isFileUri = 1;
1295 source += 7;
1296 } else
1297 #endif
1298 if (strncasecmp(source, "file:///",8) == 0) {
1299 isFileUri = 1;
1300 #ifdef PHP_WIN32
1301 source += 8;
1302 #else
1303 source += 7;
1304 #endif
1305 } else if (strncasecmp(source, "file://localhost/",17) == 0) {
1306 isFileUri = 1;
1307 #ifdef PHP_WIN32
1308 source += 17;
1309 #else
1310 source += 16;
1311 #endif
1312 }
1313 }
1314
1315 file_dest = source;
1316
1317 if ((uri->scheme == NULL || isFileUri)) {
1318 /* XXX possible buffer overflow if VCWD_REALPATH does not know size of resolved_path */
1319 if (!VCWD_REALPATH(source, resolved_path) && !expand_filepath(source, resolved_path)) {
1320 xmlFreeURI(uri);
1321 return NULL;
1322 }
1323 file_dest = resolved_path;
1324 }
1325
1326 xmlFreeURI(uri);
1327
1328 return file_dest;
1329 }
1330 /* }}} */
1331
dom_document_parser(zval * id,dom_load_mode mode,const char * source,size_t source_len,size_t options,xmlCharEncodingHandlerPtr encoding)1332 xmlDocPtr dom_document_parser(zval *id, dom_load_mode mode, const char *source, size_t source_len, size_t options, xmlCharEncodingHandlerPtr encoding) /* {{{ */
1333 {
1334 xmlDocPtr ret;
1335 xmlParserCtxtPtr ctxt = NULL;
1336 int validate, recover, resolve_externals, keep_blanks, substitute_ent;
1337 int resolved_path_len;
1338 int old_error_reporting = 0;
1339 char *directory=NULL, resolved_path[MAXPATHLEN + 1];
1340
1341 libxml_doc_props const* doc_props;
1342 if (id == NULL) {
1343 doc_props = dom_get_doc_props_read_only(NULL);
1344 } else {
1345 dom_object *intern = Z_DOMOBJ_P(id);
1346 php_libxml_ref_obj *document = intern->document;
1347 doc_props = dom_get_doc_props_read_only(document);
1348 }
1349 validate = doc_props->validateonparse;
1350 resolve_externals = doc_props->resolveexternals;
1351 keep_blanks = doc_props->preservewhitespace;
1352 substitute_ent = doc_props->substituteentities;
1353 recover = doc_props->recover || (options & XML_PARSE_RECOVER) == XML_PARSE_RECOVER;
1354
1355 xmlInitParser();
1356
1357 if (mode == DOM_LOAD_FILE) {
1358 if (CHECK_NULL_PATH(source, source_len)) {
1359 zend_argument_value_error(1, "must not contain any null bytes");
1360 return NULL;
1361 }
1362 const char *file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1363 if (file_dest) {
1364 ctxt = xmlCreateFileParserCtxt(file_dest);
1365 }
1366 } else {
1367 ctxt = xmlCreateMemoryParserCtxt(source, source_len);
1368 }
1369
1370 if (ctxt == NULL) {
1371 return(NULL);
1372 }
1373
1374 if (encoding != NULL) {
1375 /* Note: libxml 2.12+ doesn't handle NULL encoding well. */
1376 (void) xmlSwitchToEncoding(ctxt, encoding);
1377 }
1378
1379 /* If loading from memory, we need to set the base directory for the document */
1380 if (mode != DOM_LOAD_FILE) {
1381 #ifdef HAVE_GETCWD
1382 directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
1383 #elif defined(HAVE_GETWD)
1384 directory = VCWD_GETWD(resolved_path);
1385 #endif
1386 if (directory) {
1387 if(ctxt->directory != NULL) {
1388 xmlFree((char *) ctxt->directory);
1389 }
1390 resolved_path_len = strlen(resolved_path);
1391 if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) {
1392 resolved_path[resolved_path_len] = DEFAULT_SLASH;
1393 resolved_path[++resolved_path_len] = '\0';
1394 }
1395 ctxt->directory = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
1396 }
1397 }
1398
1399 ctxt->vctxt.error = php_libxml_ctx_error;
1400 ctxt->vctxt.warning = php_libxml_ctx_warning;
1401
1402 if (ctxt->sax != NULL) {
1403 ctxt->sax->error = php_libxml_ctx_error;
1404 ctxt->sax->warning = php_libxml_ctx_warning;
1405 }
1406
1407 if (validate && ! (options & XML_PARSE_DTDVALID)) {
1408 options |= XML_PARSE_DTDVALID;
1409 }
1410 if (resolve_externals && ! (options & XML_PARSE_DTDATTR)) {
1411 options |= XML_PARSE_DTDATTR;
1412 }
1413 if (substitute_ent && ! (options & XML_PARSE_NOENT)) {
1414 options |= XML_PARSE_NOENT;
1415 }
1416 if (keep_blanks == 0 && ! (options & XML_PARSE_NOBLANKS)) {
1417 options |= XML_PARSE_NOBLANKS;
1418 }
1419
1420 php_libxml_sanitize_parse_ctxt_options(ctxt);
1421 xmlCtxtUseOptions(ctxt, options);
1422
1423 ctxt->recovery = recover;
1424 if (recover) {
1425 old_error_reporting = EG(error_reporting);
1426 EG(error_reporting) = old_error_reporting | E_WARNING;
1427 }
1428
1429 xmlParseDocument(ctxt);
1430
1431 if (ctxt->wellFormed || recover) {
1432 ret = ctxt->myDoc;
1433 if (ctxt->recovery) {
1434 EG(error_reporting) = old_error_reporting;
1435 }
1436 /* If loading from memory, set the base reference uri for the document */
1437 if (ret && ret->URL == NULL && ctxt->directory != NULL) {
1438 ret->URL = xmlStrdup(BAD_CAST ctxt->directory);
1439 }
1440 } else {
1441 ret = DOM_DOCUMENT_MALFORMED;
1442 xmlFreeDoc(ctxt->myDoc);
1443 ctxt->myDoc = NULL;
1444 }
1445
1446 xmlFreeParserCtxt(ctxt);
1447
1448 return(ret);
1449 }
1450 /* }}} */
1451
php_dom_finish_loading_document(zval * this,zval * return_value,xmlDocPtr newdoc)1452 static void php_dom_finish_loading_document(zval *this, zval *return_value, xmlDocPtr newdoc)
1453 {
1454 if (!newdoc)
1455 RETURN_FALSE;
1456
1457 dom_object *intern = Z_DOMOBJ_P(this);
1458 size_t old_modification_nr = 0;
1459 if (intern != NULL) {
1460 php_libxml_class_type class_type = PHP_LIBXML_CLASS_LEGACY;
1461 xmlDocPtr docp = (xmlDocPtr) dom_object_get_node(intern);
1462 dom_doc_propsptr doc_prop = NULL;
1463 if (docp != NULL) {
1464 const php_libxml_ref_obj *doc_ptr = intern->document;
1465 ZEND_ASSERT(doc_ptr != NULL); /* Must exist, we have a document */
1466 class_type = doc_ptr->class_type;
1467 old_modification_nr = doc_ptr->cache_tag.modification_nr;
1468 php_libxml_decrement_node_ptr((php_libxml_node_object *) intern);
1469 doc_prop = intern->document->doc_props;
1470 intern->document->doc_props = NULL;
1471 int refcount = php_libxml_decrement_doc_ref((php_libxml_node_object *)intern);
1472 if (refcount != 0) {
1473 docp->_private = NULL;
1474 }
1475 }
1476 intern->document = NULL;
1477 if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc) == -1) {
1478 RETURN_FALSE;
1479 }
1480 intern->document->doc_props = doc_prop;
1481 intern->document->class_type = class_type;
1482 }
1483
1484 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)newdoc, (void *)intern);
1485 /* Since iterators should invalidate, we need to start the modification number from the old counter */
1486 if (old_modification_nr != 0) {
1487 intern->document->cache_tag.modification_nr = old_modification_nr;
1488 php_libxml_invalidate_node_list_cache(intern->document);
1489 }
1490
1491 RETURN_TRUE;
1492 }
1493
dom_parse_document(INTERNAL_FUNCTION_PARAMETERS,int mode)1494 static void dom_parse_document(INTERNAL_FUNCTION_PARAMETERS, int mode)
1495 {
1496 char *source;
1497 size_t source_len;
1498 zend_long options = 0;
1499
1500 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
1501 RETURN_THROWS();
1502 }
1503
1504 if (!source_len) {
1505 zend_argument_value_error(1, "must not be empty");
1506 RETURN_THROWS();
1507 }
1508 if (ZEND_SIZE_T_INT_OVFL(source_len)) {
1509 php_error_docref(NULL, E_WARNING, "Input string is too long");
1510 RETURN_FALSE;
1511 }
1512 if (ZEND_LONG_EXCEEDS_INT(options)) {
1513 php_error_docref(NULL, E_WARNING, "Invalid options");
1514 RETURN_FALSE;
1515 }
1516
1517 xmlDocPtr newdoc = dom_document_parser(ZEND_THIS, mode, source, source_len, options, NULL);
1518 if (newdoc == DOM_DOCUMENT_MALFORMED) {
1519 newdoc = NULL;
1520 }
1521 php_dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
1522 }
1523
1524 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-load
1525 Since: DOM Level 3
1526 */
PHP_METHOD(DOMDocument,load)1527 PHP_METHOD(DOMDocument, load)
1528 {
1529 dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1530 }
1531 /* }}} end dom_document_load */
1532
1533 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadXML
1534 Since: DOM Level 3
1535 */
PHP_METHOD(DOMDocument,loadXML)1536 PHP_METHOD(DOMDocument, loadXML)
1537 {
1538 dom_parse_document(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1539 }
1540 /* }}} end dom_document_loadxml */
1541
1542 /* {{{ Convenience method to save to file */
PHP_METHOD(DOMDocument,save)1543 PHP_METHOD(DOMDocument, save)
1544 {
1545 zval *id;
1546 xmlDoc *docp;
1547 size_t file_len = 0;
1548 int saveempty = 0;
1549 dom_object *intern;
1550 char *file;
1551 zend_long options = 0;
1552
1553 id = ZEND_THIS;
1554 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &file, &file_len, &options) == FAILURE) {
1555 RETURN_THROWS();
1556 }
1557
1558 if (file_len == 0) {
1559 zend_argument_value_error(1, "must not be empty");
1560 RETURN_THROWS();
1561 }
1562
1563 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1564
1565 /* encoding handled by property on doc */
1566
1567 libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
1568 bool format = doc_props->formatoutput;
1569 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1570 saveempty = xmlSaveNoEmptyTags;
1571 xmlSaveNoEmptyTags = 1;
1572 }
1573 zend_long bytes = intern->document->handlers->dump_doc_to_file(file, docp, format, (const char *) docp->encoding);
1574 if (options & LIBXML_SAVE_NOEMPTYTAG) {
1575 xmlSaveNoEmptyTags = saveempty;
1576 }
1577 if (bytes == -1) {
1578 RETURN_FALSE;
1579 }
1580 RETURN_LONG(bytes);
1581 }
1582 /* }}} end dom_document_save */
1583
1584 /* {{{ URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML
1585 Since: DOM Level 3
1586 */
dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * node_ce)1587 static void dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce)
1588 {
1589 zval *nodep = NULL;
1590 xmlDoc *docp;
1591 xmlNode *node;
1592 dom_object *intern, *nodeobj;
1593 int old_xml_save_no_empty_tags;
1594 zend_long options = 0;
1595
1596 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!l", &nodep, node_ce, &options) != SUCCESS) {
1597 RETURN_THROWS();
1598 }
1599
1600 DOM_GET_OBJ(docp, ZEND_THIS, xmlDocPtr, intern);
1601
1602 libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
1603 bool format = doc_props->formatoutput;
1604
1605 zend_string *res;
1606 if (nodep != NULL) {
1607 /* Dump contents of Node */
1608 DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
1609 if (node->doc != docp) {
1610 php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
1611 RETURN_FALSE;
1612 }
1613
1614 /* Save libxml2 global, override its value, and restore after saving (don't move me or risk breaking the state
1615 * w.r.t. the implicit return in DOM_GET_OBJ). */
1616 old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
1617 xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
1618 res = intern->document->handlers->dump_node_to_str(docp, node, format, (const char *) docp->encoding);
1619 xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
1620 } else {
1621 int converted_options = XML_SAVE_AS_XML;
1622 if (options & XML_SAVE_NO_DECL) {
1623 converted_options |= XML_SAVE_NO_DECL;
1624 }
1625 if (format) {
1626 converted_options |= XML_SAVE_FORMAT;
1627 }
1628
1629 /* Save libxml2 global, override its value, and restore after saving. */
1630 old_xml_save_no_empty_tags = xmlSaveNoEmptyTags;
1631 xmlSaveNoEmptyTags = (options & LIBXML_SAVE_NOEMPTYTAG) ? 1 : 0;
1632 res = intern->document->handlers->dump_doc_to_str(docp, converted_options, (const char *) docp->encoding);
1633 xmlSaveNoEmptyTags = old_xml_save_no_empty_tags;
1634 }
1635
1636 if (!res) {
1637 php_error_docref(NULL, E_WARNING, "Could not save document");
1638 RETURN_FALSE;
1639 } else {
1640 RETURN_NEW_STR(res);
1641 }
1642 }
1643
PHP_METHOD(DOMDocument,saveXML)1644 PHP_METHOD(DOMDocument, saveXML)
1645 {
1646 dom_document_save_xml(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_node_class_entry);
1647 }
1648
PHP_METHOD(Dom_XMLDocument,saveXml)1649 PHP_METHOD(Dom_XMLDocument, saveXml)
1650 {
1651 dom_document_save_xml(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_node_class_entry);
1652 }
1653 /* }}} end dom_document_savexml */
1654
php_dom_free_xinclude_node(xmlNodePtr cur)1655 static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) /* {{{ */
1656 {
1657 xmlNodePtr xincnode;
1658
1659 xincnode = cur;
1660 cur = cur->next;
1661 xmlUnlinkNode(xincnode);
1662 php_libxml_node_free_resource(xincnode);
1663
1664 return cur;
1665 }
1666 /* }}} */
1667
php_dom_remove_xinclude_nodes(xmlNodePtr cur)1668 static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
1669 {
1670 while(cur) {
1671 if (cur->type == XML_XINCLUDE_START) {
1672 cur = php_dom_free_xinclude_node(cur);
1673
1674 /* XML_XINCLUDE_END node will be a sibling of XML_XINCLUDE_START */
1675 while(cur && cur->type != XML_XINCLUDE_END) {
1676 /* remove xinclude processing nodes from recursive xincludes */
1677 if (cur->type == XML_ELEMENT_NODE) {
1678 php_dom_remove_xinclude_nodes(cur->children);
1679 }
1680 cur = cur->next;
1681 }
1682
1683 if (cur && cur->type == XML_XINCLUDE_END) {
1684 cur = php_dom_free_xinclude_node(cur);
1685 }
1686 } else {
1687 if (cur->type == XML_ELEMENT_NODE) {
1688 php_dom_remove_xinclude_nodes(cur->children);
1689 }
1690 cur = cur->next;
1691 }
1692 }
1693 }
1694 /* }}} */
1695
1696 /* {{{ Substitutues xincludes in a DomDocument */
PHP_METHOD(DOMDocument,xinclude)1697 PHP_METHOD(DOMDocument, xinclude)
1698 {
1699 zval *id;
1700 xmlDoc *docp;
1701 xmlNodePtr root;
1702 zend_long flags = 0;
1703 int err;
1704 dom_object *intern;
1705
1706 id = ZEND_THIS;
1707 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
1708 RETURN_THROWS();
1709 }
1710
1711 if (ZEND_LONG_EXCEEDS_INT(flags)) {
1712 php_error_docref(NULL, E_WARNING, "Invalid flags");
1713 RETURN_FALSE;
1714 }
1715
1716 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1717
1718 PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
1719 err = xmlXIncludeProcessFlags(docp, (int)flags);
1720 PHP_LIBXML_RESTORE_GLOBALS(xinclude);
1721
1722 /* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
1723 are added via xmlXIncludeProcess to mark beginning and ending of xincluded document
1724 but are not wanted in resulting document - must be done even if err as it could fail after
1725 having processed some xincludes */
1726 root = (xmlNodePtr) docp->children;
1727 while(root && root->type != XML_ELEMENT_NODE && root->type != XML_XINCLUDE_START) {
1728 root = root->next;
1729 }
1730 if (root) {
1731 php_dom_remove_xinclude_nodes(root);
1732 }
1733
1734 php_libxml_invalidate_node_list_cache(intern->document);
1735
1736 if (err) {
1737 RETVAL_LONG(err);
1738 } else {
1739 RETVAL_FALSE;
1740 }
1741 }
1742 /* }}} */
1743
1744 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,validate)1745 PHP_METHOD(DOMDocument, validate)
1746 {
1747 zval *id;
1748 xmlDoc *docp;
1749 dom_object *intern;
1750 xmlValidCtxt *cvp;
1751
1752 id = ZEND_THIS;
1753 if (zend_parse_parameters_none() == FAILURE) {
1754 RETURN_THROWS();
1755 }
1756
1757 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1758
1759 PHP_LIBXML_SANITIZE_GLOBALS(validate);
1760 cvp = xmlNewValidCtxt();
1761
1762 cvp->userData = NULL;
1763 cvp->error = (xmlValidityErrorFunc) php_libxml_error_handler;
1764 cvp->warning = (xmlValidityErrorFunc) php_libxml_error_handler;
1765
1766 if (xmlValidateDocument(cvp, docp)) {
1767 RETVAL_TRUE;
1768 } else {
1769 RETVAL_FALSE;
1770 }
1771 PHP_LIBXML_RESTORE_GLOBALS(validate);
1772
1773 xmlFreeValidCtxt(cvp);
1774
1775 }
1776 /* }}} */
1777
1778 #ifdef LIBXML_SCHEMAS_ENABLED
_dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1779 static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1780 {
1781 zval *id;
1782 xmlDoc *docp;
1783 dom_object *intern;
1784 char *source = NULL;
1785 const char *valid_file = NULL;
1786 size_t source_len = 0;
1787 int valid_opts = 0;
1788 zend_long flags = 0;
1789 xmlSchemaParserCtxtPtr parser;
1790 xmlSchemaPtr sptr;
1791 xmlSchemaValidCtxtPtr vptr;
1792 int is_valid;
1793 char resolved_path[MAXPATHLEN + 1];
1794
1795 id = ZEND_THIS;
1796 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &flags) == FAILURE) {
1797 RETURN_THROWS();
1798 }
1799
1800 if (!source_len) {
1801 zend_argument_value_error(1, "must not be empty");
1802 RETURN_THROWS();
1803 }
1804
1805 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1806
1807 PHP_LIBXML_SANITIZE_GLOBALS(new_parser_ctxt);
1808
1809 switch (type) {
1810 case DOM_LOAD_FILE:
1811 if (CHECK_NULL_PATH(source, source_len)) {
1812 PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1813 zend_argument_value_error(1, "must not contain any null bytes");
1814 RETURN_THROWS();
1815 }
1816 valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1817 if (!valid_file) {
1818 PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1819 php_error_docref(NULL, E_WARNING, "Invalid Schema file source");
1820 RETURN_FALSE;
1821 }
1822 parser = xmlSchemaNewParserCtxt(valid_file);
1823 break;
1824 case DOM_LOAD_STRING:
1825 parser = xmlSchemaNewMemParserCtxt(source, source_len);
1826 /* If loading from memory, we need to set the base directory for the document
1827 but it is not apparent how to do that for schema's */
1828 break;
1829 default:
1830 return;
1831 }
1832
1833 xmlSchemaSetParserErrors(parser,
1834 (xmlSchemaValidityErrorFunc) php_libxml_error_handler,
1835 (xmlSchemaValidityWarningFunc) php_libxml_error_handler,
1836 parser);
1837 sptr = xmlSchemaParse(parser);
1838 xmlSchemaFreeParserCtxt(parser);
1839 PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
1840 if (!sptr) {
1841 if (!EG(exception)) {
1842 php_error_docref(NULL, E_WARNING, "Invalid Schema");
1843 }
1844 RETURN_FALSE;
1845 }
1846
1847 docp = (xmlDocPtr) dom_object_get_node(intern);
1848
1849 vptr = xmlSchemaNewValidCtxt(sptr);
1850 if (!vptr) {
1851 xmlSchemaFree(sptr);
1852 zend_throw_error(NULL, "Invalid Schema Validation Context");
1853 RETURN_THROWS();
1854 }
1855
1856 if (flags & XML_SCHEMA_VAL_VC_I_CREATE) {
1857 valid_opts |= XML_SCHEMA_VAL_VC_I_CREATE;
1858 }
1859
1860 PHP_LIBXML_SANITIZE_GLOBALS(validate);
1861 xmlSchemaSetValidOptions(vptr, valid_opts);
1862 xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
1863 is_valid = xmlSchemaValidateDoc(vptr, docp);
1864 xmlSchemaFree(sptr);
1865 xmlSchemaFreeValidCtxt(vptr);
1866 PHP_LIBXML_RESTORE_GLOBALS(validate);
1867
1868 if (is_valid == 0) {
1869 RETURN_TRUE;
1870 } else {
1871 RETURN_FALSE;
1872 }
1873 }
1874 /* }}} */
1875
1876 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidate)1877 PHP_METHOD(DOMDocument, schemaValidate)
1878 {
1879 _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1880 }
1881 /* }}} end dom_document_schema_validate_file */
1882
1883 /* {{{ */
PHP_METHOD(DOMDocument,schemaValidateSource)1884 PHP_METHOD(DOMDocument, schemaValidateSource)
1885 {
1886 _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1887 }
1888 /* }}} end dom_document_schema_validate */
1889
_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS,int type)1890 static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1891 {
1892 zval *id;
1893 xmlDoc *docp;
1894 dom_object *intern;
1895 char *source = NULL;
1896 const char *valid_file = NULL;
1897 size_t source_len = 0;
1898 xmlRelaxNGParserCtxtPtr parser;
1899 xmlRelaxNGPtr sptr;
1900 xmlRelaxNGValidCtxtPtr vptr;
1901 int is_valid;
1902 char resolved_path[MAXPATHLEN + 1];
1903
1904 id = ZEND_THIS;
1905 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &source, &source_len) == FAILURE) {
1906 RETURN_THROWS();
1907 }
1908
1909 if (!source_len) {
1910 zend_argument_value_error(1, "must not be empty");
1911 RETURN_THROWS();
1912 }
1913
1914 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
1915
1916 switch (type) {
1917 case DOM_LOAD_FILE:
1918 if (CHECK_NULL_PATH(source, source_len)) {
1919 zend_argument_value_error(1, "must not contain any null bytes");
1920 RETURN_THROWS();
1921 }
1922 valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
1923 if (!valid_file) {
1924 php_error_docref(NULL, E_WARNING, "Invalid RelaxNG file source");
1925 RETURN_FALSE;
1926 }
1927 parser = xmlRelaxNGNewParserCtxt(valid_file);
1928 break;
1929 case DOM_LOAD_STRING:
1930 parser = xmlRelaxNGNewMemParserCtxt(source, source_len);
1931 /* If loading from memory, we need to set the base directory for the document
1932 but it is not apparent how to do that for schema's */
1933 break;
1934 default:
1935 return;
1936 }
1937
1938 PHP_LIBXML_SANITIZE_GLOBALS(parse);
1939 xmlRelaxNGSetParserErrors(parser,
1940 (xmlRelaxNGValidityErrorFunc) php_libxml_error_handler,
1941 (xmlRelaxNGValidityWarningFunc) php_libxml_error_handler,
1942 parser);
1943 sptr = xmlRelaxNGParse(parser);
1944 xmlRelaxNGFreeParserCtxt(parser);
1945 PHP_LIBXML_RESTORE_GLOBALS(parse);
1946 if (!sptr) {
1947 php_error_docref(NULL, E_WARNING, "Invalid RelaxNG");
1948 RETURN_FALSE;
1949 }
1950
1951 docp = (xmlDocPtr) dom_object_get_node(intern);
1952
1953 vptr = xmlRelaxNGNewValidCtxt(sptr);
1954 if (!vptr) {
1955 xmlRelaxNGFree(sptr);
1956 zend_throw_error(NULL, "Invalid RelaxNG Validation Context");
1957 RETURN_THROWS();
1958 }
1959
1960 xmlRelaxNGSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
1961 is_valid = xmlRelaxNGValidateDoc(vptr, docp);
1962 xmlRelaxNGFree(sptr);
1963 xmlRelaxNGFreeValidCtxt(vptr);
1964
1965 if (is_valid == 0) {
1966 RETURN_TRUE;
1967 } else {
1968 RETURN_FALSE;
1969 }
1970 }
1971 /* }}} */
1972
1973 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidate)1974 PHP_METHOD(DOMDocument, relaxNGValidate)
1975 {
1976 _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
1977 }
1978 /* }}} end dom_document_relaxNG_validate_file */
1979
1980 /* {{{ */
PHP_METHOD(DOMDocument,relaxNGValidateSource)1981 PHP_METHOD(DOMDocument, relaxNGValidateSource)
1982 {
1983 _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
1984 }
1985 /* }}} end dom_document_relaxNG_validate_xml */
1986
1987 #endif
1988
1989 #ifdef LIBXML_HTML_ENABLED
1990
dom_load_html(INTERNAL_FUNCTION_PARAMETERS,int mode)1991 static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
1992 {
1993 char *source;
1994 size_t source_len;
1995 zend_long options = 0;
1996 htmlParserCtxtPtr ctxt;
1997
1998 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) {
1999 RETURN_THROWS();
2000 }
2001
2002 if (!source_len) {
2003 zend_argument_value_error(1, "must not be empty");
2004 RETURN_THROWS();
2005 }
2006
2007 if (ZEND_LONG_EXCEEDS_INT(options)) {
2008 php_error_docref(NULL, E_WARNING, "Invalid options");
2009 RETURN_FALSE;
2010 }
2011
2012 if (mode == DOM_LOAD_FILE) {
2013 if (CHECK_NULL_PATH(source, source_len)) {
2014 zend_argument_value_error(1, "must not contain any null bytes");
2015 RETURN_THROWS();
2016 }
2017 ctxt = htmlCreateFileParserCtxt(source, NULL);
2018 } else {
2019 if (ZEND_SIZE_T_INT_OVFL(source_len)) {
2020 php_error_docref(NULL, E_WARNING, "Input string is too long");
2021 RETURN_FALSE;
2022 }
2023 ctxt = htmlCreateMemoryParserCtxt(source, (int)source_len);
2024 }
2025
2026 if (!ctxt) {
2027 RETURN_FALSE;
2028 }
2029
2030
2031 ctxt->vctxt.error = php_libxml_ctx_error;
2032 ctxt->vctxt.warning = php_libxml_ctx_warning;
2033 if (ctxt->sax != NULL) {
2034 ctxt->sax->error = php_libxml_ctx_error;
2035 ctxt->sax->warning = php_libxml_ctx_warning;
2036 }
2037 php_libxml_sanitize_parse_ctxt_options(ctxt);
2038 if (options) {
2039 htmlCtxtUseOptions(ctxt, (int)options);
2040 }
2041 htmlParseDocument(ctxt);
2042 xmlDocPtr newdoc = ctxt->myDoc;
2043 htmlFreeParserCtxt(ctxt);
2044
2045 php_dom_finish_loading_document(ZEND_THIS, return_value, newdoc);
2046 }
2047 /* }}} */
2048
2049 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTMLFile)2050 PHP_METHOD(DOMDocument, loadHTMLFile)
2051 {
2052 dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
2053 }
2054 /* }}} end dom_document_load_html_file */
2055
2056 /* {{{ Since: DOM extended */
PHP_METHOD(DOMDocument,loadHTML)2057 PHP_METHOD(DOMDocument, loadHTML)
2058 {
2059 dom_load_html(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
2060 }
2061 /* }}} end dom_document_load_html */
2062
2063 /* {{{ Convenience method to save to file as html */
PHP_METHOD(DOMDocument,saveHTMLFile)2064 PHP_METHOD(DOMDocument, saveHTMLFile)
2065 {
2066 zval *id;
2067 xmlDoc *docp;
2068 size_t file_len;
2069 int bytes, format;
2070 dom_object *intern;
2071 char *file;
2072 const char *encoding;
2073
2074 id = ZEND_THIS;
2075 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &file, &file_len) == FAILURE) {
2076 RETURN_THROWS();
2077 }
2078
2079 if (file_len == 0) {
2080 zend_argument_value_error(1, "must not be empty");
2081 RETURN_THROWS();
2082 }
2083
2084 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2085
2086
2087 encoding = (const char *) htmlGetMetaEncoding(docp);
2088
2089 libxml_doc_props const* doc_props = dom_get_doc_props_read_only(intern->document);
2090 format = doc_props->formatoutput;
2091 bytes = htmlSaveFileFormat(file, docp, encoding, format);
2092
2093 if (bytes == -1) {
2094 RETURN_FALSE;
2095 }
2096 RETURN_LONG(bytes);
2097 }
2098 /* }}} end dom_document_save_html_file */
2099
2100 /* {{{ Convenience method to output as html */
PHP_METHOD(DOMDocument,saveHTML)2101 PHP_METHOD(DOMDocument, saveHTML)
2102 {
2103 zval *id, *nodep = NULL;
2104 xmlDoc *docp;
2105 xmlNode *node;
2106 xmlOutputBufferPtr outBuf;
2107 xmlBufferPtr buf;
2108 dom_object *intern, *nodeobj;
2109 int format;
2110
2111 id = ZEND_THIS;
2112 if (zend_parse_parameters(ZEND_NUM_ARGS(),
2113 "|O!", &nodep, dom_node_class_entry)
2114 == FAILURE) {
2115 RETURN_THROWS();
2116 }
2117
2118 DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
2119
2120 libxml_doc_props const* doc_props = dom_get_doc_props(intern->document);
2121 format = doc_props->formatoutput;
2122
2123 if (nodep != NULL) {
2124 /* Dump contents of Node */
2125 DOM_GET_OBJ(node, nodep, xmlNodePtr, nodeobj);
2126 if (node->doc != docp) {
2127 php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
2128 RETURN_FALSE;
2129 }
2130
2131 buf = xmlBufferCreate();
2132 if (!buf) {
2133 php_error_docref(NULL, E_WARNING, "Could not fetch buffer");
2134 RETURN_FALSE;
2135 }
2136 outBuf = xmlOutputBufferCreateBuffer(buf, NULL);
2137 if (!outBuf) {
2138 xmlBufferFree(buf);
2139 php_error_docref(NULL, E_WARNING, "Could not fetch output buffer");
2140 RETURN_FALSE;
2141 }
2142
2143 if (node->type == XML_DOCUMENT_FRAG_NODE) {
2144 for (node = node->children; node; node = node->next) {
2145 htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2146 if (outBuf->error) {
2147 break;
2148 }
2149 }
2150 } else {
2151 htmlNodeDumpFormatOutput(outBuf, docp, node, NULL, format);
2152 }
2153 if (!outBuf->error) {
2154 xmlOutputBufferFlush(outBuf);
2155 const xmlChar *mem = xmlBufferContent(buf);
2156 if (!mem) {
2157 RETVAL_FALSE;
2158 } else {
2159 int size = xmlBufferLength(buf);
2160 RETVAL_STRINGL((const char*) mem, size);
2161 }
2162 } else {
2163 php_error_docref(NULL, E_WARNING, "Error dumping HTML node");
2164 RETVAL_FALSE;
2165 }
2166 xmlOutputBufferClose(outBuf);
2167 xmlBufferFree(buf);
2168 } else {
2169 xmlChar *mem = NULL;
2170 int size = 0;
2171 htmlDocDumpMemoryFormat(docp, &mem, &size, format);
2172 if (!size || !mem) {
2173 RETVAL_FALSE;
2174 } else {
2175 RETVAL_STRINGL((const char*) mem, size);
2176 }
2177 xmlFree(mem);
2178 }
2179
2180 }
2181 /* }}} end dom_document_save_html */
2182
2183 #endif /* defined(LIBXML_HTML_ENABLED) */
2184
2185 /* {{{ Register extended class used to create base node type */
dom_document_register_node_class(INTERNAL_FUNCTION_PARAMETERS,bool modern)2186 static void dom_document_register_node_class(INTERNAL_FUNCTION_PARAMETERS, bool modern)
2187 {
2188 zend_class_entry *basece = dom_get_node_ce(modern), *ce = NULL;
2189 dom_object *intern;
2190
2191 if (zend_parse_parameters(ZEND_NUM_ARGS(), "CC!", &basece, &ce) == FAILURE) {
2192 RETURN_THROWS();
2193 }
2194
2195 if (basece->ce_flags & ZEND_ACC_ABSTRACT) {
2196 zend_argument_value_error(1, "must not be an abstract class");
2197 RETURN_THROWS();
2198 }
2199
2200 if (ce == NULL || instanceof_function(ce, basece)) {
2201 if (UNEXPECTED(ce != NULL && (ce->ce_flags & ZEND_ACC_ABSTRACT))) {
2202 zend_argument_value_error(2, "must not be an abstract class");
2203 RETURN_THROWS();
2204 }
2205 DOM_GET_THIS_INTERN(intern);
2206 dom_set_doc_classmap(intern->document, basece, ce);
2207 if (!modern) {
2208 RETVAL_TRUE;
2209 }
2210 return;
2211 }
2212
2213 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));
2214 RETURN_THROWS();
2215 }
2216
PHP_METHOD(DOMDocument,registerNodeClass)2217 PHP_METHOD(DOMDocument, registerNodeClass)
2218 {
2219 dom_document_register_node_class(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
2220 }
2221
PHP_METHOD(Dom_Document,registerNodeClass)2222 PHP_METHOD(Dom_Document, registerNodeClass)
2223 {
2224 dom_document_register_node_class(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
2225 }
2226 /* }}} */
2227
2228 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
2229 Since:
2230 */
PHP_METHOD(DOMDocument,replaceChildren)2231 PHP_METHOD(DOMDocument, replaceChildren)
2232 {
2233 uint32_t argc = 0;
2234 zval *args;
2235 dom_object *intern;
2236
2237 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
2238 RETURN_THROWS();
2239 }
2240
2241 DOM_GET_THIS_INTERN(intern);
2242
2243 dom_parent_node_replace_children(intern, args, argc);
2244 }
2245 /* }}} */
2246
2247 #endif /* HAVE_LIBXML && HAVE_DOM */
2248