php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
libxml.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: Shane Caraveo <shane@php.net> |
14 | Wez Furlong <wez@thebrainroom.com> |
15 +----------------------------------------------------------------------+
16 */
17
18#ifdef HAVE_CONFIG_H
19#include "config.h"
20#endif
21
22#include "php.h"
23#include "SAPI.h"
24
25#include "zend_attributes.h"
26#include "zend_variables.h"
27#include "ext/standard/info.h"
28#include "ext/standard/file.h"
29
30#ifdef HAVE_LIBXML
31
32#include <libxml/parser.h>
33#include <libxml/parserInternals.h>
34#include <libxml/tree.h>
35#include <libxml/uri.h>
36#include <libxml/xmlerror.h>
37#include <libxml/xmlsave.h>
38#include <libxml/xmlerror.h>
39#include <libxml/entities.h>
40#ifdef LIBXML_SCHEMAS_ENABLED
41#include <libxml/relaxng.h>
42#include <libxml/xmlschemas.h>
43#endif
44
45#include "php_libxml.h"
46
47#define PHP_LIBXML_LOADED_VERSION ((char *)xmlParserVersion)
48
49#include "libxml_arginfo.h"
50
51/* a true global for initialization */
52static int _php_libxml_initialized = 0;
53static int _php_libxml_per_request_initialization = 1;
54static xmlExternalEntityLoader _php_libxml_default_entity_loader;
55
56typedef struct _php_libxml_func_handler {
57 php_libxml_export_node export_func;
58} php_libxml_func_handler;
59
60static HashTable php_libxml_exports;
61
62static ZEND_DECLARE_MODULE_GLOBALS(libxml)
63static PHP_GINIT_FUNCTION(libxml);
64
65static zend_class_entry *libxmlerror_class_entry;
66
67/* {{{ dynamically loadable module stuff */
68#ifdef COMPILE_DL_LIBXML
69#ifdef ZTS
71#endif
72ZEND_GET_MODULE(libxml)
73#endif /* COMPILE_DL_LIBXML */
74/* }}} */
75
76/* {{{ function prototypes */
77static PHP_MINIT_FUNCTION(libxml);
78static PHP_RINIT_FUNCTION(libxml);
79static PHP_RSHUTDOWN_FUNCTION(libxml);
80static PHP_MSHUTDOWN_FUNCTION(libxml);
81static PHP_MINFO_FUNCTION(libxml);
82static zend_result php_libxml_post_deactivate(void);
83
84static zend_string *php_libxml_default_dump_node_to_str(xmlDocPtr doc, xmlNodePtr node, bool format, const char *encoding);
85static zend_string *php_libxml_default_dump_doc_to_str(xmlDocPtr doc, int options, const char *encoding);
86static zend_long php_libxml_dump_node_to_file(const char *filename, xmlDocPtr doc, xmlNodePtr node, bool format, const char *encoding);
87static zend_long php_libxml_default_dump_doc_to_file(const char *filename, xmlDocPtr doc, bool format, const char *encoding);
88
89/* }}} */
90
91zend_module_entry libxml_module_entry = {
93 "libxml", /* extension name */
94 ext_functions, /* extension function list */
95 PHP_MINIT(libxml), /* extension-wide startup function */
96 PHP_MSHUTDOWN(libxml), /* extension-wide shutdown function */
97 PHP_RINIT(libxml), /* per-request startup function */
98 PHP_RSHUTDOWN(libxml), /* per-request shutdown function */
99 PHP_MINFO(libxml), /* information function */
100 PHP_LIBXML_VERSION,
101 PHP_MODULE_GLOBALS(libxml), /* globals descriptor */
102 PHP_GINIT(libxml), /* globals ctor */
103 NULL, /* globals dtor */
104 php_libxml_post_deactivate, /* post deactivate */
106};
107
108/* }}} */
109
110static const php_libxml_document_handlers php_libxml_default_document_handlers = {
111 .dump_node_to_str = php_libxml_default_dump_node_to_str,
112 .dump_doc_to_str = php_libxml_default_dump_doc_to_str,
113 .dump_node_to_file = php_libxml_dump_node_to_file,
114 .dump_doc_to_file = php_libxml_default_dump_doc_to_file,
115};
116
117static void php_libxml_set_old_ns_list(xmlDocPtr doc, xmlNsPtr first, xmlNsPtr last)
118{
119 if (UNEXPECTED(doc == NULL)) {
120 return;
121 }
122
123 ZEND_ASSERT(last->next == NULL);
124
125 /* Note: we'll use a prepend strategy instead of append to
126 * make sure we don't lose performance when the list is long.
127 * As libxml2 could assume the xml node is the first one, we'll place our
128 * new entries after the first one. */
129
130 if (UNEXPECTED(doc->oldNs == NULL)) {
131 doc->oldNs = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
132 if (doc->oldNs == NULL) {
133 return;
134 }
135 memset(doc->oldNs, 0, sizeof(xmlNs));
136 doc->oldNs->type = XML_LOCAL_NAMESPACE;
137 doc->oldNs->href = xmlStrdup(XML_XML_NAMESPACE);
138 doc->oldNs->prefix = xmlStrdup((const xmlChar *)"xml");
139 } else {
140 last->next = doc->oldNs->next;
141 }
142 doc->oldNs->next = first;
143}
144
145PHP_LIBXML_API void php_libxml_set_old_ns(xmlDocPtr doc, xmlNsPtr ns)
146{
147 php_libxml_set_old_ns_list(doc, ns, ns);
148}
149
150/* Function pointer typedef changed in 2.9.8, see https://github.com/GNOME/libxml2/commit/e03f0a199a67017b2f8052354cf732b2b4cae787 */
151#if LIBXML_VERSION >= 20908
152static void php_libxml_unlink_entity(void *data, void *table, const xmlChar *name)
153#else
154static void php_libxml_unlink_entity(void *data, void *table, xmlChar *name)
155#endif
156{
157 xmlEntityPtr entity = data;
158 if (entity->_private != NULL) {
159 xmlHashRemoveEntry(table, name, NULL);
160 }
161}
162
163/* {{{ internal functions for interoperability */
164static void php_libxml_unregister_node(xmlNodePtr nodep)
165{
166 php_libxml_node_ptr *nodeptr = nodep->_private;
167
168 if (nodeptr != NULL) {
169 php_libxml_node_object *wrapper = nodeptr->_private;
170 if (wrapper) {
171 php_libxml_decrement_node_ptr(wrapper);
172 php_libxml_decrement_doc_ref(wrapper);
173 } else {
174 if (nodep->type != XML_DOCUMENT_NODE) {
175 nodep->_private = NULL;
176 }
177 nodeptr->node = NULL;
178 }
179 }
180}
181
182/* Workaround for libxml2 peculiarity */
183static void php_libxml_unlink_entity_decl(xmlEntityPtr entity)
184{
185 xmlDtdPtr dtd = entity->parent;
186 if (dtd != NULL) {
187 if (xmlHashLookup(dtd->entities, entity->name) == entity) {
188 xmlHashRemoveEntry(dtd->entities, entity->name, NULL);
189 }
190 if (xmlHashLookup(dtd->pentities, entity->name) == entity) {
191 xmlHashRemoveEntry(dtd->pentities, entity->name, NULL);
192 }
193 }
194}
195
196static void php_libxml_node_free(xmlNodePtr node)
197{
198 if (node->_private != NULL) {
199 ((php_libxml_node_ptr *) node->_private)->node = NULL;
200 }
201 switch (node->type) {
203 xmlFreeProp((xmlAttrPtr) node);
204 break;
205 /* libxml2 has a peculiarity where if you unlink an entity it'll only unlink it from the dtd if the
206 * dtd is attached to the document. This works around the issue by inspecting the parent directly. */
207 case XML_ENTITY_DECL: {
208 xmlEntityPtr entity = (xmlEntityPtr) node;
209 if (entity->etype != XML_INTERNAL_PREDEFINED_ENTITY) {
210 php_libxml_unlink_entity_decl(entity);
211#if LIBXML_VERSION >= 21200
212 xmlFreeEntity(entity);
213#else
214 if (entity->children != NULL && entity->owner && entity == (xmlEntityPtr) entity->children->parent) {
215 xmlFreeNodeList(entity->children);
216 }
217 xmlDictPtr dict = entity->doc != NULL ? entity->doc->dict : NULL;
218 if (dict == NULL || !xmlDictOwns(dict, entity->name)) {
219 xmlFree((xmlChar *) entity->name);
220 }
221 if (dict == NULL || !xmlDictOwns(dict, entity->ExternalID)) {
222 xmlFree((xmlChar *) entity->ExternalID);
223 }
224 if (dict == NULL || !xmlDictOwns(dict, entity->SystemID)) {
225 xmlFree((xmlChar *) entity->SystemID);
226 }
227 if (dict == NULL || !xmlDictOwns(dict, entity->URI)) {
228 xmlFree((xmlChar *) entity->URI);
229 }
230 if (dict == NULL || !xmlDictOwns(dict, entity->content)) {
231 xmlFree(entity->content);
232 }
233 if (dict == NULL || !xmlDictOwns(dict, entity->orig)) {
234 xmlFree(entity->orig);
235 }
236 xmlFree(entity);
237#endif
238 }
239 break;
240 }
241 case XML_NOTATION_NODE: {
242 /* See create_notation(), these aren't regular XML_NOTATION_NODE, but entities in disguise... */
243 xmlEntityPtr entity = (xmlEntityPtr) node;
244 if (node->name != NULL) {
245 xmlFree((char *) node->name);
246 }
247 if (entity->ExternalID != NULL) {
248 xmlFree((char *) entity->ExternalID);
249 }
250 if (entity->SystemID != NULL) {
251 xmlFree((char *) entity->SystemID);
252 }
253 xmlFree(node);
254 break;
255 }
256 case XML_ELEMENT_DECL:
257 case XML_ATTRIBUTE_DECL:
258 break;
259 case XML_NAMESPACE_DECL:
260 if (node->ns) {
261 xmlFreeNs(node->ns);
262 node->ns = NULL;
263 }
264 node->type = XML_ELEMENT_NODE;
265 xmlFreeNode(node);
266 break;
267 case XML_DTD_NODE: {
268 xmlDtdPtr dtd = (xmlDtdPtr) node;
269 if (dtd->_private == NULL) {
270 /* There's no userland reference to the dtd,
271 * but there might be entities referenced from userland. Unlink those. */
272 xmlHashScan(dtd->entities, php_libxml_unlink_entity, dtd->entities);
273 xmlHashScan(dtd->pentities, php_libxml_unlink_entity, dtd->pentities);
274 /* No unlinking of notations, see remark above at case XML_NOTATION_NODE. */
275 }
276 xmlFreeDtd(dtd);
277 break;
278 }
279 case XML_ELEMENT_NODE: {
280 if (node->ns && (((uintptr_t) node->ns->_private) & 1) == LIBXML_NS_TAG_HOOK) {
281 /* Special destruction routine hook should be called because it belongs to a "special" namespace. */
282 php_libxml_private_data_header *header = (php_libxml_private_data_header *) (((uintptr_t) node->ns->_private) & ~1);
283 header->ns_hook(header, node);
284 }
285 if (node->nsDef && node->doc) {
286 /* Make the namespace declaration survive the destruction of the holding element.
287 * This prevents a use-after-free on the namespace declaration.
288 *
289 * The main problem is that libxml2 doesn't have a reference count on the namespace declaration.
290 * We don't actually need to save the namespace declaration if we know the subtree it belongs to
291 * has no references from userland. However, we can't know that without traversing the whole subtree
292 * (=> slow), or without adding some subtree metadata (=> also slow).
293 * So we have to assume we need to save everything.
294 *
295 * However, namespace declarations are quite rare in comparison to other node types.
296 * Most node types are either elements, text or attributes.
297 * And you only need one namespace declaration per namespace (in principle).
298 * So I expect the number of namespace declarations to be low for an average XML document.
299 *
300 * In the worst possible case we have to save all namespace declarations when we for example remove
301 * the whole document. But given the above reasoning this likely won't be a lot of declarations even
302 * in the worst case.
303 * A single declaration only takes about 48 bytes of memory, and I don't expect the worst case to occur
304 * very often (why would you remove the whole document?).
305 */
306 xmlNsPtr ns = node->nsDef;
307 xmlNsPtr last = ns;
308 while (last->next) {
309 last = last->next;
310 }
311 php_libxml_set_old_ns_list(node->doc, ns, last);
312 node->nsDef = NULL;
313 }
314 xmlFreeNode(node);
315 break;
316 }
317 default:
318 xmlFreeNode(node);
319 break;
320 }
321}
322
323PHP_LIBXML_API void php_libxml_node_free_list(xmlNodePtr node)
324{
325 xmlNodePtr curnode;
326
327 if (node != NULL) {
328 curnode = node;
329 while (curnode != NULL) {
330 /* If the _private field is set, there's still a userland reference somewhere. We'll delay freeing in this case. */
331 if (curnode->_private) {
332 xmlNodePtr next = curnode->next;
333 /* Must unlink such that freeing of the parent doesn't free this child. */
334 xmlUnlinkNode(curnode);
335 if (curnode->type == XML_ELEMENT_NODE) {
336 /* This ensures that namespace references in this subtree are defined within this subtree,
337 * otherwise a use-after-free would be possible when the original namespace holder gets freed. */
338 php_libxml_node_ptr *ptr = curnode->_private;
339
340 /* Checking in case it runs out of reference */
341 if (ptr->_private) {
342 php_libxml_node_object *obj = ptr->_private;
343 if (!obj->document || obj->document->class_type < PHP_LIBXML_CLASS_MODERN) {
344 xmlReconciliateNs(curnode->doc, curnode);
345 }
346 }
347 }
348 /* Skip freeing */
349 curnode = next;
350 continue;
351 }
352
353 node = curnode;
354 switch (node->type) {
355 /* Skip property freeing for the following types */
358 break;
359 case XML_ENTITY_DECL:
360 php_libxml_unlink_entity_decl((xmlEntityPtr) node);
361 break;
363 if ((node->doc != NULL) && (((xmlAttrPtr) node)->atype == XML_ATTRIBUTE_ID)) {
364 xmlRemoveID(node->doc, (xmlAttrPtr) node);
365 }
367 case XML_ATTRIBUTE_DECL:
368 case XML_DTD_NODE:
370 case XML_NAMESPACE_DECL:
371 case XML_TEXT_NODE:
372 php_libxml_node_free_list(node->children);
373 break;
374 default:
375 php_libxml_node_free_list(node->children);
376 php_libxml_node_free_list((xmlNodePtr) node->properties);
377 }
378
379 curnode = node->next;
380 xmlUnlinkNode(node);
381 php_libxml_unregister_node(node);
382 php_libxml_node_free(node);
383 }
384 }
385}
386
387/* }}} */
388
389/* {{{ startup, shutdown and info functions */
390static PHP_GINIT_FUNCTION(libxml)
391{
392#if defined(COMPILE_DL_LIBXML) && defined(ZTS)
394#endif
395 ZVAL_UNDEF(&libxml_globals->stream_context);
396 libxml_globals->error_buffer.s = NULL;
397 libxml_globals->error_list = NULL;
398 libxml_globals->entity_loader_callback = empty_fcall_info_cache;
399}
400
401PHP_LIBXML_API php_stream_context *php_libxml_get_stream_context(void)
402{
403 return php_stream_context_from_zval(Z_ISUNDEF(LIBXML(stream_context)) ? NULL : &LIBXML(stream_context), false);
404}
405
406/* Channel libxml file io layer through the PHP streams subsystem.
407 * This allows use of ftps:// and https:// urls */
408
409static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char *mode, const int read_only)
410{
411 php_stream_statbuf ssbuf;
412 char *resolved_path;
413 const char *path_to_open = NULL;
414 bool isescaped = false;
415
416 if (strstr(filename, "%00")) {
417 php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
418 return NULL;
419 }
420
421 xmlURI *uri = xmlParseURI(filename);
422 if (uri && (uri->scheme == NULL ||
423 (xmlStrncmp(BAD_CAST uri->scheme, BAD_CAST "file", 4) == 0))) {
424 resolved_path = xmlURIUnescapeString(filename, 0, NULL);
425 isescaped = 1;
426#if LIBXML_VERSION >= 20902 && LIBXML_VERSION < 21300 && defined(PHP_WIN32)
427 /* Libxml 2.9.2 prefixes local paths with file:/ instead of file://,
428 thus the php stream wrapper will fail on a valid case. For this
429 reason the prefix is rather better cut off.
430 As of libxml 2.13.0 this issue is resolved. */
431 {
432 size_t pre_len = sizeof("file:/") - 1;
433
434 if (strncasecmp(resolved_path, "file:/", pre_len) == 0
435 && '/' != resolved_path[pre_len]) {
436 xmlChar *tmp = xmlStrdup(resolved_path + pre_len);
437 xmlFree(resolved_path);
438 resolved_path = tmp;
439 }
440 }
441#endif
442 } else {
443 resolved_path = (char *)filename;
444 }
445
446 if (uri) {
447 xmlFreeURI(uri);
448 }
449
450 if (resolved_path == NULL) {
451 return NULL;
452 }
453
454 /* logic copied from _php_stream_stat, but we only want to fail
455 if the wrapper supports stat, otherwise, figure it out from
456 the open. This logic is only to support hiding warnings
457 that the streams layer puts out at times, but for libxml we
458 may try to open files that don't exist, but it is not a failure
459 in xml processing (eg. DTD files) */
460 php_stream_wrapper *wrapper = php_stream_locate_url_wrapper(resolved_path, &path_to_open, 0);
461 if (wrapper && read_only && wrapper->wops->url_stat) {
462 if (wrapper->wops->url_stat(wrapper, path_to_open, PHP_STREAM_URL_STAT_QUIET, &ssbuf, NULL) == -1) {
463 if (isescaped) {
464 xmlFree(resolved_path);
465 }
466 return NULL;
467 }
468 }
469
470 php_stream_context *context = php_libxml_get_stream_context();
471
473 if (ret_val) {
474 /* Prevent from closing this by fclose() */
476 }
477 if (isescaped) {
478 xmlFree(resolved_path);
479 }
480 return ret_val;
481}
482
483static void *php_libxml_streams_IO_open_read_wrapper(const char *filename)
484{
485 return php_libxml_streams_IO_open_wrapper(filename, "rb", 1);
486}
487
488static void *php_libxml_streams_IO_open_write_wrapper(const char *filename)
489{
490 return php_libxml_streams_IO_open_wrapper(filename, "wb", 0);
491}
492
493static int php_libxml_streams_IO_read(void *context, char *buffer, int len)
494{
496}
497
498static int php_libxml_streams_IO_write(void *context, const char *buffer, int len)
499{
501}
502
503static int php_libxml_streams_IO_close(void *context)
504{
506}
507
508static xmlParserInputBufferPtr
509php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc)
510{
511 xmlParserInputBufferPtr ret;
512 void *context = NULL;
513
514 if (LIBXML(entity_loader_disabled)) {
515 return NULL;
516 }
517
518 if (URI == NULL)
519 return(NULL);
520
521 context = php_libxml_streams_IO_open_read_wrapper(URI);
522
523 if (context == NULL) {
524 return(NULL);
525 }
526
527 /* Check if there's been an external transport protocol with an encoding information */
528 if (enc == XML_CHAR_ENCODING_NONE) {
530 zend_string *charset = php_libxml_sniff_charset_from_stream(s);
531 if (charset != NULL) {
532 enc = xmlParseCharEncoding(ZSTR_VAL(charset));
533 if (enc <= XML_CHAR_ENCODING_NONE) {
534 enc = XML_CHAR_ENCODING_NONE;
535 }
537 }
538 }
539
540 /* Allocate the Input buffer front-end. */
541 ret = xmlAllocParserInputBuffer(enc);
542 if (ret != NULL) {
543 ret->context = context;
544 ret->readcallback = php_libxml_streams_IO_read;
545 ret->closecallback = php_libxml_streams_IO_close;
546 } else
547 php_libxml_streams_IO_close(context);
548
549 return(ret);
550}
551
552static xmlOutputBufferPtr
553php_libxml_output_buffer_create_filename(const char *URI,
554 xmlCharEncodingHandlerPtr encoder,
555 int compression)
556{
557 ZEND_IGNORE_VALUE(compression);
558
559 xmlOutputBufferPtr ret;
560 xmlURIPtr puri;
561 void *context = NULL;
562 char *unescaped = NULL;
563
564 if (URI == NULL)
565 goto err;
566
567 if (strstr(URI, "%00")) {
568 php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
569 goto err;
570 }
571
572 puri = xmlParseURI(URI);
573 if (puri != NULL) {
574 if (puri->scheme != NULL)
575 unescaped = xmlURIUnescapeString(URI, 0, NULL);
576 xmlFreeURI(puri);
577 }
578
579 if (unescaped != NULL) {
580 context = php_libxml_streams_IO_open_write_wrapper(unescaped);
581 xmlFree(unescaped);
582 }
583
584 /* try with a non-escaped URI this may be a strange filename */
585 if (context == NULL) {
586 context = php_libxml_streams_IO_open_write_wrapper(URI);
587 }
588
589 if (context == NULL) {
590 goto err;
591 }
592
593 /* Allocate the Output buffer front-end. */
594 ret = xmlAllocOutputBuffer(encoder);
595 if (ret != NULL) {
596 ret->context = context;
597 ret->writecallback = php_libxml_streams_IO_write;
598 ret->closecallback = php_libxml_streams_IO_close;
599 }
600
601 return(ret);
602
603err:
604 /* Similarly to __xmlOutputBufferCreateFilename we should also close the encoder on failure. */
605 xmlCharEncCloseFunc(encoder);
606 return NULL;
607}
608
609static void _php_libxml_free_error(void *ptr)
610{
611 /* This will free the libxml alloc'd memory */
612 xmlResetError((xmlErrorPtr) ptr);
613}
614
615#if LIBXML_VERSION >= 21200
616static void _php_list_set_error_structure(const xmlError *error, const char *msg, int line, int column)
617#else
618static void _php_list_set_error_structure(xmlError *error, const char *msg, int line, int column)
619#endif
620{
621 xmlError error_copy;
622 int ret;
623
624
625 memset(&error_copy, 0, sizeof(xmlError));
626
627 if (error) {
628 ret = xmlCopyError(error, &error_copy);
629 } else {
630 error_copy.code = XML_ERR_INTERNAL_ERROR;
631 error_copy.level = XML_ERR_ERROR;
632 error_copy.line = line;
633 error_copy.int2 = column;
634 error_copy.message = (char*)xmlStrdup((const xmlChar*)msg);
635 ret = 0;
636 }
637
638 if (ret == 0) {
639 zend_llist_add_element(LIBXML(error_list), &error_copy);
640 }
641}
642
643static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg, int line)
644{
645 xmlParserCtxtPtr parser;
646
647 parser = (xmlParserCtxtPtr) ctx;
648
649 if (parser != NULL && parser->input != NULL) {
650 if (parser->input->filename) {
651 php_error_docref(NULL, level, "%s in %s, line: %d", msg, parser->input->filename, line);
652 } else {
653 php_error_docref(NULL, level, "%s in Entity, line: %d", msg, line);
654 }
655 } else {
657 }
658}
659
660void php_libxml_issue_error(int level, const char *msg)
661{
662 if (LIBXML(error_list)) {
663 _php_list_set_error_structure(NULL, msg, 0, 0);
664 } else {
665 php_error_docref(NULL, level, "%s", msg);
666 }
667}
668
669static void php_libxml_internal_error_handler_ex(php_libxml_error_level error_type, void *ctx, const char *msg, va_list ap, int line, int column)
670{
671 char *buf;
672 bool output = false;
673
674 size_t len = vspprintf(&buf, 0, msg, ap);
675 size_t len_iter = len;
676
677 /* remove any trailing \n */
678 while (len_iter && buf[--len_iter] == '\n') {
679 buf[len_iter] = '\0';
680 output = true;
681 }
682
683 smart_str_appendl(&LIBXML(error_buffer), buf, len);
684
685 efree(buf);
686
687 if (output) {
688 if (LIBXML(error_list)) {
689 _php_list_set_error_structure(NULL, ZSTR_VAL(LIBXML(error_buffer).s), line, column);
690 } else if (!EG(exception)) {
691 /* Don't throw additional notices/warnings if an exception has already been thrown. */
692 switch (error_type) {
693 case PHP_LIBXML_CTX_ERROR:
694 php_libxml_ctx_error_level(E_WARNING, ctx, ZSTR_VAL(LIBXML(error_buffer).s), line);
695 break;
696 case PHP_LIBXML_CTX_WARNING:
697 php_libxml_ctx_error_level(E_NOTICE, ctx, ZSTR_VAL(LIBXML(error_buffer).s), line);
698 break;
699 default:
700 php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(LIBXML(error_buffer).s));
701 }
702 }
703 smart_str_free(&LIBXML(error_buffer));
704 }
705}
706
707PHP_LIBXML_API void php_libxml_error_handler_va(php_libxml_error_level error_type, void *ctx, const char *msg, va_list ap)
708{
709 int line = 0;
710 int column = 0;
711 xmlParserCtxtPtr parser = (xmlParserCtxtPtr) ctx;
712 /* Context is not valid for PHP_LIBXML_ERROR, don't dereference it in that case */
713 if (error_type != PHP_LIBXML_ERROR && parser != NULL && parser->input != NULL) {
714 line = parser->input->line;
715 column = parser->input->col;
716 }
717 php_libxml_internal_error_handler_ex(error_type, ctx, msg, ap, line, column);
718}
719
720static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
721 const char *ID, xmlParserCtxtPtr context)
722{
723 xmlParserInputPtr ret = NULL;
724 const char *resource = NULL;
725 zval *ctxzv, retval;
726 zval params[3];
727
728 /* no custom user-land callback set up; delegate to original loader */
729 if (!ZEND_FCC_INITIALIZED(LIBXML(entity_loader_callback))) {
730 return _php_libxml_default_entity_loader(URL, ID, context);
731 }
732
733 if (ID != NULL) {
734 ZVAL_STRING(&params[0], ID);
735 } else {
736 ZVAL_NULL(&params[0]);
737 }
738 if (URL != NULL) {
739 ZVAL_STRING(&params[1], URL);
740 } else {
741 ZVAL_NULL(&params[1]);
742 }
743 ctxzv = &params[2];
744 array_init_size(ctxzv, 4);
745
746#define ADD_NULL_OR_STRING_KEY(memb) \
747 if (context->memb == NULL) { \
748 add_assoc_null_ex(ctxzv, #memb, sizeof(#memb) - 1); \
749 } else { \
750 add_assoc_string_ex(ctxzv, #memb, sizeof(#memb) - 1, \
751 (char *)context->memb); \
752 }
753
754 ADD_NULL_OR_STRING_KEY(directory)
755 ADD_NULL_OR_STRING_KEY(intSubName)
756 ADD_NULL_OR_STRING_KEY(extSubURI)
757 ADD_NULL_OR_STRING_KEY(extSubSystem)
758
759#undef ADD_NULL_OR_STRING_KEY
760
761 zend_call_known_fcc(&LIBXML(entity_loader_callback), &retval, 3, params, /* named_params */ NULL);
762
763 if (Z_ISUNDEF(retval)) {
764 php_libxml_ctx_error(context,
765 "Call to user entity loader callback '%s' has failed",
766 ZSTR_VAL(LIBXML(entity_loader_callback).function_handler->common.function_name));
767 } else {
768 if (Z_TYPE(retval) == IS_STRING) {
770 resource = Z_STRVAL(retval);
771 } else if (Z_TYPE(retval) == IS_RESOURCE) {
773 if (UNEXPECTED(stream == NULL)) {
774 zval callable;
775 zend_get_callable_zval_from_fcc(&LIBXML(entity_loader_callback), &callable);
776 zend_string *callable_name = zend_get_callable_name(&callable);
779 "%s(): The user entity loader callback \"%s\" has returned a resource, but it is not a stream",
780 ZSTR_VAL(func_name), ZSTR_VAL(callable_name));
781 zend_string_release(func_name);
782 zend_string_release(callable_name);
783 zval_ptr_dtor(&callable);
784 } else {
785 /* TODO: allow storing the encoding in the stream context? */
786 xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
787 xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
788 if (pib == NULL) {
789 php_libxml_ctx_error(context, "Could not allocate parser "
790 "input buffer");
791 } else {
792 /* make stream not being closed when the zval is freed */
793 GC_ADDREF(stream->res);
794 pib->context = stream;
795 pib->readcallback = php_libxml_streams_IO_read;
796 pib->closecallback = php_libxml_streams_IO_close;
797
798 ret = xmlNewIOInputStream(context, pib, enc);
799 if (ret == NULL) {
800 xmlFreeParserInputBuffer(pib);
801 }
802 }
803 }
804 } else if (Z_TYPE(retval) != IS_NULL) {
805 /* retval not string nor resource nor null; convert to string */
806 if (try_convert_to_string(&retval)) {
807 goto is_string;
808 }
809 } /* else is null; don't try anything */
810 }
811
812 if (ret == NULL) {
813 if (resource == NULL) {
814 if (ID == NULL) {
815 php_libxml_ctx_error(context,
816 "Failed to load external entity because the resolver function returned null\n");
817 } else {
818 php_libxml_ctx_error(context,
819 "Failed to load external entity \"%s\"\n", ID);
820 }
821 } else {
822 /* we got the resource in the form of a string; open it */
823 ret = xmlNewInputFromFile(context, resource);
824 }
825 }
826
827 zval_ptr_dtor(&params[0]);
828 zval_ptr_dtor(&params[1]);
829 zval_ptr_dtor(&params[2]);
831 return ret;
832}
833
834static xmlParserInputPtr _php_libxml_pre_ext_ent_loader(const char *URL,
835 const char *ID, xmlParserCtxtPtr context)
836{
837
838 /* Check whether we're running in a PHP context, since the entity loader
839 * we've defined is an application level (true global) setting.
840 * If we are, we also want to check whether we've finished activating
841 * the modules (RINIT phase). Using our external entity loader during a
842 * RINIT should not be problem per se (though during MINIT it is, because
843 * we don't even have a resource list by then), but then whether one
844 * extension would be using the custom external entity loader or not
845 * could depend on extension loading order
846 * (if _php_libxml_per_request_initialization */
847 if (xmlGenericError == php_libxml_error_handler && PG(modules_activated)) {
848 return _php_libxml_external_entity_loader(URL, ID, context);
849 } else {
850 return _php_libxml_default_entity_loader(URL, ID, context);
851 }
852}
853
854PHP_LIBXML_API void php_libxml_pretend_ctx_error_ex(const char *file, int line, int column, const char *msg,...)
855{
856 va_list args;
857 va_start(args, msg);
858 php_libxml_internal_error_handler_ex(PHP_LIBXML_CTX_ERROR, NULL, msg, args, line, column);
859 va_end(args);
860
861 /* Propagate back into libxml */
862 if (LIBXML(error_list)) {
863 xmlErrorPtr last = zend_llist_get_last(LIBXML(error_list));
864 if (last && !last->file) {
865 last->file = strdup(file);
866 }
867 }
868}
869
870PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
871{
872 va_list args;
873 va_start(args, msg);
874 php_libxml_error_handler_va(PHP_LIBXML_CTX_ERROR, ctx, msg, args);
875 va_end(args);
876}
877
878PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...)
879{
880 va_list args;
881 va_start(args, msg);
882 php_libxml_error_handler_va(PHP_LIBXML_CTX_WARNING, ctx, msg, args);
883 va_end(args);
884}
885
886#if LIBXML_VERSION >= 21200
887static void php_libxml_structured_error_handler(void *userData, const xmlError *error)
888#else
889static void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error)
890#endif
891{
892 _php_list_set_error_structure(error, NULL, 0, 0);
893}
894
895PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...)
896{
897 va_list args;
898 va_start(args, msg);
899 php_libxml_error_handler_va(PHP_LIBXML_ERROR, ctx, msg, args);
900 va_end(args);
901}
902
903static void php_libxml_exports_dtor(zval *zv)
904{
905 free(Z_PTR_P(zv));
906}
907
908PHP_LIBXML_API void php_libxml_initialize(void)
909{
910 if (!_php_libxml_initialized) {
911 /* we should be the only one's to ever init!! */
913 xmlInitParser();
915
916 _php_libxml_default_entity_loader = xmlGetExternalEntityLoader();
917 xmlSetExternalEntityLoader(_php_libxml_pre_ext_ent_loader);
918
919 zend_hash_init(&php_libxml_exports, 0, NULL, php_libxml_exports_dtor, 1);
920
921 _php_libxml_initialized = 1;
922 }
923}
924
925PHP_LIBXML_API void php_libxml_shutdown(void)
926{
927 if (_php_libxml_initialized) {
928#if defined(LIBXML_SCHEMAS_ENABLED) && LIBXML_VERSION < 21000
929 xmlRelaxNGCleanupTypes();
930#endif
931 /* xmlCleanupParser(); */
932 zend_hash_destroy(&php_libxml_exports);
933
934 xmlSetExternalEntityLoader(_php_libxml_default_entity_loader);
935 _php_libxml_initialized = 0;
936 }
937}
938
939PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext)
940{
941 if (oldcontext) {
942 ZVAL_COPY_VALUE(oldcontext, &LIBXML(stream_context));
943 }
944 if (context) {
945 ZVAL_COPY_VALUE(&LIBXML(stream_context), context);
946 }
947}
948
949static PHP_MINIT_FUNCTION(libxml)
950{
951 php_libxml_initialize();
952
953 register_libxml_symbols(module_number);
954
955 libxmlerror_class_entry = register_class_LibXMLError();
956
957 if (sapi_module.name) {
958 static const char * const supported_sapis[] = {
959 "cgi-fcgi",
960 "litespeed",
961 NULL
962 };
963 const char * const *sapi_name;
964
965 for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
966 if (strcmp(sapi_module.name, *sapi_name) == 0) {
967 _php_libxml_per_request_initialization = 0;
968 break;
969 }
970 }
971 }
972
973 if (!_php_libxml_per_request_initialization) {
974 /* report errors via handler rather than stderr */
975 xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
976 xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
977 xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
978 }
979
980 return SUCCESS;
981}
982
983
984static PHP_RINIT_FUNCTION(libxml)
985{
986 if (_php_libxml_per_request_initialization) {
987 /* report errors via handler rather than stderr */
988 xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
989 xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
990 xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
991 }
992
993 /* Enable the entity loader by default. This ensures that
994 * other threads/requests that might have disabled the loader
995 * do not affect the current request.
996 */
997 LIBXML(entity_loader_disabled) = 0;
998
999 return SUCCESS;
1000}
1001
1002static PHP_RSHUTDOWN_FUNCTION(libxml)
1003{
1004 if (ZEND_FCC_INITIALIZED(LIBXML(entity_loader_callback))) {
1005 zend_fcc_dtor(&LIBXML(entity_loader_callback));
1006 }
1007
1008 return SUCCESS;
1009}
1010
1011static PHP_MSHUTDOWN_FUNCTION(libxml)
1012{
1013 if (!_php_libxml_per_request_initialization) {
1014 xmlSetGenericErrorFunc(NULL, NULL);
1015
1016 xmlParserInputBufferCreateFilenameDefault(NULL);
1017 xmlOutputBufferCreateFilenameDefault(NULL);
1018 }
1019 php_libxml_shutdown();
1020
1021 return SUCCESS;
1022}
1023
1024static zend_result php_libxml_post_deactivate(void)
1025{
1026 /* reset libxml generic error handling */
1027 if (_php_libxml_per_request_initialization) {
1028 xmlSetGenericErrorFunc(NULL, NULL);
1029
1030 xmlParserInputBufferCreateFilenameDefault(NULL);
1031 xmlOutputBufferCreateFilenameDefault(NULL);
1032 }
1033 xmlSetStructuredErrorFunc(NULL, NULL);
1034
1035 /* the steam_context resource will be released by resource list destructor */
1036 ZVAL_UNDEF(&LIBXML(stream_context));
1037 smart_str_free(&LIBXML(error_buffer));
1038 if (LIBXML(error_list)) {
1039 zend_llist_destroy(LIBXML(error_list));
1040 efree(LIBXML(error_list));
1041 LIBXML(error_list) = NULL;
1042 }
1043 xmlResetLastError();
1044
1045 return SUCCESS;
1046}
1047
1048
1049static PHP_MINFO_FUNCTION(libxml)
1050{
1052 php_info_print_table_row(2, "libXML support", "active");
1053 php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
1054 php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
1055 php_info_print_table_row(2, "libXML streams", "enabled");
1057}
1058/* }}} */
1059
1060/* {{{ Set the streams context for the next libxml document load or write */
1062{
1063 zval *arg;
1064
1068
1070 if (!Z_ISUNDEF(LIBXML(stream_context))) {
1071 zval_ptr_dtor(&LIBXML(stream_context));
1072 }
1073 ZVAL_COPY(&LIBXML(stream_context), arg);
1074 }
1075}
1076/* }}} */
1077
1078PHP_LIBXML_API bool php_libxml_uses_internal_errors(void)
1079{
1080 return xmlStructuredError == php_libxml_structured_error_handler;
1081}
1082
1083/* {{{ Disable libxml errors and allow user to fetch error information as needed */
1085{
1086 bool use_errors, use_errors_is_null = true;
1087
1090 Z_PARAM_BOOL_OR_NULL(use_errors, use_errors_is_null)
1092
1093 bool retval = php_libxml_uses_internal_errors();
1094
1095 if (use_errors_is_null) {
1097 }
1098
1099 if (use_errors == 0) {
1100 xmlSetStructuredErrorFunc(NULL, NULL);
1101 if (LIBXML(error_list)) {
1102 zend_llist_destroy(LIBXML(error_list));
1103 efree(LIBXML(error_list));
1104 LIBXML(error_list) = NULL;
1105 }
1106 } else {
1107 xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
1108 if (LIBXML(error_list) == NULL) {
1109 LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
1110 zend_llist_init(LIBXML(error_list), sizeof(xmlError), _php_libxml_free_error, 0);
1111 }
1112 }
1114}
1115/* }}} */
1116
1117static void php_libxml_create_error_object(zval *return_value, const xmlError *error)
1118{
1119 object_init_ex(return_value, libxmlerror_class_entry);
1120 add_property_long(return_value, "level", error->level);
1121 add_property_long(return_value, "code", error->code);
1122 add_property_long(return_value, "column", error->int2);
1123 if (error->message) {
1124 add_property_string(return_value, "message", error->message);
1125 } else {
1126 add_property_str(return_value, "message", zend_empty_string);
1127 }
1128 if (error->file) {
1129 add_property_string(return_value, "file", error->file);
1130 } else {
1131 add_property_str(return_value, "file", zend_empty_string);
1132 }
1133 add_property_long(return_value, "line", error->line);
1134}
1135
1136/* {{{ Retrieve last error from libxml */
1138{
1140
1141 const xmlError *error;
1142
1143 if (LIBXML(error_list)) {
1144 error = zend_llist_get_last(LIBXML(error_list));
1145 } else {
1146 error = xmlGetLastError();
1147 }
1148
1149 if (error) {
1150 php_libxml_create_error_object(return_value, error);
1151 } else {
1153 }
1154}
1155/* }}} */
1156
1157/* {{{ Retrieve array of errors */
1159{
1160 xmlErrorPtr error;
1161
1163
1164 if (LIBXML(error_list)) {
1166 error = zend_llist_get_first(LIBXML(error_list));
1167
1168 while (error != NULL) {
1169 zval z_error;
1170 php_libxml_create_error_object(&z_error, error);
1171 add_next_index_zval(return_value, &z_error);
1172 error = zend_llist_get_next(LIBXML(error_list));
1173 }
1174 } else {
1176 }
1177}
1178/* }}} */
1179
1180/* {{{ Clear last error from libxml */
1182{
1184
1185 xmlResetLastError();
1186 if (LIBXML(error_list)) {
1187 zend_llist_clean(LIBXML(error_list));
1188 }
1189}
1190/* }}} */
1191
1192PHP_LIBXML_API bool php_libxml_disable_entity_loader(bool disable) /* {{{ */
1193{
1194 bool old = LIBXML(entity_loader_disabled);
1195
1196 LIBXML(entity_loader_disabled) = disable;
1197 return old;
1198} /* }}} */
1199
1200/* {{{ Disable/Enable ability to load external entities */
1202{
1203 bool disable = 1;
1204
1207 Z_PARAM_BOOL(disable)
1209
1210 RETURN_BOOL(php_libxml_disable_entity_loader(disable));
1211}
1212/* }}} */
1213
1214/* {{{ Changes the default external entity loader */
1216{
1217 zend_fcall_info fci;
1219
1223
1224 /* Unset old callback if it's defined */
1225 if (ZEND_FCC_INITIALIZED(LIBXML(entity_loader_callback))) {
1226 zend_fcc_dtor(&LIBXML(entity_loader_callback));
1227 }
1228 if (ZEND_FCI_INITIALIZED(fci)) { /* argument not null */
1229 zend_fcc_dup(&LIBXML(entity_loader_callback), &fcc);
1230 }
1232}
1233/* }}} */
1234
1235/* {{{ Get the current external entity loader, or null if the default loader is installer. */
1237{
1239
1240 if (ZEND_FCC_INITIALIZED(LIBXML(entity_loader_callback))) {
1241 zend_get_callable_zval_from_fcc(&LIBXML(entity_loader_callback), return_value);
1242 return;
1243 }
1244 RETURN_NULL();
1245}
1246/* }}} */
1247
1248/* {{{ Common functions shared by extensions */
1249int php_libxml_xmlCheckUTF8(const unsigned char *s)
1250{
1251 size_t i;
1252 unsigned char c;
1253
1254 for (i = 0; (c = s[i++]);) {
1255 if ((c & 0x80) == 0) {
1256 } else if ((c & 0xe0) == 0xc0) {
1257 if ((s[i++] & 0xc0) != 0x80) {
1258 return 0;
1259 }
1260 } else if ((c & 0xf0) == 0xe0) {
1261 if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1262 return 0;
1263 }
1264 } else if ((c & 0xf8) == 0xf0) {
1265 if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1266 return 0;
1267 }
1268 } else {
1269 return 0;
1270 }
1271 }
1272 return 1;
1273}
1274
1275zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1276{
1277 php_libxml_func_handler export_hnd;
1278
1279 /* Initialize in case this module hasn't been loaded yet */
1280 php_libxml_initialize();
1281 export_hnd.export_func = export_function;
1282
1283 return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd));
1284}
1285
1286PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object)
1287{
1288 zend_class_entry *ce = NULL;
1289 xmlNodePtr node = NULL;
1290 php_libxml_func_handler *export_hnd;
1291
1292 if (Z_TYPE_P(object) == IS_OBJECT) {
1293 ce = Z_OBJCE_P(object);
1294 while (ce->parent != NULL) {
1295 ce = ce->parent;
1296 }
1297 if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) {
1298 node = export_hnd->export_func(object);
1299 }
1300 }
1301 return node;
1302}
1303
1304PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data)
1305{
1306 int ret_refcount = -1;
1307
1308 if (object != NULL && node != NULL) {
1309 if (object->node != NULL) {
1310 if (object->node->node == node) {
1311 return object->node->refcount;
1312 } else {
1313 php_libxml_decrement_node_ptr(object);
1314 }
1315 }
1316 if (node->_private != NULL) {
1317 object->node = node->_private;
1318 ret_refcount = ++object->node->refcount;
1319 /* Only dom uses _private */
1320 if (object->node->_private == NULL) {
1321 object->node->_private = private_data;
1322 }
1323 } else {
1324 object->node = emalloc(sizeof(php_libxml_node_ptr));
1325 ret_refcount = 1;
1326 object->node->node = node;
1327 object->node->refcount = 1;
1328 object->node->_private = private_data;
1329 node->_private = object->node;
1330 }
1331 }
1332
1333 return ret_refcount;
1334}
1335
1336PHP_LIBXML_API int php_libxml_decrement_node_ptr_ref(php_libxml_node_ptr *ptr)
1337{
1338 ZEND_ASSERT(ptr != NULL);
1339
1340 int ret_refcount = --ptr->refcount;
1341 if (ret_refcount == 0) {
1342 if (ptr->node != NULL) {
1343 ptr->node->_private = NULL;
1344 }
1345 if (ptr->_private) {
1346 php_libxml_node_object *object = (php_libxml_node_object *) ptr->_private;
1347 object->node = NULL;
1348 }
1349 efree(ptr);
1350 }
1351 return ret_refcount;
1352}
1353
1354PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object)
1355{
1356 if (object != NULL && object->node != NULL) {
1357 return php_libxml_decrement_node_ptr_ref(object->node);
1358 }
1359 return -1;
1360}
1361
1362PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp)
1363{
1364 int ret_refcount = -1;
1365
1366 if (object->document != NULL) {
1367 object->document->refcount++;
1368 ret_refcount = object->document->refcount;
1369 } else if (docp != NULL) {
1370 ret_refcount = 1;
1371 object->document = emalloc(sizeof(php_libxml_ref_obj));
1372 object->document->ptr = docp;
1373 object->document->refcount = ret_refcount;
1374 object->document->doc_props = NULL;
1375 object->document->cache_tag.modification_nr = 1; /* iterators start at 0, such that they will start in an uninitialised state */
1376 object->document->private_data = NULL;
1377 object->document->class_type = PHP_LIBXML_CLASS_UNSET;
1378 object->document->handlers = &php_libxml_default_document_handlers;
1379 object->document->quirks_mode = PHP_LIBXML_NO_QUIRKS;
1380 }
1381
1382 return ret_refcount;
1383}
1384
1385PHP_LIBXML_API int php_libxml_decrement_doc_ref_directly(php_libxml_ref_obj *document)
1386{
1387 int ret_refcount = --document->refcount;
1388 if (ret_refcount == 0) {
1389 if (document->private_data != NULL) {
1390 document->private_data->dtor(document->private_data);
1391 }
1392 if (document->ptr != NULL) {
1393 xmlFreeDoc((xmlDoc *) document->ptr);
1394 }
1395 if (document->doc_props != NULL) {
1396 if (document->doc_props->classmap) {
1397 zend_hash_destroy(document->doc_props->classmap);
1398 FREE_HASHTABLE(document->doc_props->classmap);
1399 }
1400 efree(document->doc_props);
1401 }
1402 efree(document);
1403 }
1404
1405 return ret_refcount;
1406}
1407
1408PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object)
1409{
1410 int ret_refcount = -1;
1411
1412 if (object != NULL && object->document != NULL) {
1413 ret_refcount = php_libxml_decrement_doc_ref_directly(object->document);
1414 object->document = NULL;
1415 }
1416
1417 return ret_refcount;
1418}
1419
1420PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
1421{
1422 if (!node) {
1423 return;
1424 }
1425
1426 switch (node->type) {
1427 case XML_DOCUMENT_NODE:
1429 break;
1431 /* Entity reference nodes are special: their children point to entity declarations,
1432 * but they don't own the declarations and therefore shouldn't free the children.
1433 * Moreover, there can be more than one reference node for a single entity declarations. */
1434 php_libxml_unregister_node(node);
1435 if (node->parent == NULL) {
1436 php_libxml_node_free(node);
1437 }
1438 break;
1439 default:
1440 if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1441 php_libxml_node_free_list((xmlNodePtr) node->children);
1442 if (node->type == XML_ELEMENT_NODE) {
1443 php_libxml_node_free_list((xmlNodePtr) node->properties);
1444 }
1445 php_libxml_unregister_node(node);
1446 php_libxml_node_free(node);
1447 } else {
1448 php_libxml_unregister_node(node);
1449 }
1450 }
1451}
1452
1453PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object)
1454{
1455 if (object != NULL && object->node != NULL) {
1456 php_libxml_node_ptr *obj_node = (php_libxml_node_ptr *) object->node;
1457 xmlNodePtr nodep = obj_node->node;
1458 int ret_refcount = php_libxml_decrement_node_ptr(object);
1459 if (ret_refcount == 0) {
1460 php_libxml_node_free_resource(nodep);
1461 } else {
1462 if (object == obj_node->_private) {
1463 obj_node->_private = NULL;
1464 }
1465 }
1466 }
1467 if (object != NULL && object->document != NULL) {
1468 /* Safe to call as if the resource were freed then doc pointer is NULL */
1469 php_libxml_decrement_doc_ref(object);
1470 }
1471}
1472/* }}} */
1473
1474PHP_LIBXML_API xmlChar *php_libxml_attr_value(const xmlAttr *attr, bool *free)
1475{
1476 /* For attributes we can have an optimized fast-path.
1477 * This fast-path is only possible in the (common) case where the attribute
1478 * has a single text child. Note that if the child or the content is NULL, this
1479 * is equivalent to not having content (i.e. the attribute has the empty string as value). */
1480
1481 *free = false;
1482
1483 if (attr->children == NULL) {
1484 return BAD_CAST "";
1485 }
1486
1487 if (attr->children->type == XML_TEXT_NODE && attr->children->next == NULL) {
1488 if (attr->children->content == NULL) {
1489 return BAD_CAST "";
1490 } else {
1491 return attr->children->content;
1492 }
1493 }
1494
1495 xmlChar *value = xmlNodeGetContent((const xmlNode *) attr);
1496 if (UNEXPECTED(value == NULL)) {
1497 return BAD_CAST "";
1498 }
1499
1500 *free = true;
1501 return value;
1502}
1503
1504static int php_libxml_write_smart_str(void *context, const char *buffer, int len)
1505{
1506 smart_str *str = context;
1507 smart_str_appendl(str, buffer, len);
1508 return len;
1509}
1510
1511static zend_string *php_libxml_default_dump_doc_to_str(xmlDocPtr doc, int options, const char *encoding)
1512{
1513 smart_str str = {0};
1514
1515 /* Encoding is handled from the encoding property set on the document */
1516 xmlSaveCtxtPtr ctxt = xmlSaveToIO(php_libxml_write_smart_str, NULL, &str, encoding, options);
1517 if (!ctxt) {
1518 return NULL;
1519 }
1520
1521 long status = xmlSaveDoc(ctxt, doc);
1522 (void) xmlSaveClose(ctxt);
1523 if (status < 0) {
1524 smart_str_free_ex(&str, false);
1525 return NULL;
1526 }
1527
1528 return smart_str_extract(&str);
1529}
1530
1531static zend_string *php_libxml_default_dump_node_to_str(xmlDocPtr doc, xmlNodePtr node, bool format, const char *encoding)
1532{
1533 smart_str str = {0};
1534 // TODO: should this buffer take an encoding? For now keep it NULL for BC.
1535 xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(php_libxml_write_smart_str, NULL, &str, NULL);
1536 if (!buf) {
1537 return NULL;
1538 }
1539
1540 xmlNodeDumpOutput(buf, doc, node, 0, format, encoding);
1541
1542 if (xmlOutputBufferFlush(buf) < 0) {
1543 smart_str_free_ex(&str, false);
1544 xmlOutputBufferClose(buf);
1545 return NULL;
1546 }
1547
1548 xmlOutputBufferClose(buf);
1549
1550 return smart_str_extract(&str);
1551}
1552
1553static zend_long php_libxml_default_dump_doc_to_file(const char *filename, xmlDocPtr doc, bool format, const char *encoding)
1554{
1555 return xmlSaveFormatFileEnc(filename, doc, encoding, format);
1556}
1557
1558static zend_long php_libxml_dump_node_to_file(const char *filename, xmlDocPtr doc, xmlNodePtr node, bool format, const char *encoding)
1559{
1560 xmlOutputBufferPtr outbuf = xmlOutputBufferCreateFilename(filename, NULL, 0);
1561 if (!outbuf) {
1562 return -1;
1563 }
1564
1565 xmlNodeDumpOutput(outbuf, doc, node, 0, format, encoding);
1566 return xmlOutputBufferClose(outbuf);
1567}
1568
1569#if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML)
1570PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1571{
1572 return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1573}
1574#endif
1575
1576#endif
SAPI_API sapi_module_struct sapi_module
Definition SAPI.c:65
size_t len
Definition apprentice.c:174
bool exception
Definition assert.c:30
file(string $filename, int $flags=0, $context=null)
header(string $header, bool $replace=true, int $response_code=0)
strstr(string $haystack, string $needle, bool $before_needle=false)
is_string(mixed $value)
char s[4]
Definition cdf.c:77
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID dummy)
Definition dllmain.c:33
DNS_STATUS status
Definition dns_win32.c:49
#define DWORD
Definition exif.c:1762
error($message)
Definition ext_skel.php:22
zval * zv
Definition ffi.c:3975
void * ptr
Definition ffi.c:3814
char * err
Definition ffi.c:3029
zval * arg
Definition ffi.c:3975
memset(ptr, 0, type->size)
new_type attr
Definition ffi.c:4364
zend_ffi_ctype_name_buf buf
Definition ffi.c:4685
char * mode
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
foreach($dp as $el) foreach( $dp as $el) if( $pass2< 2) echo ""
enum entity_charset charset
Definition html_tables.h:39
libxml_use_internal_errors(?bool $use_errors=null)
libxml_get_external_entity_loader()
libxml_disable_entity_loader(bool $disable=true)
libxml_set_streams_context($context)
libxml_set_external_entity_loader(?callable $resolver_function)
libxml_get_last_error()
libxml_get_errors()
libxml_clear_errors()
const LIBXML_DOTTED_VERSION
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define next(ls)
Definition minilua.c:2661
int BOOL
php_info_print_table_start()
Definition info.c:1064
php_info_print_table_row(2, "PDO Driver for Firebird", "enabled")
php_info_print_table_end()
Definition info.c:1074
#define PHP_GINIT
Definition php.h:397
#define PHP_FUNCTION
Definition php.h:364
#define PHP_MSHUTDOWN_FUNCTION
Definition php.h:401
#define PHP_MINFO
Definition php.h:396
#define PHP_MINIT_FUNCTION
Definition php.h:400
#define PHP_RINIT
Definition php.h:394
#define PHP_MSHUTDOWN
Definition php.h:393
#define PHP_MINFO_FUNCTION
Definition php.h:404
#define PHP_GINIT_FUNCTION
Definition php.h:405
#define PHP_RSHUTDOWN
Definition php.h:395
#define PHP_RINIT_FUNCTION
Definition php.h:402
#define PHP_RSHUTDOWN_FUNCTION
Definition php.h:403
#define PHP_MINIT
Definition php.h:392
#define PHP_MODULE_GLOBALS
Definition php.h:408
const XML_DOCUMENT_TYPE_NODE
const XML_ATTRIBUTE_ID
const XML_DTD_NODE
const XML_HTML_DOCUMENT_NODE
const XML_TEXT_NODE
const XML_ELEMENT_NODE
const XML_ATTRIBUTE_NODE
const XML_DOCUMENT_NODE
const XML_LOCAL_NAMESPACE
const XML_NOTATION_NODE
const XML_ENTITY_REF_NODE
int line
Definition php_ffi.h:54
#define PG(v)
Definition php_globals.h:31
PHP_JSON_API size_t int options
Definition php_json.h:102
xmlCharEncodingHandlerPtr encoding
Definition php_soap.h:170
#define php_stream_context_from_zval(zcontext, nocontext)
struct _php_stream php_stream
Definition php_streams.h:96
struct _php_stream_context php_stream_context
Definition php_streams.h:98
#define REPORT_ERRORS
#define php_stream_read(stream, buf, count)
#define php_stream_open_wrapper_ex(path, mode, options, opened, context)
#define php_stream_close(stream)
PHPAPI int php_file_le_pstream(void)
Definition streams.c:47
struct _php_stream_wrapper php_stream_wrapper
Definition php_streams.h:97
#define PHP_STREAM_FLAG_NO_FCLOSE
PHPAPI php_stream_wrapper * php_stream_locate_url_wrapper(const char *path, const char **path_for_open, int options)
Definition streams.c:1964
#define PHP_STREAM_URL_STAT_QUIET
struct _php_stream_statbuf php_stream_statbuf
#define php_stream_write(stream, buf, count)
PHPAPI int php_file_le_stream(void)
Definition streams.c:42
char * msg
Definition phpdbg.h:289
zend_constant * data
const char * func_name
struct php_libxml_private_data_header php_libxml_private_data_header
#define vspprintf
Definition spprintf.h:31
int(* url_stat)(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context)
const php_stream_wrapper_ops * wops
uint32_t flags
zend_resource * res
zend_string * name
Definition zend.h:149
zend_class_entry * parent
Definition zend.h:152
Definition file.h:177
Definition dce.c:49
ZEND_API ZEND_COLD void zend_type_error(const char *format,...)
Definition zend.c:1824
#define ZEND_TSRMLS_CACHE_UPDATE()
Definition zend.h:69
#define ZEND_TSRMLS_CACHE_DEFINE()
Definition zend.h:68
ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type)
Definition zend_API.c:1849
ZEND_API void zend_get_callable_zval_from_fcc(const zend_fcall_info_cache *fcc, zval *callable)
Definition zend_API.c:4468
ZEND_API zend_string * zend_get_callable_name(zval *callable)
Definition zend_API.c:4154
ZEND_API const zend_fcall_info_cache empty_fcall_info_cache
struct _zend_fcall_info_cache zend_fcall_info_cache
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define Z_PARAM_RESOURCE(dest)
Definition zend_API.h:2056
#define ZEND_PARSE_PARAMETERS_NONE()
Definition zend_API.h:1623
#define RETURN_NULL()
Definition zend_API.h:1036
#define ZVAL_STRING(z, s)
Definition zend_API.h:956
#define ZEND_DECLARE_MODULE_GLOBALS(module_name)
Definition zend_API.h:268
#define array_init_size(arg, size)
Definition zend_API.h:538
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define ZEND_GET_MODULE(name)
Definition zend_API.h:241
#define ZEND_FCI_INITIALIZED(fci)
Definition zend_API.h:340
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define Z_PARAM_BOOL_OR_NULL(dest, is_null)
Definition zend_API.h:1729
#define RETURN_BOOL(b)
Definition zend_API.h:1035
struct _zend_fcall_info zend_fcall_info
#define Z_PARAM_FUNC_NO_TRAMPOLINE_FREE_OR_NULL(dest_fci, dest_fcc)
Definition zend_API.h:1833
#define ZEND_FCC_INITIALIZED(fcc)
Definition zend_API.h:341
#define Z_PARAM_BOOL(dest)
Definition zend_API.h:1726
#define RETURN_EMPTY_ARRAY()
Definition zend_API.h:1051
#define RETURN_TRUE
Definition zend_API.h:1059
#define array_init(arg)
Definition zend_API.h:537
#define efree(ptr)
Definition zend_alloc.h:155
#define FREE_HASHTABLE(ht)
Definition zend_alloc.h:234
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strcmp(string $string1, string $string2)
zend_string_release_ex(func->internal_function.function_name, 0)
zval * args
#define strncasecmp(s1, s2, n)
#define E_NOTICE
Definition zend_errors.h:26
#define E_WARNING
Definition zend_errors.h:24
ZEND_API void(ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data)
ZEND_API zend_string * get_active_function_or_method_name(void)
#define EG(v)
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
Definition zend_hash.c:1727
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
Definition zend_hash.h:108
ZEND_API void * zend_fetch_resource2_ex(zval *res, const char *resource_type_name, int resource_type1, int resource_type2)
Definition zend_list.c:153
ZEND_API void zend_llist_destroy(zend_llist *l)
Definition zend_llist.c:102
ZEND_API void zend_llist_add_element(zend_llist *l, const void *element)
Definition zend_llist.c:34
ZEND_API void zend_llist_init(zend_llist *l, size_t size, llist_dtor_func_t dtor, unsigned char persistent)
Definition zend_llist.c:24
ZEND_API void zend_llist_clean(zend_llist *l)
Definition zend_llist.c:121
struct _zend_llist zend_llist
int32_t zend_long
Definition zend_long.h:42
struct _zend_string zend_string
#define STANDARD_MODULE_HEADER
struct _zend_module_entry zend_module_entry
#define STANDARD_MODULE_PROPERTIES_EX
int last
#define ZEND_IGNORE_LEAKS_END()
#define ZEND_IGNORE_VALUE(x)
#define ZEND_FALLTHROUGH
#define ZEND_ASSERT(c)
#define ZEND_IGNORE_LEAKS_BEGIN()
#define UNEXPECTED(condition)
struct _zend_class_entry zend_class_entry
ZEND_API zend_string * zend_empty_string
Definition zend_string.c:51
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define ZVAL_UNDEF(z)
#define ZVAL_NULL(z)
#define IS_STRING
Definition zend_types.h:606
struct _zend_array HashTable
Definition zend_types.h:386
#define IS_RESOURCE
Definition zend_types.h:609
#define Z_ISUNDEF(zval)
Definition zend_types.h:956
#define Z_PTR_P(zval_p)
#define GC_ADDREF(p)
Definition zend_types.h:709
#define IS_NULL
Definition zend_types.h:601
#define Z_OBJCE_P(zval_p)
#define Z_STRVAL(zval)
Definition zend_types.h:974
#define IS_OBJECT
Definition zend_types.h:608
#define ZVAL_COPY(z, v)
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
#define Z_TYPE(zval)
Definition zend_types.h:659
#define ZVAL_COPY_VALUE(z, v)
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval retval
zval * return_value
zend_string * name
object
zval * ret
value