xref: /php-src/ext/xmlwriter/php_xmlwriter.c (revision f5e81fe1)
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