/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 | 2 | { |
79 | 2 | memset(G, 0, sizeof(*G)); |
80 | 2 | } |
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 | 13.0M | { |
104 | 13.0M | if (!SG(headers_sent)) { |
105 | 5.28k | if (!OG(output_start_filename)) { |
106 | 5.28k | 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 | 5.28k | } else if (zend_is_executing()) { |
110 | 3.99k | OG(output_start_filename) = zend_get_executed_filename_ex(); |
111 | 3.99k | OG(output_start_lineno) = zend_get_executed_lineno(); |
112 | 3.99k | } |
113 | 5.28k | if (OG(output_start_filename)) { |
114 | 3.99k | zend_string_addref(OG(output_start_filename)); |
115 | 3.99k | } |
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 | 5.28k | } |
121 | 5.28k | if (!php_header()) { |
122 | 0 | OG(flags) |= PHP_OUTPUT_DISABLED; |
123 | 0 | } |
124 | 5.28k | } |
125 | 13.0M | } |
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 | 2 | { |
138 | 2 | ZEND_INIT_MODULE_GLOBALS(output, php_output_init_globals, NULL); |
139 | 2 | zend_hash_init(&php_output_handler_aliases, 8, NULL, NULL, 1); |
140 | 2 | zend_hash_init(&php_output_handler_conflicts, 8, NULL, NULL, 1); |
141 | 2 | zend_hash_init(&php_output_handler_reverse_conflicts, 8, NULL, reverse_conflict_dtor, 1); |
142 | 2 | php_output_direct = php_output_stdout; |
143 | 2 | } |
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 | 5.28k | { |
161 | | #ifdef ZTS |
162 | | memset(TSRMG_BULK_STATIC(output_globals_id, zend_output_globals*), 0, sizeof(zend_output_globals)); |
163 | | #else |
164 | 5.28k | memset(&output_globals, 0, sizeof(zend_output_globals)); |
165 | 5.28k | #endif |
166 | | |
167 | 5.28k | zend_stack_init(&OG(handlers), sizeof(php_output_handler *)); |
168 | 5.28k | OG(flags) |= PHP_OUTPUT_ACTIVATED; |
169 | | |
170 | 5.28k | return SUCCESS; |
171 | 5.28k | } |
172 | | /* }}} */ |
173 | | |
174 | | /* {{{ void php_output_deactivate(void) |
175 | | * Destroy the output handler stack */ |
176 | | PHPAPI void php_output_deactivate(void) |
177 | 5.28k | { |
178 | 5.28k | php_output_handler **handler = NULL; |
179 | | |
180 | 5.28k | if ((OG(flags) & PHP_OUTPUT_ACTIVATED)) { |
181 | 5.28k | php_output_header(); |
182 | | |
183 | 5.28k | OG(flags) ^= PHP_OUTPUT_ACTIVATED; |
184 | 5.28k | OG(active) = NULL; |
185 | 5.28k | OG(running) = NULL; |
186 | | |
187 | | /* release all output handlers */ |
188 | 5.28k | if (OG(handlers).elements) { |
189 | 28 | while ((handler = zend_stack_top(&OG(handlers)))) { |
190 | 0 | 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 | 0 | OG(active) = NULL; |
194 | 0 | ZEND_ASSERT(OG(running) == NULL && "output is deactivated therefore running should stay NULL"); |
195 | 0 | php_output_handler_free(handler); |
196 | 0 | } |
197 | 28 | } |
198 | 5.28k | zend_stack_destroy(&OG(handlers)); |
199 | 5.28k | } |
200 | | |
201 | 5.28k | if (OG(output_start_filename)) { |
202 | 3.99k | zend_string_release(OG(output_start_filename)); |
203 | 3.99k | OG(output_start_filename) = NULL; |
204 | 3.99k | } |
205 | 5.28k | } |
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 | 13.1M | { |
243 | 13.1M | if (OG(flags) & PHP_OUTPUT_ACTIVATED) { |
244 | 13.1M | php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len); |
245 | 13.1M | return len; |
246 | 13.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 | 0 | { |
317 | 0 | if (php_output_stack_pop(PHP_OUTPUT_POP_TRY)) { |
318 | 0 | return SUCCESS; |
319 | 0 | } |
320 | 0 | return FAILURE; |
321 | 0 | } |
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 | 5.28k | { |
328 | 5.28k | while (OG(active) && php_output_stack_pop(PHP_OUTPUT_POP_FORCE)); |
329 | 5.28k | } |
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 | 108 | { |
336 | 108 | if (php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_TRY)) { |
337 | 108 | return SUCCESS; |
338 | 108 | } |
339 | 0 | return FAILURE; |
340 | 108 | } |
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 | 57 | { |
347 | 57 | while (OG(active)) { |
348 | 0 | php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_FORCE); |
349 | 0 | } |
350 | 57 | } |
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 | 108 | { |
357 | 108 | return OG(active) ? zend_stack_count(&OG(handlers)) : 0; |
358 | 108 | } |
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 | 108 | { |
365 | 108 | if (OG(active)) { |
366 | 108 | if (OG(active)->buffer.used) { |
367 | 108 | ZVAL_STRINGL(p, OG(active)->buffer.data, OG(active)->buffer.used); |
368 | 108 | } else { |
369 | 0 | ZVAL_EMPTY_STRING(p); |
370 | 0 | } |
371 | 108 | return SUCCESS; |
372 | 108 | } else { |
373 | 0 | ZVAL_NULL(p); |
374 | 0 | return FAILURE; |
375 | 0 | } |
376 | 108 | } |
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 | 108 | { |
404 | 108 | php_output_handler *handler; |
405 | | |
406 | 108 | handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, 0, PHP_OUTPUT_HANDLER_STDFLAGS); |
407 | 108 | if (SUCCESS == php_output_handler_start(handler)) { |
408 | 108 | return SUCCESS; |
409 | 108 | } |
410 | 0 | php_output_handler_free(&handler); |
411 | 0 | return FAILURE; |
412 | 108 | } |
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 | 0 | { |
434 | 0 | php_output_handler *handler; |
435 | |
|
436 | 0 | if (output_handler) { |
437 | 0 | handler = php_output_handler_create_user(output_handler, chunk_size, flags); |
438 | 0 | } else { |
439 | 0 | handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags); |
440 | 0 | } |
441 | 0 | if (SUCCESS == php_output_handler_start(handler)) { |
442 | 0 | return SUCCESS; |
443 | 0 | } |
444 | 0 | php_output_handler_free(&handler); |
445 | 0 | return FAILURE; |
446 | 0 | } |
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 | 0 | { |
469 | 0 | zend_string *handler_name = NULL; |
470 | 0 | char *error = NULL; |
471 | 0 | php_output_handler *handler = NULL; |
472 | 0 | php_output_handler_alias_ctor_t alias = NULL; |
473 | 0 | php_output_handler_user_func_t *user = NULL; |
474 | |
|
475 | 0 | 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 | 0 | default: |
486 | 0 | user = ecalloc(1, sizeof(php_output_handler_user_func_t)); |
487 | 0 | if (SUCCESS == zend_fcall_info_init(output_handler, 0, &user->fci, &user->fcc, &handler_name, &error)) { |
488 | 0 | handler = php_output_handler_init(handler_name, chunk_size, PHP_OUTPUT_HANDLER_ABILITY_FLAGS(flags) | PHP_OUTPUT_HANDLER_USER); |
489 | 0 | ZVAL_COPY(&user->zoh, output_handler); |
490 | 0 | handler->func.user = user; |
491 | 0 | } else { |
492 | 0 | efree(user); |
493 | 0 | } |
494 | 0 | if (error) { |
495 | 0 | php_error_docref("ref.outcontrol", E_WARNING, "%s", error); |
496 | 0 | efree(error); |
497 | 0 | } |
498 | 0 | if (handler_name) { |
499 | 0 | zend_string_release_ex(handler_name, 0); |
500 | 0 | } |
501 | 0 | } |
502 | | |
503 | 0 | return handler; |
504 | 0 | } |
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 | 108 | { |
511 | 108 | php_output_handler *handler; |
512 | 108 | zend_string *str = zend_string_init(name, name_len, 0); |
513 | | |
514 | 108 | handler = php_output_handler_init(str, chunk_size, PHP_OUTPUT_HANDLER_ABILITY_FLAGS(flags) | PHP_OUTPUT_HANDLER_INTERNAL); |
515 | 108 | handler->func.internal = output_handler; |
516 | 108 | zend_string_release_ex(str, 0); |
517 | | |
518 | 108 | return handler; |
519 | 108 | } |
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 | 108 | { |
538 | 108 | HashTable *rconflicts; |
539 | 108 | php_output_handler_conflict_check_t conflict; |
540 | | |
541 | 108 | if (!(OG(flags) & PHP_OUTPUT_ACTIVATED)) { |
542 | 0 | return FAILURE; |
543 | 0 | } |
544 | | |
545 | 108 | if (php_output_lock_error(PHP_OUTPUT_HANDLER_START) || !handler) { |
546 | 0 | return FAILURE; |
547 | 0 | } |
548 | 108 | 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 | 108 | 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 | 108 | handler->level = zend_stack_push(&OG(handlers), &handler); |
562 | 108 | OG(active) = handler; |
563 | 108 | return SUCCESS; |
564 | 108 | } |
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 | return FAILURE; |
613 | 0 | } |
614 | 0 | str = zend_string_init_interned(name, name_len, 1); |
615 | 0 | zend_hash_update_ptr(&php_output_handler_conflicts, str, check_func); |
616 | 0 | zend_string_release_ex(str, 1); |
617 | 0 | return SUCCESS; |
618 | 0 | } |
619 | | /* }}} */ |
620 | | |
621 | | /* {{{ SUCCESS|FAILURE php_output_handler_reverse_conflict_register(zval *name, php_output_handler_conflict_check_t check_func) |
622 | | * Register a reverse conflict checking function on MINIT */ |
623 | | PHPAPI zend_result php_output_handler_reverse_conflict_register(const char *name, size_t name_len, php_output_handler_conflict_check_t check_func) |
624 | 0 | { |
625 | 0 | HashTable rev, *rev_ptr = NULL; |
626 | |
|
627 | 0 | if (!EG(current_module)) { |
628 | 0 | zend_error_noreturn(E_ERROR, "Cannot register a reverse output handler conflict outside of MINIT"); |
629 | 0 | return FAILURE; |
630 | 0 | } |
631 | | |
632 | 0 | if (NULL != (rev_ptr = zend_hash_str_find_ptr(&php_output_handler_reverse_conflicts, name, name_len))) { |
633 | 0 | return zend_hash_next_index_insert_ptr(rev_ptr, check_func) ? SUCCESS : FAILURE; |
634 | 0 | } else { |
635 | 0 | zend_string *str; |
636 | |
|
637 | 0 | zend_hash_init(&rev, 8, NULL, NULL, 1); |
638 | 0 | if (NULL == zend_hash_next_index_insert_ptr(&rev, check_func)) { |
639 | 0 | zend_hash_destroy(&rev); |
640 | 0 | return FAILURE; |
641 | 0 | } |
642 | 0 | str = zend_string_init_interned(name, name_len, 1); |
643 | 0 | zend_hash_update_mem(&php_output_handler_reverse_conflicts, str, &rev, sizeof(HashTable)); |
644 | 0 | zend_string_release_ex(str, 1); |
645 | 0 | return SUCCESS; |
646 | 0 | } |
647 | 0 | } |
648 | | /* }}} */ |
649 | | |
650 | | /* {{{ php_output_handler_alias_ctor_t php_output_handler_alias(zval *name) |
651 | | * Get an internal output handler for a user handler if it exists */ |
652 | | PHPAPI php_output_handler_alias_ctor_t php_output_handler_alias(const char *name, size_t name_len) |
653 | 0 | { |
654 | 0 | return zend_hash_str_find_ptr(&php_output_handler_aliases, name, name_len); |
655 | 0 | } |
656 | | /* }}} */ |
657 | | |
658 | | /* {{{ SUCCESS|FAILURE php_output_handler_alias_register(zval *name, php_output_handler_alias_ctor_t func) |
659 | | * Registers an internal output handler as alias for a user handler */ |
660 | | PHPAPI zend_result php_output_handler_alias_register(const char *name, size_t name_len, php_output_handler_alias_ctor_t func) |
661 | 0 | { |
662 | 0 | zend_string *str; |
663 | |
|
664 | 0 | if (!EG(current_module)) { |
665 | 0 | zend_error_noreturn(E_ERROR, "Cannot register an output handler alias outside of MINIT"); |
666 | 0 | return FAILURE; |
667 | 0 | } |
668 | 0 | str = zend_string_init_interned(name, name_len, 1); |
669 | 0 | zend_hash_update_ptr(&php_output_handler_aliases, str, func); |
670 | 0 | zend_string_release_ex(str, 1); |
671 | 0 | return SUCCESS; |
672 | 0 | } |
673 | | /* }}} */ |
674 | | |
675 | | /* {{{ SUCCESS|FAILURE php_output_handler_hook(php_output_handler_hook_t type, void *arg) |
676 | | * Output handler hook for output handler functions to check/modify the current handlers abilities */ |
677 | | PHPAPI zend_result php_output_handler_hook(php_output_handler_hook_t type, void *arg) |
678 | 0 | { |
679 | 0 | if (OG(running)) { |
680 | 0 | switch (type) { |
681 | 0 | case PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ: |
682 | 0 | *(void ***) arg = &OG(running)->opaq; |
683 | 0 | return SUCCESS; |
684 | 0 | case PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS: |
685 | 0 | *(int *) arg = OG(running)->flags; |
686 | 0 | return SUCCESS; |
687 | 0 | case PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL: |
688 | 0 | *(int *) arg = OG(running)->level; |
689 | 0 | return SUCCESS; |
690 | 0 | case PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE: |
691 | 0 | OG(running)->flags &= ~(PHP_OUTPUT_HANDLER_REMOVABLE|PHP_OUTPUT_HANDLER_CLEANABLE); |
692 | 0 | return SUCCESS; |
693 | 0 | case PHP_OUTPUT_HANDLER_HOOK_DISABLE: |
694 | 0 | OG(running)->flags |= PHP_OUTPUT_HANDLER_DISABLED; |
695 | 0 | return SUCCESS; |
696 | 0 | default: |
697 | 0 | break; |
698 | 0 | } |
699 | 0 | } |
700 | 0 | return FAILURE; |
701 | 0 | } |
702 | | /* }}} */ |
703 | | |
704 | | /* {{{ void php_output_handler_dtor(php_output_handler *handler) |
705 | | * Destroy an output handler */ |
706 | | PHPAPI void php_output_handler_dtor(php_output_handler *handler) |
707 | 108 | { |
708 | 108 | if (handler->name) { |
709 | 108 | zend_string_release_ex(handler->name, 0); |
710 | 108 | } |
711 | 108 | if (handler->buffer.data) { |
712 | 108 | efree(handler->buffer.data); |
713 | 108 | } |
714 | 108 | if (handler->flags & PHP_OUTPUT_HANDLER_USER) { |
715 | 0 | zval_ptr_dtor(&handler->func.user->zoh); |
716 | 0 | efree(handler->func.user); |
717 | 0 | } |
718 | 108 | if (handler->dtor && handler->opaq) { |
719 | 0 | handler->dtor(handler->opaq); |
720 | 0 | } |
721 | 108 | memset(handler, 0, sizeof(*handler)); |
722 | 108 | } |
723 | | /* }}} */ |
724 | | |
725 | | /* {{{ void php_output_handler_free(php_output_handler **handler) |
726 | | * Destroy and free an output handler */ |
727 | | PHPAPI void php_output_handler_free(php_output_handler **h) |
728 | 108 | { |
729 | 108 | php_output_handler *handler = *h; |
730 | 108 | if (handler) { |
731 | 108 | *h = NULL; |
732 | 108 | php_output_handler_dtor(handler); |
733 | 108 | efree(handler); |
734 | 108 | } |
735 | 108 | } |
736 | | /* }}} */ |
737 | | |
738 | | /* void php_output_set_implicit_flush(int enabled) |
739 | | * Enable or disable implicit flush */ |
740 | | PHPAPI void php_output_set_implicit_flush(int flush) |
741 | 5.28k | { |
742 | 5.28k | if (flush) { |
743 | 5.28k | OG(flags) |= PHP_OUTPUT_IMPLICITFLUSH; |
744 | 5.28k | } else { |
745 | 0 | OG(flags) &= ~PHP_OUTPUT_IMPLICITFLUSH; |
746 | 0 | } |
747 | 5.28k | } |
748 | | /* }}} */ |
749 | | |
750 | | /* {{{ char *php_output_get_start_filename(void) |
751 | | * Get the file name where output has started */ |
752 | | PHPAPI const char *php_output_get_start_filename(void) |
753 | 0 | { |
754 | 0 | return OG(output_start_filename) ? ZSTR_VAL(OG(output_start_filename)) : NULL; |
755 | 0 | } |
756 | | /* }}} */ |
757 | | |
758 | | /* {{{ int php_output_get_start_lineno(void) |
759 | | * Get the line number where output has started */ |
760 | | PHPAPI int php_output_get_start_lineno(void) |
761 | 0 | { |
762 | 0 | return OG(output_start_lineno); |
763 | 0 | } |
764 | | /* }}} */ |
765 | | |
766 | | /* {{{ static bool php_output_lock_error(int op) |
767 | | * Checks whether an unallowed operation is attempted from within the output handler and issues a fatal error */ |
768 | | static inline bool php_output_lock_error(int op) |
769 | 13.2M | { |
770 | | /* if there's no ob active, ob has been stopped */ |
771 | 13.2M | if (op && OG(active) && OG(running)) { |
772 | | /* fatal error */ |
773 | 0 | php_output_deactivate(); |
774 | 0 | php_error_docref("ref.outcontrol", E_ERROR, "Cannot use output buffering in output buffering display handlers"); |
775 | 0 | return true; |
776 | 0 | } |
777 | 13.2M | return false; |
778 | 13.2M | } |
779 | | /* }}} */ |
780 | | |
781 | | /* {{{ static php_output_context *php_output_context_init(php_output_context *context, int op) |
782 | | * Initialize a new output context */ |
783 | | static inline void php_output_context_init(php_output_context *context, int op) |
784 | 13.1M | { |
785 | 13.1M | memset(context, 0, sizeof(php_output_context)); |
786 | 13.1M | context->op = op; |
787 | 13.1M | } |
788 | | /* }}} */ |
789 | | |
790 | | /* {{{ static void php_output_context_reset(php_output_context *context) |
791 | | * Reset an output context */ |
792 | | static inline void php_output_context_reset(php_output_context *context) |
793 | 0 | { |
794 | 0 | int op = context->op; |
795 | 0 | php_output_context_dtor(context); |
796 | 0 | memset(context, 0, sizeof(php_output_context)); |
797 | 0 | context->op = op; |
798 | 0 | } |
799 | | /* }}} */ |
800 | | |
801 | | /* {{{ static void php_output_context_feed(php_output_context *context, char *, size_t, size_t) |
802 | | * Feed output contexts input buffer */ |
803 | | static inline void php_output_context_feed(php_output_context *context, char *data, size_t size, size_t used, bool free) |
804 | 108 | { |
805 | 108 | if (context->in.free && context->in.data) { |
806 | 0 | efree(context->in.data); |
807 | 0 | } |
808 | 108 | context->in.data = data; |
809 | 108 | context->in.used = used; |
810 | 108 | context->in.free = free; |
811 | 108 | context->in.size = size; |
812 | 108 | } |
813 | | /* }}} */ |
814 | | |
815 | | /* {{{ static void php_output_context_swap(php_output_context *context) |
816 | | * Swap output contexts buffers */ |
817 | | static inline void php_output_context_swap(php_output_context *context) |
818 | 0 | { |
819 | 0 | if (context->in.free && context->in.data) { |
820 | 0 | efree(context->in.data); |
821 | 0 | } |
822 | 0 | context->in.data = context->out.data; |
823 | 0 | context->in.used = context->out.used; |
824 | 0 | context->in.free = context->out.free; |
825 | 0 | context->in.size = context->out.size; |
826 | 0 | context->out.data = NULL; |
827 | 0 | context->out.used = 0; |
828 | 0 | context->out.free = 0; |
829 | 0 | context->out.size = 0; |
830 | 0 | } |
831 | | /* }}} */ |
832 | | |
833 | | /* {{{ static void php_output_context_pass(php_output_context *context) |
834 | | * Pass input to output buffer */ |
835 | | static inline void php_output_context_pass(php_output_context *context) |
836 | 108 | { |
837 | 108 | context->out.data = context->in.data; |
838 | 108 | context->out.used = context->in.used; |
839 | 108 | context->out.size = context->in.size; |
840 | 108 | context->out.free = context->in.free; |
841 | 108 | context->in.data = NULL; |
842 | 108 | context->in.used = 0; |
843 | 108 | context->in.free = 0; |
844 | 108 | context->in.size = 0; |
845 | 108 | } |
846 | | /* }}} */ |
847 | | |
848 | | /* {{{ static void php_output_context_dtor(php_output_context *context) |
849 | | * Destroy the contents of an output context */ |
850 | | static inline void php_output_context_dtor(php_output_context *context) |
851 | 13.1M | { |
852 | 13.1M | if (context->in.free && context->in.data) { |
853 | 0 | efree(context->in.data); |
854 | 0 | context->in.data = NULL; |
855 | 0 | } |
856 | 13.1M | if (context->out.free && context->out.data) { |
857 | 0 | efree(context->out.data); |
858 | 0 | context->out.data = NULL; |
859 | 0 | } |
860 | 13.1M | } |
861 | | /* }}} */ |
862 | | |
863 | | /* {{{ static php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags) |
864 | | * Allocates and initializes a php_output_handler structure */ |
865 | | static inline php_output_handler *php_output_handler_init(zend_string *name, size_t chunk_size, int flags) |
866 | 108 | { |
867 | 108 | php_output_handler *handler; |
868 | | |
869 | 108 | handler = ecalloc(1, sizeof(php_output_handler)); |
870 | 108 | handler->name = zend_string_copy(name); |
871 | 108 | handler->size = chunk_size; |
872 | 108 | handler->flags = flags; |
873 | 108 | handler->buffer.size = PHP_OUTPUT_HANDLER_INITBUF_SIZE(chunk_size); |
874 | 108 | handler->buffer.data = emalloc(handler->buffer.size); |
875 | | |
876 | 108 | return handler; |
877 | 108 | } |
878 | | /* }}} */ |
879 | | |
880 | | /* {{{ static bool php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf) |
881 | | * Appends input to the output handlers buffer and indicates whether the buffer does not have to be processed by the output handler */ |
882 | | static inline bool php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf) |
883 | 98.6k | { |
884 | 98.6k | if (buf->used) { |
885 | 98.5k | OG(flags) |= PHP_OUTPUT_WRITTEN; |
886 | | /* store it away */ |
887 | 98.5k | if ((handler->buffer.size - handler->buffer.used) <= buf->used) { |
888 | 1 | size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size); |
889 | 1 | size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used)); |
890 | 1 | size_t grow_max = MAX(grow_int, grow_buf); |
891 | | |
892 | 1 | handler->buffer.data = safe_erealloc(handler->buffer.data, 1, handler->buffer.size, grow_max); |
893 | 1 | handler->buffer.size += grow_max; |
894 | 1 | } |
895 | 98.5k | memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used); |
896 | 98.5k | handler->buffer.used += buf->used; |
897 | | |
898 | | /* chunked buffering */ |
899 | 98.5k | if (handler->size && (handler->buffer.used >= handler->size)) { |
900 | | /* store away errors and/or any intermediate output */ |
901 | 0 | return OG(running) ? true : false; |
902 | 0 | } |
903 | 98.5k | } |
904 | 98.6k | return true; |
905 | 98.6k | } |
906 | | /* }}} */ |
907 | | |
908 | | /* {{{ static php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context) |
909 | | * Output handler operation dispatcher, applying context op to the php_output_handler handler */ |
910 | | static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context) |
911 | 98.6k | { |
912 | 98.6k | php_output_handler_status_t status; |
913 | 98.6k | int original_op = context->op; |
914 | | |
915 | | #if PHP_OUTPUT_DEBUG |
916 | | fprintf(stderr, ">>> op(%d, " |
917 | | "handler=%p, " |
918 | | "name=%s, " |
919 | | "flags=%d, " |
920 | | "buffer.data=%s, " |
921 | | "buffer.used=%zu, " |
922 | | "buffer.size=%zu, " |
923 | | "in.data=%s, " |
924 | | "in.used=%zu)\n", |
925 | | context->op, |
926 | | handler, |
927 | | handler->name, |
928 | | handler->flags, |
929 | | handler->buffer.used?handler->buffer.data:"", |
930 | | handler->buffer.used, |
931 | | handler->buffer.size, |
932 | | context->in.used?context->in.data:"", |
933 | | context->in.used |
934 | | ); |
935 | | #endif |
936 | | |
937 | 98.6k | if (handler->flags & PHP_OUTPUT_HANDLER_DISABLED) { |
938 | 0 | return PHP_OUTPUT_HANDLER_FAILURE; |
939 | 0 | } |
940 | | |
941 | 98.6k | if (php_output_lock_error(context->op)) { |
942 | | /* fatal error */ |
943 | 0 | return PHP_OUTPUT_HANDLER_FAILURE; |
944 | 0 | } |
945 | | |
946 | | /* php_output_lock_error() doesn't fail for PHP_OUTPUT_HANDLER_WRITE but |
947 | | * anything that gets written will silently be discarded, remember that we |
948 | | * tried to write so a deprecation warning can be emitted at the end. */ |
949 | 98.6k | if (context->op == PHP_OUTPUT_HANDLER_WRITE && OG(active) && OG(running)) { |
950 | 0 | handler->flags |= PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT; |
951 | 0 | } |
952 | | |
953 | 98.6k | bool still_have_handler = true; |
954 | | /* storable? */ |
955 | 98.6k | if (php_output_handler_append(handler, &context->in) && !context->op) { |
956 | 98.5k | context->op = original_op; |
957 | 98.5k | return PHP_OUTPUT_HANDLER_NO_DATA; |
958 | 98.5k | } else { |
959 | | /* need to start? */ |
960 | 108 | if (!(handler->flags & PHP_OUTPUT_HANDLER_STARTED)) { |
961 | 108 | context->op |= PHP_OUTPUT_HANDLER_START; |
962 | 108 | } |
963 | | |
964 | 108 | OG(running) = handler; |
965 | 108 | if (handler->flags & PHP_OUTPUT_HANDLER_USER) { |
966 | 0 | zval ob_args[2]; |
967 | 0 | zval retval; |
968 | | |
969 | | /* ob_data */ |
970 | 0 | ZVAL_STRINGL(&ob_args[0], handler->buffer.data, handler->buffer.used); |
971 | | /* ob_mode */ |
972 | 0 | ZVAL_LONG(&ob_args[1], (zend_long) context->op); |
973 | | |
974 | | /* Set FCI info */ |
975 | 0 | handler->func.user->fci.param_count = 2; |
976 | 0 | handler->func.user->fci.params = ob_args; |
977 | 0 | handler->func.user->fci.retval = &retval; |
978 | |
|
979 | 0 | if (SUCCESS == zend_call_function(&handler->func.user->fci, &handler->func.user->fcc) && Z_TYPE(retval) != IS_UNDEF) { |
980 | 0 | if (handler->flags & PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT) { |
981 | | // Make sure that we don't get lost in the current output buffer |
982 | | // by disabling it |
983 | 0 | handler->flags |= PHP_OUTPUT_HANDLER_DISABLED; |
984 | 0 | if (handler->flags & PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT) { |
985 | | // The handler might not always produce output |
986 | 0 | handler->flags &= ~PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT; |
987 | 0 | php_error_docref( |
988 | 0 | NULL, |
989 | 0 | E_DEPRECATED, |
990 | 0 | "Producing output from user output handler %s is deprecated", |
991 | 0 | ZSTR_VAL(handler->name) |
992 | 0 | ); |
993 | 0 | } |
994 | | |
995 | | // Check if the handler is still in the list of handlers to |
996 | | // determine if the PHP_OUTPUT_HANDLER_DISABLED flag can |
997 | | // be removed |
998 | 0 | still_have_handler = false; |
999 | 0 | int handler_count = php_output_get_level(); |
1000 | 0 | if (handler_count) { |
1001 | 0 | php_output_handler **handlers = (php_output_handler **) zend_stack_base(&OG(handlers)); |
1002 | 0 | for (int handler_num = 0; handler_num < handler_count; ++handler_num) { |
1003 | 0 | php_output_handler *curr_handler = handlers[handler_num]; |
1004 | 0 | if (curr_handler == handler) { |
1005 | 0 | handler->flags &= (~PHP_OUTPUT_HANDLER_DISABLED); |
1006 | 0 | still_have_handler = true; |
1007 | 0 | break; |
1008 | 0 | } |
1009 | 0 | } |
1010 | 0 | } |
1011 | 0 | } |
1012 | 0 | if (Z_TYPE(retval) == IS_FALSE) { |
1013 | | /* call failed, pass internal buffer along */ |
1014 | 0 | status = PHP_OUTPUT_HANDLER_FAILURE; |
1015 | 0 | } else { |
1016 | | /* user handler may have returned TRUE */ |
1017 | 0 | status = PHP_OUTPUT_HANDLER_NO_DATA; |
1018 | 0 | if (Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) != IS_TRUE) { |
1019 | 0 | convert_to_string(&retval); |
1020 | 0 | if (Z_STRLEN(retval)) { |
1021 | 0 | context->out.data = estrndup(Z_STRVAL(retval), Z_STRLEN(retval)); |
1022 | 0 | context->out.used = Z_STRLEN(retval); |
1023 | 0 | context->out.free = 1; |
1024 | 0 | status = PHP_OUTPUT_HANDLER_SUCCESS; |
1025 | 0 | } |
1026 | 0 | } |
1027 | 0 | } |
1028 | 0 | } else { |
1029 | | /* call failed, pass internal buffer along */ |
1030 | 0 | status = PHP_OUTPUT_HANDLER_FAILURE; |
1031 | 0 | } |
1032 | | |
1033 | | /* Free arguments and return value */ |
1034 | 0 | zval_ptr_dtor(&ob_args[0]); |
1035 | 0 | zval_ptr_dtor(&ob_args[1]); |
1036 | 0 | zval_ptr_dtor(&retval); |
1037 | |
|
1038 | 108 | } else { |
1039 | | |
1040 | 108 | php_output_context_feed(context, handler->buffer.data, handler->buffer.size, handler->buffer.used, 0); |
1041 | | |
1042 | 108 | if (SUCCESS == handler->func.internal(&handler->opaq, context)) { |
1043 | 108 | if (context->out.used) { |
1044 | 108 | status = PHP_OUTPUT_HANDLER_SUCCESS; |
1045 | 108 | } else { |
1046 | 0 | status = PHP_OUTPUT_HANDLER_NO_DATA; |
1047 | 0 | } |
1048 | 108 | } else { |
1049 | 0 | status = PHP_OUTPUT_HANDLER_FAILURE; |
1050 | 0 | } |
1051 | 108 | } |
1052 | 108 | if (still_have_handler) { |
1053 | 108 | handler->flags |= PHP_OUTPUT_HANDLER_STARTED; |
1054 | 108 | } |
1055 | 108 | OG(running) = NULL; |
1056 | 108 | } |
1057 | | |
1058 | 108 | if (!still_have_handler) { |
1059 | | // Handler and context will have both already been freed |
1060 | 0 | return status; |
1061 | 0 | } |
1062 | | |
1063 | 108 | switch (status) { |
1064 | 0 | case PHP_OUTPUT_HANDLER_FAILURE: |
1065 | | /* disable this handler */ |
1066 | 0 | handler->flags |= PHP_OUTPUT_HANDLER_DISABLED; |
1067 | | /* discard any output */ |
1068 | 0 | if (context->out.data && context->out.free) { |
1069 | 0 | efree(context->out.data); |
1070 | 0 | } |
1071 | | /* returns handlers buffer */ |
1072 | 0 | context->out.data = handler->buffer.data; |
1073 | 0 | context->out.used = handler->buffer.used; |
1074 | 0 | context->out.free = 1; |
1075 | 0 | handler->buffer.data = NULL; |
1076 | 0 | handler->buffer.used = 0; |
1077 | 0 | handler->buffer.size = 0; |
1078 | 0 | break; |
1079 | 0 | case PHP_OUTPUT_HANDLER_NO_DATA: |
1080 | | /* handler ate all */ |
1081 | 0 | php_output_context_reset(context); |
1082 | 0 | ZEND_FALLTHROUGH; |
1083 | 108 | case PHP_OUTPUT_HANDLER_SUCCESS: |
1084 | | /* no more buffered data */ |
1085 | 108 | handler->buffer.used = 0; |
1086 | 108 | handler->flags |= PHP_OUTPUT_HANDLER_PROCESSED; |
1087 | 108 | break; |
1088 | 108 | } |
1089 | | |
1090 | 108 | context->op = original_op; |
1091 | 108 | return status; |
1092 | 108 | } |
1093 | | /* }}} */ |
1094 | | |
1095 | | |
1096 | | /* {{{ static void php_output_op(int op, const char *str, size_t len) |
1097 | | * Output op dispatcher, passes input and output handlers output through the output handler stack until it gets written to the SAPI */ |
1098 | | static inline void php_output_op(int op, const char *str, size_t len) |
1099 | 13.1M | { |
1100 | 13.1M | php_output_context context; |
1101 | 13.1M | php_output_handler **active; |
1102 | 13.1M | int obh_cnt; |
1103 | | |
1104 | 13.1M | if (php_output_lock_error(op)) { |
1105 | 0 | return; |
1106 | 0 | } |
1107 | | |
1108 | 13.1M | php_output_context_init(&context, op); |
1109 | | |
1110 | | /* |
1111 | | * broken up for better performance: |
1112 | | * - apply op to the one active handler; note that OG(active) might be popped off the stack on a flush |
1113 | | * - or apply op to the handler stack |
1114 | | */ |
1115 | 13.1M | if (OG(active) && (obh_cnt = zend_stack_count(&OG(handlers)))) { |
1116 | 98.5k | context.in.data = (char *) str; |
1117 | 98.5k | context.in.used = len; |
1118 | | |
1119 | 98.5k | if (obh_cnt > 1) { |
1120 | 0 | zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_op, &context); |
1121 | 98.5k | } else if ((active = zend_stack_top(&OG(handlers))) && (!((*active)->flags & PHP_OUTPUT_HANDLER_DISABLED))) { |
1122 | 98.5k | php_output_handler_op(*active, &context); |
1123 | 98.5k | } else { |
1124 | 0 | php_output_context_pass(&context); |
1125 | 0 | } |
1126 | 13.0M | } else { |
1127 | 13.0M | context.out.data = (char *) str; |
1128 | 13.0M | context.out.used = len; |
1129 | 13.0M | } |
1130 | | |
1131 | 13.1M | if (context.out.data && context.out.used) { |
1132 | 13.0M | php_output_header(); |
1133 | | |
1134 | 13.0M | if (!(OG(flags) & PHP_OUTPUT_DISABLED)) { |
1135 | | #if PHP_OUTPUT_DEBUG |
1136 | | fprintf(stderr, "::: sapi_write('%s', %zu)\n", context.out.data, context.out.used); |
1137 | | #endif |
1138 | 13.0M | sapi_module.ub_write(context.out.data, context.out.used); |
1139 | | |
1140 | 13.0M | if (OG(flags) & PHP_OUTPUT_IMPLICITFLUSH) { |
1141 | 13.0M | sapi_flush(); |
1142 | 13.0M | } |
1143 | | |
1144 | 13.0M | OG(flags) |= PHP_OUTPUT_SENT; |
1145 | 13.0M | } |
1146 | 13.0M | } |
1147 | 13.1M | php_output_context_dtor(&context); |
1148 | 13.1M | } |
1149 | | /* }}} */ |
1150 | | |
1151 | | /* {{{ static int php_output_stack_apply_op(void *h, void *c) |
1152 | | * Operation callback for the stack apply function */ |
1153 | | static int php_output_stack_apply_op(void *h, void *c) |
1154 | 0 | { |
1155 | 0 | int was_disabled; |
1156 | 0 | php_output_handler_status_t status; |
1157 | 0 | php_output_handler *handler = *(php_output_handler **) h; |
1158 | 0 | php_output_context *context = (php_output_context *) c; |
1159 | |
|
1160 | 0 | if ((was_disabled = (handler->flags & PHP_OUTPUT_HANDLER_DISABLED))) { |
1161 | 0 | status = PHP_OUTPUT_HANDLER_FAILURE; |
1162 | 0 | } else { |
1163 | 0 | status = php_output_handler_op(handler, context); |
1164 | 0 | } |
1165 | | |
1166 | | /* |
1167 | | * handler ate all => break |
1168 | | * handler returned data or failed resp. is disabled => continue |
1169 | | */ |
1170 | 0 | switch (status) { |
1171 | 0 | case PHP_OUTPUT_HANDLER_NO_DATA: |
1172 | 0 | return 1; |
1173 | | |
1174 | 0 | case PHP_OUTPUT_HANDLER_SUCCESS: |
1175 | | /* swap contexts buffers, unless this is the last handler in the stack */ |
1176 | 0 | if (handler->level) { |
1177 | 0 | php_output_context_swap(context); |
1178 | 0 | } |
1179 | 0 | return 0; |
1180 | | |
1181 | 0 | case PHP_OUTPUT_HANDLER_FAILURE: |
1182 | 0 | default: |
1183 | 0 | if (was_disabled) { |
1184 | | /* pass input along, if it's the last handler in the stack */ |
1185 | 0 | if (!handler->level) { |
1186 | 0 | php_output_context_pass(context); |
1187 | 0 | } |
1188 | 0 | } else { |
1189 | | /* swap buffers, unless this is the last handler */ |
1190 | 0 | if (handler->level) { |
1191 | 0 | php_output_context_swap(context); |
1192 | 0 | } |
1193 | 0 | } |
1194 | 0 | return 0; |
1195 | 0 | } |
1196 | 0 | } |
1197 | | /* }}} */ |
1198 | | |
1199 | | /* {{{ static int php_output_stack_apply_clean(void *h, void *c) |
1200 | | * Clean callback for the stack apply function */ |
1201 | | static int php_output_stack_apply_clean(void *h, void *c) |
1202 | 0 | { |
1203 | 0 | php_output_handler *handler = *(php_output_handler **) h; |
1204 | 0 | php_output_context *context = (php_output_context *) c; |
1205 | |
|
1206 | 0 | handler->buffer.used = 0; |
1207 | 0 | php_output_handler_op(handler, context); |
1208 | 0 | php_output_context_reset(context); |
1209 | 0 | return 0; |
1210 | 0 | } |
1211 | | /* }}} */ |
1212 | | |
1213 | | /* {{{ static int php_output_stack_apply_list(void *h, void *z) |
1214 | | * List callback for the stack apply function */ |
1215 | | static int php_output_stack_apply_list(void *h, void *z) |
1216 | 0 | { |
1217 | 0 | php_output_handler *handler = *(php_output_handler **) h; |
1218 | 0 | zval *array = (zval *) z; |
1219 | |
|
1220 | 0 | add_next_index_str(array, zend_string_copy(handler->name)); |
1221 | 0 | return 0; |
1222 | 0 | } |
1223 | | /* }}} */ |
1224 | | |
1225 | | /* {{{ static int php_output_stack_apply_status(void *h, void *z) |
1226 | | * Status callback for the stack apply function */ |
1227 | | static int php_output_stack_apply_status(void *h, void *z) |
1228 | 0 | { |
1229 | 0 | php_output_handler *handler = *(php_output_handler **) h; |
1230 | 0 | zval arr, *array = (zval *) z; |
1231 | |
|
1232 | 0 | add_next_index_zval(array, php_output_handler_status(handler, &arr)); |
1233 | |
|
1234 | 0 | return 0; |
1235 | 0 | } |
1236 | | |
1237 | | /* {{{ static zval *php_output_handler_status(php_output_handler *handler, zval *entry) |
1238 | | * Returns an array with the status of the output handler */ |
1239 | | static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry) |
1240 | 0 | { |
1241 | 0 | ZEND_ASSERT(entry != NULL); |
1242 | |
|
1243 | 0 | array_init(entry); |
1244 | 0 | add_assoc_str(entry, "name", zend_string_copy(handler->name)); |
1245 | 0 | add_assoc_long(entry, "type", (zend_long) (handler->flags & 0xf)); |
1246 | 0 | add_assoc_long(entry, "flags", (zend_long) handler->flags); |
1247 | 0 | add_assoc_long(entry, "level", (zend_long) handler->level); |
1248 | 0 | add_assoc_long(entry, "chunk_size", (zend_long) handler->size); |
1249 | 0 | add_assoc_long(entry, "buffer_size", (zend_long) handler->buffer.size); |
1250 | 0 | add_assoc_long(entry, "buffer_used", (zend_long) handler->buffer.used); |
1251 | |
|
1252 | 0 | return entry; |
1253 | 0 | } |
1254 | | /* }}} */ |
1255 | | |
1256 | | /* {{{ static int php_output_stack_pop(int flags) |
1257 | | * Pops an output handler off the stack */ |
1258 | | static int php_output_stack_pop(int flags) |
1259 | 108 | { |
1260 | 108 | php_output_context context; |
1261 | 108 | php_output_handler **current, *orphan = OG(active); |
1262 | | |
1263 | 108 | if (!orphan) { |
1264 | 0 | if (!(flags & PHP_OUTPUT_POP_SILENT)) { |
1265 | 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"); |
1266 | 0 | } |
1267 | 0 | return 0; |
1268 | 108 | } else if (!(flags & PHP_OUTPUT_POP_FORCE) && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) { |
1269 | 0 | if (!(flags & PHP_OUTPUT_POP_SILENT)) { |
1270 | 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); |
1271 | 0 | } |
1272 | 0 | return 0; |
1273 | 108 | } else { |
1274 | 108 | php_output_context_init(&context, PHP_OUTPUT_HANDLER_FINAL); |
1275 | | |
1276 | | /* don't run the output handler if it's disabled */ |
1277 | 108 | if (!(orphan->flags & PHP_OUTPUT_HANDLER_DISABLED)) { |
1278 | | /* didn't it start yet? */ |
1279 | 108 | if (!(orphan->flags & PHP_OUTPUT_HANDLER_STARTED)) { |
1280 | 108 | context.op |= PHP_OUTPUT_HANDLER_START; |
1281 | 108 | } |
1282 | | /* signal that we're cleaning up */ |
1283 | 108 | if (flags & PHP_OUTPUT_POP_DISCARD) { |
1284 | 108 | context.op |= PHP_OUTPUT_HANDLER_CLEAN; |
1285 | 108 | } |
1286 | 108 | php_output_handler_op(orphan, &context); |
1287 | 108 | } |
1288 | | // If it isn't still in the stack, cannot free it |
1289 | 108 | bool still_have_handler = false; |
1290 | 108 | int handler_count = php_output_get_level(); |
1291 | 108 | if (handler_count) { |
1292 | 108 | php_output_handler **handlers = (php_output_handler **) zend_stack_base(&OG(handlers)); |
1293 | 108 | for (int handler_num = 0; handler_num < handler_count; ++handler_num) { |
1294 | 108 | php_output_handler *curr_handler = handlers[handler_num]; |
1295 | 108 | if (curr_handler == orphan) { |
1296 | 108 | still_have_handler = true; |
1297 | 108 | break; |
1298 | 108 | } |
1299 | 108 | } |
1300 | 108 | } |
1301 | | |
1302 | | /* pop it off the stack */ |
1303 | 108 | zend_stack_del_top(&OG(handlers)); |
1304 | 108 | if ((current = zend_stack_top(&OG(handlers)))) { |
1305 | 0 | OG(active) = *current; |
1306 | 108 | } else { |
1307 | 108 | OG(active) = NULL; |
1308 | 108 | } |
1309 | | |
1310 | | /* pass output along */ |
1311 | 108 | if (context.out.data && context.out.used && !(flags & PHP_OUTPUT_POP_DISCARD)) { |
1312 | 0 | php_output_write(context.out.data, context.out.used); |
1313 | 0 | } |
1314 | | |
1315 | | /* destroy the handler (after write!) */ |
1316 | 108 | if (still_have_handler) { |
1317 | 108 | php_output_handler_free(&orphan); |
1318 | 108 | } |
1319 | 108 | php_output_context_dtor(&context); |
1320 | | |
1321 | 108 | return 1; |
1322 | 108 | } |
1323 | 108 | } |
1324 | | /* }}} */ |
1325 | | |
1326 | | /* {{{ static SUCCESS|FAILURE php_output_handler_compat_func(void *ctx, php_output_context *) |
1327 | | * php_output_handler_context_func_t for php_output_handler_func_t output handlers */ |
1328 | | static zend_result php_output_handler_compat_func(void **handler_context, php_output_context *output_context) |
1329 | 0 | { |
1330 | 0 | php_output_handler_func_t func = *(php_output_handler_func_t *) handler_context; |
1331 | |
|
1332 | 0 | if (func) { |
1333 | 0 | char *out_str = NULL; |
1334 | 0 | size_t out_len = 0; |
1335 | |
|
1336 | 0 | func(output_context->in.data, output_context->in.used, &out_str, &out_len, output_context->op); |
1337 | |
|
1338 | 0 | if (out_str) { |
1339 | 0 | output_context->out.data = out_str; |
1340 | 0 | output_context->out.used = out_len; |
1341 | 0 | output_context->out.free = 1; |
1342 | 0 | } else { |
1343 | 0 | php_output_context_pass(output_context); |
1344 | 0 | } |
1345 | |
|
1346 | 0 | return SUCCESS; |
1347 | 0 | } |
1348 | 0 | return FAILURE; |
1349 | 0 | } |
1350 | | /* }}} */ |
1351 | | |
1352 | | /* {{{ static SUCCESS|FAILURE php_output_handler_default_func(void *ctx, php_output_context *) |
1353 | | * Default output handler */ |
1354 | | static zend_result php_output_handler_default_func(void **handler_context, php_output_context *output_context) |
1355 | 108 | { |
1356 | 108 | php_output_context_pass(output_context); |
1357 | 108 | return SUCCESS; |
1358 | 108 | } |
1359 | | /* }}} */ |
1360 | | |
1361 | | /* {{{ static SUCCESS|FAILURE php_output_handler_devnull_func(void *ctx, php_output_context *) |
1362 | | * Null output handler */ |
1363 | | static zend_result php_output_handler_devnull_func(void **handler_context, php_output_context *output_context) |
1364 | 0 | { |
1365 | 0 | return SUCCESS; |
1366 | 0 | } |
1367 | | /* }}} */ |
1368 | | |
1369 | | /* |
1370 | | * USERLAND (nearly 1:1 of old output.c) |
1371 | | */ |
1372 | | |
1373 | | /* {{{ Turn on Output Buffering (specifying an optional output handler). */ |
1374 | | PHP_FUNCTION(ob_start) |
1375 | 0 | { |
1376 | 0 | zval *output_handler = NULL; |
1377 | 0 | zend_long chunk_size = 0; |
1378 | 0 | zend_long flags = PHP_OUTPUT_HANDLER_STDFLAGS; |
1379 | |
|
1380 | 0 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "|zll", &output_handler, &chunk_size, &flags) == FAILURE) { |
1381 | 0 | RETURN_THROWS(); |
1382 | 0 | } |
1383 | | |
1384 | 0 | if (chunk_size < 0) { |
1385 | 0 | chunk_size = 0; |
1386 | 0 | } |
1387 | |
|
1388 | 0 | if (php_output_start_user(output_handler, chunk_size, flags) == FAILURE) { |
1389 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to create buffer"); |
1390 | 0 | RETURN_FALSE; |
1391 | 0 | } |
1392 | 0 | RETURN_TRUE; |
1393 | 0 | } |
1394 | | /* }}} */ |
1395 | | |
1396 | | /* {{{ Flush (send) contents of the output buffer. The last buffer content is sent to next buffer */ |
1397 | | PHP_FUNCTION(ob_flush) |
1398 | 0 | { |
1399 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1400 | 0 | RETURN_THROWS(); |
1401 | 0 | } |
1402 | | |
1403 | 0 | if (!OG(active)) { |
1404 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to flush buffer. No buffer to flush"); |
1405 | 0 | RETURN_FALSE; |
1406 | 0 | } |
1407 | | |
1408 | 0 | if (SUCCESS != php_output_flush()) { |
1409 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to flush buffer of %s (%d)", ZSTR_VAL(OG(active)->name), OG(active)->level); |
1410 | 0 | RETURN_FALSE; |
1411 | 0 | } |
1412 | 0 | RETURN_TRUE; |
1413 | 0 | } |
1414 | | /* }}} */ |
1415 | | |
1416 | | /* {{{ Clean (delete) the current output buffer */ |
1417 | | PHP_FUNCTION(ob_clean) |
1418 | 0 | { |
1419 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1420 | 0 | RETURN_THROWS(); |
1421 | 0 | } |
1422 | | |
1423 | 0 | if (!OG(active)) { |
1424 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to delete buffer. No buffer to delete"); |
1425 | 0 | RETURN_FALSE; |
1426 | 0 | } |
1427 | | |
1428 | 0 | if (SUCCESS != php_output_clean()) { |
1429 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to delete buffer of %s (%d)", ZSTR_VAL(OG(active)->name), OG(active)->level); |
1430 | 0 | RETURN_FALSE; |
1431 | 0 | } |
1432 | 0 | RETURN_TRUE; |
1433 | 0 | } |
1434 | | /* }}} */ |
1435 | | |
1436 | | /* {{{ Flush (send) the output buffer, and delete current output buffer */ |
1437 | | PHP_FUNCTION(ob_end_flush) |
1438 | 0 | { |
1439 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1440 | 0 | RETURN_THROWS(); |
1441 | 0 | } |
1442 | | |
1443 | 0 | if (!OG(active)) { |
1444 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to delete and flush buffer. No buffer to delete or flush"); |
1445 | 0 | RETURN_FALSE; |
1446 | 0 | } |
1447 | | |
1448 | 0 | RETURN_BOOL(SUCCESS == php_output_end()); |
1449 | 0 | } |
1450 | | /* }}} */ |
1451 | | |
1452 | | /* {{{ Clean the output buffer, and delete current output buffer */ |
1453 | | PHP_FUNCTION(ob_end_clean) |
1454 | 0 | { |
1455 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1456 | 0 | RETURN_THROWS(); |
1457 | 0 | } |
1458 | | |
1459 | 0 | if (!OG(active)) { |
1460 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to delete buffer. No buffer to delete"); |
1461 | 0 | RETURN_FALSE; |
1462 | 0 | } |
1463 | | |
1464 | 0 | RETURN_BOOL(SUCCESS == php_output_discard()); |
1465 | 0 | } |
1466 | | /* }}} */ |
1467 | | |
1468 | | /* {{{ Get current buffer contents, flush (send) the output buffer, and delete current output buffer */ |
1469 | | PHP_FUNCTION(ob_get_flush) |
1470 | 0 | { |
1471 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1472 | 0 | RETURN_THROWS(); |
1473 | 0 | } |
1474 | | |
1475 | 0 | if (php_output_get_contents(return_value) == FAILURE) { |
1476 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to delete and flush buffer. No buffer to delete or flush"); |
1477 | 0 | RETURN_FALSE; |
1478 | 0 | } |
1479 | | |
1480 | 0 | if (SUCCESS != php_output_end()) { |
1481 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to delete buffer of %s (%d)", ZSTR_VAL(OG(active)->name), OG(active)->level); |
1482 | 0 | } |
1483 | 0 | } |
1484 | | /* }}} */ |
1485 | | |
1486 | | /* {{{ Get current buffer contents and delete current output buffer */ |
1487 | | PHP_FUNCTION(ob_get_clean) |
1488 | 0 | { |
1489 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1490 | 0 | RETURN_THROWS(); |
1491 | 0 | } |
1492 | | |
1493 | 0 | if(!OG(active)) { |
1494 | 0 | RETURN_FALSE; |
1495 | 0 | } |
1496 | | |
1497 | 0 | if (php_output_get_contents(return_value) == FAILURE) { |
1498 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to delete buffer. No buffer to delete"); |
1499 | 0 | RETURN_FALSE; |
1500 | 0 | } |
1501 | | |
1502 | 0 | if (SUCCESS != php_output_discard()) { |
1503 | 0 | php_error_docref("ref.outcontrol", E_NOTICE, "Failed to delete buffer of %s (%d)", ZSTR_VAL(OG(active)->name), OG(active)->level); |
1504 | 0 | } |
1505 | 0 | } |
1506 | | /* }}} */ |
1507 | | |
1508 | | /* {{{ Return the contents of the output buffer */ |
1509 | | PHP_FUNCTION(ob_get_contents) |
1510 | 0 | { |
1511 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1512 | 0 | RETURN_THROWS(); |
1513 | 0 | } |
1514 | | |
1515 | 0 | if (php_output_get_contents(return_value) == FAILURE) { |
1516 | 0 | RETURN_FALSE; |
1517 | 0 | } |
1518 | 0 | } |
1519 | | /* }}} */ |
1520 | | |
1521 | | /* {{{ Return the nesting level of the output buffer */ |
1522 | | PHP_FUNCTION(ob_get_level) |
1523 | 0 | { |
1524 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1525 | 0 | RETURN_THROWS(); |
1526 | 0 | } |
1527 | | |
1528 | 0 | RETURN_LONG(php_output_get_level()); |
1529 | 0 | } |
1530 | | /* }}} */ |
1531 | | |
1532 | | /* {{{ Return the length of the output buffer */ |
1533 | | PHP_FUNCTION(ob_get_length) |
1534 | 0 | { |
1535 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1536 | 0 | RETURN_THROWS(); |
1537 | 0 | } |
1538 | | |
1539 | 0 | if (php_output_get_length(return_value) == FAILURE) { |
1540 | 0 | RETURN_FALSE; |
1541 | 0 | } |
1542 | 0 | } |
1543 | | /* }}} */ |
1544 | | |
1545 | | /* {{{ List all output_buffers in an array */ |
1546 | | PHP_FUNCTION(ob_list_handlers) |
1547 | 0 | { |
1548 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1549 | 0 | RETURN_THROWS(); |
1550 | 0 | } |
1551 | | |
1552 | 0 | array_init(return_value); |
1553 | |
|
1554 | 0 | if (!OG(active)) { |
1555 | 0 | return; |
1556 | 0 | } |
1557 | | |
1558 | 0 | zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_list, return_value); |
1559 | 0 | } |
1560 | | /* }}} */ |
1561 | | |
1562 | | /* {{{ Return the status of the active or all output buffers */ |
1563 | | PHP_FUNCTION(ob_get_status) |
1564 | 0 | { |
1565 | 0 | bool full_status = 0; |
1566 | |
|
1567 | 0 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &full_status) == FAILURE) { |
1568 | 0 | RETURN_THROWS(); |
1569 | 0 | } |
1570 | | |
1571 | 0 | if (!OG(active)) { |
1572 | 0 | array_init(return_value); |
1573 | 0 | return; |
1574 | 0 | } |
1575 | | |
1576 | 0 | if (full_status) { |
1577 | 0 | array_init(return_value); |
1578 | 0 | zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_status, return_value); |
1579 | 0 | } else { |
1580 | 0 | php_output_handler_status(OG(active), return_value); |
1581 | 0 | } |
1582 | 0 | } |
1583 | | /* }}} */ |
1584 | | |
1585 | | /* {{{ Turn implicit flush on/off and is equivalent to calling flush() after every output call */ |
1586 | | PHP_FUNCTION(ob_implicit_flush) |
1587 | 0 | { |
1588 | 0 | zend_long flag = 1; |
1589 | |
|
1590 | 0 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &flag) == FAILURE) { |
1591 | 0 | RETURN_THROWS(); |
1592 | 0 | } |
1593 | | |
1594 | 0 | php_output_set_implicit_flush((int) flag); |
1595 | 0 | } |
1596 | | /* }}} */ |
1597 | | |
1598 | | /* {{{ Reset(clear) URL rewriter values */ |
1599 | | PHP_FUNCTION(output_reset_rewrite_vars) |
1600 | 0 | { |
1601 | 0 | if (zend_parse_parameters_none() == FAILURE) { |
1602 | 0 | RETURN_THROWS(); |
1603 | 0 | } |
1604 | | |
1605 | 0 | if (php_url_scanner_reset_vars() == SUCCESS) { |
1606 | 0 | RETURN_TRUE; |
1607 | 0 | } else { |
1608 | 0 | RETURN_FALSE; |
1609 | 0 | } |
1610 | 0 | } |
1611 | | /* }}} */ |
1612 | | |
1613 | | /* {{{ Add URL rewriter values */ |
1614 | | PHP_FUNCTION(output_add_rewrite_var) |
1615 | 0 | { |
1616 | 0 | char *name, *value; |
1617 | 0 | size_t name_len, value_len; |
1618 | |
|
1619 | 0 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &value, &value_len) == FAILURE) { |
1620 | 0 | RETURN_THROWS(); |
1621 | 0 | } |
1622 | | |
1623 | 0 | if (php_url_scanner_add_var(name, name_len, value, value_len, 1) == SUCCESS) { |
1624 | 0 | RETURN_TRUE; |
1625 | 0 | } else { |
1626 | 0 | RETURN_FALSE; |
1627 | 0 | } |
1628 | 0 | } |
1629 | | /* }}} */ |