diff options
author | Jani Taskinen <jani@php.net> | 2010-03-12 10:28:59 +0000 |
---|---|---|
committer | Jani Taskinen <jani@php.net> | 2010-03-12 10:28:59 +0000 |
commit | af49e58f5155383a440041c77cc1ecbaf507fde7 (patch) | |
tree | 01ffe64d6f78450fb828f73d0e1e59f3cc7c8c76 /main/output.c | |
parent | ea539c8b88c9278363b6de0b39446e4e8e043391 (diff) | |
download | php-git-af49e58f5155383a440041c77cc1ecbaf507fde7.tar.gz |
- Reverted r296062 and r296065
Diffstat (limited to 'main/output.c')
-rw-r--r-- | main/output.c | 1781 |
1 files changed, 677 insertions, 1104 deletions
diff --git a/main/output.c b/main/output.c index e6074e12ba..bee6ef8aa8 100644 --- a/main/output.c +++ b/main/output.c @@ -1,4 +1,4 @@ -/* +/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ @@ -15,1288 +15,775 @@ | Authors: Zeev Suraski <zeev@zend.com> | | Thies C. Arntzen <thies@thieso.net> | | Marcus Boerger <helly@php.net> | - | New API: Michael Wallner <mike@php.net> | +----------------------------------------------------------------------+ */ /* $Id$ */ -#ifndef PHP_OUTPUT_DEBUG -# define PHP_OUTPUT_DEBUG 0 -#endif -#ifndef PHP_OUTPUT_NOINLINE -# define PHP_OUTPUT_NOINLINE 0 -#endif - #include "php.h" #include "ext/standard/head.h" +#include "ext/standard/basic_functions.h" #include "ext/standard/url_scanner_ex.h" -#include "SAPI.h" -#include "zend_stack.h" -#include "php_output.h" - -ZEND_DECLARE_MODULE_GLOBALS(output); - -#if PHP_OUTPUT_NOINLINE || PHP_OUTPUT_DEBUG -# undef inline -# define inline +#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB) +#include "ext/zlib/php_zlib.h" #endif +#include "SAPI.h" -/* {{{ aliases, conflict and reverse conflict hash tables */ -static HashTable php_output_handler_aliases; -static HashTable php_output_handler_conflicts; -static HashTable php_output_handler_reverse_conflicts; -/* }}} */ - -/* {{{ forward declarations */ -static inline int php_output_lock_error(int op TSRMLS_DC); -static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC); - -#define php_output_handler_init(n, cs, f) php_output_handler_init_ex((n), (cs), (f) TSRMLS_CC) -static inline php_output_handler *php_output_handler_init_ex(zval *name, size_t chunk_size, int flags TSRMLS_DC); -static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context); -static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC); -static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry); - -static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC); -static inline void php_output_context_reset(php_output_context *context); -static inline void php_output_context_swap(php_output_context *context); -static inline void php_output_context_dtor(php_output_context *context); +#define OB_DEFAULT_HANDLER_NAME "default output handler" -static inline int php_output_stack_pop(int flags TSRMLS_DC); +/* output functions */ +static int php_b_body_write(const char *str, uint str_length TSRMLS_DC); -static int php_output_stack_apply_op(void *h, void *c); -static int php_output_stack_apply_clean(void *h, void *c); -static int php_output_stack_apply_list(void *h, void *z); -static int php_output_stack_apply_status(void *h, void *z); +static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC); +static void php_ob_append(const char *text, uint text_length TSRMLS_DC); +#if 0 +static void php_ob_prepend(const char *text, uint text_length); +#endif -static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context); -static int php_output_handler_default_func(void **handler_context, php_output_context *output_context); -static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context); -/* }}} */ +#ifdef ZTS +int output_globals_id; +#else +php_output_globals output_globals; +#endif -/* {{{ static void php_output_init_globals(zend_output_globals *G) - Initialize the module globals on MINIT */ -static inline void php_output_init_globals(zend_output_globals *G) +/* {{{ php_default_output_func */ +PHPAPI int php_default_output_func(const char *str, uint str_len TSRMLS_DC) { - memset(G, 0, sizeof(*G)); + fwrite(str, 1, str_len, stderr); +/* See http://support.microsoft.com/kb/190351 */ +#ifdef PHP_WIN32 + fflush(stderr); +#endif + return str_len; } /* }}} */ -/* {{{ void php_output_startup(void) - Set up module globals and initalize the conflict and reverse conflict hash tables */ -PHPAPI void php_output_startup(void) +/* {{{ php_output_init_globals */ +static void php_output_init_globals(php_output_globals *output_globals_p TSRMLS_DC) { - ZEND_INIT_MODULE_GLOBALS(output, php_output_init_globals, NULL); - zend_hash_init(&php_output_handler_aliases, 0, NULL, NULL, 1); - zend_hash_init(&php_output_handler_conflicts, 0, NULL, NULL, 1); - zend_hash_init(&php_output_handler_reverse_conflicts, 0, NULL, (void (*)(void *)) zend_hash_destroy, 1); + OG(php_body_write) = php_default_output_func; + OG(php_header_write) = php_default_output_func; + OG(implicit_flush) = 0; + OG(output_start_filename) = NULL; + OG(output_start_lineno) = 0; } /* }}} */ -/* {{{ void php_output_shutdown(void) - Destroy module globals and the conflict and reverse conflict hash tables */ -PHPAPI void php_output_shutdown(void) -{ - zend_hash_destroy(&php_output_handler_aliases); - zend_hash_destroy(&php_output_handler_conflicts); - zend_hash_destroy(&php_output_handler_reverse_conflicts); -} -/* }}} */ -/* {{{ SUCCESS|FAILURE php_output_activate(TSRMLS_D) - Reset output globals and setup the output handler stack */ -PHPAPI int php_output_activate(TSRMLS_D) +/* {{{ php_output_startup + Start output layer */ +PHPAPI void php_output_startup(void) { #ifdef ZTS - memset((*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)], 0, sizeof(zend_output_globals)); -#else - memset(&output_globals, 0, sizeof(zend_output_globals)); + ts_allocate_id(&output_globals_id, sizeof(php_output_globals), (ts_allocate_ctor) php_output_init_globals, NULL); +#else + php_output_init_globals(&output_globals TSRMLS_CC); #endif - - OG(handlers) = emalloc(sizeof(zend_stack)); - if (SUCCESS != zend_stack_init(OG(handlers))) { - return FAILURE; - } - - MAKE_STD_ZVAL(OG(default_output_handler_name)); - ZVAL_STRINGL(OG(default_output_handler_name), "default output handler", sizeof("default output handler")-1, 1); - MAKE_STD_ZVAL(OG(devnull_output_handler_name)); - ZVAL_STRINGL(OG(devnull_output_handler_name), "null output handler", sizeof("null output handler")-1, 1); - - return SUCCESS; } /* }}} */ -/* {{{ void php_output_deactivate(TSRMLS_D) - Destroy the output handler stack */ -PHPAPI void php_output_deactivate(TSRMLS_D) -{ - php_output_handler **handler = NULL; - - OG(active) = NULL; - OG(running) = NULL; - - /* release all output handlers */ - if (OG(handlers)) { - while (SUCCESS == zend_stack_top(OG(handlers), (void *) &handler)) { - php_output_handler_free(handler TSRMLS_CC); - zend_stack_del_top(OG(handlers)); - } - zend_stack_destroy(OG(handlers)); - efree(OG(handlers)); - OG(handlers) = NULL; - } - - if (OG(default_output_handler_name)) { - zval_ptr_dtor(&OG(default_output_handler_name)); - OG(default_output_handler_name) = NULL; - } - if (OG(devnull_output_handler_name)) { - zval_ptr_dtor(&OG(devnull_output_handler_name)); - OG(devnull_output_handler_name) = NULL; - } -} -/* }}} */ -/* {{{ void php_output_register_constants() */ -PHPAPI void php_output_register_constants(TSRMLS_D) +/* {{{ php_output_activate + Initilize output global for activation */ +PHPAPI void php_output_activate(TSRMLS_D) { - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_WRITE", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSH", PHP_OUTPUT_HANDLER_FLUSH, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEAN", PHP_OUTPUT_HANDLER_CLEAN, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FINAL", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT); - - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEANABLE", PHP_OUTPUT_HANDLER_CLEANABLE, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSHABLE", PHP_OUTPUT_HANDLER_FLUSHABLE, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_REMOVABLE", PHP_OUTPUT_HANDLER_REMOVABLE, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STDFLAGS", PHP_OUTPUT_HANDLER_STDFLAGS, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STARTED", PHP_OUTPUT_HANDLER_STARTED, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_DISABLED", PHP_OUTPUT_HANDLER_DISABLED, CONST_CS | CONST_PERSISTENT); + OG(php_body_write) = php_ub_body_write; + OG(php_header_write) = sapi_module.ub_write; + OG(ob_nesting_level) = 0; + OG(ob_lock) = 0; + OG(disable_output) = 0; + OG(output_start_filename) = NULL; + OG(output_start_lineno) = 0; } /* }}} */ -/* {{{ void php_output_set_status(int status TSRMLS_DC) - Used by SAPIs to disable output */ -PHPAPI void php_output_set_status(int status TSRMLS_DC) -{ - OG(flags) = status & 0xf; -} -/* }}} */ -/* {{{ int php_output_get_status(TSRMLS_C) - Get output control status */ -PHPAPI int php_output_get_status(TSRMLS_D) +/* {{{ php_output_set_status + Toggle output status. Do NOT use in application code, only in SAPIs where appropriate. */ +PHPAPI void php_output_set_status(zend_bool status TSRMLS_DC) { - return OG(flags) - | (OG(active) ? PHP_OUTPUT_ACTIVE : 0) - | (OG(running)? PHP_OUTPUT_LOCKED : 0); + OG(disable_output) = !status; } /* }}} */ -/* {{{ zval *php_output_get_default_handler_name(TSRMLS_C) */ -PHPAPI zval *php_output_get_default_handler_name(TSRMLS_D) +/* {{{ php_output_register_constants */ +void php_output_register_constants(TSRMLS_D) { - return OG(default_output_handler_name); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_CONT, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_END, CONST_CS | CONST_PERSISTENT); } /* }}} */ -/* {{{ zval *php_output_get_devnull_handler_name(TSRMLS_C) */ -PHPAPI zval *php_output_get_devnull_handler_name(TSRMLS_D) -{ - return OG(devnull_output_handler_name); -} -/* }}} */ -/* {{{ int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC) - Unbuffered write */ -PHPAPI int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC) +/* {{{ php_body_write + * Write body part */ +PHPAPI int php_body_write(const char *str, uint str_length TSRMLS_DC) { - if (OG(flags) & PHP_OUTPUT_DISABLED) { - return 0; - } - return sapi_module.ub_write(str, len TSRMLS_CC); + return OG(php_body_write)(str, str_length TSRMLS_CC); } /* }}} */ -/* {{{ int php_output_write(const char *str, size_t len TSRMLS_DC) - Buffered write */ -PHPAPI int php_output_write(const char *str, size_t len TSRMLS_DC) +/* {{{ php_header_write + * Write HTTP header */ +PHPAPI int php_header_write(const char *str, uint str_length TSRMLS_DC) { - if (OG(flags) & PHP_OUTPUT_DISABLED) { + if (OG(disable_output)) { return 0; + } else { + return OG(php_header_write)(str, str_length TSRMLS_CC); } - php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len TSRMLS_CC); - return (int) len; } /* }}} */ -/* {{{ SUCCESS|FAILURE php_output_flush(TSRMLS_D) - Flush the most recent output handlers buffer */ -PHPAPI int php_output_flush(TSRMLS_D) +/* {{{ php_start_ob_buffer + * Start output buffering */ +PHPAPI int php_start_ob_buffer(zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC) { - php_output_context context; - - if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_FLUSHABLE)) { - php_output_context_init(&context, PHP_OUTPUT_HANDLER_FLUSH TSRMLS_CC); - php_output_handler_op(OG(active), &context); - if (context.out.data && context.out.used) { - zend_stack_del_top(OG(handlers)); - php_output_write(context.out.data, context.out.used TSRMLS_CC); - zend_stack_push(OG(handlers), &OG(active), sizeof(php_output_handler *)); + uint initial_size, block_size; + + if (OG(ob_lock)) { + if (SG(headers_sent) && !SG(request_info).headers_only) { + OG(php_body_write) = php_ub_body_write_no_header; + } else { + OG(php_body_write) = php_ub_body_write; } - php_output_context_dtor(&context); - return SUCCESS; + OG(ob_nesting_level) = 0; + php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers"); + return FAILURE; } - return FAILURE; -} -/* }}} */ - -/* {{{ void php_output_flush_all(TSRMLS_C) - Flush all output buffers subsequently */ -PHPAPI void php_output_flush_all(TSRMLS_D) -{ - if (OG(active)) { - php_output_op(PHP_OUTPUT_HANDLER_FLUSH, NULL, 0 TSRMLS_CC); + if (chunk_size > 0) { + if (chunk_size==1) { + chunk_size = 4096; + } + initial_size = (chunk_size*3/2); + block_size = chunk_size/2; + } else { + initial_size = 40*1024; + block_size = 10*1024; } + return php_ob_init(initial_size, block_size, output_handler, chunk_size, erase TSRMLS_CC); } /* }}} */ -/* {{{ SUCCESS|FAILURE php_output_clean(TSRMLS_D) - Cleans the most recent output handlers buffer if the handler is cleanable */ -PHPAPI int php_output_clean(TSRMLS_D) +/* {{{ php_start_ob_buffer_named + * Start output buffering */ +PHPAPI int php_start_ob_buffer_named(const char *output_handler_name, uint chunk_size, zend_bool erase TSRMLS_DC) { - php_output_context context; - - if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_CLEANABLE)) { - OG(active)->buffer.used = 0; - php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC); - php_output_handler_op(OG(active), &context); - php_output_context_dtor(&context); - return SUCCESS; - } - return FAILURE; + zval *output_handler; + int result; + + ALLOC_INIT_ZVAL(output_handler); + Z_STRLEN_P(output_handler) = strlen(output_handler_name); /* this can be optimized */ + Z_STRVAL_P(output_handler) = estrndup(output_handler_name, Z_STRLEN_P(output_handler)); + Z_TYPE_P(output_handler) = IS_STRING; + result = php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC); + zval_dtor(output_handler); + FREE_ZVAL(output_handler); + return result; } /* }}} */ -/* {{{ void php_output_clean_all(TSRMLS_D) - Cleans all output handler buffers, without regard whether the handler is cleanable */ -PHPAPI void php_output_clean_all(TSRMLS_D) +/* {{{ php_end_ob_buffer + * End output buffering (one level) */ +PHPAPI void php_end_ob_buffer(zend_bool send_buffer, zend_bool just_flush TSRMLS_DC) { - php_output_context context; - - if (OG(active)) { - php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC); - zend_stack_apply_with_argument(OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_clean, &context); - } -} + char *final_buffer=NULL; + unsigned int final_buffer_length=0; + zval *alternate_buffer=NULL; + char *to_be_destroyed_buffer, *to_be_destroyed_handler_name; + char *to_be_destroyed_handled_output[2] = { 0, 0 }; + int status; + php_ob_buffer *prev_ob_buffer_p=NULL; + php_ob_buffer orig_ob_buffer; -/* {{{ SUCCESS|FAILURE php_output_end(TSRMLS_D) - Finalizes the most recent output handler at pops it off the stack if the handler is removable */ -PHPAPI int php_output_end(TSRMLS_D) -{ - if (php_output_stack_pop(PHP_OUTPUT_POP_TRY TSRMLS_CC)) { - return SUCCESS; + if (OG(ob_nesting_level)==0) { + return; + } + status = 0; + if (!OG(active_ob_buffer).status & PHP_OUTPUT_HANDLER_START) { + /* our first call */ + status |= PHP_OUTPUT_HANDLER_START; + } + if (just_flush) { + status |= PHP_OUTPUT_HANDLER_CONT; + } else { + status |= PHP_OUTPUT_HANDLER_END; } - return FAILURE; -} -/* }}} */ -/* {{{ void php_output_end_all(TSRMLS_D) - Finalizes all output handlers and ends output buffering without regard whether a handler is removable */ -PHPAPI void php_output_end_all(TSRMLS_D) -{ - while (OG(active) && php_output_stack_pop(PHP_OUTPUT_POP_FORCE TSRMLS_CC)); -} -/* }}} */ +#if 0 + { + FILE *fp; + fp = fopen("/tmp/ob_log", "a"); + fprintf(fp, "NestLevel: %d ObStatus: %d HandlerName: %s\n", OG(ob_nesting_level), status, OG(active_ob_buffer).handler_name); + fclose(fp); + } +#endif + + if (OG(active_ob_buffer).internal_output_handler) { + final_buffer = OG(active_ob_buffer).internal_output_handler_buffer; + final_buffer_length = OG(active_ob_buffer).internal_output_handler_buffer_size; + OG(active_ob_buffer).internal_output_handler(OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, &final_buffer, &final_buffer_length, status TSRMLS_CC); + } else if (OG(active_ob_buffer).output_handler) { + zval **params[2]; + zval *orig_buffer; + zval *z_status; + + ALLOC_INIT_ZVAL(orig_buffer); + ZVAL_STRINGL(orig_buffer, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1); + + ALLOC_INIT_ZVAL(z_status); + ZVAL_LONG(z_status, status); + + params[0] = &orig_buffer; + params[1] = &z_status; + OG(ob_lock) = 1; + + if (call_user_function_ex(CG(function_table), NULL, OG(active_ob_buffer).output_handler, &alternate_buffer, 2, params, 1, NULL TSRMLS_CC)==SUCCESS) { + if (alternate_buffer && !(Z_TYPE_P(alternate_buffer)==IS_BOOL && Z_BVAL_P(alternate_buffer)==0)) { + convert_to_string_ex(&alternate_buffer); + final_buffer = Z_STRVAL_P(alternate_buffer); + final_buffer_length = Z_STRLEN_P(alternate_buffer); + } + } + OG(ob_lock) = 0; + if (!just_flush) { + zval_ptr_dtor(&OG(active_ob_buffer).output_handler); + } + zval_ptr_dtor(&orig_buffer); + zval_ptr_dtor(&z_status); + } -/* {{{ SUCCESS|FAILURE php_output_discard(TSRMLS_D) - Discards the most recent output handlers buffer and pops it off the stack if the handler is removable */ -PHPAPI int php_output_discard(TSRMLS_D) -{ - if (php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_TRY TSRMLS_CC)) { - return SUCCESS; + if (!final_buffer) { + final_buffer = OG(active_ob_buffer).buffer; + final_buffer_length = OG(active_ob_buffer).text_length; } - return FAILURE; -} -/* }}} */ -/* {{{ void php_output_discard_all(TSRMLS_D) - Discard all output handlers and buffers without regard whether a handler is removable */ -PHPAPI void php_output_discard_all(TSRMLS_D) -{ - while (OG(active)) { - php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_FORCE TSRMLS_CC); + if (OG(ob_nesting_level)==1) { /* end buffering */ + if (SG(headers_sent) && !SG(request_info).headers_only) { + OG(php_body_write) = php_ub_body_write_no_header; + } else { + OG(php_body_write) = php_ub_body_write; + } } -} -/* }}} */ -/* {{{ int php_output_get_level(TSRMLS_D) - Get output buffering level, ie. how many output handlers the stack contains */ -PHPAPI int php_output_get_level(TSRMLS_D) -{ - return OG(active) ? zend_stack_count(OG(handlers)) : 0; -} -/* }}} */ + to_be_destroyed_buffer = OG(active_ob_buffer).buffer; + to_be_destroyed_handler_name = OG(active_ob_buffer).handler_name; + if (OG(active_ob_buffer).internal_output_handler + && (final_buffer != OG(active_ob_buffer).internal_output_handler_buffer) + && (final_buffer != OG(active_ob_buffer).buffer)) { + to_be_destroyed_handled_output[0] = final_buffer; + } -/* {{{ SUCCESS|FAILURE php_output_get_contents(zval *z TSRMLS_DC) - Get the contents of the active output handlers buffer */ -PHPAPI int php_output_get_contents(zval *p TSRMLS_DC) -{ - if (OG(active)) { - ZVAL_STRINGL(p, OG(active)->buffer.data, OG(active)->buffer.used, 1); - return SUCCESS; - } else { - ZVAL_NULL(p); - return FAILURE; + if (!just_flush) { + if (OG(active_ob_buffer).internal_output_handler) { + to_be_destroyed_handled_output[1] = OG(active_ob_buffer).internal_output_handler_buffer; + } } -} + if (OG(ob_nesting_level)>1) { /* restore previous buffer */ + zend_stack_top(&OG(ob_buffers), (void **) &prev_ob_buffer_p); + orig_ob_buffer = OG(active_ob_buffer); + OG(active_ob_buffer) = *prev_ob_buffer_p; + zend_stack_del_top(&OG(ob_buffers)); + if (!just_flush && OG(ob_nesting_level)==2) { /* destroy the stack */ + zend_stack_destroy(&OG(ob_buffers)); + } + } + OG(ob_nesting_level)--; -/* {{{ SUCCESS|FAILURE php_output_get_length(zval *z TSRMLS_DC) - Get the length of the active output handlers buffer */ -PHPAPI int php_output_get_length(zval *p TSRMLS_DC) -{ - if (OG(active)) { - ZVAL_LONG(p, OG(active)->buffer.used); - return SUCCESS; - } else { - ZVAL_NULL(p); - return FAILURE; + if (send_buffer) { + if (just_flush) { /* if flush is called prior to proper end, ensure presence of NUL */ + final_buffer[final_buffer_length] = '\0'; + } + OG(php_body_write)(final_buffer, final_buffer_length TSRMLS_CC); } -} -/* }}} */ -/* {{{ SUCCESS|FAILURE php_output_handler_start_default(TSRMLS_D) - Start a "default output handler" */ -PHPAPI int php_output_start_default(TSRMLS_D) -{ - php_output_handler *handler; - - handler = php_output_handler_create_internal(OG(default_output_handler_name), php_output_handler_default_func, 0, PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC); - if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { - return SUCCESS; + if (just_flush) { /* we restored the previous ob, return to the current */ + if (prev_ob_buffer_p) { + zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer)); + OG(active_ob_buffer) = orig_ob_buffer; + } + OG(ob_nesting_level)++; } - php_output_handler_free(&handler TSRMLS_CC); - return FAILURE; -} -/* }}} */ -/* {{{ SUCCESS|FAILURE php_output_handler_start_devnull(TSRMLS_D) - Start a "null output handler" */ -PHPAPI int php_output_start_devnull(TSRMLS_D) -{ - php_output_handler *handler; - - handler = php_output_handler_create_internal(OG(devnull_output_handler_name), php_output_handler_devnull_func, PHP_OUTPUT_HANDLER_DEFAULT_SIZE, 0 TSRMLS_CC); - if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { - return SUCCESS; + if (alternate_buffer) { + zval_ptr_dtor(&alternate_buffer); } - php_output_handler_free(&handler TSRMLS_CC); - return FAILURE; -} -/* }}} */ -/* {{{ SUCCESS|FAILURE php_output_start_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC) - Start a user level output handler */ -PHPAPI int php_output_start_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC) -{ - php_output_handler *handler; - - if (output_handler) { - handler = php_output_handler_create_user(output_handler, chunk_size, flags TSRMLS_CC); + if (status & PHP_OUTPUT_HANDLER_END) { + efree(to_be_destroyed_handler_name); + } + if (!just_flush) { + efree(to_be_destroyed_buffer); } else { - handler = php_output_handler_create_internal(OG(default_output_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC); + OG(active_ob_buffer).text_length = 0; + OG(active_ob_buffer).status |= PHP_OUTPUT_HANDLER_START; + OG(php_body_write) = php_b_body_write; } - if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { - return SUCCESS; + if (to_be_destroyed_handled_output[0]) { + efree(to_be_destroyed_handled_output[0]); } - php_output_handler_free(&handler TSRMLS_CC); - return FAILURE; -} -/* }}} */ - -/* {{{ SUCCESS|FAILURE php_output_start_internal(zval *name, php_output_handler_func_t handler, size_t chunk_size, int flags TSRMLS_DC) - Start an internal output handler that does not have to maintain a non-global state */ -PHPAPI int php_output_start_internal(zval *name, php_output_handler_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC) -{ - php_output_handler *handler; - - handler = php_output_handler_create_internal(name, php_output_handler_compat_func, chunk_size, flags TSRMLS_CC); - php_output_handler_set_context(handler, output_handler, NULL TSRMLS_CC); - if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { - return SUCCESS; + if (to_be_destroyed_handled_output[1]) { + efree(to_be_destroyed_handled_output[1]); } - php_output_handler_free(&handler TSRMLS_CC); - return FAILURE; } /* }}} */ -/* {{{ php_output_handler *php_output_handler_create_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC) - Create a user level output handler */ -PHPAPI php_output_handler *php_output_handler_create_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC) +/* {{{ php_end_ob_buffers + * End output buffering (all buffers) */ +PHPAPI void php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC) { - zval *handler_name = NULL; - char *tmp_name = NULL; - php_output_handler *handler = NULL; - php_output_handler_alias_ctor_t *alias = NULL; - php_output_handler_user_func_t *user = NULL; - - switch (Z_TYPE_P(output_handler)) { - case IS_NULL: - handler = php_output_handler_create_internal(OG(default_output_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC); - break; - case IS_STRING: - if (Z_STRLEN_P(output_handler) && (alias = php_output_handler_alias(output_handler TSRMLS_CC))) { - handler = (*alias)(output_handler, chunk_size, flags TSRMLS_CC); - break; - } - default: - user = ecalloc(1, sizeof(php_output_handler_user_func_t)); - MAKE_STD_ZVAL(handler_name); - if (SUCCESS == zend_fcall_info_init(output_handler, 0, &user->fci, &user->fcc, &tmp_name, NULL TSRMLS_CC)) { - ZVAL_STRING(handler_name, tmp_name, 1); - handler = php_output_handler_init(handler_name, chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_USER); - Z_ADDREF_P(output_handler); - user->zoh = output_handler; - handler->func.user = user; - } else { - /* TODO(helly) log the rror? */ - efree(user); - } - if (tmp_name) { - efree(tmp_name); - } - zval_ptr_dtor(&handler_name); + while (OG(ob_nesting_level)!=0) { + php_end_ob_buffer(send_buffer, 0 TSRMLS_CC); } - - return handler; } /* }}} */ -/* {{{ php_output_handler *php_output_handler_create_internal(zval *name, php_output_handler_context_func_t handler, size_t chunk_size, int flags TSRMLS_DC) - Create an internal output handler that can maintain a non-global state */ -PHPAPI php_output_handler *php_output_handler_create_internal(zval *name, php_output_handler_context_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC) +/* {{{ php_start_implicit_flush + */ +PHPAPI void php_start_implicit_flush(TSRMLS_D) { - php_output_handler *handler; - - handler = php_output_handler_init(name, chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_INTERNAL); - handler->func.internal = output_handler; - - return handler; + OG(implicit_flush)=1; } +/* }}} */ -/* {{{ void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC) - Set the context/state of an output handler. Calls the dtor of the previous context if there is one */ -PHPAPI void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC) +/* {{{ php_end_implicit_flush + */ +PHPAPI void php_end_implicit_flush(TSRMLS_D) { - if (handler->dtor && handler->opaq) { - handler->dtor(handler->opaq TSRMLS_CC); - } - handler->dtor = dtor; - handler->opaq = opaq; + OG(implicit_flush)=0; } /* }}} */ -/* {{{ SUCCESS|FAILURE php_output_handler_start(php_output_handler *handler TSRMLS_DC) - Starts the set up output handler and pushes it on top of the stack. Checks for any conflicts regarding the output handler to start */ -PHPAPI int php_output_handler_start(php_output_handler *handler TSRMLS_DC) +/* {{{ php_ob_set_internal_handler + */ +PHPAPI void php_ob_set_internal_handler(php_output_handler_func_t internal_output_handler, uint buffer_size, char *handler_name, zend_bool erase TSRMLS_DC) { - HashPosition pos; - HashTable *rconflicts; - php_output_handler_conflict_check_t *conflict; - - if (php_output_lock_error(PHP_OUTPUT_HANDLER_START TSRMLS_CC) || !handler) { - return FAILURE; - } - if (SUCCESS == zend_hash_find(&php_output_handler_conflicts, Z_STRVAL_P(handler->name), Z_STRLEN_P(handler->name)+1, (void *) &conflict)) { - if (SUCCESS != (*conflict)(handler->name TSRMLS_CC)) { - return FAILURE; - } + if (OG(ob_nesting_level)==0 || OG(active_ob_buffer).internal_output_handler || strcmp(OG(active_ob_buffer).handler_name, OB_DEFAULT_HANDLER_NAME)) { + php_start_ob_buffer(NULL, buffer_size, erase TSRMLS_CC); } - if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, Z_STRVAL_P(handler->name), Z_STRLEN_P(handler->name)+1, (void *) &rconflicts)) { - for ( zend_hash_internal_pointer_reset_ex(rconflicts, &pos); - zend_hash_get_current_data_ex(rconflicts, (void *) &conflict, &pos) == SUCCESS; - zend_hash_move_forward_ex(rconflicts, &pos)) { - if (SUCCESS != (*conflict)(handler->name TSRMLS_CC)) { - return FAILURE; - } - } - } - /* zend_stack_push never returns SUCCESS but FAILURE or stack level */ - if (FAILURE == (handler->level = zend_stack_push(OG(handlers), &handler, sizeof(php_output_handler *)))) { - return FAILURE; + + OG(active_ob_buffer).internal_output_handler = internal_output_handler; + OG(active_ob_buffer).internal_output_handler_buffer = (char *) emalloc(buffer_size); + OG(active_ob_buffer).internal_output_handler_buffer_size = buffer_size; + if (OG(active_ob_buffer).handler_name) { + efree(OG(active_ob_buffer).handler_name); } - OG(active) = handler; - return SUCCESS; + OG(active_ob_buffer).handler_name = estrdup(handler_name); + OG(active_ob_buffer).erase = erase; } /* }}} */ -/* {{{ int php_output_handler_started(zval *name TSRMLS_DC) - Check whether a certain output handler is in use */ -PHPAPI int php_output_handler_started(zval *name TSRMLS_DC) +/* + * Output buffering - implementation + */ + +/* {{{ php_ob_allocate + */ +static inline void php_ob_allocate(uint text_length TSRMLS_DC) { - php_output_handler **handlers; - int i, count = php_output_get_level(TSRMLS_C); - - if (count) { - handlers = *(php_output_handler ***) zend_stack_base(OG(handlers)); - - for (i = 0; i < count; ++i) { - if (!zend_binary_zval_strcmp(handlers[i]->name, name)) { - return 1; - } + uint new_len = OG(active_ob_buffer).text_length + text_length; + + if (OG(active_ob_buffer).size < new_len) { + uint buf_size = OG(active_ob_buffer).size; + while (buf_size <= new_len) { + buf_size += OG(active_ob_buffer).block_size; } + + OG(active_ob_buffer).buffer = (char *) erealloc(OG(active_ob_buffer).buffer, buf_size+1); + OG(active_ob_buffer).size = buf_size; } - - return 0; + OG(active_ob_buffer).text_length = new_len; } /* }}} */ -/* {{{ int php_output_handler_conflict(zval *handler_new, zval *handler_old TSRMLS_DC) - Check whether a certain handler is in use and issue a warning that the new handler would conflict with the already used one */ -PHPAPI int php_output_handler_conflict(zval *handler_new, zval *handler_set TSRMLS_DC) +/* {{{ php_ob_init_conflict + * Returns 1 if handler_set is already used and generates error message + */ +PHPAPI int php_ob_init_conflict(char *handler_new, char *handler_set TSRMLS_DC) { - if (php_output_handler_started(handler_set TSRMLS_CC)) { - if (zend_binary_zval_strcmp(handler_new, handler_set)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", Z_STRVAL_P(handler_new), Z_STRVAL_P(handler_set)); - } else { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' cannot be used twice", Z_STRVAL_P(handler_new)); - } + if (php_ob_handler_used(handler_set TSRMLS_CC)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set); return 1; } return 0; } /* }}} */ -/* {{{ SUCCESS|FAILURE php_output_handler_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC) - Register a conflict checking function on MINIT */ -PHPAPI int php_output_handler_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC) +/* {{{ php_ob_init_named + */ +static int php_ob_init_named(uint initial_size, uint block_size, char *handler_name, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC) { - if (!EG(current_module)) { - zend_error(E_ERROR, "Cannot register an output handler conflict outside of MINIT"); - return FAILURE; - } - return zend_hash_update(&php_output_handler_conflicts, Z_STRVAL_P(name), Z_STRLEN_P(name)+1, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL); -} -/* }}} */ + php_ob_buffer tmp_buf; -/* {{{ SUCCESS|FAILURE php_output_handler_reverse_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC) - Register a reverse conflict checking function on MINIT */ -PHPAPI int php_output_handler_reverse_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC) -{ - HashTable rev, *rev_ptr = NULL; - - if (!EG(current_module)) { - zend_error(E_ERROR, "Cannot register a reverse output handler conflict outside of MINIT"); + if (output_handler && !zend_is_callable(output_handler, 0, NULL TSRMLS_CC)) { return FAILURE; } - if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, Z_STRVAL_P(name), Z_STRLEN_P(name)+1, (void *) &rev_ptr)) { - return zend_hash_next_index_insert(rev_ptr, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL); - } else { - zend_hash_init(&rev, 1, NULL, NULL, 1); - if (SUCCESS != zend_hash_next_index_insert(&rev, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL)) { - zend_hash_destroy(&rev); + + tmp_buf.block_size = block_size; + tmp_buf.size = initial_size; + tmp_buf.buffer = (char *) emalloc(initial_size+1); + tmp_buf.text_length = 0; + tmp_buf.output_handler = output_handler; + tmp_buf.chunk_size = chunk_size; + tmp_buf.status = 0; + tmp_buf.internal_output_handler = NULL; + tmp_buf.internal_output_handler_buffer = NULL; + tmp_buf.internal_output_handler_buffer_size = 0; + tmp_buf.handler_name = estrdup(handler_name&&handler_name[0]?handler_name:OB_DEFAULT_HANDLER_NAME); + tmp_buf.erase = erase; + + if (OG(ob_nesting_level)>0) { +#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB) + if (!strncmp(handler_name, "ob_gzhandler", sizeof("ob_gzhandler")) && php_ob_gzhandler_check(TSRMLS_C)) { return FAILURE; } - if (SUCCESS != zend_hash_update(&php_output_handler_reverse_conflicts, Z_STRVAL_P(name), Z_STRLEN_P(name)+1, &rev, sizeof(HashTable), NULL)) { - zend_hash_destroy(&rev); - return FAILURE; +#endif + if (OG(ob_nesting_level)==1) { /* initialize stack */ + zend_stack_init(&OG(ob_buffers)); } - return SUCCESS; + zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer)); } + OG(ob_nesting_level)++; + OG(active_ob_buffer) = tmp_buf; + OG(php_body_write) = php_b_body_write; + return SUCCESS; } /* }}} */ -/* {{{ php_output_handler_alias_ctor_t php_output_handler_alias(zval *name TSRMLS_DC) - Get an internal output handler for a user handler if it exists */ -PHPAPI php_output_handler_alias_ctor_t *php_output_handler_alias(zval *name TSRMLS_DC) +/* {{{ php_ob_handler_from_string + * Create zval output handler from string + */ +static zval* php_ob_handler_from_string(const char *handler_name, int len TSRMLS_DC) { - php_output_handler_alias_ctor_t *func = NULL; - - zend_hash_find(&php_output_handler_aliases, Z_STRVAL_P(name), Z_STRLEN_P(name)+1, (void *) &func); - return func; -} -/* }}} */ + zval *output_handler; -/* {{{ SUCCESS|FAILURE php_output_handler_alias_register(zval *name, php_output_handler_alias_ctor_t func TSRMLS_DC) - Registers an internal output handler as alias for a user handler */ -PHPAPI int php_output_handler_alias_register(zval *name, php_output_handler_alias_ctor_t func TSRMLS_DC) -{ - if (!EG(current_module)) { - zend_error(E_ERROR, "Cannot register an output handler alias outside of MINIT"); - return FAILURE; - } - return zend_hash_update(&php_output_handler_aliases, Z_STRVAL_P(name), Z_STRLEN_P(name)+1, &func, sizeof(php_output_handler_alias_ctor_t *), NULL); + ALLOC_INIT_ZVAL(output_handler); + Z_STRLEN_P(output_handler) = len; + Z_STRVAL_P(output_handler) = estrndup(handler_name, len); + Z_TYPE_P(output_handler) = IS_STRING; + return output_handler; } /* }}} */ -/* {{{ SUCCESS|FAILURE php_output_handler_hook(php_output_handler_hook_t type, void *arg TSMRLS_DC) - Output handler hook for output handler functions to check/modify the current handlers abilities */ -PHPAPI int php_output_handler_hook(php_output_handler_hook_t type, void *arg TSRMLS_DC) +/* {{{ php_ob_init + */ +static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC) { - if (OG(running)) { - switch (type) { - case PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ: - *(void ***) arg = &OG(running)->opaq; - return SUCCESS; - case PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS: - *(int *) arg = OG(running)->flags; - return SUCCESS; - case PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL: - *(int *) arg = OG(running)->level; - case PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE: - OG(running)->flags &= ~(PHP_OUTPUT_HANDLER_REMOVABLE|PHP_OUTPUT_HANDLER_CLEANABLE); - return SUCCESS; - case PHP_OUTPUT_HANDLER_HOOK_DISABLE: - OG(running)->flags |= PHP_OUTPUT_HANDLER_DISABLED; - return SUCCESS; - default: - break; + int result = FAILURE, handler_len, len; + char *handler_name, *next_handler_name; + HashPosition pos; + zval **tmp; + zval *handler_zval; + + if (output_handler && output_handler->type == IS_STRING) { + handler_name = Z_STRVAL_P(output_handler); + handler_len = Z_STRLEN_P(output_handler); + + result = SUCCESS; + if (handler_len && handler_name[0] != '\0') { + while ((next_handler_name=strchr(handler_name, ',')) != NULL) { + len = next_handler_name-handler_name; + next_handler_name = estrndup(handler_name, len); + handler_zval = php_ob_handler_from_string(next_handler_name, len TSRMLS_CC); + result = php_ob_init_named(initial_size, block_size, next_handler_name, handler_zval, chunk_size, erase TSRMLS_CC); + if (result != SUCCESS) { + zval_dtor(handler_zval); + FREE_ZVAL(handler_zval); + } + handler_name += len+1; + handler_len -= len+1; + efree(next_handler_name); + } + } + if (result == SUCCESS) { + handler_zval = php_ob_handler_from_string(handler_name, handler_len TSRMLS_CC); + result = php_ob_init_named(initial_size, block_size, handler_name, handler_zval, chunk_size, erase TSRMLS_CC); + if (result != SUCCESS) { + zval_dtor(handler_zval); + FREE_ZVAL(handler_zval); + } + } + } else if (output_handler && output_handler->type == IS_ARRAY) { + /* do we have array(object,method) */ + if (zend_is_callable(output_handler, 0, &handler_name TSRMLS_CC)) { + SEPARATE_ZVAL(&output_handler); + Z_ADDREF_P(output_handler); + result = php_ob_init_named(initial_size, block_size, handler_name, output_handler, chunk_size, erase TSRMLS_CC); + efree(handler_name); + } else { + efree(handler_name); + /* init all array elements recursively */ + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(output_handler), &pos); + while (zend_hash_get_current_data_ex(Z_ARRVAL_P(output_handler), (void **)&tmp, &pos) == SUCCESS) { + result = php_ob_init(initial_size, block_size, *tmp, chunk_size, erase TSRMLS_CC); + if (result == FAILURE) { + break; + } + zend_hash_move_forward_ex(Z_ARRVAL_P(output_handler), &pos); + } } + } else if (output_handler && output_handler->type == IS_OBJECT) { + /* do we have callable object */ + if (zend_is_callable(output_handler, 0, &handler_name TSRMLS_CC)) { + SEPARATE_ZVAL(&output_handler); + Z_ADDREF_P(output_handler); + result = php_ob_init_named(initial_size, block_size, handler_name, output_handler, chunk_size, erase TSRMLS_CC); + efree(handler_name); + } else { + efree(handler_name); + php_error_docref(NULL TSRMLS_CC, E_ERROR, "No method name given: use ob_start(array($object,'method')) to specify instance $object and the name of a method of class %s to use as output handler", Z_OBJCE_P(output_handler)->name); + result = FAILURE; + } + } else { + result = php_ob_init_named(initial_size, block_size, OB_DEFAULT_HANDLER_NAME, NULL, chunk_size, erase TSRMLS_CC); } - return FAILURE; + return result; } /* }}} */ -/* {{{ void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC) - Destroy an output handler */ -PHPAPI void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC) +/* {{{ php_ob_list_each + */ +static int php_ob_list_each(php_ob_buffer *ob_buffer, zval *ob_handler_array) { - zval_ptr_dtor(&handler->name); - STR_FREE(handler->buffer.data); - if (handler->flags & PHP_OUTPUT_HANDLER_USER) { - zval_ptr_dtor(&handler->func.user->zoh); - efree(handler->func.user); - } - if (handler->dtor && handler->opaq) { - handler->dtor(handler->opaq TSRMLS_CC); - } - memset(handler, 0, sizeof(*handler)); + add_next_index_string(ob_handler_array, ob_buffer->handler_name, 1); + return 0; } /* }}} */ -/* {{{ void php_output_handler_free(php_output_handler **handler TSMRLS_DC) - Destroy and free an output handler */ -PHPAPI void php_output_handler_free(php_output_handler **h TSRMLS_DC) +/* {{{ proto false|array ob_list_handlers() + * List all output_buffers in an array + */ +PHP_FUNCTION(ob_list_handlers) { - if (*h) { - php_output_handler_dtor(*h TSRMLS_CC); - efree(*h); - *h = NULL; + if (zend_parse_parameters_none() == FAILURE) { + return; } -} -/* }}} */ - -/* void php_output_set_implicit_flush(int enabled TSRMLS_DC) - Enable or disable implicit flush */ -PHPAPI void php_output_set_implicit_flush(int flush TSRMLS_DC) -{ - if (flush) { - OG(flags) |= PHP_OUTPUT_IMPLICITFLUSH; - } else { - OG(flags) &= ~PHP_OUTPUT_IMPLICITFLUSH; + + array_init(return_value); + if (OG(ob_nesting_level)) { + if (OG(ob_nesting_level)>1) { + zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_list_each, return_value); + } + php_ob_list_each(&OG(active_ob_buffer), return_value); } } /* }}} */ -/* {{{ char *php_output_get_start_filename(TSRMLS_D) - Get the file name where output has started */ -PHPAPI char *php_output_get_start_filename(TSRMLS_D) -{ - return OG(output_start_filename); -} -/* }}} */ - -/* {{{ int php_output_get_start_lineno(TSRMLS_D) - Get the line number where output has started */ -PHPAPI int php_output_get_start_lineno(TSRMLS_D) -{ - return OG(output_start_lineno); -} -/* }}} */ - -/* {{{ static int php_output_lock_error(int op TSRMLS_DC) - Checks whether an unallowed operation is attempted from within the output handler and issues a fatal error */ -static inline int php_output_lock_error(int op TSRMLS_DC) +/* {{{ php_ob_used_each + * Sets handler_name to NULL is found + */ +static int php_ob_handler_used_each(php_ob_buffer *ob_buffer, char **handler_name) { - /* if there's no ob active, ob has been stopped */ - if (op && OG(active) && OG(running)) { - /* fatal error */ - php_output_deactivate(TSRMLS_C); - php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers"); + if (!strcmp(ob_buffer->handler_name, *handler_name)) { + *handler_name = NULL; return 1; } return 0; } /* }}} */ -/* {{{ static php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC) - Initialize a new output context */ -static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC) +/* {{{ php_ob_used + * returns 1 if given handler_name is used as output_handler + */ +PHPAPI int php_ob_handler_used(char *handler_name TSRMLS_DC) { - if (!context) { - context = emalloc(sizeof(php_output_context)); + char *tmp = handler_name; + + if (OG(ob_nesting_level)) { + if (!strcmp(OG(active_ob_buffer).handler_name, handler_name)) { + return 1; + } + if (OG(ob_nesting_level)>1) { + zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_handler_used_each, &tmp); + } } - - memset(context, 0, sizeof(php_output_context)); - TSRMLS_SET_CTX(context->tsrm_ls); - context->op = op; - - return context; + return tmp ? 0 : 1; } /* }}} */ -/* {{{ static void php_output_context_reset(php_output_context *context) - Reset an output context */ -static inline void php_output_context_reset(php_output_context *context) +/* {{{ php_ob_append + */ +static inline void php_ob_append(const char *text, uint text_length TSRMLS_DC) { - int op = context->op; - php_output_context_dtor(context); - memset(context, 0, sizeof(php_output_context)); - context->op = op; -} -/* }}} */ + char *target; + int original_ob_text_length; -/* {{{ static void php_output_context_swap(php_output_context *context) - Swap output contexts buffers */ -static inline void php_output_context_swap(php_output_context *context) -{ - if (context->in.free && context->in.data) { - efree(context->in.data); - } - context->in.data = context->out.data; - context->in.used = context->out.used; - context->in.free = context->out.free; - context->in.size = context->out.size; - context->out.data = NULL; - context->out.used = 0; - context->out.size = 0; -} -/* }}} */ + original_ob_text_length=OG(active_ob_buffer).text_length; -/* {{{ static void php_output_context_pass(php_output_context *context) - Pass input to output buffer */ -static inline void php_output_context_pass(php_output_context *context) -{ - context->out.data = context->in.data; - context->out.used = context->in.used; - context->out.size = context->in.size; - context->out.free = context->in.free; - context->in.data = NULL; - context->in.used = 0; - context->in.size = 0; -} -/* }}} */ + php_ob_allocate(text_length TSRMLS_CC); + target = OG(active_ob_buffer).buffer+original_ob_text_length; + memcpy(target, text, text_length); + target[text_length]=0; -/* {{{ static void php_output_context_dtor(php_output_context *context) - Destroy the contents of an output context */ -static inline void php_output_context_dtor(php_output_context *context) -{ - if (context->in.free && context->in.data) { - efree(context->in.data); - context->in.data = NULL; - } - if (context->out.free && context->out.data) { - efree(context->out.data); - context->out.data = NULL; + /* If implicit_flush is On or chunked buffering, send contents to next buffer and return. */ + if (OG(active_ob_buffer).chunk_size + && OG(active_ob_buffer).text_length >= OG(active_ob_buffer).chunk_size) { + + php_end_ob_buffer(1, 1 TSRMLS_CC); + return; } } /* }}} */ -/* {{{ static php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags) - Allocates and initializes a php_output_handler structure */ -static inline php_output_handler *php_output_handler_init_ex(zval *name, size_t chunk_size, int flags TSRMLS_DC) +#if 0 +static inline void php_ob_prepend(const char *text, uint text_length) { - php_output_handler *handler; - - handler = ecalloc(1, sizeof(php_output_handler)); - Z_ADDREF_P(name); - handler->name = name; - handler->size = chunk_size; - handler->flags = flags; - handler->buffer.size = PHP_OUTPUT_HANDLER_INITBUF_SIZE(chunk_size); - handler->buffer.data = emalloc(handler->buffer.size); - - return handler; -} -/* }}} */ + char *p, *start; + TSRMLS_FETCH(); -/* {{{ static int php_output_handler_appen(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC) - Appends input to the output handlers buffer and indicates whether the buffer does not have to be processed by the output handler */ -static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC) -{ - if (buf->used) { - OG(flags) |= PHP_OUTPUT_WRITTEN; - /* store it away */ - if ((handler->buffer.size - handler->buffer.used) <= buf->used) { - size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size); - size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used)); - size_t grow_max = MAX(grow_int, grow_buf); - - handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max); - handler->buffer.size += grow_max; - } - memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used); - handler->buffer.used += buf->used; - - /* chunked buffering */ - if (handler->size && (handler->buffer.used >= handler->size)) { - /* store away errors and/or any intermediate output */ - return OG(running) ? 1 : 0; - } - } - return 1; -} -/* }}} */ + php_ob_allocate(text_length TSRMLS_CC); -/* {{{ static php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context) - Output handler operation dispatcher, applying context op to the php_output_handler handler */ -static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context) -{ - php_output_handler_status_t status; - int original_op = context->op; - PHP_OUTPUT_TSRMLS(context); - -#if PHP_OUTPUT_DEBUG - fprintf(stderr, ">>> op(%d, " - "handler=%p, " - "name=%s, " - "flags=%d, " - "buffer.data=%s, " - "buffer.used=%zu, " - "buffer.size=%zu, " - "in.data=%s, " - "in.used=%zu)\n", - context->op, - handler, - Z_STRVAL_P(handler->name), - handler->flags, - handler->buffer.used?handler->buffer.data:"", - handler->buffer.used, - handler->buffer.size, - context->in.used?context->in.data:"", - context->in.used - ); -#endif - - if (php_output_lock_error(context->op TSRMLS_CC)) { - /* fatal error */ - return PHP_OUTPUT_HANDLER_FAILURE; - } - - /* storable? */ - if (php_output_handler_append(handler, &context->in TSRMLS_CC) && !context->op) { - status = PHP_OUTPUT_HANDLER_NO_DATA; - } else { - /* need to start? */ - if (!(handler->flags & PHP_OUTPUT_HANDLER_STARTED)) { - context->op |= PHP_OUTPUT_HANDLER_START; - } - - OG(running) = handler; - if (handler->flags & PHP_OUTPUT_HANDLER_USER) { - zval *retval = NULL, *ob_data, *ob_mode; - - MAKE_STD_ZVAL(ob_data); - ZVAL_STRINGL(ob_data, handler->buffer.data, handler->buffer.used, 1); - MAKE_STD_ZVAL(ob_mode); - ZVAL_LONG(ob_mode, (long) context->op); - zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 2, &ob_data, &ob_mode); - -#define PHP_OUTPUT_USER_SUCCESS(retval) (retval && !(Z_TYPE_P(retval) == IS_BOOL && Z_BVAL_P(retval)==0)) - if (SUCCESS == zend_fcall_info_call(&handler->func.user->fci, &handler->func.user->fcc, &retval, NULL TSRMLS_CC) && PHP_OUTPUT_USER_SUCCESS(retval)) { - /* user handler may have returned TRUE */ - status = PHP_OUTPUT_HANDLER_NO_DATA; - if (Z_TYPE_P(retval) != IS_BOOL) { - convert_to_string_ex(&retval); - if (Z_STRLEN_P(retval)) { - context->out.data = estrndup(Z_STRVAL_P(retval), Z_STRLEN_P(retval)); - context->out.used = Z_STRLEN_P(retval); - context->out.free = 1; - status = PHP_OUTPUT_HANDLER_SUCCESS; - } - } - } else { - /* call failed, pass internal buffer along */ - status = PHP_OUTPUT_HANDLER_FAILURE; - } - - zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 0); - zval_ptr_dtor(&ob_data); - zval_ptr_dtor(&ob_mode); - if (retval) { - zval_ptr_dtor(&retval); - } - - } else { - - context->in.data = handler->buffer.data; - context->in.used = handler->buffer.used; - context->in.free = 0; - - if (SUCCESS == handler->func.internal(&handler->opaq, context)) { - if (context->out.used) { - status = PHP_OUTPUT_HANDLER_SUCCESS; - } else { - status = PHP_OUTPUT_HANDLER_NO_DATA; - } - } else { - status = PHP_OUTPUT_HANDLER_FAILURE; - } - } - handler->flags |= PHP_OUTPUT_HANDLER_STARTED; - OG(running) = NULL; - } - - switch (status) { - case PHP_OUTPUT_HANDLER_FAILURE: - /* disable this handler */ - handler->flags |= PHP_OUTPUT_HANDLER_DISABLED; - /* discard any output */ - if (context->out.data && context->out.free) { - efree(context->out.data); - } - /* returns handlers buffer */ - context->out.data = handler->buffer.data; - context->out.used = handler->buffer.used; - context->out.free = 1; - handler->buffer.data = NULL; - handler->buffer.used = 0; - handler->buffer.size = 0; - break; - case PHP_OUTPUT_HANDLER_SUCCESS: - /* no more buffered data */ - handler->buffer.used = 0; - break; - case PHP_OUTPUT_HANDLER_NO_DATA: - /* handler ate all */ - php_output_context_reset(context); - break; + /* php_ob_allocate() may change OG(ob_buffer), so we can't initialize p&start earlier */ + p = OG(ob_buffer)+OG(ob_text_length); + start = OG(ob_buffer); + + while (--p>=start) { + p[text_length] = *p; } - - context->op = original_op; - return status; + memcpy(OG(ob_buffer), text, text_length); + OG(ob_buffer)[OG(active_ob_buffer).text_length]=0; } -/* }}} */ +#endif -/* {{{ static void php_output_op(int op, const char *str, size_t len TSRMLS_DC) - Output op dispatcher, passes input and output handlers output through the output handler stack until it gets written to the SAPI */ -static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC) +/* {{{ php_ob_get_buffer + * Return the current output buffer */ +PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC) { - php_output_context context; - php_output_handler **active; - int obh_cnt; - - if (php_output_lock_error(op TSRMLS_CC)) { - return; - } - - php_output_context_init(&context, op TSRMLS_CC); - - /* - * broken up for better performance: - * - apply op to the one active handler; note that OG(active) might be popped off the stack on a flush - * - or apply op to the handler stack - */ - if (OG(active) && (obh_cnt = zend_stack_count(OG(handlers)))) { - context.in.data = (char *) str; - context.in.used = len; - - if (obh_cnt > 1) { - zend_stack_apply_with_argument(OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_op, &context); - } else if ((SUCCESS == zend_stack_top(OG(handlers), (void *) &active)) && (!((*active)->flags & PHP_OUTPUT_HANDLER_DISABLED))) { - php_output_handler_op(*active, &context); - } else { - php_output_context_pass(&context); - } - } else { - context.out.data = (char *) str; - context.out.used = len; - } - - if (context.out.data && context.out.used) { -#if PHP_OUTPUT_DEBUG - fprintf(stderr, "::: sapi_write('%s', %zu)\n", context.out.data, context.out.used); -#endif - if (!SG(headers_sent) && php_header(TSRMLS_C)) { - if (zend_is_compiling(TSRMLS_C)) { - OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C); - OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C); - } else if (zend_is_executing(TSRMLS_C)) { - OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C); - OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C); - } -#if PHP_OUTPUT_DEBUG - fprintf(stderr, "!!! output started at: %s (%d)\n", OG(output_start_filename), OG(output_start_lineno)); -#endif - } - sapi_module.ub_write(context.out.data, context.out.used TSRMLS_CC); - if (OG(flags) & PHP_OUTPUT_IMPLICITFLUSH) { - sapi_flush(TSRMLS_C); - } - OG(flags) |= PHP_OUTPUT_SENT; + if (OG(ob_nesting_level)==0) { + return FAILURE; } - php_output_context_dtor(&context); + ZVAL_STRINGL(p, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1); + return SUCCESS; } /* }}} */ -/* {{{ static int php_output_stack_apply_op(void *h, void *c) - Operation callback for the stack apply function */ -static int php_output_stack_apply_op(void *h, void *c) +/* {{{ php_ob_get_length + * Return the size of the current output buffer */ +PHPAPI int php_ob_get_length(zval *p TSRMLS_DC) { - int was_disabled; - php_output_handler_status_t status; - php_output_handler *handler = *(php_output_handler **) h; - php_output_context *context = (php_output_context *) c; - - if ((was_disabled = (handler->flags & PHP_OUTPUT_HANDLER_DISABLED))) { - status = PHP_OUTPUT_HANDLER_FAILURE; - } else { - status = php_output_handler_op(handler, context); - } - - /* - * handler ate all => break - * handler returned data or failed resp. is disabled => continue - */ - switch (status) { - case PHP_OUTPUT_HANDLER_NO_DATA: - return 1; - - case PHP_OUTPUT_HANDLER_SUCCESS: - /* swap contexts buffers, unless this is the last handler in the stack */ - if (handler->level) { - php_output_context_swap(context); - } - return 0; - - case PHP_OUTPUT_HANDLER_FAILURE: - default: - if (was_disabled) { - /* pass input along, if it's the last handler in the stack */ - if (!handler->level) { - php_output_context_pass(context); - } - } else { - /* swap buffers, unless this is the last handler */ - if (handler->level) { - php_output_context_swap(context); - } - } - return 0; + if (OG(ob_nesting_level) == 0) { + return FAILURE; } + ZVAL_LONG(p, OG(active_ob_buffer).text_length); + return SUCCESS; } /* }}} */ -/* {{{ static int php_output_stack_apply_clean(void *h, void *c) - Clean callback for the stack apply function */ -static int php_output_stack_apply_clean(void *h, void *c) -{ - php_output_handler *handler = *(php_output_handler **) h; - php_output_context *context = (php_output_context *) c; - - handler->buffer.used = 0; - php_output_handler_op(handler, context); - php_output_context_reset(context); - return 0; -} -/* }}} */ +/* + * Wrapper functions - implementation + */ -/* {{{ static int php_output_stack_apply_list(void *h, void *z) - List callback for the stack apply function */ -static int php_output_stack_apply_list(void *h, void *z) -{ - php_output_handler *handler = *(php_output_handler **) h; - zval *array = (zval *) z; - - Z_ADDREF_P(handler->name); - add_next_index_zval(array, handler->name); - return 0; -} -/* }}} */ -/* {{{ static int php_output_stack_apply_status(void *h, void *z) - Status callback for the stack apply function */ -static int php_output_stack_apply_status(void *h, void *z) +/* buffered output function */ +static int php_b_body_write(const char *str, uint str_length TSRMLS_DC) { - php_output_handler *handler = *(php_output_handler **) h; - zval *array = (zval *) z; - - add_next_index_zval(array, php_output_handler_status(handler, NULL)); - - return 0; + php_ob_append(str, str_length TSRMLS_CC); + return str_length; } -/* }}} */ -/* {{{ static zval *php_output_handler_status(php_output_handler *handler, zval *entry) - Returns an array with the status of the output handler */ -static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry) +/* {{{ php_ub_body_write_no_header + */ +PHPAPI int php_ub_body_write_no_header(const char *str, uint str_length TSRMLS_DC) { - if (!entry) { - MAKE_STD_ZVAL(entry); - array_init(entry); + int result; + + if (OG(disable_output)) { + return 0; + } + + result = OG(php_header_write)(str, str_length TSRMLS_CC); + + if (OG(implicit_flush)) { + sapi_flush(TSRMLS_C); } - - Z_ADDREF_P(handler->name); - add_assoc_zval(entry, "name", handler->name); - add_assoc_long(entry, "type", (long) (handler->flags & 0xf)); - add_assoc_long(entry, "flags", (long) handler->flags); - add_assoc_long(entry, "level", (long) handler->level); - add_assoc_long(entry, "chunk_size", (long) handler->size); - add_assoc_long(entry, "buffer_size", (long) handler->buffer.size); - add_assoc_long(entry, "buffer_used", (long) handler->buffer.used); - - return entry; + + return result; } /* }}} */ -/* {{{ static int php_output_stack_pop(int flags TSRMLS_DC) - Pops an output handler off the stack */ -static inline int php_output_stack_pop(int flags TSRMLS_DC) +/* {{{ php_ub_body_write + */ +PHPAPI int php_ub_body_write(const char *str, uint str_length TSRMLS_DC) { - php_output_context context; - php_output_handler **current, *orphan = OG(active); - - if (!orphan) { - if (!(flags & PHP_OUTPUT_POP_SILENT)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer. No buffer to %s", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send"); - } - return 0; - } else if (!(flags & PHP_OUTPUT_POP_FORCE) && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) { - if (!(flags & PHP_OUTPUT_POP_SILENT)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer of %s (%d)", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", Z_STRVAL_P(orphan->name), orphan->level); - } - return 0; - } else { - php_output_context_init(&context, PHP_OUTPUT_HANDLER_FINAL TSRMLS_CC); - - /* don't run the output handler if it's disabled */ - if (!(orphan->flags & PHP_OUTPUT_HANDLER_DISABLED)) { - /* didn't it start yet? */ - if (!(orphan->flags & PHP_OUTPUT_HANDLER_STARTED)) { - context.op |= PHP_OUTPUT_HANDLER_START; - } - /* signal that we're cleaning up */ - if (flags & PHP_OUTPUT_POP_DISCARD) { - context.op |= PHP_OUTPUT_HANDLER_CLEAN; - orphan->buffer.used = 0; - } - php_output_handler_op(orphan, &context); + int result = 0; + + if (SG(request_info).headers_only) { + if(SG(headers_sent)) { + return 0; } - - /* pop it off the stack */ - zend_stack_del_top(OG(handlers)); - if (SUCCESS == zend_stack_top(OG(handlers), (void *) ¤t)) { - OG(active) = *current; - } else { - OG(active) = NULL; + php_header(TSRMLS_C); + zend_bailout(); + } + if (php_header(TSRMLS_C)) { + if (zend_is_compiling(TSRMLS_C)) { + OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C); + OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C); + } else if (zend_is_executing(TSRMLS_C)) { + OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C); + OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C); } - - /* pass output along */ - if (context.out.data && context.out.used && !(flags & PHP_OUTPUT_POP_DISCARD)) { - php_output_write(context.out.data, context.out.used TSRMLS_CC); - } - - /* destroy the handler (after write!) */ - php_output_handler_free(&orphan TSRMLS_CC); - php_output_context_dtor(&context); - - return 1; - } -} -/* }}} */ -/* {{{ static SUCCESS|FAILURE php_output_handler_compat_func(void *ctx, php_output_context *) - php_output_handler_context_func_t for php_output_handler_func_t output handlers */ -static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context) -{ - php_output_handler_func_t func = *(php_output_handler_func_t *) handler_context; - PHP_OUTPUT_TSRMLS(output_context); - - if (func) { - func(output_context->in.data, output_context->in.used, &output_context->out.data, &output_context->out.used, output_context->op TSRMLS_CC); - output_context->out.free = 1; - return SUCCESS; + OG(php_body_write) = php_ub_body_write_no_header; + result = php_ub_body_write_no_header(str, str_length TSRMLS_CC); } - return FAILURE; -} -/* }}} */ - -/* {{{ static SUCCESS|FAILURE php_output_handler_default_func(void *ctx, php_output_context *) - Default output handler */ -static int php_output_handler_default_func(void **handler_context, php_output_context *output_context) -{ - output_context->out.data = output_context->in.data; - output_context->out.used = output_context->in.used; - output_context->out.free = output_context->in.free; - output_context->in.data = NULL; - output_context->in.used = 0; - output_context->in.free = 0; - return SUCCESS; -} -/* }}} */ -/* {{{ static SUCCESS|FAILURE php_output_handler_devnull_func(void *ctx, php_output_context *) - Null output handler */ -static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context) -{ - return SUCCESS; + return result; } /* }}} */ /* - * USERLAND (nearly 1:1 of old output.c) + * HEAD support */ -/* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, int flags]]]) U +/* {{{ proto bool ob_start([ string|array user_function [, int chunk_size [, bool erase]]]) Turn on Output Buffering (specifying an optional output handler). */ PHP_FUNCTION(ob_start) { - zval *output_handler = NULL; - long chunk_size = 0; - long flags = PHP_OUTPUT_HANDLER_STDFLAGS; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/ll", &output_handler, &chunk_size, &flags)) { - RETURN_FALSE; + zval *output_handler=NULL; + long chunk_size=0; + zend_bool erase=1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zlb", &output_handler, &chunk_size, &erase) == FAILURE) { + return; } - if (chunk_size < 0) { + + if (chunk_size < 0) chunk_size = 0; - } - - if (SUCCESS != php_output_start_user(output_handler, chunk_size, flags TSRMLS_CC)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to create buffer"); + + if (php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC)==FAILURE) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ -/* {{{ proto bool ob_flush(void) U +/* {{{ proto bool ob_flush(void) Flush (send) contents of the output buffer. The last buffer content is sent to next buffer */ PHP_FUNCTION(ob_flush) { if (zend_parse_parameters_none() == FAILURE) { return; } - - if (!OG(active)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush"); + + if (!OG(ob_nesting_level)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush."); RETURN_FALSE; } - if (SUCCESS != php_output_flush(TSRMLS_C)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer of %s (%d)", Z_STRVAL_P(OG(active)->name), OG(active)->level); + + if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer %s.", OG(active_ob_buffer).handler_name); RETURN_FALSE; } + + php_end_ob_buffer(1, 1 TSRMLS_CC); RETURN_TRUE; } /* }}} */ -/* {{{ proto bool ob_clean(void) U +/* {{{ proto bool ob_clean(void) Clean (delete) the current output buffer */ PHP_FUNCTION(ob_clean) { @@ -1304,19 +791,22 @@ PHP_FUNCTION(ob_clean) return; } - if (!OG(active)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); + if (!OG(ob_nesting_level)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete."); RETURN_FALSE; } - if (SUCCESS != php_output_clean(TSRMLS_C)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", Z_STRVAL_P(OG(active)->name), OG(active)->level); + + if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active_ob_buffer).handler_name); RETURN_FALSE; } + + php_end_ob_buffer(0, 1 TSRMLS_CC); RETURN_TRUE; } /* }}} */ -/* {{{ proto bool ob_end_flush(void) U +/* {{{ proto bool ob_end_flush(void) Flush (send) the output buffer, and delete current output buffer */ PHP_FUNCTION(ob_end_flush) { @@ -1324,156 +814,238 @@ PHP_FUNCTION(ob_end_flush) return; } - if (!OG(active)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush"); + if (!OG(ob_nesting_level)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush."); RETURN_FALSE; } - RETURN_BOOL(SUCCESS == php_output_end(TSRMLS_C)); + if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active_ob_buffer).handler_name); + RETURN_FALSE; + } + + php_end_ob_buffer(1, 0 TSRMLS_CC); + RETURN_TRUE; } /* }}} */ -/* {{{ proto bool ob_end_clean(void) U +/* {{{ proto bool ob_end_clean(void) Clean the output buffer, and delete current output buffer */ PHP_FUNCTION(ob_end_clean) { if (zend_parse_parameters_none() == FAILURE) { return; } - - if (!OG(active)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); + + if (!OG(ob_nesting_level)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete."); RETURN_FALSE; } - RETURN_BOOL(SUCCESS == php_output_discard(TSRMLS_C)); + if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active_ob_buffer).handler_name); + RETURN_FALSE; + } + + php_end_ob_buffer(0, 0 TSRMLS_CC); + RETURN_TRUE; } /* }}} */ -/* {{{ proto bool ob_get_flush(void) U +/* {{{ proto bool ob_get_flush(void) Get current buffer contents, flush (send) the output buffer, and delete current output buffer */ PHP_FUNCTION(ob_get_flush) { if (zend_parse_parameters_none() == FAILURE) { return; } - - if (SUCCESS != php_output_get_contents(return_value TSRMLS_CC)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush"); + + /* get contents */ + if (php_ob_get_buffer(return_value TSRMLS_CC)==FAILURE) { RETURN_FALSE; } - if (SUCCESS != php_output_end(TSRMLS_C)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", Z_STRVAL_P(OG(active)->name), OG(active)->level); + /* error checks */ + if (!OG(ob_nesting_level)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush."); + zval_dtor(return_value); + RETURN_FALSE; } + if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active_ob_buffer).handler_name); + zval_dtor(return_value); + RETURN_FALSE; + } + /* flush */ + php_end_ob_buffer(1, 0 TSRMLS_CC); } /* }}} */ -/* {{{ proto bool ob_get_clean(void) U +/* {{{ proto bool ob_get_clean(void) Get current buffer contents and delete current output buffer */ PHP_FUNCTION(ob_get_clean) { if (zend_parse_parameters_none() == FAILURE) { return; } - - if (SUCCESS != php_output_get_contents(return_value TSRMLS_CC)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); + /* get contents */ + if (php_ob_get_buffer(return_value TSRMLS_CC)==FAILURE) { RETURN_FALSE; } - if (SUCCESS != php_output_discard(TSRMLS_C)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", Z_STRVAL_P(OG(active)->name), OG(active)->level); + /* error checks */ + if (!OG(ob_nesting_level)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete."); + zval_dtor(return_value); + RETURN_FALSE; } + if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active_ob_buffer).handler_name); + zval_dtor(return_value); + RETURN_FALSE; + } + /* delete buffer */ + php_end_ob_buffer(0, 0 TSRMLS_CC); } /* }}} */ -/* {{{ proto string ob_get_contents(void) U +/* {{{ proto string ob_get_contents(void) Return the contents of the output buffer */ PHP_FUNCTION(ob_get_contents) { if (zend_parse_parameters_none() == FAILURE) { return; } - if (SUCCESS != php_output_get_contents(return_value TSRMLS_CC)) { + + if (php_ob_get_buffer(return_value TSRMLS_CC)==FAILURE) { RETURN_FALSE; } } /* }}} */ -/* {{{ proto int ob_get_level(void) U +/* {{{ proto int ob_get_level(void) Return the nesting level of the output buffer */ PHP_FUNCTION(ob_get_level) { if (zend_parse_parameters_none() == FAILURE) { return; } - RETURN_LONG(php_output_get_level(TSRMLS_C)); + + RETURN_LONG (OG(ob_nesting_level)); } /* }}} */ -/* {{{ proto int ob_get_length(void) U +/* {{{ proto int ob_get_length(void) Return the length of the output buffer */ PHP_FUNCTION(ob_get_length) { if (zend_parse_parameters_none() == FAILURE) { return; } - if (SUCCESS != php_output_get_length(return_value TSRMLS_CC)) { + + if (php_ob_get_length(return_value TSRMLS_CC)==FAILURE) { RETURN_FALSE; } } /* }}} */ -/* {{{ proto false|array ob_list_handlers() U - * List all output_buffers in an array - */ -PHP_FUNCTION(ob_list_handlers) +/* {{{ int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result) */ +static int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result) { - if (zend_parse_parameters_none() == FAILURE) { - return; - } + zval *elem; - array_init(return_value); + MAKE_STD_ZVAL(elem); + array_init(elem); - if (!OG(active)) { - return; + add_assoc_long(elem, "chunk_size", ob_buffer->chunk_size); + if (!ob_buffer->chunk_size) { + add_assoc_long(elem, "size", ob_buffer->size); + add_assoc_long(elem, "block_size", ob_buffer->block_size); } - - zend_stack_apply_with_argument(OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_list, return_value); + if (ob_buffer->internal_output_handler) { + add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_INTERNAL); + add_assoc_long(elem, "buffer_size", ob_buffer->internal_output_handler_buffer_size); + } + else { + add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_USER); + } + add_assoc_long(elem, "status", ob_buffer->status); + add_assoc_string(elem, "name", ob_buffer->handler_name, 1); + add_assoc_bool(elem, "del", ob_buffer->erase); + add_next_index_zval(result, elem); + + return SUCCESS; } /* }}} */ -/* {{{ proto false|array ob_get_status([bool full_status]) U + +/* {{{ proto false|array ob_get_status([bool full_status]) Return the status of the active or all output buffers */ PHP_FUNCTION(ob_get_status) { zend_bool full_status = 0; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &full_status)) { - RETURN_FALSE; - } - if (!OG(active)) { - RETURN_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &full_status) == FAILURE ) { + return; } - + array_init(return_value); + if (full_status) { - zend_stack_apply_with_argument(OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_status, return_value); - } else { - php_output_handler_status(OG(active), return_value); + if (OG(ob_nesting_level)>1) { + zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *elem, void *))php_ob_buffer_status, return_value); + } + if (OG(ob_nesting_level)>0 && php_ob_buffer_status(&OG(active_ob_buffer), return_value)==FAILURE) { + RETURN_FALSE; + } + } else if (OG(ob_nesting_level)>0) { + add_assoc_long(return_value, "level", OG(ob_nesting_level)); + if (OG(active_ob_buffer).internal_output_handler) { + add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_INTERNAL); + } else { + add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_USER); + } + add_assoc_long(return_value, "status", OG(active_ob_buffer).status); + add_assoc_string(return_value, "name", OG(active_ob_buffer).handler_name, 1); + add_assoc_bool(return_value, "del", OG(active_ob_buffer).erase); } } /* }}} */ -/* {{{ proto void ob_implicit_flush([int flag]) U + +/* {{{ proto void ob_implicit_flush([int flag]) Turn implicit flush on/off and is equivalent to calling flush() after every output call */ PHP_FUNCTION(ob_implicit_flush) { long flag = 1; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flag)) { - php_output_set_implicit_flush(flag TSRMLS_CC); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flag) == FAILURE) { + return; } + + if (flag) { + php_start_implicit_flush(TSRMLS_C); + } else { + php_end_implicit_flush(TSRMLS_C); + } +} +/* }}} */ + + +/* {{{ char *php_get_output_start_filename(TSRMLS_D) + Return filename start output something */ +PHPAPI char *php_get_output_start_filename(TSRMLS_D) +{ + return OG(output_start_filename); +} +/* }}} */ + + +/* {{{ char *php_get_output_start_lineno(TSRMLS_D) + Return line number start output something */ +PHPAPI int php_get_output_start_lineno(TSRMLS_D) +{ + return OG(output_start_lineno); } /* }}} */ + /* {{{ proto bool output_reset_rewrite_vars(void) Reset(clear) URL rewriter values */ PHP_FUNCTION(output_reset_rewrite_vars) @@ -1486,6 +1058,7 @@ PHP_FUNCTION(output_reset_rewrite_vars) } /* }}} */ + /* {{{ proto bool output_add_rewrite_var(string name, string value) Add URL rewriter values */ PHP_FUNCTION(output_add_rewrite_var) @@ -1494,7 +1067,7 @@ PHP_FUNCTION(output_add_rewrite_var) int name_len, value_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &value, &value_len) == FAILURE) { - RETURN_FALSE; + return; } if (php_url_scanner_add_var(name, name_len, value, value_len, 1 TSRMLS_CC) == SUCCESS) { |