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