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