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 | Author: Rob Richards <rrichards@php.net> |
14 | Pierre-A. Joye <pajoye@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22
23 #include "php.h"
24 #include "ext/standard/info.h"
25 #include "php_xmlwriter.h"
26 #include "php_xmlwriter_arginfo.h"
27 #include "zend_smart_str.h"
28
29 static zend_class_entry *xmlwriter_class_entry_ce;
30
31 typedef int (*xmlwriter_read_one_char_t)(xmlTextWriterPtr writer, const xmlChar *content);
32 typedef int (*xmlwriter_read_int_t)(xmlTextWriterPtr writer);
33
34 /* {{{ XMLWRITER_FROM_OBJECT */
35 #define XMLWRITER_FROM_OBJECT(ptr, object) \
36 { \
37 ze_xmlwriter_object *obj = Z_XMLWRITER_P(object); \
38 ptr = obj->ptr; \
39 if (!ptr) { \
40 zend_throw_error(NULL, "Invalid or uninitialized XMLWriter object"); \
41 RETURN_THROWS(); \
42 } \
43 }
44 /* }}} */
45
46 static zend_object_handlers xmlwriter_object_handlers;
47
xmlwriter_destroy_libxml_objects(ze_xmlwriter_object * intern)48 static zend_always_inline void xmlwriter_destroy_libxml_objects(ze_xmlwriter_object *intern)
49 {
50 if (intern->ptr) {
51 /* Note: this call will also free the output pointer. */
52 xmlFreeTextWriter(intern->ptr);
53 intern->ptr = NULL;
54 intern->output = NULL;
55 }
56 }
57
58 /* {{{{ xmlwriter_object_dtor */
xmlwriter_object_dtor(zend_object * object)59 static void xmlwriter_object_dtor(zend_object *object)
60 {
61 ze_xmlwriter_object *intern = php_xmlwriter_fetch_object(object);
62
63 /* freeing the resource here may leak, but otherwise we may use it after it has been freed */
64 xmlwriter_destroy_libxml_objects(intern);
65 zend_objects_destroy_object(object);
66 }
67 /* }}} */
68
69 /* {{{ xmlwriter_object_free_storage */
xmlwriter_object_free_storage(zend_object * object)70 static void xmlwriter_object_free_storage(zend_object *object)
71 {
72 ze_xmlwriter_object *intern = php_xmlwriter_fetch_object(object);
73
74 zend_object_std_dtor(&intern->std);
75 }
76 /* }}} */
77
78
79 /* {{{ xmlwriter_object_new */
xmlwriter_object_new(zend_class_entry * class_type)80 static zend_object *xmlwriter_object_new(zend_class_entry *class_type)
81 {
82 ze_xmlwriter_object *intern;
83
84 intern = zend_object_alloc(sizeof(ze_xmlwriter_object), class_type);
85 zend_object_std_init(&intern->std, class_type);
86 object_properties_init(&intern->std, class_type);
87
88 return &intern->std;
89 }
90 /* }}} */
91
92 #define XMLW_NAME_CHK(__arg_no, __subject) \
93 if (xmlValidateName((xmlChar *) name, 0) != 0) { \
94 zend_argument_value_error(__arg_no, "must be a valid %s, \"%s\" given", __subject, name); \
95 RETURN_THROWS(); \
96 } \
97
98 /* {{{ function prototypes */
99 static PHP_MINIT_FUNCTION(xmlwriter);
100 static PHP_MSHUTDOWN_FUNCTION(xmlwriter);
101 static PHP_MINFO_FUNCTION(xmlwriter);
102 /* }}} */
103
104 /* _xmlwriter_get_valid_file_path should be made a
105 common function in libxml extension as code is common to a few xml extensions */
106 /* {{{ _xmlwriter_get_valid_file_path */
_xmlwriter_get_valid_file_path(char * source,char * resolved_path,int resolved_path_len)107 static char *_xmlwriter_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len ) {
108 xmlURI *uri;
109 xmlChar *escsource;
110 char *file_dest;
111 int isFileUri = 0;
112
113 uri = xmlCreateURI();
114 if (uri == NULL) {
115 return NULL;
116 }
117 escsource = xmlURIEscapeStr((xmlChar *)source, (xmlChar *) ":");
118 xmlParseURIReference(uri, (char *)escsource);
119 xmlFree(escsource);
120
121 if (uri->scheme != NULL) {
122 /* absolute file uris - libxml only supports localhost or empty host */
123 if (strncasecmp(source, "file:///", 8) == 0) {
124 if (source[sizeof("file:///") - 1] == '\0') {
125 xmlFreeURI(uri);
126 return NULL;
127 }
128 isFileUri = 1;
129 #ifdef PHP_WIN32
130 source += 8;
131 #else
132 source += 7;
133 #endif
134 } else if (strncasecmp(source, "file://localhost/",17) == 0) {
135 if (source[sizeof("file://localhost/") - 1] == '\0') {
136 xmlFreeURI(uri);
137 return NULL;
138 }
139
140 isFileUri = 1;
141 #ifdef PHP_WIN32
142 source += 17;
143 #else
144 source += 16;
145 #endif
146 }
147 }
148
149 if ((uri->scheme == NULL || isFileUri)) {
150 char file_dirname[MAXPATHLEN];
151 size_t dir_len;
152
153 if (!VCWD_REALPATH(source, resolved_path) && !expand_filepath(source, resolved_path)) {
154 xmlFreeURI(uri);
155 return NULL;
156 }
157
158 memcpy(file_dirname, source, strlen(source));
159 dir_len = zend_dirname(file_dirname, strlen(source));
160
161 if (dir_len > 0) {
162 zend_stat_t buf = {0};
163 if (php_sys_stat(file_dirname, &buf) != 0) {
164 xmlFreeURI(uri);
165 return NULL;
166 }
167 }
168
169 file_dest = resolved_path;
170 } else {
171 file_dest = source;
172 }
173
174 xmlFreeURI(uri);
175
176 return file_dest;
177 }
178 /* }}} */
179
xml_writer_create_static(INTERNAL_FUNCTION_PARAMETERS,xmlTextWriterPtr writer,smart_str * output)180 static void xml_writer_create_static(INTERNAL_FUNCTION_PARAMETERS, xmlTextWriterPtr writer, smart_str *output)
181 {
182 if (object_init_with_constructor(return_value, Z_CE_P(ZEND_THIS), 0, NULL, NULL) == SUCCESS) {
183 ze_xmlwriter_object *intern = Z_XMLWRITER_P(return_value);
184 intern->ptr = writer;
185 intern->output = output;
186 } else {
187 // output is freed by writer, so we don't need to free it here.
188 xmlFreeTextWriter(writer);
189 }
190 }
191
192 static const zend_module_dep xmlwriter_deps[] = {
193 ZEND_MOD_REQUIRED("libxml")
194 ZEND_MOD_END
195 };
196
197 /* {{{ xmlwriter_module_entry */
198 zend_module_entry xmlwriter_module_entry = {
199 STANDARD_MODULE_HEADER_EX, NULL,
200 xmlwriter_deps,
201 "xmlwriter",
202 ext_functions,
203 PHP_MINIT(xmlwriter),
204 PHP_MSHUTDOWN(xmlwriter),
205 NULL,
206 NULL,
207 PHP_MINFO(xmlwriter),
208 PHP_XMLWRITER_VERSION,
209 STANDARD_MODULE_PROPERTIES
210 };
211 /* }}} */
212
213 #ifdef COMPILE_DL_XMLWRITER
ZEND_GET_MODULE(xmlwriter)214 ZEND_GET_MODULE(xmlwriter)
215 #endif
216
217 /* {{{ xmlwriter_objects_clone
218 static void xmlwriter_objects_clone(void *object, void **object_clone)
219 {
220 TODO
221 }
222 }}} */
223
224 static void php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAMETERS, xmlwriter_read_one_char_t internal_function, char *subject_name)
225 {
226 xmlTextWriterPtr ptr;
227 char *name;
228 size_t name_len;
229 int retval;
230 zval *self;
231
232 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &self, xmlwriter_class_entry_ce, &name, &name_len) == FAILURE) {
233 RETURN_THROWS();
234 }
235 XMLWRITER_FROM_OBJECT(ptr, self);
236
237 if (subject_name != NULL) {
238 XMLW_NAME_CHK(2, subject_name);
239 }
240
241 retval = internal_function(ptr, (xmlChar *) name);
242
243 RETURN_BOOL(retval != -1);
244 }
245
php_xmlwriter_end(INTERNAL_FUNCTION_PARAMETERS,xmlwriter_read_int_t internal_function)246 static void php_xmlwriter_end(INTERNAL_FUNCTION_PARAMETERS, xmlwriter_read_int_t internal_function)
247 {
248 xmlTextWriterPtr ptr;
249 int retval;
250 zval *self;
251
252 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &self, xmlwriter_class_entry_ce) == FAILURE) {
253 RETURN_THROWS();
254 }
255 XMLWRITER_FROM_OBJECT(ptr, self);
256
257 retval = internal_function(ptr);
258
259 RETURN_BOOL(retval != -1);
260 }
261
262 /* {{{ Toggle indentation on/off - returns FALSE on error */
PHP_FUNCTION(xmlwriter_set_indent)263 PHP_FUNCTION(xmlwriter_set_indent)
264 {
265 xmlTextWriterPtr ptr;
266 int retval;
267 bool indent;
268 zval *self;
269
270 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ob", &self, xmlwriter_class_entry_ce, &indent) == FAILURE) {
271 RETURN_THROWS();
272 }
273 XMLWRITER_FROM_OBJECT(ptr, self);
274
275 retval = xmlTextWriterSetIndent(ptr, indent);
276
277 RETURN_BOOL(retval == 0);
278 }
279 /* }}} */
280
281 /* {{{ Set string used for indenting - returns FALSE on error */
PHP_FUNCTION(xmlwriter_set_indent_string)282 PHP_FUNCTION(xmlwriter_set_indent_string)
283 {
284 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterSetIndentString, NULL);
285 }
286 /* }}} */
287
288 /* {{{ Create start attribute - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_attribute)289 PHP_FUNCTION(xmlwriter_start_attribute)
290 {
291 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterStartAttribute, "attribute name");
292 }
293 /* }}} */
294
295 /* {{{ End attribute - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_attribute)296 PHP_FUNCTION(xmlwriter_end_attribute)
297 {
298 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndAttribute);
299 }
300 /* }}} */
301
302 /* {{{ Create start namespaced attribute - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_attribute_ns)303 PHP_FUNCTION(xmlwriter_start_attribute_ns)
304 {
305 xmlTextWriterPtr ptr;
306 char *name, *prefix, *uri;
307 size_t name_len, prefix_len, uri_len;
308 int retval;
309 zval *self;
310
311 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os!ss!", &self, xmlwriter_class_entry_ce,
312 &prefix, &prefix_len, &name, &name_len, &uri, &uri_len) == FAILURE) {
313 RETURN_THROWS();
314 }
315 XMLWRITER_FROM_OBJECT(ptr, self);
316
317 XMLW_NAME_CHK(3, "attribute name");
318
319 retval = xmlTextWriterStartAttributeNS(ptr, (xmlChar *)prefix, (xmlChar *)name, (xmlChar *)uri);
320
321 RETURN_BOOL(retval != -1);
322 }
323 /* }}} */
324
325 /* {{{ Write full attribute - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_attribute)326 PHP_FUNCTION(xmlwriter_write_attribute)
327 {
328 xmlTextWriterPtr ptr;
329 char *name, *content;
330 size_t name_len, content_len;
331 int retval;
332 zval *self;
333
334 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oss", &self, xmlwriter_class_entry_ce,
335 &name, &name_len, &content, &content_len) == FAILURE) {
336 RETURN_THROWS();
337 }
338 XMLWRITER_FROM_OBJECT(ptr, self);
339
340 XMLW_NAME_CHK(2, "attribute name");
341
342 retval = xmlTextWriterWriteAttribute(ptr, (xmlChar *)name, (xmlChar *)content);
343
344 RETURN_BOOL(retval != -1);
345 }
346 /* }}} */
347
348 /* {{{ Write full namespaced attribute - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_attribute_ns)349 PHP_FUNCTION(xmlwriter_write_attribute_ns)
350 {
351 xmlTextWriterPtr ptr;
352 char *name, *prefix, *uri, *content;
353 size_t name_len, prefix_len, uri_len, content_len;
354 int retval;
355 zval *self;
356
357 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os!ss!s", &self, xmlwriter_class_entry_ce,
358 &prefix, &prefix_len, &name, &name_len, &uri, &uri_len, &content, &content_len) == FAILURE) {
359 RETURN_THROWS();
360 }
361 XMLWRITER_FROM_OBJECT(ptr, self);
362
363 XMLW_NAME_CHK(3, "attribute name");
364
365 retval = xmlTextWriterWriteAttributeNS(ptr, (xmlChar *)prefix, (xmlChar *)name, (xmlChar *)uri, (xmlChar *)content);
366
367 RETURN_BOOL(retval != -1);
368 }
369 /* }}} */
370
371 /* {{{ Create start element tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_element)372 PHP_FUNCTION(xmlwriter_start_element)
373 {
374 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterStartElement, "element name");
375 }
376 /* }}} */
377
378 /* {{{ Create start namespaced element tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_element_ns)379 PHP_FUNCTION(xmlwriter_start_element_ns)
380 {
381 xmlTextWriterPtr ptr;
382 char *name, *prefix, *uri;
383 size_t name_len, prefix_len, uri_len;
384 int retval;
385 zval *self;
386
387 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os!ss!", &self, xmlwriter_class_entry_ce,
388 &prefix, &prefix_len, &name, &name_len, &uri, &uri_len) == FAILURE) {
389 RETURN_THROWS();
390 }
391 XMLWRITER_FROM_OBJECT(ptr, self);
392
393 XMLW_NAME_CHK(3, "element name");
394
395 retval = xmlTextWriterStartElementNS(ptr, (xmlChar *)prefix, (xmlChar *)name, (xmlChar *)uri);
396
397 RETURN_BOOL(retval != -1);
398 }
399 /* }}} */
400
401 /* {{{ End current element - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_element)402 PHP_FUNCTION(xmlwriter_end_element)
403 {
404 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndElement);
405 }
406 /* }}} */
407
408 /* {{{ End current element - returns FALSE on error */
PHP_FUNCTION(xmlwriter_full_end_element)409 PHP_FUNCTION(xmlwriter_full_end_element)
410 {
411 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterFullEndElement);
412 }
413 /* }}} */
414
415 /* {{{ Write full element tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_element)416 PHP_FUNCTION(xmlwriter_write_element)
417 {
418 xmlTextWriterPtr ptr;
419 char *name, *content = NULL;
420 size_t name_len, content_len;
421 int retval;
422 zval *self;
423
424 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|s!", &self, xmlwriter_class_entry_ce,
425 &name, &name_len, &content, &content_len) == FAILURE) {
426 RETURN_THROWS();
427 }
428 XMLWRITER_FROM_OBJECT(ptr, self);
429
430 XMLW_NAME_CHK(2, "element name");
431
432 if (!content) {
433 retval = xmlTextWriterStartElement(ptr, (xmlChar *)name);
434 if (retval == -1) {
435 RETURN_FALSE;
436 }
437 retval = xmlTextWriterEndElement(ptr);
438 } else {
439 retval = xmlTextWriterWriteElement(ptr, (xmlChar *)name, (xmlChar *)content);
440 }
441
442 RETURN_BOOL(retval != -1);
443 }
444 /* }}} */
445
446 /* {{{ Write full namesapced element tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_element_ns)447 PHP_FUNCTION(xmlwriter_write_element_ns)
448 {
449 xmlTextWriterPtr ptr;
450 char *name, *prefix, *uri, *content = NULL;
451 size_t name_len, prefix_len, uri_len, content_len;
452 int retval;
453 zval *self;
454
455 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os!ss!|s!", &self, xmlwriter_class_entry_ce,
456 &prefix, &prefix_len, &name, &name_len, &uri, &uri_len, &content, &content_len) == FAILURE) {
457 RETURN_THROWS();
458 }
459 XMLWRITER_FROM_OBJECT(ptr, self);
460
461 XMLW_NAME_CHK(3, "element name");
462
463 if (!content) {
464 retval = xmlTextWriterStartElementNS(ptr,(xmlChar *)prefix, (xmlChar *)name, (xmlChar *)uri);
465 if (retval == -1) {
466 RETURN_FALSE;
467 }
468 retval = xmlTextWriterEndElement(ptr);
469 } else {
470 retval = xmlTextWriterWriteElementNS(ptr, (xmlChar *)prefix, (xmlChar *)name, (xmlChar *)uri, (xmlChar *)content);
471 }
472
473 RETURN_BOOL(retval != -1);
474 }
475 /* }}} */
476
477 /* {{{ Create start PI tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_pi)478 PHP_FUNCTION(xmlwriter_start_pi)
479 {
480 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterStartPI, "PI target");
481 }
482 /* }}} */
483
484 /* {{{ End current PI - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_pi)485 PHP_FUNCTION(xmlwriter_end_pi)
486 {
487 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndPI);
488 }
489 /* }}} */
490
491 /* {{{ Write full PI tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_pi)492 PHP_FUNCTION(xmlwriter_write_pi)
493 {
494 xmlTextWriterPtr ptr;
495 char *name, *content;
496 size_t name_len, content_len;
497 int retval;
498 zval *self;
499
500 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oss", &self, xmlwriter_class_entry_ce,
501 &name, &name_len, &content, &content_len) == FAILURE) {
502 RETURN_THROWS();
503 }
504 XMLWRITER_FROM_OBJECT(ptr, self);
505
506 XMLW_NAME_CHK(2, "PI target");
507
508 retval = xmlTextWriterWritePI(ptr, (xmlChar *)name, (xmlChar *)content);
509
510 RETURN_BOOL(retval != -1);
511 }
512 /* }}} */
513
514 /* {{{ Create start CDATA tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_cdata)515 PHP_FUNCTION(xmlwriter_start_cdata)
516 {
517 xmlTextWriterPtr ptr;
518 int retval;
519 zval *self;
520
521 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &self, xmlwriter_class_entry_ce) == FAILURE) {
522 RETURN_THROWS();
523 }
524 XMLWRITER_FROM_OBJECT(ptr, self);
525
526 retval = xmlTextWriterStartCDATA(ptr);
527
528 RETURN_BOOL(retval != -1);
529 }
530 /* }}} */
531
532 /* {{{ End current CDATA - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_cdata)533 PHP_FUNCTION(xmlwriter_end_cdata)
534 {
535 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndCDATA);
536 }
537 /* }}} */
538
539 /* {{{ Write full CDATA tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_cdata)540 PHP_FUNCTION(xmlwriter_write_cdata)
541 {
542 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterWriteCDATA, NULL);
543 }
544 /* }}} */
545
546 /* {{{ Write text - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_raw)547 PHP_FUNCTION(xmlwriter_write_raw)
548 {
549 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterWriteRaw, NULL);
550 }
551 /* }}} */
552
553 /* {{{ Write text - returns FALSE on error */
PHP_FUNCTION(xmlwriter_text)554 PHP_FUNCTION(xmlwriter_text)
555 {
556 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterWriteString, NULL);
557 }
558 /* }}} */
559
560 /* {{{ Create start comment - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_comment)561 PHP_FUNCTION(xmlwriter_start_comment)
562 {
563 xmlTextWriterPtr ptr;
564 int retval;
565 zval *self;
566
567 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &self, xmlwriter_class_entry_ce) == FAILURE) {
568 RETURN_THROWS();
569 }
570 XMLWRITER_FROM_OBJECT(ptr, self);
571
572 retval = xmlTextWriterStartComment(ptr);
573
574 RETURN_BOOL(retval != -1);
575 }
576 /* }}} */
577
578 /* {{{ Create end comment - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_comment)579 PHP_FUNCTION(xmlwriter_end_comment)
580 {
581 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndComment);
582 }
583 /* }}} */
584
585 /* {{{ Write full comment tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_comment)586 PHP_FUNCTION(xmlwriter_write_comment)
587 {
588 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterWriteComment, NULL);
589 }
590 /* }}} */
591
592 /* {{{ Create document tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_document)593 PHP_FUNCTION(xmlwriter_start_document)
594 {
595 xmlTextWriterPtr ptr;
596 char *version = NULL, *enc = NULL, *alone = NULL;
597 size_t version_len, enc_len, alone_len;
598 int retval;
599 zval *self;
600
601 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|s!p!s!", &self, xmlwriter_class_entry_ce, &version, &version_len, &enc, &enc_len, &alone, &alone_len) == FAILURE) {
602 RETURN_THROWS();
603 }
604 XMLWRITER_FROM_OBJECT(ptr, self);
605
606 retval = xmlTextWriterStartDocument(ptr, version, enc, alone);
607
608 RETURN_BOOL(retval != -1);
609 }
610 /* }}} */
611
612 /* {{{ End current document - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_document)613 PHP_FUNCTION(xmlwriter_end_document)
614 {
615 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndDocument);
616 }
617 /* }}} */
618
619 /* {{{ Create start DTD tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_dtd)620 PHP_FUNCTION(xmlwriter_start_dtd)
621 {
622 xmlTextWriterPtr ptr;
623 char *name, *pubid = NULL, *sysid = NULL;
624 size_t name_len, pubid_len, sysid_len;
625 int retval;
626 zval *self;
627
628 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|s!s!", &self, xmlwriter_class_entry_ce, &name, &name_len, &pubid, &pubid_len, &sysid, &sysid_len) == FAILURE) {
629 RETURN_THROWS();
630 }
631 XMLWRITER_FROM_OBJECT(ptr, self);
632
633 retval = xmlTextWriterStartDTD(ptr, (xmlChar *)name, (xmlChar *)pubid, (xmlChar *)sysid);
634
635 RETURN_BOOL(retval != -1);
636 }
637 /* }}} */
638
639 /* {{{ End current DTD - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_dtd)640 PHP_FUNCTION(xmlwriter_end_dtd)
641 {
642 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndDTD);
643 }
644 /* }}} */
645
646 /* {{{ Write full DTD tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_dtd)647 PHP_FUNCTION(xmlwriter_write_dtd)
648 {
649 xmlTextWriterPtr ptr;
650 char *name, *pubid = NULL, *sysid = NULL, *subset = NULL;
651 size_t name_len, pubid_len, sysid_len, subset_len;
652 int retval;
653 zval *self;
654
655 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|s!s!s!", &self, xmlwriter_class_entry_ce, &name, &name_len, &pubid, &pubid_len, &sysid, &sysid_len, &subset, &subset_len) == FAILURE) {
656 RETURN_THROWS();
657 }
658 XMLWRITER_FROM_OBJECT(ptr, self);
659
660 retval = xmlTextWriterWriteDTD(ptr, (xmlChar *)name, (xmlChar *)pubid, (xmlChar *)sysid, (xmlChar *)subset);
661
662 RETURN_BOOL(retval != -1);
663 }
664 /* }}} */
665
666 /* {{{ Create start DTD element - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_dtd_element)667 PHP_FUNCTION(xmlwriter_start_dtd_element)
668 {
669 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterStartDTDElement, "element name");
670 }
671 /* }}} */
672
673 /* {{{ End current DTD element - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_dtd_element)674 PHP_FUNCTION(xmlwriter_end_dtd_element)
675 {
676 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndDTDElement);
677 }
678 /* }}} */
679
680 /* {{{ Write full DTD element tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_dtd_element)681 PHP_FUNCTION(xmlwriter_write_dtd_element)
682 {
683 xmlTextWriterPtr ptr;
684 char *name, *content;
685 size_t name_len, content_len;
686 int retval;
687 zval *self;
688
689 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oss", &self, xmlwriter_class_entry_ce,
690 &name, &name_len, &content, &content_len) == FAILURE) {
691 RETURN_THROWS();
692 }
693 XMLWRITER_FROM_OBJECT(ptr, self);
694
695 XMLW_NAME_CHK(2, "element name");
696
697 retval = xmlTextWriterWriteDTDElement(ptr, (xmlChar *)name, (xmlChar *)content);
698
699 RETURN_BOOL(retval != -1);
700 }
701 /* }}} */
702
703 /* {{{ Create start DTD AttList - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_dtd_attlist)704 PHP_FUNCTION(xmlwriter_start_dtd_attlist)
705 {
706 php_xmlwriter_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterStartDTDAttlist, "element name");
707 }
708 /* }}} */
709
710 /* {{{ End current DTD AttList - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_dtd_attlist)711 PHP_FUNCTION(xmlwriter_end_dtd_attlist)
712 {
713 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndDTDAttlist);
714 }
715 /* }}} */
716
717 /* {{{ Write full DTD AttList tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_dtd_attlist)718 PHP_FUNCTION(xmlwriter_write_dtd_attlist)
719 {
720 xmlTextWriterPtr ptr;
721 char *name, *content;
722 size_t name_len, content_len;
723 int retval;
724 zval *self;
725
726 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oss", &self, xmlwriter_class_entry_ce,
727 &name, &name_len, &content, &content_len) == FAILURE) {
728 RETURN_THROWS();
729 }
730 XMLWRITER_FROM_OBJECT(ptr, self);
731
732 XMLW_NAME_CHK(2, "element name");
733
734 retval = xmlTextWriterWriteDTDAttlist(ptr, (xmlChar *)name, (xmlChar *)content);
735
736 RETURN_BOOL(retval != -1);
737 }
738 /* }}} */
739
740 /* {{{ Create start DTD Entity - returns FALSE on error */
PHP_FUNCTION(xmlwriter_start_dtd_entity)741 PHP_FUNCTION(xmlwriter_start_dtd_entity)
742 {
743 xmlTextWriterPtr ptr;
744 char *name;
745 size_t name_len;
746 int retval;
747 bool isparm;
748 zval *self;
749
750 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Osb", &self, xmlwriter_class_entry_ce, &name, &name_len, &isparm) == FAILURE) {
751 RETURN_THROWS();
752 }
753 XMLWRITER_FROM_OBJECT(ptr, self);
754
755 XMLW_NAME_CHK(2, "attribute name");
756
757 retval = xmlTextWriterStartDTDEntity(ptr, isparm, (xmlChar *)name);
758
759 RETURN_BOOL(retval != -1);
760 }
761 /* }}} */
762
763 /* {{{ End current DTD Entity - returns FALSE on error */
PHP_FUNCTION(xmlwriter_end_dtd_entity)764 PHP_FUNCTION(xmlwriter_end_dtd_entity)
765 {
766 php_xmlwriter_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextWriterEndDTDEntity);
767 }
768 /* }}} */
769
770 /* {{{ Write full DTD Entity tag - returns FALSE on error */
PHP_FUNCTION(xmlwriter_write_dtd_entity)771 PHP_FUNCTION(xmlwriter_write_dtd_entity)
772 {
773 xmlTextWriterPtr ptr;
774 char *name, *content;
775 size_t name_len, content_len;
776 int retval;
777 /* Optional parameters */
778 char *pubid = NULL, *sysid = NULL, *ndataid = NULL;
779 bool pe = 0;
780 size_t pubid_len, sysid_len, ndataid_len;
781 zval *self;
782
783 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oss|bs!s!s!", &self, xmlwriter_class_entry_ce,
784 &name, &name_len, &content, &content_len, &pe, &pubid, &pubid_len,
785 &sysid, &sysid_len, &ndataid, &ndataid_len) == FAILURE) {
786 RETURN_THROWS();
787 }
788 XMLWRITER_FROM_OBJECT(ptr, self);
789
790 XMLW_NAME_CHK(2, "element name");
791
792 retval = xmlTextWriterWriteDTDEntity(ptr, pe, (xmlChar *)name, (xmlChar *)pubid, (xmlChar *)sysid, (xmlChar *)ndataid, (xmlChar *)content);
793
794 RETURN_BOOL(retval != -1);
795 }
796 /* }}} */
797
798 /* {{{ Create new xmlwriter using source uri for output */
PHP_FUNCTION(xmlwriter_open_uri)799 PHP_FUNCTION(xmlwriter_open_uri)
800 {
801 char *valid_file = NULL;
802 xmlTextWriterPtr ptr;
803 char *source;
804 char resolved_path[MAXPATHLEN + 1];
805 size_t source_len;
806 zval *self = getThis();
807 ze_xmlwriter_object *ze_obj = NULL;
808
809 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &source, &source_len) == FAILURE) {
810 RETURN_THROWS();
811 }
812
813 if (self) {
814 /* We do not use XMLWRITER_FROM_OBJECT, xmlwriter init function here */
815 ze_obj = Z_XMLWRITER_P(self);
816 }
817
818 if (source_len == 0) {
819 zend_argument_must_not_be_empty_error(1);
820 RETURN_THROWS();
821 }
822
823 valid_file = _xmlwriter_get_valid_file_path(source, resolved_path, MAXPATHLEN);
824 if (!valid_file) {
825 php_error_docref(NULL, E_WARNING, "Unable to resolve file path");
826 RETURN_FALSE;
827 }
828
829 ptr = xmlNewTextWriterFilename(valid_file, 0);
830
831 if (!ptr) {
832 RETURN_FALSE;
833 }
834
835 if (self) {
836 xmlwriter_destroy_libxml_objects(ze_obj);
837 ze_obj->ptr = ptr;
838 ze_obj->output = NULL;
839 RETURN_TRUE;
840 } else {
841 ze_obj = php_xmlwriter_fetch_object(xmlwriter_object_new(xmlwriter_class_entry_ce));
842 ze_obj->ptr = ptr;
843 ze_obj->output = NULL;
844 RETURN_OBJ(&ze_obj->std);
845 }
846 }
847 /* }}} */
848
PHP_METHOD(XMLWriter,toUri)849 PHP_METHOD(XMLWriter, toUri)
850 {
851 char *source;
852 size_t source_len;
853 char resolved_path[MAXPATHLEN + 1];
854
855 ZEND_PARSE_PARAMETERS_START(1, 1)
856 Z_PARAM_PATH(source, source_len)
857 ZEND_PARSE_PARAMETERS_END();
858
859 if (source_len == 0) {
860 zend_argument_must_not_be_empty_error(1);
861 RETURN_THROWS();
862 }
863
864 const char *valid_file = _xmlwriter_get_valid_file_path(source, resolved_path, MAXPATHLEN);
865 if (!valid_file) {
866 zend_argument_value_error(1, "must resolve to a valid file path");
867 RETURN_THROWS();
868 }
869
870 xmlTextWriterPtr writer = xmlNewTextWriterFilename(valid_file, 0);
871 if (!writer) {
872 zend_throw_error(NULL, "Could not construct libxml writer");
873 RETURN_THROWS();
874 }
875
876 xml_writer_create_static(INTERNAL_FUNCTION_PARAM_PASSTHRU, writer, NULL);
877 }
878
xml_writer_stream_write_memory(void * context,const char * buffer,int len)879 static int xml_writer_stream_write_memory(void *context, const char *buffer, int len)
880 {
881 smart_str *output = context;
882 smart_str_appendl(output, buffer, len);
883 return len;
884 }
885
xml_writer_stream_close_memory(void * context)886 static int xml_writer_stream_close_memory(void *context)
887 {
888 smart_str *output = context;
889 smart_str_free_ex(output, false);
890 efree(output);
891 return 0;
892 }
893
xml_writer_create_in_memory(smart_str ** output_ptr)894 static xmlTextWriterPtr xml_writer_create_in_memory(smart_str **output_ptr)
895 {
896 smart_str *output = emalloc(sizeof(*output));
897 memset(output, 0, sizeof(*output));
898
899 xmlOutputBufferPtr output_buffer = xmlOutputBufferCreateIO(xml_writer_stream_write_memory, xml_writer_stream_close_memory, output, NULL);
900 if (output_buffer == NULL) {
901 efree(output);
902 return NULL;
903 }
904
905 xmlTextWriterPtr writer = xmlNewTextWriter(output_buffer);
906 if (!writer) {
907 /* This call will free output too. */
908 xmlOutputBufferClose(output_buffer);
909 return NULL;
910 }
911 *output_ptr = output;
912 return writer;
913 }
914
915 /* {{{ Create new xmlwriter using memory for string output */
PHP_FUNCTION(xmlwriter_open_memory)916 PHP_FUNCTION(xmlwriter_open_memory)
917 {
918 zval *self = getThis();
919 ze_xmlwriter_object *ze_obj = NULL;
920
921 if (zend_parse_parameters_none() == FAILURE) {
922 RETURN_THROWS();
923 }
924
925 if (self) {
926 /* We do not use XMLWRITER_FROM_OBJECT, xmlwriter init function here */
927 ze_obj = Z_XMLWRITER_P(self);
928 }
929
930 smart_str *output;
931 xmlTextWriterPtr ptr = xml_writer_create_in_memory(&output);
932 if (! ptr) {
933 RETURN_FALSE;
934 }
935
936 if (self) {
937 xmlwriter_destroy_libxml_objects(ze_obj);
938 ze_obj->ptr = ptr;
939 ze_obj->output = output;
940 RETURN_TRUE;
941 } else {
942 ze_obj = php_xmlwriter_fetch_object(xmlwriter_object_new(xmlwriter_class_entry_ce));
943 ze_obj->ptr = ptr;
944 ze_obj->output = output;
945 RETURN_OBJ(&ze_obj->std);
946 }
947
948 }
949 /* }}} */
950
PHP_METHOD(XMLWriter,toMemory)951 PHP_METHOD(XMLWriter, toMemory)
952 {
953 ZEND_PARSE_PARAMETERS_NONE();
954
955 smart_str *output;
956 xmlTextWriterPtr writer = xml_writer_create_in_memory(&output);
957
958 /* No need for an explicit buffer check as this will fail on a NULL buffer. */
959 if (!writer) {
960 zend_throw_error(NULL, "Could not construct libxml writer");
961 RETURN_THROWS();
962 }
963
964 xml_writer_create_static(INTERNAL_FUNCTION_PARAM_PASSTHRU, writer, output);
965 }
966
xml_writer_stream_write(void * context,const char * buffer,int len)967 static int xml_writer_stream_write(void *context, const char *buffer, int len)
968 {
969 zend_resource *resource = context;
970 if (EXPECTED(resource->ptr)) {
971 php_stream *stream = resource->ptr;
972 return php_stream_write(stream, buffer, len);
973 }
974 return -1;
975 }
976
xml_writer_stream_close(void * context)977 static int xml_writer_stream_close(void *context)
978 {
979 zend_resource *resource = context;
980 /* Don't close it as others may still use it! We don't own the resource!
981 * Just delete our reference (and clean up if we're the last one). */
982 zend_list_delete(resource);
983 return 0;
984 }
985
PHP_METHOD(XMLWriter,toStream)986 PHP_METHOD(XMLWriter, toStream)
987 {
988 zval *stream_zv;
989 php_stream *stream;
990
991 ZEND_PARSE_PARAMETERS_START(1, 1)
992 Z_PARAM_RESOURCE(stream_zv)
993 ZEND_PARSE_PARAMETERS_END();
994
995 php_stream_from_res(stream, Z_RES_P(stream_zv));
996
997 xmlOutputBufferPtr output_buffer = xmlOutputBufferCreateIO(xml_writer_stream_write, xml_writer_stream_close, stream->res, NULL);
998 if (UNEXPECTED(output_buffer == NULL)) {
999 zend_throw_error(NULL, "Could not construct libxml output buffer");
1000 RETURN_THROWS();
1001 }
1002
1003 /* When the buffer is closed (even in error paths) the reference is destroyed. */
1004 Z_ADDREF_P(stream_zv);
1005
1006 xmlTextWriterPtr writer = xmlNewTextWriter(output_buffer);
1007 if (UNEXPECTED(writer == NULL)) {
1008 xmlOutputBufferClose(output_buffer);
1009 zend_throw_error(NULL, "Could not construct libxml writer");
1010 RETURN_THROWS();
1011 }
1012
1013 /* output_buffer is owned by writer, and so writer will clean that up for us. */
1014 xml_writer_create_static(INTERNAL_FUNCTION_PARAM_PASSTHRU, writer, NULL);
1015 }
1016
1017 /* {{{ php_xmlwriter_flush */
php_xmlwriter_flush(INTERNAL_FUNCTION_PARAMETERS,int force_string)1018 static void php_xmlwriter_flush(INTERNAL_FUNCTION_PARAMETERS, int force_string) {
1019 xmlTextWriterPtr ptr;
1020 bool empty = 1;
1021 int output_bytes;
1022 zval *self;
1023
1024 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|b", &self, xmlwriter_class_entry_ce, &empty) == FAILURE) {
1025 RETURN_THROWS();
1026 }
1027 XMLWRITER_FROM_OBJECT(ptr, self);
1028
1029 smart_str *output = Z_XMLWRITER_P(self)->output;
1030 if (force_string == 1 && output == NULL) {
1031 RETURN_EMPTY_STRING();
1032 }
1033 output_bytes = xmlTextWriterFlush(ptr);
1034 if (output) {
1035 if (empty) {
1036 RETURN_STR(smart_str_extract(output));
1037 } else if (smart_str_get_len(output) > 0) {
1038 RETURN_NEW_STR(zend_string_dup(output->s, false));
1039 } else {
1040 RETURN_EMPTY_STRING();
1041 }
1042 } else {
1043 RETVAL_LONG(output_bytes);
1044 }
1045 }
1046 /* }}} */
1047
1048 /* {{{ Output current buffer as string */
PHP_FUNCTION(xmlwriter_output_memory)1049 PHP_FUNCTION(xmlwriter_output_memory)
1050 {
1051 php_xmlwriter_flush(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1052 }
1053 /* }}} */
1054
1055 /* {{{ Output current buffer */
PHP_FUNCTION(xmlwriter_flush)1056 PHP_FUNCTION(xmlwriter_flush)
1057 {
1058 php_xmlwriter_flush(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1059 }
1060 /* }}} */
1061
1062 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(xmlwriter)1063 static PHP_MINIT_FUNCTION(xmlwriter)
1064 {
1065 memcpy(&xmlwriter_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1066 xmlwriter_object_handlers.offset = XtOffsetOf(ze_xmlwriter_object, std);
1067 xmlwriter_object_handlers.dtor_obj = xmlwriter_object_dtor;
1068 xmlwriter_object_handlers.free_obj = xmlwriter_object_free_storage;
1069 xmlwriter_object_handlers.clone_obj = NULL;
1070 xmlwriter_class_entry_ce = register_class_XMLWriter();
1071 xmlwriter_class_entry_ce->create_object = xmlwriter_object_new;
1072 xmlwriter_class_entry_ce->default_object_handlers = &xmlwriter_object_handlers;
1073
1074 return SUCCESS;
1075 }
1076 /* }}} */
1077
1078 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(xmlwriter)1079 static PHP_MSHUTDOWN_FUNCTION(xmlwriter)
1080 {
1081 return SUCCESS;
1082 }
1083 /* }}} */
1084
1085 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(xmlwriter)1086 static PHP_MINFO_FUNCTION(xmlwriter)
1087 {
1088 php_info_print_table_start();
1089 {
1090 php_info_print_table_row(2, "XMLWriter", "enabled");
1091 }
1092 php_info_print_table_end();
1093 }
1094 /* }}} */
1095