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