xref: /PHP-5.5/main/output.c (revision 225af964)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2015 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Zeev Suraski <zeev@zend.com>                                |
16    |          Thies C. Arntzen <thies@thieso.net>                         |
17    |          Marcus Boerger <helly@php.net>                              |
18    | New API: Michael Wallner <mike@php.net>                              |
19    +----------------------------------------------------------------------+
20 */
21 
22 /* $Id$ */
23 
24 #ifndef PHP_OUTPUT_DEBUG
25 # define PHP_OUTPUT_DEBUG 0
26 #endif
27 #ifndef PHP_OUTPUT_NOINLINE
28 # define PHP_OUTPUT_NOINLINE 0
29 #endif
30 
31 #include "php.h"
32 #include "ext/standard/head.h"
33 #include "ext/standard/url_scanner_ex.h"
34 #include "SAPI.h"
35 #include "zend_stack.h"
36 #include "php_output.h"
37 
38 ZEND_DECLARE_MODULE_GLOBALS(output);
39 
40 const char php_output_default_handler_name[sizeof("default output handler")] = "default output handler";
41 const char php_output_devnull_handler_name[sizeof("null output handler")] = "null output handler";
42 
43 #if PHP_OUTPUT_NOINLINE || PHP_OUTPUT_DEBUG
44 # undef inline
45 # define inline
46 #endif
47 
48 /* {{{ aliases, conflict and reverse conflict hash tables */
49 static HashTable php_output_handler_aliases;
50 static HashTable php_output_handler_conflicts;
51 static HashTable php_output_handler_reverse_conflicts;
52 /* }}} */
53 
54 /* {{{ forward declarations */
55 static inline int php_output_lock_error(int op TSRMLS_DC);
56 static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC);
57 
58 static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC);
59 static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context);
60 static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC);
61 static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry);
62 
63 static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC);
64 static inline void php_output_context_reset(php_output_context *context);
65 static inline void php_output_context_swap(php_output_context *context);
66 static inline void php_output_context_dtor(php_output_context *context);
67 
68 static inline int php_output_stack_pop(int flags TSRMLS_DC);
69 
70 static int php_output_stack_apply_op(void *h, void *c);
71 static int php_output_stack_apply_clean(void *h, void *c);
72 static int php_output_stack_apply_list(void *h, void *z);
73 static int php_output_stack_apply_status(void *h, void *z);
74 
75 static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context);
76 static int php_output_handler_default_func(void **handler_context, php_output_context *output_context);
77 static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context);
78 /* }}} */
79 
80 /* {{{ static void php_output_init_globals(zend_output_globals *G)
81  * Initialize the module globals on MINIT */
php_output_init_globals(zend_output_globals * G)82 static inline void php_output_init_globals(zend_output_globals *G)
83 {
84 	memset(G, 0, sizeof(*G));
85 }
86 /* }}} */
87 
88 /* {{{ stderr/stdout writer if not PHP_OUTPUT_ACTIVATED */
php_output_stdout(const char * str,size_t str_len)89 static int php_output_stdout(const char *str, size_t str_len)
90 {
91 	fwrite(str, 1, str_len, stdout);
92 	return str_len;
93 }
php_output_stderr(const char * str,size_t str_len)94 static int php_output_stderr(const char *str, size_t str_len)
95 {
96 	fwrite(str, 1, str_len, stderr);
97 /* See http://support.microsoft.com/kb/190351 */
98 #ifdef PHP_WIN32
99 	fflush(stderr);
100 #endif
101 	return str_len;
102 }
103 static int (*php_output_direct)(const char *str, size_t str_len) = php_output_stderr;
104 /* }}} */
105 
106 /* {{{ void php_output_header(TSRMLS_D) */
php_output_header(TSRMLS_D)107 static void php_output_header(TSRMLS_D)
108 {
109 	if (!SG(headers_sent)) {
110 		if (!OG(output_start_filename)) {
111 			if (zend_is_compiling(TSRMLS_C)) {
112 				OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C);
113 				OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C);
114 			} else if (zend_is_executing(TSRMLS_C)) {
115 				OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C);
116 				OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C);
117 			}
118 #if PHP_OUTPUT_DEBUG
119 			fprintf(stderr, "!!! output started at: %s (%d)\n", OG(output_start_filename), OG(output_start_lineno));
120 #endif
121 		}
122 		if (!php_header(TSRMLS_C)) {
123 			OG(flags) |= PHP_OUTPUT_DISABLED;
124 		}
125 	}
126 }
127 /* }}} */
128 
129 /* {{{ void php_output_startup(void)
130  * Set up module globals and initalize the conflict and reverse conflict hash tables */
php_output_startup(void)131 PHPAPI void php_output_startup(void)
132 {
133 	ZEND_INIT_MODULE_GLOBALS(output, php_output_init_globals, NULL);
134 	zend_hash_init(&php_output_handler_aliases, 0, NULL, NULL, 1);
135 	zend_hash_init(&php_output_handler_conflicts, 0, NULL, NULL, 1);
136 	zend_hash_init(&php_output_handler_reverse_conflicts, 0, NULL, (void (*)(void *)) zend_hash_destroy, 1);
137 	php_output_direct = php_output_stdout;
138 }
139 /* }}} */
140 
141 /* {{{ void php_output_shutdown(void)
142  * Destroy module globals and the conflict and reverse conflict hash tables */
php_output_shutdown(void)143 PHPAPI void php_output_shutdown(void)
144 {
145 	php_output_direct = php_output_stderr;
146 	zend_hash_destroy(&php_output_handler_aliases);
147 	zend_hash_destroy(&php_output_handler_conflicts);
148 	zend_hash_destroy(&php_output_handler_reverse_conflicts);
149 }
150 /* }}} */
151 
152 /* {{{ SUCCESS|FAILURE php_output_activate(TSRMLS_D)
153  * Reset output globals and setup the output handler stack */
php_output_activate(TSRMLS_D)154 PHPAPI int php_output_activate(TSRMLS_D)
155 {
156 #ifdef ZTS
157 	memset((*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)], 0, sizeof(zend_output_globals));
158 #else
159 	memset(&output_globals, 0, sizeof(zend_output_globals));
160 #endif
161 
162 	zend_stack_init(&OG(handlers));
163 	OG(flags) |= PHP_OUTPUT_ACTIVATED;
164 
165 	return SUCCESS;
166 }
167 /* }}} */
168 
169 /* {{{ void php_output_deactivate(TSRMLS_D)
170  * Destroy the output handler stack */
php_output_deactivate(TSRMLS_D)171 PHPAPI void php_output_deactivate(TSRMLS_D)
172 {
173 	php_output_handler **handler = NULL;
174 
175 	if ((OG(flags) & PHP_OUTPUT_ACTIVATED)) {
176 		php_output_header(TSRMLS_C);
177 
178 		OG(flags) ^= PHP_OUTPUT_ACTIVATED;
179 		OG(active) = NULL;
180 		OG(running) = NULL;
181 
182 		/* release all output handlers */
183 		if (OG(handlers).elements) {
184 			while (SUCCESS == zend_stack_top(&OG(handlers), (void *) &handler)) {
185 				php_output_handler_free(handler TSRMLS_CC);
186 				zend_stack_del_top(&OG(handlers));
187 			}
188 			zend_stack_destroy(&OG(handlers));
189 		}
190 	}
191 }
192 /* }}} */
193 
194 /* {{{ void php_output_register_constants() */
php_output_register_constants(TSRMLS_D)195 PHPAPI void php_output_register_constants(TSRMLS_D)
196 {
197 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_CS | CONST_PERSISTENT);
198 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_WRITE", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT);
199 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSH", PHP_OUTPUT_HANDLER_FLUSH, CONST_CS | CONST_PERSISTENT);
200 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEAN", PHP_OUTPUT_HANDLER_CLEAN, CONST_CS | CONST_PERSISTENT);
201 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FINAL", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT);
202 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT);
203 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT);
204 
205 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEANABLE", PHP_OUTPUT_HANDLER_CLEANABLE, CONST_CS | CONST_PERSISTENT);
206 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSHABLE", PHP_OUTPUT_HANDLER_FLUSHABLE, CONST_CS | CONST_PERSISTENT);
207 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_REMOVABLE", PHP_OUTPUT_HANDLER_REMOVABLE, CONST_CS | CONST_PERSISTENT);
208 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STDFLAGS", PHP_OUTPUT_HANDLER_STDFLAGS, CONST_CS | CONST_PERSISTENT);
209 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STARTED", PHP_OUTPUT_HANDLER_STARTED, CONST_CS | CONST_PERSISTENT);
210 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_DISABLED", PHP_OUTPUT_HANDLER_DISABLED, CONST_CS | CONST_PERSISTENT);
211 }
212 /* }}} */
213 
214 /* {{{ void php_output_set_status(int status TSRMLS_DC)
215  * Used by SAPIs to disable output */
php_output_set_status(int status TSRMLS_DC)216 PHPAPI void php_output_set_status(int status TSRMLS_DC)
217 {
218 	OG(flags) = (OG(flags) & ~0xf) | (status & 0xf);
219 }
220 /* }}} */
221 
222 /* {{{ int php_output_get_status(TSRMLS_C)
223  * Get output control status */
php_output_get_status(TSRMLS_D)224 PHPAPI int php_output_get_status(TSRMLS_D)
225 {
226 	return (
227 		OG(flags)
228 		|	(OG(active) ? PHP_OUTPUT_ACTIVE : 0)
229 		|	(OG(running)? PHP_OUTPUT_LOCKED : 0)
230 	) & 0xff;
231 }
232 /* }}} */
233 
234 /* {{{ int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC)
235  * Unbuffered write */
php_output_write_unbuffered(const char * str,size_t len TSRMLS_DC)236 PHPAPI int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC)
237 {
238 #if PHP_DEBUG
239 	if (len > UINT_MAX) {
240 		php_error(E_WARNING, "Attempt to output more than UINT_MAX bytes at once; "
241 				"output will be truncated %lu => %lu",
242 				(unsigned long) len, (unsigned long) (len % UINT_MAX));
243 	}
244 #endif
245 	if (OG(flags) & PHP_OUTPUT_DISABLED) {
246 		return 0;
247 	}
248 	if (OG(flags) & PHP_OUTPUT_ACTIVATED) {
249 		return sapi_module.ub_write(str, len TSRMLS_CC);
250 	}
251 	return php_output_direct(str, len);
252 }
253 /* }}} */
254 
255 /* {{{ int php_output_write(const char *str, size_t len TSRMLS_DC)
256  * Buffered write */
php_output_write(const char * str,size_t len TSRMLS_DC)257 PHPAPI int php_output_write(const char *str, size_t len TSRMLS_DC)
258 {
259 #if PHP_DEBUG
260 	if (len > UINT_MAX) {
261 		php_error(E_WARNING, "Attempt to output more than UINT_MAX bytes at once; "
262 				"output will be truncated %lu => %lu",
263 				(unsigned long) len, (unsigned long) (len % UINT_MAX));
264 	}
265 #endif
266 	if (OG(flags) & PHP_OUTPUT_DISABLED) {
267 		return 0;
268 	}
269 	if (OG(flags) & PHP_OUTPUT_ACTIVATED) {
270 		php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len TSRMLS_CC);
271 		return (int) len;
272 	}
273 	return php_output_direct(str, len);
274 }
275 /* }}} */
276 
277 /* {{{ SUCCESS|FAILURE php_output_flush(TSRMLS_D)
278  * Flush the most recent output handlers buffer */
php_output_flush(TSRMLS_D)279 PHPAPI int php_output_flush(TSRMLS_D)
280 {
281 	php_output_context context;
282 
283 	if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_FLUSHABLE)) {
284 		php_output_context_init(&context, PHP_OUTPUT_HANDLER_FLUSH TSRMLS_CC);
285 		php_output_handler_op(OG(active), &context);
286 		if (context.out.data && context.out.used) {
287 			zend_stack_del_top(&OG(handlers));
288 			php_output_write(context.out.data, context.out.used TSRMLS_CC);
289 			zend_stack_push(&OG(handlers), &OG(active), sizeof(php_output_handler *));
290 		}
291 		php_output_context_dtor(&context);
292 		return SUCCESS;
293 	}
294 	return FAILURE;
295 }
296 /* }}} */
297 
298 /* {{{ void php_output_flush_all(TSRMLS_C)
299  * Flush all output buffers subsequently */
php_output_flush_all(TSRMLS_D)300 PHPAPI void php_output_flush_all(TSRMLS_D)
301 {
302 	if (OG(active)) {
303 		php_output_op(PHP_OUTPUT_HANDLER_FLUSH, NULL, 0 TSRMLS_CC);
304 	}
305 }
306 /* }}} */
307 
308 /* {{{ SUCCESS|FAILURE php_output_clean(TSRMLS_D)
309  * Cleans the most recent output handlers buffer if the handler is cleanable */
php_output_clean(TSRMLS_D)310 PHPAPI int php_output_clean(TSRMLS_D)
311 {
312 	php_output_context context;
313 
314 	if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_CLEANABLE)) {
315 		php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC);
316 		php_output_handler_op(OG(active), &context);
317 		php_output_context_dtor(&context);
318 		return SUCCESS;
319 	}
320 	return FAILURE;
321 }
322 /* }}} */
323 
324 /* {{{ void php_output_clean_all(TSRMLS_D)
325  * Cleans all output handler buffers, without regard whether the handler is cleanable */
php_output_clean_all(TSRMLS_D)326 PHPAPI void php_output_clean_all(TSRMLS_D)
327 {
328 	php_output_context context;
329 
330 	if (OG(active)) {
331 		php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC);
332 		zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_clean, &context);
333 	}
334 }
335 
336 /* {{{ SUCCESS|FAILURE php_output_end(TSRMLS_D)
337  * Finalizes the most recent output handler at pops it off the stack if the handler is removable */
php_output_end(TSRMLS_D)338 PHPAPI int php_output_end(TSRMLS_D)
339 {
340 	if (php_output_stack_pop(PHP_OUTPUT_POP_TRY TSRMLS_CC)) {
341 		return SUCCESS;
342 	}
343 	return FAILURE;
344 }
345 /* }}} */
346 
347 /* {{{ void php_output_end_all(TSRMLS_D)
348  * Finalizes all output handlers and ends output buffering without regard whether a handler is removable */
php_output_end_all(TSRMLS_D)349 PHPAPI void php_output_end_all(TSRMLS_D)
350 {
351 	while (OG(active) && php_output_stack_pop(PHP_OUTPUT_POP_FORCE TSRMLS_CC));
352 }
353 /* }}} */
354 
355 /* {{{ SUCCESS|FAILURE php_output_discard(TSRMLS_D)
356  * Discards the most recent output handlers buffer and pops it off the stack if the handler is removable */
php_output_discard(TSRMLS_D)357 PHPAPI int php_output_discard(TSRMLS_D)
358 {
359 	if (php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_TRY TSRMLS_CC)) {
360 		return SUCCESS;
361 	}
362 	return FAILURE;
363 }
364 /* }}} */
365 
366 /* {{{ void php_output_discard_all(TSRMLS_D)
367  * Discard all output handlers and buffers without regard whether a handler is removable */
php_output_discard_all(TSRMLS_D)368 PHPAPI void php_output_discard_all(TSRMLS_D)
369 {
370 	while (OG(active)) {
371 		php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_FORCE TSRMLS_CC);
372 	}
373 }
374 /* }}} */
375 
376 /* {{{ int php_output_get_level(TSRMLS_D)
377  * Get output buffering level, ie. how many output handlers the stack contains */
php_output_get_level(TSRMLS_D)378 PHPAPI int php_output_get_level(TSRMLS_D)
379 {
380 	return OG(active) ? zend_stack_count(&OG(handlers)) : 0;
381 }
382 /* }}} */
383 
384 /* {{{ SUCCESS|FAILURE php_output_get_contents(zval *z TSRMLS_DC)
385  * Get the contents of the active output handlers buffer */
php_output_get_contents(zval * p TSRMLS_DC)386 PHPAPI int php_output_get_contents(zval *p TSRMLS_DC)
387 {
388 	if (OG(active)) {
389 		ZVAL_STRINGL(p, OG(active)->buffer.data, OG(active)->buffer.used, 1);
390 		return SUCCESS;
391 	} else {
392 		ZVAL_NULL(p);
393 		return FAILURE;
394 	}
395 }
396 
397 /* {{{ SUCCESS|FAILURE php_output_get_length(zval *z TSRMLS_DC)
398  * Get the length of the active output handlers buffer */
php_output_get_length(zval * p TSRMLS_DC)399 PHPAPI int php_output_get_length(zval *p TSRMLS_DC)
400 {
401 	if (OG(active)) {
402 		ZVAL_LONG(p, OG(active)->buffer.used);
403 		return SUCCESS;
404 	} else {
405 		ZVAL_NULL(p);
406 		return FAILURE;
407 	}
408 }
409 /* }}} */
410 
411 /* {{{ php_output_handler* php_output_get_active_handler(TSRMLS_D)
412  * Get active output handler */
php_output_get_active_handler(TSRMLS_D)413 PHPAPI php_output_handler* php_output_get_active_handler(TSRMLS_D)
414 {
415 	return OG(active);
416 }
417 /* }}} */
418 
419 /* {{{ SUCCESS|FAILURE php_output_handler_start_default(TSRMLS_D)
420  * Start a "default output handler" */
php_output_start_default(TSRMLS_D)421 PHPAPI int php_output_start_default(TSRMLS_D)
422 {
423 	php_output_handler *handler;
424 
425 	handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, 0, PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC);
426 	if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
427 		return SUCCESS;
428 	}
429 	php_output_handler_free(&handler TSRMLS_CC);
430 	return FAILURE;
431 }
432 /* }}} */
433 
434 /* {{{ SUCCESS|FAILURE php_output_handler_start_devnull(TSRMLS_D)
435  * Start a "null output handler" */
php_output_start_devnull(TSRMLS_D)436 PHPAPI int php_output_start_devnull(TSRMLS_D)
437 {
438 	php_output_handler *handler;
439 
440 	handler = php_output_handler_create_internal(ZEND_STRL(php_output_devnull_handler_name), php_output_handler_devnull_func, PHP_OUTPUT_HANDLER_DEFAULT_SIZE, 0 TSRMLS_CC);
441 	if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
442 		return SUCCESS;
443 	}
444 	php_output_handler_free(&handler TSRMLS_CC);
445 	return FAILURE;
446 }
447 /* }}} */
448 
449 /* {{{ SUCCESS|FAILURE php_output_start_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC)
450  * Start a user level output handler */
php_output_start_user(zval * output_handler,size_t chunk_size,int flags TSRMLS_DC)451 PHPAPI int php_output_start_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC)
452 {
453 	php_output_handler *handler;
454 
455 	if (output_handler) {
456 		handler = php_output_handler_create_user(output_handler, chunk_size, flags TSRMLS_CC);
457 	} else {
458 		handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC);
459 	}
460 	if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
461 		return SUCCESS;
462 	}
463 	php_output_handler_free(&handler TSRMLS_CC);
464 	return FAILURE;
465 }
466 /* }}} */
467 
468 /* {{{ SUCCESS|FAILURE php_output_start_internal(zval *name, php_output_handler_func_t handler, size_t chunk_size, int flags TSRMLS_DC)
469  * Start an internal output handler that does not have to maintain a non-global state */
php_output_start_internal(const char * name,size_t name_len,php_output_handler_func_t output_handler,size_t chunk_size,int flags TSRMLS_DC)470 PHPAPI int php_output_start_internal(const char *name, size_t name_len, php_output_handler_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC)
471 {
472 	php_output_handler *handler;
473 
474 	handler = php_output_handler_create_internal(name, name_len, php_output_handler_compat_func, chunk_size, flags TSRMLS_CC);
475 	php_output_handler_set_context(handler, output_handler, NULL TSRMLS_CC);
476 	if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
477 		return SUCCESS;
478 	}
479 	php_output_handler_free(&handler TSRMLS_CC);
480 	return FAILURE;
481 }
482 /* }}} */
483 
484 /* {{{ php_output_handler *php_output_handler_create_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC)
485  * Create a user level output handler */
php_output_handler_create_user(zval * output_handler,size_t chunk_size,int flags TSRMLS_DC)486 PHPAPI php_output_handler *php_output_handler_create_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC)
487 {
488 	char *handler_name = NULL, *error = NULL;
489 	php_output_handler *handler = NULL;
490 	php_output_handler_alias_ctor_t *alias = NULL;
491 	php_output_handler_user_func_t *user = NULL;
492 
493 	switch (Z_TYPE_P(output_handler)) {
494 		case IS_NULL:
495 			handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC);
496 			break;
497 		case IS_STRING:
498 			if (Z_STRLEN_P(output_handler) && (alias = php_output_handler_alias(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler) TSRMLS_CC))) {
499 				handler = (*alias)(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler), chunk_size, flags TSRMLS_CC);
500 				break;
501 			}
502 		default:
503 			user = ecalloc(1, sizeof(php_output_handler_user_func_t));
504 			if (SUCCESS == zend_fcall_info_init(output_handler, 0, &user->fci, &user->fcc, &handler_name, &error TSRMLS_CC)) {
505 				handler = php_output_handler_init(handler_name, strlen(handler_name), chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_USER TSRMLS_CC);
506 				Z_ADDREF_P(output_handler);
507 				user->zoh = output_handler;
508 				handler->func.user = user;
509 			} else {
510 				efree(user);
511 			}
512 			if (error) {
513 				php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "%s", error);
514 				efree(error);
515 			}
516 			if (handler_name) {
517 				efree(handler_name);
518 			}
519 	}
520 
521 	return handler;
522 }
523 /* }}} */
524 
525 /* {{{ php_output_handler *php_output_handler_create_internal(zval *name, php_output_handler_context_func_t handler, size_t chunk_size, int flags TSRMLS_DC)
526  * Create an internal output handler that can maintain a non-global state */
php_output_handler_create_internal(const char * name,size_t name_len,php_output_handler_context_func_t output_handler,size_t chunk_size,int flags TSRMLS_DC)527 PHPAPI php_output_handler *php_output_handler_create_internal(const char *name, size_t name_len, php_output_handler_context_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC)
528 {
529 	php_output_handler *handler;
530 
531 	handler = php_output_handler_init(name, name_len, chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_INTERNAL TSRMLS_CC);
532 	handler->func.internal = output_handler;
533 
534 	return handler;
535 }
536 /* }}} */
537 
538 /* {{{ void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC)
539  * Set the context/state of an output handler. Calls the dtor of the previous context if there is one */
php_output_handler_set_context(php_output_handler * handler,void * opaq,void (* dtor)(void * TSRMLS_DC)TSRMLS_DC)540 PHPAPI void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC)
541 {
542 	if (handler->dtor && handler->opaq) {
543 		handler->dtor(handler->opaq TSRMLS_CC);
544 	}
545 	handler->dtor = dtor;
546 	handler->opaq = opaq;
547 }
548 /* }}} */
549 
550 /* {{{ SUCCESS|FAILURE php_output_handler_start(php_output_handler *handler TSRMLS_DC)
551  * Starts the set up output handler and pushes it on top of the stack. Checks for any conflicts regarding the output handler to start */
php_output_handler_start(php_output_handler * handler TSRMLS_DC)552 PHPAPI int php_output_handler_start(php_output_handler *handler TSRMLS_DC)
553 {
554 	HashPosition pos;
555 	HashTable *rconflicts;
556 	php_output_handler_conflict_check_t *conflict;
557 
558 	if (php_output_lock_error(PHP_OUTPUT_HANDLER_START TSRMLS_CC) || !handler) {
559 		return FAILURE;
560 	}
561 	if (SUCCESS == zend_hash_find(&php_output_handler_conflicts, handler->name, handler->name_len+1, (void *) &conflict)) {
562 		if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) {
563 			return FAILURE;
564 		}
565 	}
566 	if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, handler->name, handler->name_len+1, (void *) &rconflicts)) {
567 		for (zend_hash_internal_pointer_reset_ex(rconflicts, &pos);
568 			zend_hash_get_current_data_ex(rconflicts, (void *) &conflict, &pos) == SUCCESS;
569 			zend_hash_move_forward_ex(rconflicts, &pos)
570 		) {
571 			if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) {
572 				return FAILURE;
573 			}
574 		}
575 	}
576 	/* zend_stack_push never returns SUCCESS but FAILURE or stack level */
577 	if (FAILURE == (handler->level = zend_stack_push(&OG(handlers), &handler, sizeof(php_output_handler *)))) {
578 		return FAILURE;
579 	}
580 	OG(active) = handler;
581 	return SUCCESS;
582 }
583 /* }}} */
584 
585 /* {{{ int php_output_handler_started(zval *name TSRMLS_DC)
586  * Check whether a certain output handler is in use */
php_output_handler_started(const char * name,size_t name_len TSRMLS_DC)587 PHPAPI int php_output_handler_started(const char *name, size_t name_len TSRMLS_DC)
588 {
589 	php_output_handler ***handlers;
590 	int i, count = php_output_get_level(TSRMLS_C);
591 
592 	if (count) {
593 		handlers = (php_output_handler ***) zend_stack_base(&OG(handlers));
594 
595 		for (i = 0; i < count; ++i) {
596 			if (name_len == (*(handlers[i]))->name_len && !memcmp((*(handlers[i]))->name, name, name_len)) {
597 				return 1;
598 			}
599 		}
600 	}
601 
602 	return 0;
603 }
604 /* }}} */
605 
606 /* {{{ int php_output_handler_conflict(zval *handler_new, zval *handler_old TSRMLS_DC)
607  * Check whether a certain handler is in use and issue a warning that the new handler would conflict with the already used one */
php_output_handler_conflict(const char * handler_new,size_t handler_new_len,const char * handler_set,size_t handler_set_len TSRMLS_DC)608 PHPAPI int php_output_handler_conflict(const char *handler_new, size_t handler_new_len, const char *handler_set, size_t handler_set_len TSRMLS_DC)
609 {
610 	if (php_output_handler_started(handler_set, handler_set_len TSRMLS_CC)) {
611 		if (handler_new_len != handler_set_len || memcmp(handler_new, handler_set, handler_set_len)) {
612 			php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set);
613 		} else {
614 			php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' cannot be used twice", handler_new);
615 		}
616 		return 1;
617 	}
618 	return 0;
619 }
620 /* }}} */
621 
622 /* {{{ SUCCESS|FAILURE php_output_handler_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC)
623  * Register a conflict checking function on MINIT */
php_output_handler_conflict_register(const char * name,size_t name_len,php_output_handler_conflict_check_t check_func TSRMLS_DC)624 PHPAPI int php_output_handler_conflict_register(const char *name, size_t name_len, php_output_handler_conflict_check_t check_func TSRMLS_DC)
625 {
626 	if (!EG(current_module)) {
627 		zend_error(E_ERROR, "Cannot register an output handler conflict outside of MINIT");
628 		return FAILURE;
629 	}
630 	return zend_hash_update(&php_output_handler_conflicts, name, name_len+1, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL);
631 }
632 /* }}} */
633 
634 /* {{{ SUCCESS|FAILURE php_output_handler_reverse_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC)
635  * Register a reverse conflict checking function on MINIT */
php_output_handler_reverse_conflict_register(const char * name,size_t name_len,php_output_handler_conflict_check_t check_func TSRMLS_DC)636 PHPAPI int php_output_handler_reverse_conflict_register(const char *name, size_t name_len, php_output_handler_conflict_check_t check_func TSRMLS_DC)
637 {
638 	HashTable rev, *rev_ptr = NULL;
639 
640 	if (!EG(current_module)) {
641 		zend_error(E_ERROR, "Cannot register a reverse output handler conflict outside of MINIT");
642 		return FAILURE;
643 	}
644 
645 	if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, name, name_len+1, (void *) &rev_ptr)) {
646 		return zend_hash_next_index_insert(rev_ptr, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL);
647 	} else {
648 		zend_hash_init(&rev, 1, NULL, NULL, 1);
649 		if (SUCCESS != zend_hash_next_index_insert(&rev, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL)) {
650 			zend_hash_destroy(&rev);
651 			return FAILURE;
652 		}
653 		if (SUCCESS != zend_hash_update(&php_output_handler_reverse_conflicts, name, name_len+1, &rev, sizeof(HashTable), NULL)) {
654 			zend_hash_destroy(&rev);
655 			return FAILURE;
656 		}
657 		return SUCCESS;
658 	}
659 }
660 /* }}} */
661 
662 /* {{{ php_output_handler_alias_ctor_t php_output_handler_alias(zval *name TSRMLS_DC)
663  * Get an internal output handler for a user handler if it exists */
php_output_handler_alias(const char * name,size_t name_len TSRMLS_DC)664 PHPAPI php_output_handler_alias_ctor_t *php_output_handler_alias(const char *name, size_t name_len TSRMLS_DC)
665 {
666 	php_output_handler_alias_ctor_t *func = NULL;
667 
668 	zend_hash_find(&php_output_handler_aliases, name, name_len+1, (void *) &func);
669 	return func;
670 }
671 /* }}} */
672 
673 /* {{{ SUCCESS|FAILURE php_output_handler_alias_register(zval *name, php_output_handler_alias_ctor_t func TSRMLS_DC)
674  * Registers an internal output handler as alias for a user handler */
php_output_handler_alias_register(const char * name,size_t name_len,php_output_handler_alias_ctor_t func TSRMLS_DC)675 PHPAPI int php_output_handler_alias_register(const char *name, size_t name_len, php_output_handler_alias_ctor_t func TSRMLS_DC)
676 {
677 	if (!EG(current_module)) {
678 		zend_error(E_ERROR, "Cannot register an output handler alias outside of MINIT");
679 		return FAILURE;
680 	}
681 	return zend_hash_update(&php_output_handler_aliases, name, name_len+1, &func, sizeof(php_output_handler_alias_ctor_t *), NULL);
682 }
683 /* }}} */
684 
685 /* {{{ SUCCESS|FAILURE php_output_handler_hook(php_output_handler_hook_t type, void *arg TSMRLS_DC)
686  * Output handler hook for output handler functions to check/modify the current handlers abilities */
php_output_handler_hook(php_output_handler_hook_t type,void * arg TSRMLS_DC)687 PHPAPI int php_output_handler_hook(php_output_handler_hook_t type, void *arg TSRMLS_DC)
688 {
689 	if (OG(running)) {
690 		switch (type) {
691 			case PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ:
692 				*(void ***) arg = &OG(running)->opaq;
693 				return SUCCESS;
694 			case PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS:
695 				*(int *) arg = OG(running)->flags;
696 				return SUCCESS;
697 			case PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL:
698 				*(int *) arg = OG(running)->level;
699                 return SUCCESS;
700 			case PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE:
701 				OG(running)->flags &= ~(PHP_OUTPUT_HANDLER_REMOVABLE|PHP_OUTPUT_HANDLER_CLEANABLE);
702 				return SUCCESS;
703 			case PHP_OUTPUT_HANDLER_HOOK_DISABLE:
704 				OG(running)->flags |= PHP_OUTPUT_HANDLER_DISABLED;
705 				return SUCCESS;
706 			default:
707 				break;
708 		}
709 	}
710 	return FAILURE;
711 }
712 /* }}} */
713 
714 /* {{{ void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC)
715  * Destroy an output handler */
php_output_handler_dtor(php_output_handler * handler TSRMLS_DC)716 PHPAPI void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC)
717 {
718 	STR_FREE(handler->name);
719 	STR_FREE(handler->buffer.data);
720 	if (handler->flags & PHP_OUTPUT_HANDLER_USER) {
721 		zval_ptr_dtor(&handler->func.user->zoh);
722 		efree(handler->func.user);
723 	}
724 	if (handler->dtor && handler->opaq) {
725 		handler->dtor(handler->opaq TSRMLS_CC);
726 	}
727 	memset(handler, 0, sizeof(*handler));
728 }
729 /* }}} */
730 
731 /* {{{ void php_output_handler_free(php_output_handler **handler TSMRLS_DC)
732  * Destroy and free an output handler */
php_output_handler_free(php_output_handler ** h TSRMLS_DC)733 PHPAPI void php_output_handler_free(php_output_handler **h TSRMLS_DC)
734 {
735 	if (*h) {
736 		php_output_handler_dtor(*h TSRMLS_CC);
737 		efree(*h);
738 		*h = NULL;
739 	}
740 }
741 /* }}} */
742 
743 /* void php_output_set_implicit_flush(int enabled TSRMLS_DC)
744  * Enable or disable implicit flush */
php_output_set_implicit_flush(int flush TSRMLS_DC)745 PHPAPI void php_output_set_implicit_flush(int flush TSRMLS_DC)
746 {
747 	if (flush) {
748 		OG(flags) |= PHP_OUTPUT_IMPLICITFLUSH;
749 	} else {
750 		OG(flags) &= ~PHP_OUTPUT_IMPLICITFLUSH;
751 	}
752 }
753 /* }}} */
754 
755 /* {{{ char *php_output_get_start_filename(TSRMLS_D)
756  * Get the file name where output has started */
php_output_get_start_filename(TSRMLS_D)757 PHPAPI const char *php_output_get_start_filename(TSRMLS_D)
758 {
759 	return OG(output_start_filename);
760 }
761 /* }}} */
762 
763 /* {{{ int php_output_get_start_lineno(TSRMLS_D)
764  * Get the line number where output has started */
php_output_get_start_lineno(TSRMLS_D)765 PHPAPI int php_output_get_start_lineno(TSRMLS_D)
766 {
767 	return OG(output_start_lineno);
768 }
769 /* }}} */
770 
771 /* {{{ static int php_output_lock_error(int op TSRMLS_DC)
772  * Checks whether an unallowed operation is attempted from within the output handler and issues a fatal error */
php_output_lock_error(int op TSRMLS_DC)773 static inline int php_output_lock_error(int op TSRMLS_DC)
774 {
775 	/* if there's no ob active, ob has been stopped */
776 	if (op && OG(active) && OG(running)) {
777 		/* fatal error */
778 		php_output_deactivate(TSRMLS_C);
779 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers");
780 		return 1;
781 	}
782 	return 0;
783 }
784 /* }}} */
785 
786 /* {{{ static php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC)
787  * Initialize a new output context */
php_output_context_init(php_output_context * context,int op TSRMLS_DC)788 static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC)
789 {
790 	if (!context) {
791 		context = emalloc(sizeof(php_output_context));
792 	}
793 
794 	memset(context, 0, sizeof(php_output_context));
795 	TSRMLS_SET_CTX(context->tsrm_ls);
796 	context->op = op;
797 
798 	return context;
799 }
800 /* }}} */
801 
802 /* {{{ static void php_output_context_reset(php_output_context *context)
803  * Reset an output context */
php_output_context_reset(php_output_context * context)804 static inline void php_output_context_reset(php_output_context *context)
805 {
806 	int op = context->op;
807 	php_output_context_dtor(context);
808 	memset(context, 0, sizeof(php_output_context));
809 	context->op = op;
810 }
811 /* }}} */
812 
813 /* {{{ static void php_output_context_feed(php_output_context *context, char *, size_t, size_t)
814  * Feed output contexts input buffer */
php_output_context_feed(php_output_context * context,char * data,size_t size,size_t used,zend_bool free)815 static inline void php_output_context_feed(php_output_context *context, char *data, size_t size, size_t used, zend_bool free)
816 {
817 	if (context->in.free && context->in.data) {
818 		efree(context->in.data);
819 	}
820 	context->in.data = data;
821 	context->in.used = used;
822 	context->in.free = free;
823 	context->in.size = size;
824 }
825 /* }}} */
826 
827 /* {{{ static void php_output_context_swap(php_output_context *context)
828  * Swap output contexts buffers */
php_output_context_swap(php_output_context * context)829 static inline void php_output_context_swap(php_output_context *context)
830 {
831 	if (context->in.free && context->in.data) {
832 		efree(context->in.data);
833 	}
834 	context->in.data = context->out.data;
835 	context->in.used = context->out.used;
836 	context->in.free = context->out.free;
837 	context->in.size = context->out.size;
838 	context->out.data = NULL;
839 	context->out.used = 0;
840 	context->out.free = 0;
841 	context->out.size = 0;
842 }
843 /* }}} */
844 
845 /* {{{ static void php_output_context_pass(php_output_context *context)
846  * Pass input to output buffer */
php_output_context_pass(php_output_context * context)847 static inline void php_output_context_pass(php_output_context *context)
848 {
849 	context->out.data = context->in.data;
850 	context->out.used = context->in.used;
851 	context->out.size = context->in.size;
852 	context->out.free = context->in.free;
853 	context->in.data = NULL;
854 	context->in.used = 0;
855 	context->in.free = 0;
856 	context->in.size = 0;
857 }
858 /* }}} */
859 
860 /* {{{ static void php_output_context_dtor(php_output_context *context)
861  * Destroy the contents of an output context */
php_output_context_dtor(php_output_context * context)862 static inline void php_output_context_dtor(php_output_context *context)
863 {
864 	if (context->in.free && context->in.data) {
865 		efree(context->in.data);
866 		context->in.data = NULL;
867 	}
868 	if (context->out.free && context->out.data) {
869 		efree(context->out.data);
870 		context->out.data = NULL;
871 	}
872 }
873 /* }}} */
874 
875 /* {{{ static php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags TSRMLS_DC)
876  * Allocates and initializes a php_output_handler structure */
php_output_handler_init(const char * name,size_t name_len,size_t chunk_size,int flags TSRMLS_DC)877 static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC)
878 {
879 	php_output_handler *handler;
880 
881 	handler = ecalloc(1, sizeof(php_output_handler));
882 	handler->name = estrndup(name, name_len);
883 	handler->name_len = name_len;
884 	handler->size = chunk_size;
885 	handler->flags = flags;
886 	handler->buffer.size = PHP_OUTPUT_HANDLER_INITBUF_SIZE(chunk_size);
887 	handler->buffer.data = emalloc(handler->buffer.size);
888 
889 	return handler;
890 }
891 /* }}} */
892 
893 /* {{{ static int php_output_handler_appen(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC)
894  * Appends input to the output handlers buffer and indicates whether the buffer does not have to be processed by the output handler */
php_output_handler_append(php_output_handler * handler,const php_output_buffer * buf TSRMLS_DC)895 static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC)
896 {
897 	if (buf->used) {
898 		OG(flags) |= PHP_OUTPUT_WRITTEN;
899 		/* store it away */
900 		if ((handler->buffer.size - handler->buffer.used) <= buf->used) {
901 			size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size);
902 			size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used));
903 			size_t grow_max = MAX(grow_int, grow_buf);
904 
905 			handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max);
906 			handler->buffer.size += grow_max;
907 		}
908 		memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used);
909 		handler->buffer.used += buf->used;
910 
911 		/* chunked buffering */
912 		if (handler->size && (handler->buffer.used >= handler->size)) {
913 			/* store away errors and/or any intermediate output */
914 			return OG(running) ? 1 : 0;
915 		}
916 	}
917 	return 1;
918 }
919 /* }}} */
920 
921 /* {{{ static php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context)
922  * Output handler operation dispatcher, applying context op to the php_output_handler handler */
php_output_handler_op(php_output_handler * handler,php_output_context * context)923 static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context)
924 {
925 	php_output_handler_status_t status;
926 	int original_op = context->op;
927 	PHP_OUTPUT_TSRMLS(context);
928 
929 #if PHP_OUTPUT_DEBUG
930 	fprintf(stderr, ">>> op(%d, "
931 					"handler=%p, "
932 					"name=%s, "
933 					"flags=%d, "
934 					"buffer.data=%s, "
935 					"buffer.used=%zu, "
936 					"buffer.size=%zu, "
937 					"in.data=%s, "
938 					"in.used=%zu)\n",
939 			context->op,
940 			handler,
941 			handler->name,
942 			handler->flags,
943 			handler->buffer.used?handler->buffer.data:"",
944 			handler->buffer.used,
945 			handler->buffer.size,
946 			context->in.used?context->in.data:"",
947 			context->in.used
948 	);
949 #endif
950 
951 	if (php_output_lock_error(context->op TSRMLS_CC)) {
952 		/* fatal error */
953 		return PHP_OUTPUT_HANDLER_FAILURE;
954 	}
955 
956 	/* storable? */
957 	if (php_output_handler_append(handler, &context->in TSRMLS_CC) && !context->op) {
958 		context->op = original_op;
959 		return PHP_OUTPUT_HANDLER_NO_DATA;
960 	} else {
961 		/* need to start? */
962 		if (!(handler->flags & PHP_OUTPUT_HANDLER_STARTED)) {
963 			context->op |= PHP_OUTPUT_HANDLER_START;
964 		}
965 
966 		OG(running) = handler;
967 		if (handler->flags & PHP_OUTPUT_HANDLER_USER) {
968 			zval *retval = NULL, *ob_data, *ob_mode;
969 
970 			MAKE_STD_ZVAL(ob_data);
971 			ZVAL_STRINGL(ob_data, handler->buffer.data, handler->buffer.used, 1);
972 			MAKE_STD_ZVAL(ob_mode);
973 			ZVAL_LONG(ob_mode, (long) context->op);
974 			zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 2, &ob_data, &ob_mode);
975 
976 #define PHP_OUTPUT_USER_SUCCESS(retval) (retval && !(Z_TYPE_P(retval) == IS_BOOL && Z_BVAL_P(retval)==0))
977 			if (SUCCESS == zend_fcall_info_call(&handler->func.user->fci, &handler->func.user->fcc, &retval, NULL TSRMLS_CC) && PHP_OUTPUT_USER_SUCCESS(retval)) {
978 				/* user handler may have returned TRUE */
979 				status = PHP_OUTPUT_HANDLER_NO_DATA;
980 				if (Z_TYPE_P(retval) != IS_BOOL) {
981 					convert_to_string_ex(&retval);
982 					if (Z_STRLEN_P(retval)) {
983 						context->out.data = estrndup(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
984 						context->out.used = Z_STRLEN_P(retval);
985 						context->out.free = 1;
986 						status = PHP_OUTPUT_HANDLER_SUCCESS;
987 					}
988 				}
989 			} else {
990 				/* call failed, pass internal buffer along */
991 				status = PHP_OUTPUT_HANDLER_FAILURE;
992 			}
993 
994 			zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 0);
995 			zval_ptr_dtor(&ob_data);
996 			zval_ptr_dtor(&ob_mode);
997 			if (retval) {
998 				zval_ptr_dtor(&retval);
999 			}
1000 
1001 		} else {
1002 
1003 			php_output_context_feed(context, handler->buffer.data, handler->buffer.size, handler->buffer.used, 0);
1004 
1005 			if (SUCCESS == handler->func.internal(&handler->opaq, context)) {
1006 				if (context->out.used) {
1007 					status = PHP_OUTPUT_HANDLER_SUCCESS;
1008 				} else {
1009 					status = PHP_OUTPUT_HANDLER_NO_DATA;
1010 				}
1011 			} else {
1012 				status = PHP_OUTPUT_HANDLER_FAILURE;
1013 			}
1014 		}
1015 		handler->flags |= PHP_OUTPUT_HANDLER_STARTED;
1016 		OG(running) = NULL;
1017 	}
1018 
1019 	switch (status) {
1020 		case PHP_OUTPUT_HANDLER_FAILURE:
1021 			/* disable this handler */
1022 			handler->flags |= PHP_OUTPUT_HANDLER_DISABLED;
1023 			/* discard any output */
1024 			if (context->out.data && context->out.free) {
1025 				efree(context->out.data);
1026 			}
1027 			/* returns handlers buffer */
1028 			context->out.data = handler->buffer.data;
1029 			context->out.used = handler->buffer.used;
1030 			context->out.free = 1;
1031 			handler->buffer.data = NULL;
1032 			handler->buffer.used = 0;
1033 			handler->buffer.size = 0;
1034 			break;
1035 		case PHP_OUTPUT_HANDLER_NO_DATA:
1036 			/* handler ate all */
1037 			php_output_context_reset(context);
1038 			/* no break */
1039 		case PHP_OUTPUT_HANDLER_SUCCESS:
1040 			/* no more buffered data */
1041 			handler->buffer.used = 0;
1042 			handler->flags |= PHP_OUTPUT_HANDLER_PROCESSED;
1043 			break;
1044 	}
1045 
1046 	context->op = original_op;
1047 	return status;
1048 }
1049 /* }}} */
1050 
1051 
1052 /* {{{ static void php_output_op(int op, const char *str, size_t len TSRMLS_DC)
1053  * Output op dispatcher, passes input and output handlers output through the output handler stack until it gets written to the SAPI */
php_output_op(int op,const char * str,size_t len TSRMLS_DC)1054 static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC)
1055 {
1056 	php_output_context context;
1057 	php_output_handler **active;
1058 	int obh_cnt;
1059 
1060 	if (php_output_lock_error(op TSRMLS_CC)) {
1061 		return;
1062 	}
1063 
1064 	php_output_context_init(&context, op TSRMLS_CC);
1065 
1066 	/*
1067 	 * broken up for better performance:
1068 	 *  - apply op to the one active handler; note that OG(active) might be popped off the stack on a flush
1069 	 *  - or apply op to the handler stack
1070 	 */
1071 	if (OG(active) && (obh_cnt = zend_stack_count(&OG(handlers)))) {
1072 		context.in.data = (char *) str;
1073 		context.in.used = len;
1074 
1075 		if (obh_cnt > 1) {
1076 			zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_op, &context);
1077 		} else if ((SUCCESS == zend_stack_top(&OG(handlers), (void *) &active)) && (!((*active)->flags & PHP_OUTPUT_HANDLER_DISABLED))) {
1078 			php_output_handler_op(*active, &context);
1079 		} else {
1080 			php_output_context_pass(&context);
1081 		}
1082 	} else {
1083 		context.out.data = (char *) str;
1084 		context.out.used = len;
1085 	}
1086 
1087 	if (context.out.data && context.out.used) {
1088 		php_output_header(TSRMLS_C);
1089 
1090 		if (!(OG(flags) & PHP_OUTPUT_DISABLED)) {
1091 #if PHP_OUTPUT_DEBUG
1092 			fprintf(stderr, "::: sapi_write('%s', %zu)\n", context.out.data, context.out.used);
1093 #endif
1094 			sapi_module.ub_write(context.out.data, context.out.used TSRMLS_CC);
1095 
1096 			if (OG(flags) & PHP_OUTPUT_IMPLICITFLUSH) {
1097 				sapi_flush(TSRMLS_C);
1098 			}
1099 
1100 			OG(flags) |= PHP_OUTPUT_SENT;
1101 		}
1102 	}
1103 	php_output_context_dtor(&context);
1104 }
1105 /* }}} */
1106 
1107 /* {{{ static int php_output_stack_apply_op(void *h, void *c)
1108  * Operation callback for the stack apply function */
php_output_stack_apply_op(void * h,void * c)1109 static int php_output_stack_apply_op(void *h, void *c)
1110 {
1111 	int was_disabled;
1112 	php_output_handler_status_t status;
1113 	php_output_handler *handler = *(php_output_handler **) h;
1114 	php_output_context *context = (php_output_context *) c;
1115 
1116 	if ((was_disabled = (handler->flags & PHP_OUTPUT_HANDLER_DISABLED))) {
1117 		status = PHP_OUTPUT_HANDLER_FAILURE;
1118 	} else {
1119 		status = php_output_handler_op(handler, context);
1120 	}
1121 
1122 	/*
1123 	 * handler ate all => break
1124 	 * handler returned data or failed resp. is disabled => continue
1125 	 */
1126 	switch (status) {
1127 		case PHP_OUTPUT_HANDLER_NO_DATA:
1128 			return 1;
1129 
1130 		case PHP_OUTPUT_HANDLER_SUCCESS:
1131 			/* swap contexts buffers, unless this is the last handler in the stack */
1132 			if (handler->level) {
1133 				php_output_context_swap(context);
1134 			}
1135 			return 0;
1136 
1137 		case PHP_OUTPUT_HANDLER_FAILURE:
1138 		default:
1139 			if (was_disabled) {
1140 				/* pass input along, if it's the last handler in the stack */
1141 				if (!handler->level) {
1142 					php_output_context_pass(context);
1143 				}
1144 			} else {
1145 				/* swap buffers, unless this is the last handler */
1146 				if (handler->level) {
1147 					php_output_context_swap(context);
1148 				}
1149 			}
1150 			return 0;
1151 	}
1152 }
1153 /* }}} */
1154 
1155 /* {{{ static int php_output_stack_apply_clean(void *h, void *c)
1156  * Clean callback for the stack apply function */
php_output_stack_apply_clean(void * h,void * c)1157 static int php_output_stack_apply_clean(void *h, void *c)
1158 {
1159 	php_output_handler *handler = *(php_output_handler **) h;
1160 	php_output_context *context = (php_output_context *) c;
1161 
1162 	handler->buffer.used = 0;
1163 	php_output_handler_op(handler, context);
1164 	php_output_context_reset(context);
1165 	return 0;
1166 }
1167 /* }}} */
1168 
1169 /* {{{ static int php_output_stack_apply_list(void *h, void *z)
1170  * List callback for the stack apply function */
php_output_stack_apply_list(void * h,void * z)1171 static int php_output_stack_apply_list(void *h, void *z)
1172 {
1173 	php_output_handler *handler = *(php_output_handler **) h;
1174 	zval *array = (zval *) z;
1175 
1176 	add_next_index_stringl(array, handler->name, handler->name_len, 1);
1177 	return 0;
1178 }
1179 /* }}} */
1180 
1181 /* {{{ static int php_output_stack_apply_status(void *h, void *z)
1182  * Status callback for the stack apply function */
php_output_stack_apply_status(void * h,void * z)1183 static int php_output_stack_apply_status(void *h, void *z)
1184 {
1185 	php_output_handler *handler = *(php_output_handler **) h;
1186 	zval *array = (zval *) z;
1187 
1188 	add_next_index_zval(array, php_output_handler_status(handler, NULL));
1189 
1190 	return 0;
1191 }
1192 
1193 /* {{{ static zval *php_output_handler_status(php_output_handler *handler, zval *entry)
1194  * Returns an array with the status of the output handler */
php_output_handler_status(php_output_handler * handler,zval * entry)1195 static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry)
1196 {
1197 	if (!entry) {
1198 		MAKE_STD_ZVAL(entry);
1199 		array_init(entry);
1200 	}
1201 
1202 	add_assoc_stringl(entry, "name", handler->name, handler->name_len, 1);
1203 	add_assoc_long(entry, "type", (long) (handler->flags & 0xf));
1204 	add_assoc_long(entry, "flags", (long) handler->flags);
1205 	add_assoc_long(entry, "level", (long) handler->level);
1206 	add_assoc_long(entry, "chunk_size", (long) handler->size);
1207 	add_assoc_long(entry, "buffer_size", (long) handler->buffer.size);
1208 	add_assoc_long(entry, "buffer_used", (long) handler->buffer.used);
1209 
1210 	return entry;
1211 }
1212 /* }}} */
1213 
1214 /* {{{ static int php_output_stack_pop(int flags TSRMLS_DC)
1215  * Pops an output handler off the stack */
php_output_stack_pop(int flags TSRMLS_DC)1216 static inline int php_output_stack_pop(int flags TSRMLS_DC)
1217 {
1218 	php_output_context context;
1219 	php_output_handler **current, *orphan = OG(active);
1220 
1221 	if (!orphan) {
1222 		if (!(flags & PHP_OUTPUT_POP_SILENT)) {
1223 			php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer. No buffer to %s", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send");
1224 		}
1225 		return 0;
1226 	} else if (!(flags & PHP_OUTPUT_POP_FORCE) && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) {
1227 		if (!(flags & PHP_OUTPUT_POP_SILENT)) {
1228 			php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer of %s (%d)", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", orphan->name, orphan->level);
1229 		}
1230 		return 0;
1231 	} else {
1232 		php_output_context_init(&context, PHP_OUTPUT_HANDLER_FINAL TSRMLS_CC);
1233 
1234 		/* don't run the output handler if it's disabled */
1235 		if (!(orphan->flags & PHP_OUTPUT_HANDLER_DISABLED)) {
1236 			/* didn't it start yet? */
1237 			if (!(orphan->flags & PHP_OUTPUT_HANDLER_STARTED)) {
1238 				context.op |= PHP_OUTPUT_HANDLER_START;
1239 			}
1240 			/* signal that we're cleaning up */
1241 			if (flags & PHP_OUTPUT_POP_DISCARD) {
1242 				context.op |= PHP_OUTPUT_HANDLER_CLEAN;
1243 			}
1244 			php_output_handler_op(orphan, &context);
1245 		}
1246 
1247 		/* pop it off the stack */
1248 		zend_stack_del_top(&OG(handlers));
1249 		if (SUCCESS == zend_stack_top(&OG(handlers), (void *) &current)) {
1250 			OG(active) = *current;
1251 		} else {
1252 			OG(active) = NULL;
1253 		}
1254 
1255 		/* pass output along */
1256 		if (context.out.data && context.out.used && !(flags & PHP_OUTPUT_POP_DISCARD)) {
1257 			php_output_write(context.out.data, context.out.used TSRMLS_CC);
1258 		}
1259 
1260 		/* destroy the handler (after write!) */
1261 		php_output_handler_free(&orphan TSRMLS_CC);
1262 		php_output_context_dtor(&context);
1263 
1264 		return 1;
1265 	}
1266 }
1267 /* }}} */
1268 
1269 /* {{{ static SUCCESS|FAILURE php_output_handler_compat_func(void *ctx, php_output_context *)
1270  * php_output_handler_context_func_t for php_output_handler_func_t output handlers */
php_output_handler_compat_func(void ** handler_context,php_output_context * output_context)1271 static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context)
1272 {
1273 	php_output_handler_func_t func = *(php_output_handler_func_t *) handler_context;
1274 	PHP_OUTPUT_TSRMLS(output_context);
1275 
1276 	if (func) {
1277 		char *out_str = NULL;
1278 		uint out_len = 0;
1279 
1280 		func(output_context->in.data, output_context->in.used, &out_str, &out_len, output_context->op TSRMLS_CC);
1281 
1282 		if (out_str) {
1283 			output_context->out.data = out_str;
1284 			output_context->out.used = out_len;
1285 			output_context->out.free = 1;
1286 		} else {
1287 			php_output_context_pass(output_context);
1288 		}
1289 
1290 		return SUCCESS;
1291 	}
1292 	return FAILURE;
1293 }
1294 /* }}} */
1295 
1296 /* {{{ static SUCCESS|FAILURE php_output_handler_default_func(void *ctx, php_output_context *)
1297  * Default output handler */
php_output_handler_default_func(void ** handler_context,php_output_context * output_context)1298 static int php_output_handler_default_func(void **handler_context, php_output_context *output_context)
1299 {
1300 	php_output_context_pass(output_context);
1301 	return SUCCESS;
1302 }
1303 /* }}} */
1304 
1305 /* {{{ static SUCCESS|FAILURE php_output_handler_devnull_func(void *ctx, php_output_context *)
1306  * Null output handler */
php_output_handler_devnull_func(void ** handler_context,php_output_context * output_context)1307 static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context)
1308 {
1309 	return SUCCESS;
1310 }
1311 /* }}} */
1312 
1313 /*
1314  * USERLAND (nearly 1:1 of old output.c)
1315  */
1316 
1317 /* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, int flags]]])
1318    Turn on Output Buffering (specifying an optional output handler). */
PHP_FUNCTION(ob_start)1319 PHP_FUNCTION(ob_start)
1320 {
1321 	zval *output_handler = NULL;
1322 	long chunk_size = 0;
1323 	long flags = PHP_OUTPUT_HANDLER_STDFLAGS;
1324 
1325 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/ll", &output_handler, &chunk_size, &flags) == FAILURE) {
1326 		return;
1327 	}
1328 
1329 	if (chunk_size < 0) {
1330 		chunk_size = 0;
1331 	}
1332 
1333 	if (php_output_start_user(output_handler, chunk_size, flags TSRMLS_CC) == FAILURE) {
1334 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to create buffer");
1335 		RETURN_FALSE;
1336 	}
1337 	RETURN_TRUE;
1338 }
1339 /* }}} */
1340 
1341 /* {{{ proto bool ob_flush(void)
1342    Flush (send) contents of the output buffer. The last buffer content is sent to next buffer */
PHP_FUNCTION(ob_flush)1343 PHP_FUNCTION(ob_flush)
1344 {
1345 	if (zend_parse_parameters_none() == FAILURE) {
1346 		return;
1347 	}
1348 
1349 	if (!OG(active)) {
1350 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush");
1351 		RETURN_FALSE;
1352 	}
1353 
1354 	if (SUCCESS != php_output_flush(TSRMLS_C)) {
1355 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer of %s (%d)", OG(active)->name, OG(active)->level);
1356 		RETURN_FALSE;
1357 	}
1358 	RETURN_TRUE;
1359 }
1360 /* }}} */
1361 
1362 /* {{{ proto bool ob_clean(void)
1363    Clean (delete) the current output buffer */
PHP_FUNCTION(ob_clean)1364 PHP_FUNCTION(ob_clean)
1365 {
1366 	if (zend_parse_parameters_none() == FAILURE) {
1367 		return;
1368 	}
1369 
1370 	if (!OG(active)) {
1371 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
1372 		RETURN_FALSE;
1373 	}
1374 
1375 	if (SUCCESS != php_output_clean(TSRMLS_C)) {
1376 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level);
1377 		RETURN_FALSE;
1378 	}
1379 	RETURN_TRUE;
1380 }
1381 /* }}} */
1382 
1383 /* {{{ proto bool ob_end_flush(void)
1384    Flush (send) the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_end_flush)1385 PHP_FUNCTION(ob_end_flush)
1386 {
1387 	if (zend_parse_parameters_none() == FAILURE) {
1388 		return;
1389 	}
1390 
1391 	if (!OG(active)) {
1392 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
1393 		RETURN_FALSE;
1394 	}
1395 
1396 	RETURN_BOOL(SUCCESS == php_output_end(TSRMLS_C));
1397 }
1398 /* }}} */
1399 
1400 /* {{{ proto bool ob_end_clean(void)
1401    Clean the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_end_clean)1402 PHP_FUNCTION(ob_end_clean)
1403 {
1404 	if (zend_parse_parameters_none() == FAILURE) {
1405 		return;
1406 	}
1407 
1408 	if (!OG(active)) {
1409 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
1410 		RETURN_FALSE;
1411 	}
1412 
1413 	RETURN_BOOL(SUCCESS == php_output_discard(TSRMLS_C));
1414 }
1415 /* }}} */
1416 
1417 /* {{{ proto bool ob_get_flush(void)
1418    Get current buffer contents, flush (send) the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_get_flush)1419 PHP_FUNCTION(ob_get_flush)
1420 {
1421 	if (zend_parse_parameters_none() == FAILURE) {
1422 		return;
1423 	}
1424 
1425 	if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) {
1426 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
1427 		RETURN_FALSE;
1428 	}
1429 
1430 	if (SUCCESS != php_output_end(TSRMLS_C)) {
1431 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level);
1432 	}
1433 }
1434 /* }}} */
1435 
1436 /* {{{ proto bool ob_get_clean(void)
1437    Get current buffer contents and delete current output buffer */
PHP_FUNCTION(ob_get_clean)1438 PHP_FUNCTION(ob_get_clean)
1439 {
1440 	if (zend_parse_parameters_none() == FAILURE) {
1441 		return;
1442 	}
1443 
1444 	if(!OG(active)) {
1445 		RETURN_FALSE;
1446 	}
1447 
1448 	if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) {
1449 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
1450 		RETURN_FALSE;
1451 	}
1452 
1453 	if (SUCCESS != php_output_discard(TSRMLS_C)) {
1454 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level);
1455 	}
1456 }
1457 /* }}} */
1458 
1459 /* {{{ proto string ob_get_contents(void)
1460    Return the contents of the output buffer */
PHP_FUNCTION(ob_get_contents)1461 PHP_FUNCTION(ob_get_contents)
1462 {
1463 	if (zend_parse_parameters_none() == FAILURE) {
1464 		return;
1465 	}
1466 
1467 	if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) {
1468 		RETURN_FALSE;
1469 	}
1470 }
1471 /* }}} */
1472 
1473 /* {{{ proto int ob_get_level(void)
1474    Return the nesting level of the output buffer */
PHP_FUNCTION(ob_get_level)1475 PHP_FUNCTION(ob_get_level)
1476 {
1477 	if (zend_parse_parameters_none() == FAILURE) {
1478 		return;
1479 	}
1480 
1481 	RETURN_LONG(php_output_get_level(TSRMLS_C));
1482 }
1483 /* }}} */
1484 
1485 /* {{{ proto int ob_get_length(void)
1486    Return the length of the output buffer */
PHP_FUNCTION(ob_get_length)1487 PHP_FUNCTION(ob_get_length)
1488 {
1489 	if (zend_parse_parameters_none() == FAILURE) {
1490 		return;
1491 	}
1492 
1493 	if (php_output_get_length(return_value TSRMLS_CC) == FAILURE) {
1494 		RETURN_FALSE;
1495 	}
1496 }
1497 /* }}} */
1498 
1499 /* {{{ proto false|array ob_list_handlers()
1500    List all output_buffers in an array */
PHP_FUNCTION(ob_list_handlers)1501 PHP_FUNCTION(ob_list_handlers)
1502 {
1503 	if (zend_parse_parameters_none() == FAILURE) {
1504 		return;
1505 	}
1506 
1507 	array_init(return_value);
1508 
1509 	if (!OG(active)) {
1510 		return;
1511 	}
1512 
1513 	zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_list, return_value);
1514 }
1515 /* }}} */
1516 
1517 /* {{{ proto false|array ob_get_status([bool full_status])
1518    Return the status of the active or all output buffers */
PHP_FUNCTION(ob_get_status)1519 PHP_FUNCTION(ob_get_status)
1520 {
1521 	zend_bool full_status = 0;
1522 
1523 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &full_status) == FAILURE) {
1524 		return;
1525 	}
1526 
1527 	array_init(return_value);
1528 
1529 	if (!OG(active)) {
1530 		return;
1531 	}
1532 
1533 	if (full_status) {
1534 		zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_status, return_value);
1535 	} else {
1536 		php_output_handler_status(OG(active), return_value);
1537 	}
1538 }
1539 /* }}} */
1540 
1541 /* {{{ proto void ob_implicit_flush([int flag])
1542    Turn implicit flush on/off and is equivalent to calling flush() after every output call */
PHP_FUNCTION(ob_implicit_flush)1543 PHP_FUNCTION(ob_implicit_flush)
1544 {
1545 	long flag = 1;
1546 
1547 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flag) == FAILURE) {
1548 		return;
1549 	}
1550 
1551 	php_output_set_implicit_flush(flag TSRMLS_CC);
1552 }
1553 /* }}} */
1554 
1555 /* {{{ proto bool output_reset_rewrite_vars(void)
1556    Reset(clear) URL rewriter values */
PHP_FUNCTION(output_reset_rewrite_vars)1557 PHP_FUNCTION(output_reset_rewrite_vars)
1558 {
1559 	if (php_url_scanner_reset_vars(TSRMLS_C) == SUCCESS) {
1560 		RETURN_TRUE;
1561 	} else {
1562 		RETURN_FALSE;
1563 	}
1564 }
1565 /* }}} */
1566 
1567 /* {{{ proto bool output_add_rewrite_var(string name, string value)
1568    Add URL rewriter values */
PHP_FUNCTION(output_add_rewrite_var)1569 PHP_FUNCTION(output_add_rewrite_var)
1570 {
1571 	char *name, *value;
1572 	int name_len, value_len;
1573 
1574 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &value, &value_len) == FAILURE) {
1575 		return;
1576 	}
1577 
1578 	if (php_url_scanner_add_var(name, name_len, value, value_len, 1 TSRMLS_CC) == SUCCESS) {
1579 		RETURN_TRUE;
1580 	} else {
1581 		RETURN_FALSE;
1582 	}
1583 }
1584 /* }}} */
1585 
1586 /*
1587  * Local variables:
1588  * tab-width: 4
1589  * c-basic-offset: 4
1590  * End:
1591  * vim600: sw=4 ts=4 fdm=marker
1592  * vim<600: sw=4 ts=4
1593  */
1594