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