version 1.1.1.1, 2012/02/21 23:48:05
|
version 1.1.1.2, 2012/05/29 12:34:35
|
Line 15
|
Line 15
|
| Authors: Zeev Suraski <zeev@zend.com> | |
| Authors: Zeev Suraski <zeev@zend.com> | |
| Thies C. Arntzen <thies@thieso.net> | |
| Thies C. Arntzen <thies@thieso.net> | |
| Marcus Boerger <helly@php.net> | |
| Marcus Boerger <helly@php.net> | |
|
| New API: Michael Wallner <mike@php.net> | |
+----------------------------------------------------------------------+ |
+----------------------------------------------------------------------+ |
*/ |
*/ |
|
|
/* $Id$ */ |
/* $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 "php.h" |
#include "ext/standard/head.h" |
#include "ext/standard/head.h" |
#include "ext/standard/basic_functions.h" |
|
#include "ext/standard/url_scanner_ex.h" |
#include "ext/standard/url_scanner_ex.h" |
#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB) |
|
#include "ext/zlib/php_zlib.h" |
|
#endif |
|
#include "SAPI.h" |
#include "SAPI.h" |
|
#include "zend_stack.h" |
|
#include "php_output.h" |
|
|
#define OB_DEFAULT_HANDLER_NAME "default output handler" | ZEND_DECLARE_MODULE_GLOBALS(output); |
|
|
/* output functions */ | const char php_output_default_handler_name[sizeof("default output handler")] = "default output handler"; |
static int php_b_body_write(const char *str, uint str_length TSRMLS_DC); | const char php_output_devnull_handler_name[sizeof("null output handler")] = "null output handler"; |
|
|
static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC); | #if PHP_OUTPUT_NOINLINE || PHP_OUTPUT_DEBUG |
static void php_ob_append(const char *text, uint text_length TSRMLS_DC); | # undef inline |
#if 0 | # define inline |
static void php_ob_prepend(const char *text, uint text_length); | |
#endif |
#endif |
|
|
#ifdef ZTS | /* {{{ aliases, conflict and reverse conflict hash tables */ |
int output_globals_id; | static HashTable php_output_handler_aliases; |
#else | static HashTable php_output_handler_conflicts; |
php_output_globals output_globals; | static HashTable php_output_handler_reverse_conflicts; |
#endif | /* }}} */ |
|
|
/* {{{ php_default_output_func */ | /* {{{ forward declarations */ |
PHPAPI int php_default_output_func(const char *str, uint str_len TSRMLS_DC) | 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); |
| |
| static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, 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); |
| |
| static inline int php_output_stack_pop(int flags 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_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); |
| /* }}} */ |
| |
| /* {{{ 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) |
{ |
{ |
|
memset(G, 0, sizeof(*G)); |
|
} |
|
/* }}} */ |
|
|
|
/* {{{ stderr/stdout writer if not PHP_OUTPUT_ACTIVATED */ |
|
static int php_output_stdout(const char *str, size_t str_len) |
|
{ |
|
fwrite(str, 1, str_len, stdout); |
|
return str_len; |
|
} |
|
static int php_output_stderr(const char *str, size_t str_len) |
|
{ |
fwrite(str, 1, str_len, stderr); |
fwrite(str, 1, str_len, stderr); |
/* See http://support.microsoft.com/kb/190351 */ |
/* See http://support.microsoft.com/kb/190351 */ |
#ifdef PHP_WIN32 |
#ifdef PHP_WIN32 |
Line 56 PHPAPI int php_default_output_func(const char *str, ui
|
Line 100 PHPAPI int php_default_output_func(const char *str, ui
|
#endif |
#endif |
return str_len; |
return str_len; |
} |
} |
|
static int (*php_output_direct)(const char *str, size_t str_len) = php_output_stderr; |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_output_init_globals */ | /* {{{ void php_output_header(TSRMLS_D) */ |
static void php_output_init_globals(php_output_globals *output_globals_p TSRMLS_DC) | static void php_output_header(TSRMLS_D) |
{ |
{ |
OG(php_body_write) = php_default_output_func; | if (!SG(headers_sent)) { |
OG(php_header_write) = php_default_output_func; | if (!OG(output_start_filename)) { |
OG(implicit_flush) = 0; | if (zend_is_compiling(TSRMLS_C)) { |
OG(output_start_filename) = NULL; | OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C); |
OG(output_start_lineno) = 0; | 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 |
| } |
| if (!php_header(TSRMLS_C)) { |
| OG(flags) |= PHP_OUTPUT_DISABLED; |
| } |
| } |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_output_startup | /* {{{ void php_output_startup(void) |
* Start output layer */ | * Set up module globals and initalize the conflict and reverse conflict hash tables */ |
PHPAPI void php_output_startup(void) |
PHPAPI void php_output_startup(void) |
{ |
{ |
|
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); |
|
php_output_direct = php_output_stdout; |
|
} |
|
/* }}} */ |
|
|
|
/* {{{ void php_output_shutdown(void) |
|
* Destroy module globals and the conflict and reverse conflict hash tables */ |
|
PHPAPI void php_output_shutdown(void) |
|
{ |
|
php_output_direct = php_output_stderr; |
|
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) |
|
{ |
#ifdef ZTS |
#ifdef ZTS |
ts_allocate_id(&output_globals_id, sizeof(php_output_globals), (ts_allocate_ctor) php_output_init_globals, NULL); | memset((*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)], 0, sizeof(zend_output_globals)); |
#else |
#else |
php_output_init_globals(&output_globals TSRMLS_CC); | memset(&output_globals, 0, sizeof(zend_output_globals)); |
#endif |
#endif |
|
|
|
zend_stack_init(&OG(handlers)); |
|
OG(flags) |= PHP_OUTPUT_ACTIVATED; |
|
|
|
return SUCCESS; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_output_activate | /* {{{ void php_output_deactivate(TSRMLS_D) |
* Initilize output global for activation */ | * Destroy the output handler stack */ |
PHPAPI void php_output_activate(TSRMLS_D) | PHPAPI void php_output_deactivate(TSRMLS_D) |
{ |
{ |
OG(php_body_write) = php_ub_body_write; | php_output_handler **handler = NULL; |
OG(php_header_write) = sapi_module.ub_write; | |
OG(ob_nesting_level) = 0; | php_output_header(TSRMLS_C); |
OG(ob_lock) = 0; | |
OG(disable_output) = 0; | OG(flags) ^= PHP_OUTPUT_ACTIVATED; |
OG(output_start_filename) = NULL; | OG(active) = NULL; |
OG(output_start_lineno) = 0; | OG(running) = NULL; |
| |
| /* release all output handlers */ |
| if (OG(handlers).elements) { |
| 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)); |
| } |
| |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_output_register_constants */ | /* {{{ void php_output_register_constants() */ |
void php_output_register_constants(TSRMLS_D) | PHPAPI void php_output_register_constants(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_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_WRITE", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT); |
REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_END, 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); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_output_set_status | /* {{{ void php_output_set_status(int status TSRMLS_DC) |
* Toggle output status. Do NOT use in application code, only in SAPIs where appropriate. */ | * Used by SAPIs to disable output */ |
PHPAPI void php_output_set_status(zend_bool status TSRMLS_DC) | PHPAPI void php_output_set_status(int status TSRMLS_DC) |
{ |
{ |
OG(disable_output) = !status; | OG(flags) = status & 0xf; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_body_write | /* {{{ int php_output_get_status(TSRMLS_C) |
* Write body part */ | * Get output control status */ |
PHPAPI int php_body_write(const char *str, uint str_length TSRMLS_DC) | PHPAPI int php_output_get_status(TSRMLS_D) |
{ |
{ |
return OG(php_body_write)(str, str_length TSRMLS_CC); | return ( |
| OG(flags) |
| | (OG(active) ? PHP_OUTPUT_ACTIVE : 0) |
| | (OG(running)? PHP_OUTPUT_LOCKED : 0) |
| ) & 0xff; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_header_write | /* {{{ int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC) |
* Write HTTP header */ | * Unbuffered write */ |
PHPAPI int php_header_write(const char *str, uint str_length TSRMLS_DC) | PHPAPI int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC) |
{ |
{ |
if (OG(disable_output)) { | if (OG(flags) & PHP_OUTPUT_DISABLED) { |
return 0; |
return 0; |
} else { |
|
return OG(php_header_write)(str, str_length TSRMLS_CC); |
|
} |
} |
|
if (OG(flags) & PHP_OUTPUT_ACTIVATED) { |
|
return sapi_module.ub_write(str, len TSRMLS_CC); |
|
} |
|
return php_output_direct(str, len); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_start_ob_buffer | /* {{{ int php_output_write(const char *str, size_t len TSRMLS_DC) |
* Start output buffering */ | * Buffered write */ |
PHPAPI int php_start_ob_buffer(zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC) | PHPAPI int php_output_write(const char *str, size_t len TSRMLS_DC) |
{ |
{ |
uint initial_size, block_size; | if (OG(flags) & PHP_OUTPUT_DISABLED) { |
| return 0; |
| } |
| if (OG(flags) & PHP_OUTPUT_ACTIVATED) { |
| php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len TSRMLS_CC); |
| return (int) len; |
| } |
| return php_output_direct(str, len); |
| } |
| /* }}} */ |
|
|
if (OG(ob_lock)) { | /* {{{ SUCCESS|FAILURE php_output_flush(TSRMLS_D) |
if (SG(headers_sent) && !SG(request_info).headers_only) { | * Flush the most recent output handlers buffer */ |
OG(php_body_write) = php_ub_body_write_no_header; | PHPAPI int php_output_flush(TSRMLS_D) |
} else { | { |
OG(php_body_write) = php_ub_body_write; | 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 *)); |
} |
} |
OG(ob_nesting_level) = 0; | php_output_context_dtor(&context); |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers"); | return SUCCESS; |
return FAILURE; | |
} |
} |
if (chunk_size > 0) { | return FAILURE; |
if (chunk_size==1) { | } |
chunk_size = 4096; | /* }}} */ |
} | |
initial_size = (chunk_size*3/2); | /* {{{ void php_output_flush_all(TSRMLS_C) |
block_size = chunk_size/2; | * Flush all output buffers subsequently */ |
} else { | PHPAPI void php_output_flush_all(TSRMLS_D) |
initial_size = 40*1024; | { |
block_size = 10*1024; | if (OG(active)) { |
| php_output_op(PHP_OUTPUT_HANDLER_FLUSH, NULL, 0 TSRMLS_CC); |
} |
} |
return php_ob_init(initial_size, block_size, output_handler, chunk_size, erase TSRMLS_CC); |
|
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_start_ob_buffer_named | /* {{{ SUCCESS|FAILURE php_output_clean(TSRMLS_D) |
* Start output buffering */ | * Cleans the most recent output handlers buffer if the handler is cleanable */ |
PHPAPI int php_start_ob_buffer_named(const char *output_handler_name, uint chunk_size, zend_bool erase TSRMLS_DC) | PHPAPI int php_output_clean(TSRMLS_D) |
{ |
{ |
zval *output_handler; | php_output_context context; |
int result; | |
|
|
ALLOC_INIT_ZVAL(output_handler); | if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_CLEANABLE)) { |
Z_STRLEN_P(output_handler) = strlen(output_handler_name); /* this can be optimized */ | OG(active)->buffer.used = 0; |
Z_STRVAL_P(output_handler) = estrndup(output_handler_name, Z_STRLEN_P(output_handler)); | php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC); |
Z_TYPE_P(output_handler) = IS_STRING; | php_output_handler_op(OG(active), &context); |
result = php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC); | php_output_context_dtor(&context); |
zval_dtor(output_handler); | return SUCCESS; |
FREE_ZVAL(output_handler); | } |
return result; | return FAILURE; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_end_ob_buffer | /* {{{ void php_output_clean_all(TSRMLS_D) |
* End output buffering (one level) */ | * Cleans all output handler buffers, without regard whether the handler is cleanable */ |
PHPAPI void php_end_ob_buffer(zend_bool send_buffer, zend_bool just_flush TSRMLS_DC) | PHPAPI void php_output_clean_all(TSRMLS_D) |
{ |
{ |
char *final_buffer=NULL; | php_output_context context; |
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; | |
|
|
if (OG(ob_nesting_level)==0) { | if (OG(active)) { |
return; | 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); |
} |
} |
status = 0; | } |
if (!OG(active_ob_buffer).status & PHP_OUTPUT_HANDLER_START) { | |
/* our first call */ | /* {{{ SUCCESS|FAILURE php_output_end(TSRMLS_D) |
status |= PHP_OUTPUT_HANDLER_START; | * 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 (just_flush) { | return FAILURE; |
status |= PHP_OUTPUT_HANDLER_CONT; | } |
| /* }}} */ |
| |
| /* {{{ 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)); |
| } |
| /* }}} */ |
| |
| /* {{{ 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; |
| } |
| 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); |
| } |
| } |
| /* }}} */ |
| |
| /* {{{ 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; |
| } |
| /* }}} */ |
| |
| /* {{{ 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 { |
} else { |
status |= PHP_OUTPUT_HANDLER_END; | ZVAL_NULL(p); |
| return FAILURE; |
} |
} |
|
} |
|
|
#if 0 | /* {{{ SUCCESS|FAILURE php_output_get_length(zval *z TSRMLS_DC) |
{ | * Get the length of the active output handlers buffer */ |
FILE *fp; | PHPAPI int php_output_get_length(zval *p TSRMLS_DC) |
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); | if (OG(active)) { |
fclose(fp); | ZVAL_LONG(p, OG(active)->buffer.used); |
} | return SUCCESS; |
#endif | } else { |
| ZVAL_NULL(p); |
| return FAILURE; |
| } |
| } |
| /* }}} */ |
|
|
if (OG(active_ob_buffer).internal_output_handler) { | /* {{{ php_output_handler* php_output_get_active_handler(TSRMLS_D) |
final_buffer = OG(active_ob_buffer).internal_output_handler_buffer; | * Get active output handler */ |
final_buffer_length = OG(active_ob_buffer).internal_output_handler_buffer_size; | PHPAPI php_output_handler* php_output_get_active_handler(TSRMLS_D) |
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) { | return OG(active); |
zval **params[2]; | } |
zval *orig_buffer; | /* }}} */ |
zval *z_status; | |
|
|
ALLOC_INIT_ZVAL(orig_buffer); | /* {{{ SUCCESS|FAILURE php_output_handler_start_default(TSRMLS_D) |
ZVAL_STRINGL(orig_buffer, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1); | * Start a "default output handler" */ |
| PHPAPI int php_output_start_default(TSRMLS_D) |
| { |
| php_output_handler *handler; |
|
|
ALLOC_INIT_ZVAL(z_status); | handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, 0, PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC); |
ZVAL_LONG(z_status, status); | if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { |
| return SUCCESS; |
| } |
| php_output_handler_free(&handler TSRMLS_CC); |
| return FAILURE; |
| } |
| /* }}} */ |
|
|
params[0] = &orig_buffer; | /* {{{ SUCCESS|FAILURE php_output_handler_start_devnull(TSRMLS_D) |
params[1] = &z_status; | * Start a "null output handler" */ |
OG(ob_lock) = 1; | PHPAPI int php_output_start_devnull(TSRMLS_D) |
| { |
| php_output_handler *handler; |
|
|
if (call_user_function_ex(CG(function_table), NULL, OG(active_ob_buffer).output_handler, &alternate_buffer, 2, params, 1, NULL TSRMLS_CC)==SUCCESS) { | handler = php_output_handler_create_internal(ZEND_STRL(php_output_devnull_handler_name), php_output_handler_devnull_func, PHP_OUTPUT_HANDLER_DEFAULT_SIZE, 0 TSRMLS_CC); |
if (alternate_buffer && !(Z_TYPE_P(alternate_buffer)==IS_BOOL && Z_BVAL_P(alternate_buffer)==0)) { | if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { |
convert_to_string_ex(&alternate_buffer); | return SUCCESS; |
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); | |
} |
} |
|
php_output_handler_free(&handler TSRMLS_CC); |
|
return FAILURE; |
|
} |
|
/* }}} */ |
|
|
if (!final_buffer) { | /* {{{ SUCCESS|FAILURE php_output_start_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC) |
final_buffer = OG(active_ob_buffer).buffer; | * Start a user level output handler */ |
final_buffer_length = OG(active_ob_buffer).text_length; | 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); |
| } else { |
| handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC); |
} |
} |
|
if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { |
|
return SUCCESS; |
|
} |
|
php_output_handler_free(&handler TSRMLS_CC); |
|
return FAILURE; |
|
} |
|
/* }}} */ |
|
|
if (OG(ob_nesting_level)==1) { /* end buffering */ | /* {{{ SUCCESS|FAILURE php_output_start_internal(zval *name, php_output_handler_func_t handler, size_t chunk_size, int flags TSRMLS_DC) |
if (SG(headers_sent) && !SG(request_info).headers_only) { | * Start an internal output handler that does not have to maintain a non-global state */ |
OG(php_body_write) = php_ub_body_write_no_header; | PHPAPI int php_output_start_internal(const char *name, size_t name_len, php_output_handler_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC) |
} else { | { |
OG(php_body_write) = php_ub_body_write; | php_output_handler *handler; |
} | |
| handler = php_output_handler_create_internal(name, name_len, 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; |
} |
} |
|
php_output_handler_free(&handler TSRMLS_CC); |
|
return FAILURE; |
|
} |
|
/* }}} */ |
|
|
to_be_destroyed_buffer = OG(active_ob_buffer).buffer; | /* {{{ php_output_handler *php_output_handler_create_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC) |
to_be_destroyed_handler_name = OG(active_ob_buffer).handler_name; | * Create a user level output handler */ |
if (OG(active_ob_buffer).internal_output_handler | PHPAPI php_output_handler *php_output_handler_create_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC) |
&& (final_buffer != OG(active_ob_buffer).internal_output_handler_buffer) | { |
&& (final_buffer != OG(active_ob_buffer).buffer)) { | char *handler_name = NULL, *error = NULL; |
to_be_destroyed_handled_output[0] = final_buffer; | 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(ZEND_STRL(php_output_default_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(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler) TSRMLS_CC))) { |
| handler = (*alias)(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler), chunk_size, flags TSRMLS_CC); |
| break; |
| } |
| default: |
| user = ecalloc(1, sizeof(php_output_handler_user_func_t)); |
| if (SUCCESS == zend_fcall_info_init(output_handler, 0, &user->fci, &user->fcc, &handler_name, &error TSRMLS_CC)) { |
| handler = php_output_handler_init(handler_name, strlen(handler_name), chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_USER TSRMLS_CC); |
| Z_ADDREF_P(output_handler); |
| user->zoh = output_handler; |
| handler->func.user = user; |
| } else { |
| efree(user); |
| } |
| if (error) { |
| php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "%s", error); |
| efree(error); |
| } |
| if (handler_name) { |
| efree(handler_name); |
| } |
} |
} |
|
|
if (!just_flush) { | return handler; |
if (OG(active_ob_buffer).internal_output_handler) { | } |
to_be_destroyed_handled_output[1] = OG(active_ob_buffer).internal_output_handler_buffer; | /* }}} */ |
| |
| /* {{{ 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(const char *name, size_t name_len, php_output_handler_context_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC) |
| { |
| php_output_handler *handler; |
| |
| handler = php_output_handler_init(name, name_len, chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_INTERNAL TSRMLS_CC); |
| handler->func.internal = output_handler; |
| |
| return handler; |
| } |
| /* }}} */ |
| |
| /* {{{ 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) |
| { |
| if (handler->dtor && handler->opaq) { |
| handler->dtor(handler->opaq TSRMLS_CC); |
| } |
| handler->dtor = dtor; |
| handler->opaq = opaq; |
| } |
| /* }}} */ |
| |
| /* {{{ 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) |
| { |
| 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, handler->name, handler->name_len+1, (void *) &conflict)) { |
| if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) { |
| return FAILURE; |
} |
} |
} |
} |
if (OG(ob_nesting_level)>1) { /* restore previous buffer */ | if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, handler->name, handler->name_len+1, (void *) &rconflicts)) { |
zend_stack_top(&OG(ob_buffers), (void **) &prev_ob_buffer_p); | for (zend_hash_internal_pointer_reset_ex(rconflicts, &pos); |
orig_ob_buffer = OG(active_ob_buffer); | zend_hash_get_current_data_ex(rconflicts, (void *) &conflict, &pos) == SUCCESS; |
OG(active_ob_buffer) = *prev_ob_buffer_p; | zend_hash_move_forward_ex(rconflicts, &pos) |
zend_stack_del_top(&OG(ob_buffers)); | ) { |
if (!just_flush && OG(ob_nesting_level)==2) { /* destroy the stack */ | if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) { |
zend_stack_destroy(&OG(ob_buffers)); | return FAILURE; |
| } |
} |
} |
} |
} |
OG(ob_nesting_level)--; | /* 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) = handler; |
| return SUCCESS; |
| } |
| /* }}} */ |
|
|
if (send_buffer) { | /* {{{ int php_output_handler_started(zval *name TSRMLS_DC) |
if (just_flush) { /* if flush is called prior to proper end, ensure presence of NUL */ | * Check whether a certain output handler is in use */ |
final_buffer[final_buffer_length] = '\0'; | PHPAPI int php_output_handler_started(const char *name, size_t name_len 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 (name_len == (*(handlers[i]))->name_len && !memcmp((*(handlers[i]))->name, name, name_len)) { |
| return 1; |
| } |
} |
} |
OG(php_body_write)(final_buffer, final_buffer_length TSRMLS_CC); |
|
} |
} |
|
|
if (just_flush) { /* we restored the previous ob, return to the current */ | return 0; |
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; | |
| /* {{{ 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(const char *handler_new, size_t handler_new_len, const char *handler_set, size_t handler_set_len TSRMLS_DC) |
| { |
| if (php_output_handler_started(handler_set, handler_set_len TSRMLS_CC)) { |
| if (handler_new_len != handler_set_len || memcmp(handler_new, handler_set, handler_set_len)) { |
| php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set); |
| } else { |
| php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' cannot be used twice", handler_new); |
} |
} |
OG(ob_nesting_level)++; | return 1; |
} |
} |
|
return 0; |
|
} |
|
/* }}} */ |
|
|
if (alternate_buffer) { | /* {{{ SUCCESS|FAILURE php_output_handler_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC) |
zval_ptr_dtor(&alternate_buffer); | * Register a conflict checking function on MINIT */ |
| PHPAPI int php_output_handler_conflict_register(const char *name, size_t name_len, php_output_handler_conflict_check_t check_func 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, name, name_len+1, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL); |
|
} |
|
/* }}} */ |
|
|
if (status & PHP_OUTPUT_HANDLER_END) { | /* {{{ SUCCESS|FAILURE php_output_handler_reverse_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC) |
efree(to_be_destroyed_handler_name); | * Register a reverse conflict checking function on MINIT */ |
| PHPAPI int php_output_handler_reverse_conflict_register(const char *name, size_t name_len, 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"); |
| return FAILURE; |
} |
} |
if (!just_flush) { | |
efree(to_be_destroyed_buffer); | if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, name, name_len+1, (void *) &rev_ptr)) { |
| return zend_hash_next_index_insert(rev_ptr, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL); |
} else { |
} else { |
OG(active_ob_buffer).text_length = 0; | zend_hash_init(&rev, 1, NULL, NULL, 1); |
OG(active_ob_buffer).status |= PHP_OUTPUT_HANDLER_START; | if (SUCCESS != zend_hash_next_index_insert(&rev, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL)) { |
OG(php_body_write) = php_b_body_write; | zend_hash_destroy(&rev); |
| return FAILURE; |
| } |
| if (SUCCESS != zend_hash_update(&php_output_handler_reverse_conflicts, name, name_len+1, &rev, sizeof(HashTable), NULL)) { |
| zend_hash_destroy(&rev); |
| return FAILURE; |
| } |
| return SUCCESS; |
} |
} |
if (to_be_destroyed_handled_output[0]) { | } |
efree(to_be_destroyed_handled_output[0]); | /* }}} */ |
| |
| /* {{{ 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(const char *name, size_t name_len TSRMLS_DC) |
| { |
| php_output_handler_alias_ctor_t *func = NULL; |
| |
| zend_hash_find(&php_output_handler_aliases, name, name_len+1, (void *) &func); |
| return func; |
| } |
| /* }}} */ |
| |
| /* {{{ 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(const char *name, size_t name_len, 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; |
} |
} |
if (to_be_destroyed_handled_output[1]) { | return zend_hash_update(&php_output_handler_aliases, name, name_len+1, &func, sizeof(php_output_handler_alias_ctor_t *), NULL); |
efree(to_be_destroyed_handled_output[1]); | } |
| /* }}} */ |
| |
| /* {{{ 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) |
| { |
| 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; |
| return SUCCESS; |
| 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; |
| } |
} |
} |
|
return FAILURE; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_end_ob_buffers | /* {{{ void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC) |
* End output buffering (all buffers) */ | * Destroy an output handler */ |
PHPAPI void php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC) | PHPAPI void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC) |
{ |
{ |
while (OG(ob_nesting_level)!=0) { | STR_FREE(handler->name); |
php_end_ob_buffer(send_buffer, 0 TSRMLS_CC); | 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)); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_start_implicit_flush | /* {{{ void php_output_handler_free(php_output_handler **handler TSMRLS_DC) |
*/ | * Destroy and free an output handler */ |
PHPAPI void php_start_implicit_flush(TSRMLS_D) | PHPAPI void php_output_handler_free(php_output_handler **h TSRMLS_DC) |
{ |
{ |
OG(implicit_flush) = 1; | if (*h) { |
| php_output_handler_dtor(*h TSRMLS_CC); |
| efree(*h); |
| *h = NULL; |
| } |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_end_implicit_flush | /* void php_output_set_implicit_flush(int enabled TSRMLS_DC) |
*/ | * Enable or disable implicit flush */ |
PHPAPI void php_end_implicit_flush(TSRMLS_D) | PHPAPI void php_output_set_implicit_flush(int flush TSRMLS_DC) |
{ |
{ |
OG(implicit_flush) = 0; | if (flush) { |
| OG(flags) |= PHP_OUTPUT_IMPLICITFLUSH; |
| } else { |
| OG(flags) &= ~PHP_OUTPUT_IMPLICITFLUSH; |
| } |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ char *php_get_output_start_filename(TSRMLS_D) | /* {{{ char *php_output_get_start_filename(TSRMLS_D) |
* Return filename start output something */ | * Get the file name where output has started */ |
PHPAPI char *php_get_output_start_filename(TSRMLS_D) | PHPAPI const char *php_output_get_start_filename(TSRMLS_D) |
{ |
{ |
return OG(output_start_filename); |
return OG(output_start_filename); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ char *php_get_output_start_lineno(TSRMLS_D) | /* {{{ int php_output_get_start_lineno(TSRMLS_D) |
* Return line number start output something */ | * Get the line number where output has started */ |
PHPAPI int php_get_output_start_lineno(TSRMLS_D) | PHPAPI int php_output_get_start_lineno(TSRMLS_D) |
{ |
{ |
return OG(output_start_lineno); |
return OG(output_start_lineno); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_ob_set_internal_handler | /* {{{ 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 */ |
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) | static inline int php_output_lock_error(int op TSRMLS_DC) |
{ |
{ |
if (OG(ob_nesting_level) == 0 || OG(active_ob_buffer).internal_output_handler || strcmp(OG(active_ob_buffer).handler_name, OB_DEFAULT_HANDLER_NAME)) { | /* if there's no ob active, ob has been stopped */ |
php_start_ob_buffer(NULL, buffer_size, erase TSRMLS_CC); | 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"); |
| return 1; |
} |
} |
|
return 0; |
|
} |
|
/* }}} */ |
|
|
OG(active_ob_buffer).internal_output_handler = internal_output_handler; | /* {{{ static php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC) |
OG(active_ob_buffer).internal_output_handler_buffer = (char *) emalloc(buffer_size); | * Initialize a new output context */ |
OG(active_ob_buffer).internal_output_handler_buffer_size = buffer_size; | static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC) |
if (OG(active_ob_buffer).handler_name) { | { |
efree(OG(active_ob_buffer).handler_name); | if (!context) { |
| context = emalloc(sizeof(php_output_context)); |
} |
} |
OG(active_ob_buffer).handler_name = estrdup(handler_name); | |
OG(active_ob_buffer).erase = erase; | memset(context, 0, sizeof(php_output_context)); |
| TSRMLS_SET_CTX(context->tsrm_ls); |
| context->op = op; |
| |
| return context; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* | /* {{{ static void php_output_context_reset(php_output_context *context) |
* Output buffering - implementation | * Reset an output context */ |
*/ | static inline void php_output_context_reset(php_output_context *context) |
| |
/* {{{ php_ob_allocate | |
*/ | |
static inline void php_ob_allocate(uint text_length TSRMLS_DC) | |
{ |
{ |
uint new_len = OG(active_ob_buffer).text_length + text_length; | int op = context->op; |
| php_output_context_dtor(context); |
| memset(context, 0, sizeof(php_output_context)); |
| context->op = op; |
| } |
| /* }}} */ |
|
|
if (OG(active_ob_buffer).size < new_len) { | /* {{{ static void php_output_context_feed(php_output_context *context, char *, size_t, size_t) |
uint buf_size = OG(active_ob_buffer).size; | * Feed output contexts input buffer */ |
while (buf_size <= new_len) { | static inline void php_output_context_feed(php_output_context *context, char *data, size_t size, size_t used, zend_bool free) |
buf_size += OG(active_ob_buffer).block_size; | { |
} | if (context->in.free && context->in.data) { |
| efree(context->in.data); |
OG(active_ob_buffer).buffer = (char *) erealloc(OG(active_ob_buffer).buffer, buf_size+1); | |
OG(active_ob_buffer).size = buf_size; | |
} |
} |
OG(active_ob_buffer).text_length = new_len; | context->in.data = data; |
| context->in.used = used; |
| context->in.free = free; |
| context->in.size = size; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_ob_init_conflict | /* {{{ static void php_output_context_swap(php_output_context *context) |
* Returns 1 if handler_set is already used and generates error message */ | * Swap output contexts buffers */ |
PHPAPI int php_ob_init_conflict(char *handler_new, char *handler_set TSRMLS_DC) | static inline void php_output_context_swap(php_output_context *context) |
{ |
{ |
if (php_ob_handler_used(handler_set TSRMLS_CC)) { | if (context->in.free && context->in.data) { |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set); | efree(context->in.data); |
return 1; | |
} |
} |
return 0; | 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.free = 0; |
| context->out.size = 0; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_ob_init_named | /* {{{ static void php_output_context_pass(php_output_context *context) |
*/ | * Pass input to output buffer */ |
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) | static inline void php_output_context_pass(php_output_context *context) |
{ |
{ |
php_ob_buffer tmp_buf; | 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.free = 0; |
| context->in.size = 0; |
| } |
| /* }}} */ |
|
|
if (output_handler && !zend_is_callable(output_handler, 0, NULL TSRMLS_CC)) { | /* {{{ static void php_output_context_dtor(php_output_context *context) |
return FAILURE; | * 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; |
|
} |
|
} |
|
/* }}} */ |
|
|
tmp_buf.block_size = block_size; | /* {{{ static php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags TSRMLS_DC) |
tmp_buf.size = initial_size; | * Allocates and initializes a php_output_handler structure */ |
tmp_buf.buffer = (char *) emalloc(initial_size+1); | static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC) |
tmp_buf.text_length = 0; | { |
tmp_buf.output_handler = output_handler; | php_output_handler *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) { | handler = ecalloc(1, sizeof(php_output_handler)); |
#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB) | handler->name = estrndup(name, name_len); |
if (!strncmp(handler_name, "ob_gzhandler", sizeof("ob_gzhandler")) && php_ob_gzhandler_check(TSRMLS_C)) { | handler->name_len = name_len; |
return FAILURE; | handler->size = chunk_size; |
} | handler->flags = flags; |
#endif | handler->buffer.size = PHP_OUTPUT_HANDLER_INITBUF_SIZE(chunk_size); |
if (OG(ob_nesting_level)==1) { /* initialize stack */ | handler->buffer.data = emalloc(handler->buffer.size); |
zend_stack_init(&OG(ob_buffers)); | |
} | return handler; |
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_ob_handler_from_string | /* {{{ static int php_output_handler_appen(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC) |
* Create zval output handler from string */ | * Appends input to the output handlers buffer and indicates whether the buffer does not have to be processed by the output handler */ |
static zval* php_ob_handler_from_string(const char *handler_name, int len TSRMLS_DC) | static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC) |
{ |
{ |
zval *output_handler; | 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); |
|
|
ALLOC_INIT_ZVAL(output_handler); | handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max); |
Z_STRLEN_P(output_handler) = len; | handler->buffer.size += grow_max; |
Z_STRVAL_P(output_handler) = estrndup(handler_name, len); | } |
Z_TYPE_P(output_handler) = IS_STRING; | memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used); |
return output_handler; | 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_init | /* {{{ 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 int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC) | static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context) |
{ |
{ |
int result = FAILURE, handler_len, len; | php_output_handler_status_t status; |
char *handler_name, *next_handler_name; | int original_op = context->op; |
HashPosition pos; | PHP_OUTPUT_TSRMLS(context); |
zval **tmp; | |
zval *handler_zval; | |
|
|
if (output_handler && output_handler->type == IS_STRING) { | #if PHP_OUTPUT_DEBUG |
handler_name = Z_STRVAL_P(output_handler); | fprintf(stderr, ">>> op(%d, " |
handler_len = Z_STRLEN_P(output_handler); | "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, |
| 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 |
|
|
result = SUCCESS; | if (php_output_lock_error(context->op TSRMLS_CC)) { |
if (handler_len && handler_name[0] != '\0') { | /* fatal error */ |
while ((next_handler_name=strchr(handler_name, ',')) != NULL) { | return PHP_OUTPUT_HANDLER_FAILURE; |
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); | /* storable? */ |
result = php_ob_init_named(initial_size, block_size, next_handler_name, handler_zval, chunk_size, erase TSRMLS_CC); | if (php_output_handler_append(handler, &context->in TSRMLS_CC) && !context->op) { |
if (result != SUCCESS) { | context->op = original_op; |
zval_dtor(handler_zval); | return PHP_OUTPUT_HANDLER_NO_DATA; |
FREE_ZVAL(handler_zval); | } 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; |
| } |
} |
} |
handler_name += len+1; | } else { |
handler_len -= len+1; | /* call failed, pass internal buffer along */ |
efree(next_handler_name); | status = PHP_OUTPUT_HANDLER_FAILURE; |
} |
} |
} | |
if (result == SUCCESS) { | zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 0); |
handler_zval = php_ob_handler_from_string(handler_name, handler_len TSRMLS_CC); | zval_ptr_dtor(&ob_data); |
result = php_ob_init_named(initial_size, block_size, handler_name, handler_zval, chunk_size, erase TSRMLS_CC); | zval_ptr_dtor(&ob_mode); |
if (result != SUCCESS) { | if (retval) { |
zval_dtor(handler_zval); | zval_ptr_dtor(&retval); |
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 { |
} else { |
efree(handler_name); | |
/* init all array elements recursively */ | php_output_context_feed(context, handler->buffer.data, handler->buffer.size, handler->buffer.used, 0); |
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) { | if (SUCCESS == handler->func.internal(&handler->opaq, context)) { |
result = php_ob_init(initial_size, block_size, *tmp, chunk_size, erase TSRMLS_CC); | if (context->out.used) { |
if (result == FAILURE) { | status = PHP_OUTPUT_HANDLER_SUCCESS; |
break; | } else { |
| status = PHP_OUTPUT_HANDLER_NO_DATA; |
} |
} |
zend_hash_move_forward_ex(Z_ARRVAL_P(output_handler), &pos); | } else { |
| status = PHP_OUTPUT_HANDLER_FAILURE; |
} |
} |
} |
} |
} else if (output_handler && output_handler->type == IS_OBJECT) { | handler->flags |= PHP_OUTPUT_HANDLER_STARTED; |
/* do we have callable object */ | OG(running) = NULL; |
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 result; |
|
} |
|
/* }}} */ |
|
|
|
/* {{{ php_ob_list_each | switch (status) { |
*/ | case PHP_OUTPUT_HANDLER_FAILURE: |
static int php_ob_list_each(php_ob_buffer *ob_buffer, zval *ob_handler_array) | /* disable this handler */ |
{ | handler->flags |= PHP_OUTPUT_HANDLER_DISABLED; |
add_next_index_string(ob_handler_array, ob_buffer->handler_name, 1); | /* discard any output */ |
return 0; | 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_NO_DATA: |
| /* handler ate all */ |
| php_output_context_reset(context); |
| /* no break */ |
| case PHP_OUTPUT_HANDLER_SUCCESS: |
| /* no more buffered data */ |
| handler->buffer.used = 0; |
| break; |
| } |
| |
| context->op = original_op; |
| return status; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_ob_used_each | |
Sets handler_name to NULL is found */ | /* {{{ static void php_output_op(int op, const char *str, size_t len TSRMLS_DC) |
static int php_ob_handler_used_each(php_ob_buffer *ob_buffer, char **handler_name) | * 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) |
{ |
{ |
if (!strcmp(ob_buffer->handler_name, *handler_name)) { | php_output_context context; |
*handler_name = NULL; | php_output_handler **active; |
return 1; | int obh_cnt; |
| |
| if (php_output_lock_error(op TSRMLS_CC)) { |
| return; |
} |
} |
return 0; |
|
} |
|
/* }}} */ |
|
|
|
/* {{{ php_ob_used | php_output_context_init(&context, op TSRMLS_CC); |
returns 1 if given handler_name is used as output_handler */ | |
PHPAPI int php_ob_handler_used(char *handler_name TSRMLS_DC) | |
{ | |
char *tmp = handler_name; | |
|
|
if (OG(ob_nesting_level)) { | /* |
if (!strcmp(OG(active_ob_buffer).handler_name, handler_name)) { | * broken up for better performance: |
return 1; | * - 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); |
} |
} |
if (OG(ob_nesting_level)>1) { | } else { |
zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_handler_used_each, &tmp); | context.out.data = (char *) str; |
} | context.out.used = len; |
} |
} |
return tmp ? 0 : 1; |
|
} |
|
/* }}} */ |
|
|
|
/* {{{ php_ob_append | if (context.out.data && context.out.used) { |
*/ | php_output_header(TSRMLS_C); |
static inline void php_ob_append(const char *text, uint text_length TSRMLS_DC) | |
{ | |
char *target; | |
int original_ob_text_length; | |
|
|
original_ob_text_length=OG(active_ob_buffer).text_length; | if (!(OG(flags) & PHP_OUTPUT_DISABLED)) { |
| #if PHP_OUTPUT_DEBUG |
| fprintf(stderr, "::: sapi_write('%s', %zu)\n", context.out.data, context.out.used); |
| #endif |
| sapi_module.ub_write(context.out.data, context.out.used TSRMLS_CC); |
|
|
php_ob_allocate(text_length TSRMLS_CC); | if (OG(flags) & PHP_OUTPUT_IMPLICITFLUSH) { |
target = OG(active_ob_buffer).buffer+original_ob_text_length; | sapi_flush(TSRMLS_C); |
memcpy(target, text, text_length); | } |
target[text_length]=0; | |
|
|
/* If implicit_flush is On or chunked buffering, send contents to next buffer and return. */ | OG(flags) |= PHP_OUTPUT_SENT; |
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; | |
} |
} |
|
php_output_context_dtor(&context); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
#if 0 | /* {{{ static int php_output_stack_apply_op(void *h, void *c) |
static inline void php_ob_prepend(const char *text, uint text_length) | * Operation callback for the stack apply function */ |
| static int php_output_stack_apply_op(void *h, void *c) |
{ |
{ |
char *p, *start; | int was_disabled; |
TSRMLS_FETCH(); | php_output_handler_status_t status; |
| php_output_handler *handler = *(php_output_handler **) h; |
| php_output_context *context = (php_output_context *) c; |
|
|
php_ob_allocate(text_length TSRMLS_CC); | if ((was_disabled = (handler->flags & PHP_OUTPUT_HANDLER_DISABLED))) { |
| status = PHP_OUTPUT_HANDLER_FAILURE; |
| } else { |
| status = php_output_handler_op(handler, context); |
| } |
|
|
/* php_ob_allocate() may change OG(ob_buffer), so we can't initialize p&start earlier */ | /* |
p = OG(ob_buffer)+OG(ob_text_length); | * handler ate all => break |
start = OG(ob_buffer); | * handler returned data or failed resp. is disabled => continue |
| */ |
| switch (status) { |
| case PHP_OUTPUT_HANDLER_NO_DATA: |
| return 1; |
|
|
while (--p>=start) { | case PHP_OUTPUT_HANDLER_SUCCESS: |
p[text_length] = *p; | /* swap contexts buffers, unless this is the last handler in the stack */ |
} | if (handler->level) { |
memcpy(OG(ob_buffer), text, text_length); | php_output_context_swap(context); |
OG(ob_buffer)[OG(active_ob_buffer).text_length]=0; | } |
} | return 0; |
#endif | |
|
|
/* {{{ php_ob_get_buffer | case PHP_OUTPUT_HANDLER_FAILURE: |
* Return the current output buffer */ | default: |
PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC) | if (was_disabled) { |
{ | /* pass input along, if it's the last handler in the stack */ |
if (OG(ob_nesting_level)==0) { | if (!handler->level) { |
return FAILURE; | php_output_context_pass(context); |
| } |
| } else { |
| /* swap buffers, unless this is the last handler */ |
| if (handler->level) { |
| php_output_context_swap(context); |
| } |
| } |
| return 0; |
} |
} |
ZVAL_STRINGL(p, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1); |
|
return SUCCESS; |
|
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_ob_get_length | /* {{{ static int php_output_stack_apply_clean(void *h, void *c) |
* Return the size of the current output buffer */ | * Clean callback for the stack apply function */ |
PHPAPI int php_ob_get_length(zval *p TSRMLS_DC) | static int php_output_stack_apply_clean(void *h, void *c) |
{ |
{ |
if (OG(ob_nesting_level) == 0) { | php_output_handler *handler = *(php_output_handler **) h; |
return FAILURE; | php_output_context *context = (php_output_context *) c; |
} | |
ZVAL_LONG(p, OG(active_ob_buffer).text_length); | handler->buffer.used = 0; |
return SUCCESS; | php_output_handler_op(handler, context); |
| php_output_context_reset(context); |
| return 0; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* | /* {{{ static int php_output_stack_apply_list(void *h, void *z) |
* Wrapper functions - implementation | * List callback for the stack apply function */ |
*/ | static int php_output_stack_apply_list(void *h, void *z) |
| |
/* buffered output function */ | |
static int php_b_body_write(const char *str, uint str_length TSRMLS_DC) | |
{ |
{ |
php_ob_append(str, str_length TSRMLS_CC); | php_output_handler *handler = *(php_output_handler **) h; |
return str_length; | zval *array = (zval *) z; |
| |
| add_next_index_stringl(array, handler->name, handler->name_len, 1); |
| return 0; |
} |
} |
|
/* }}} */ |
|
|
/* {{{ php_ub_body_write_no_header | /* {{{ static int php_output_stack_apply_status(void *h, void *z) |
*/ | * Status callback for the stack apply function */ |
PHPAPI int php_ub_body_write_no_header(const char *str, uint str_length TSRMLS_DC) | static int php_output_stack_apply_status(void *h, void *z) |
{ |
{ |
int result; | php_output_handler *handler = *(php_output_handler **) h; |
| zval *array = (zval *) z; |
|
|
if (OG(disable_output)) { | add_next_index_zval(array, php_output_handler_status(handler, NULL)); |
return 0; | |
} | |
|
|
result = OG(php_header_write)(str, str_length TSRMLS_CC); | return 0; |
| } |
|
|
if (OG(implicit_flush)) { | /* {{{ static zval *php_output_handler_status(php_output_handler *handler, zval *entry) |
sapi_flush(TSRMLS_C); | * Returns an array with the status of the output handler */ |
| static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry) |
| { |
| if (!entry) { |
| MAKE_STD_ZVAL(entry); |
| array_init(entry); |
} |
} |
|
|
return result; | add_assoc_stringl(entry, "name", handler->name, handler->name_len, 1); |
| 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; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ php_ub_body_write | /* {{{ static int php_output_stack_pop(int flags TSRMLS_DC) |
*/ | * Pops an output handler off the stack */ |
PHPAPI int php_ub_body_write(const char *str, uint str_length TSRMLS_DC) | static inline int php_output_stack_pop(int flags TSRMLS_DC) |
{ |
{ |
int result = 0; | php_output_context context; |
| php_output_handler **current, *orphan = OG(active); |
|
|
if (SG(request_info).headers_only) { | if (!orphan) { |
if(SG(headers_sent)) { | if (!(flags & PHP_OUTPUT_POP_SILENT)) { |
return 0; | 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"); |
} |
} |
php_header(TSRMLS_C); | return 0; |
zend_bailout(); | } else if (!(flags & PHP_OUTPUT_POP_FORCE) && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) { |
} | if (!(flags & PHP_OUTPUT_POP_SILENT)) { |
if (php_header(TSRMLS_C)) { | php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer of %s (%d)", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", orphan->name, orphan->level); |
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); | |
} |
} |
|
return 0; |
|
} else { |
|
php_output_context_init(&context, PHP_OUTPUT_HANDLER_FINAL TSRMLS_CC); |
|
|
OG(php_body_write) = php_ub_body_write_no_header; | /* don't run the output handler if it's disabled */ |
result = php_ub_body_write_no_header(str, str_length TSRMLS_CC); | 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); |
| } |
|
|
return result; | /* 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; |
| } |
| |
| /* 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; |
| } |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* {{{ int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result) | /* {{{ 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_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result) | static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context) |
{ |
{ |
zval *elem; | php_output_handler_func_t func = *(php_output_handler_func_t *) handler_context; |
| PHP_OUTPUT_TSRMLS(output_context); |
|
|
MAKE_STD_ZVAL(elem); | if (func) { |
array_init(elem); | char *out_str = NULL; |
| uint out_len = 0; |
|
|
add_assoc_long(elem, "chunk_size", ob_buffer->chunk_size); | func(output_context->in.data, output_context->in.used, &out_str, &out_len, output_context->op TSRMLS_CC); |
if (!ob_buffer->chunk_size) { | |
add_assoc_long(elem, "size", ob_buffer->size); | if (out_str) { |
add_assoc_long(elem, "block_size", ob_buffer->block_size); | output_context->out.data = out_str; |
| output_context->out.used = out_len; |
| output_context->out.free = 1; |
| } else { |
| php_output_context_pass(output_context); |
| } |
| |
| return SUCCESS; |
} |
} |
if (ob_buffer->internal_output_handler) { | return FAILURE; |
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); | |
|
|
|
/* {{{ 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) |
|
{ |
|
php_output_context_pass(output_context); |
return SUCCESS; |
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; |
|
} |
|
/* }}} */ |
|
|
/* |
/* |
* USERLAND (nearly 1:1 of old output.c) |
* USERLAND (nearly 1:1 of old output.c) |
*/ |
*/ |
|
|
/* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, bool erase]]]) | /* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, int flags]]]) |
Turn on Output Buffering (specifying an optional output handler). */ |
Turn on Output Buffering (specifying an optional output handler). */ |
PHP_FUNCTION(ob_start) |
PHP_FUNCTION(ob_start) |
{ |
{ |
zval *output_handler = NULL; |
zval *output_handler = NULL; |
long chunk_size = 0; |
long chunk_size = 0; |
zend_bool erase = 1; | long flags = PHP_OUTPUT_HANDLER_STDFLAGS; |
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/lb", &output_handler, &chunk_size, &erase) == FAILURE) { | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/ll", &output_handler, &chunk_size, &flags) == FAILURE) { |
return; |
return; |
} |
} |
|
|
Line 769 PHP_FUNCTION(ob_start)
|
Line 1316 PHP_FUNCTION(ob_start)
|
chunk_size = 0; |
chunk_size = 0; |
} |
} |
|
|
if (php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC) == FAILURE) { | if (php_output_start_user(output_handler, chunk_size, flags TSRMLS_CC) == FAILURE) { |
| php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to create buffer"); |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
RETURN_TRUE; |
RETURN_TRUE; |
Line 784 PHP_FUNCTION(ob_flush)
|
Line 1332 PHP_FUNCTION(ob_flush)
|
return; |
return; |
} |
} |
|
|
if (!OG(ob_nesting_level)) { | if (!OG(active)) { |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush"); |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush"); |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
|
|
if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { | if (SUCCESS != php_output_flush(TSRMLS_C)) { |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer %s", OG(active_ob_buffer).handler_name); | php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer of %s (%d)", OG(active)->name, OG(active)->level); |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
|
|
php_end_ob_buffer(1, 1 TSRMLS_CC); |
|
RETURN_TRUE; |
RETURN_TRUE; |
} |
} |
/* }}} */ |
/* }}} */ |
Line 807 PHP_FUNCTION(ob_clean)
|
Line 1353 PHP_FUNCTION(ob_clean)
|
return; |
return; |
} |
} |
|
|
if (!OG(ob_nesting_level)) { | if (!OG(active)) { |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
|
|
if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { | if (SUCCESS != php_output_clean(TSRMLS_C)) { |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name); | php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level); |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
|
|
php_end_ob_buffer(0, 1 TSRMLS_CC); |
|
RETURN_TRUE; |
RETURN_TRUE; |
} |
} |
/* }}} */ |
/* }}} */ |
Line 830 PHP_FUNCTION(ob_end_flush)
|
Line 1374 PHP_FUNCTION(ob_end_flush)
|
return; |
return; |
} |
} |
|
|
if (!OG(ob_nesting_level)) { | if (!OG(active)) { |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush"); |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush"); |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
|
|
if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { | RETURN_BOOL(SUCCESS == php_output_end(TSRMLS_C)); |
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; | |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
Line 853 PHP_FUNCTION(ob_end_clean)
|
Line 1391 PHP_FUNCTION(ob_end_clean)
|
return; |
return; |
} |
} |
|
|
if (!OG(ob_nesting_level)) { | if (!OG(active)) { |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
|
|
if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { | RETURN_BOOL(SUCCESS == php_output_discard(TSRMLS_C)); |
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; | |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
Line 876 PHP_FUNCTION(ob_get_flush)
|
Line 1408 PHP_FUNCTION(ob_get_flush)
|
return; |
return; |
} |
} |
|
|
if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) { | if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) { |
RETURN_FALSE; | |
} | |
| |
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"); |
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; |
RETURN_FALSE; |
} |
} |
|
|
if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { | if (SUCCESS != php_output_end(TSRMLS_C)) { |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name); | php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level); |
zval_dtor(return_value); | |
RETURN_FALSE; | |
} |
} |
|
|
php_end_ob_buffer(1, 0 TSRMLS_CC); |
|
} |
} |
/* }}} */ |
/* }}} */ |
|
|
Line 904 PHP_FUNCTION(ob_get_clean)
|
Line 1427 PHP_FUNCTION(ob_get_clean)
|
return; |
return; |
} |
} |
|
|
if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) { | if(!OG(active)) { |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
|
|
if (!OG(ob_nesting_level)) { | if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) { |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); |
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); |
zval_dtor(return_value); |
|
RETURN_FALSE; |
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; |
|
} |
|
|
|
php_end_ob_buffer(0, 0 TSRMLS_CC); | if (SUCCESS != php_output_discard(TSRMLS_C)) { |
| php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level); |
| } |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
Line 931 PHP_FUNCTION(ob_get_contents)
|
Line 1450 PHP_FUNCTION(ob_get_contents)
|
return; |
return; |
} |
} |
|
|
if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) { | if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) { |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
} |
} |
Line 945 PHP_FUNCTION(ob_get_level)
|
Line 1464 PHP_FUNCTION(ob_get_level)
|
return; |
return; |
} |
} |
|
|
RETURN_LONG(OG(ob_nesting_level)); | RETURN_LONG(php_output_get_level(TSRMLS_C)); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
Line 957 PHP_FUNCTION(ob_get_length)
|
Line 1476 PHP_FUNCTION(ob_get_length)
|
return; |
return; |
} |
} |
|
|
if (php_ob_get_length(return_value TSRMLS_CC) == FAILURE) { | if (php_output_get_length(return_value TSRMLS_CC) == FAILURE) { |
RETURN_FALSE; |
RETURN_FALSE; |
} |
} |
} |
} |
Line 973 PHP_FUNCTION(ob_list_handlers)
|
Line 1492 PHP_FUNCTION(ob_list_handlers)
|
|
|
array_init(return_value); |
array_init(return_value); |
|
|
if (OG(ob_nesting_level)) { | if (!OG(active)) { |
if (OG(ob_nesting_level) > 1) { | return; |
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); | |
} |
} |
|
|
|
zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_list, return_value); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
Line 994 PHP_FUNCTION(ob_get_status)
|
Line 1512 PHP_FUNCTION(ob_get_status)
|
|
|
array_init(return_value); |
array_init(return_value); |
|
|
|
if (!OG(active)) { |
|
return; |
|
} |
|
|
if (full_status) { |
if (full_status) { |
if (OG(ob_nesting_level) > 1) { | zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_status, return_value); |
zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *elem, void *))php_ob_buffer_status, return_value); | } else { |
} | php_output_handler_status(OG(active), 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); | |
} |
} |
} |
} |
/* }}} */ |
/* }}} */ |
Line 1025 PHP_FUNCTION(ob_implicit_flush)
|
Line 1534 PHP_FUNCTION(ob_implicit_flush)
|
return; |
return; |
} |
} |
|
|
if (flag) { | php_output_set_implicit_flush(flag TSRMLS_CC); |
php_start_implicit_flush(TSRMLS_C); | |
} else { | |
php_end_implicit_flush(TSRMLS_C); | |
} | |
} |
} |
/* }}} */ |
/* }}} */ |
|
|