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