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