Skip to content

Commit 9c8ba93

Browse files
committed
Improved handling of output buffers (see news)\n#No trim for the string parameter...
1 parent 2e29e53 commit 9c8ba93

File tree

8 files changed

+185
-70
lines changed

8 files changed

+185
-70
lines changed

Diff for: ext/standard/head.c

+1-3
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,7 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t
126126
ctr.line_len = strlen(cookie);
127127

128128
result = sapi_header_op(SAPI_HEADER_ADD, &ctr TSRMLS_CC);
129-
if (result == FAILURE) {
130-
efree(cookie);
131-
}
129+
efree(cookie);
132130
return result;
133131
}
134132

Diff for: ext/zlib/php_zlib.h

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zlib)
3333
int ob_gzip_coding;
3434
int output_compression;
3535
int output_compression_level;
36+
char *output_handler;
3637
ZEND_END_MODULE_GLOBALS(zlib)
3738

3839
extern zend_module_entry php_zlib_module_entry;

Diff for: ext/zlib/zlib.c

+18
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,25 @@ static PHP_INI_MH(OnUpdate_zlib_output_compression_level)
177177
}
178178
/* }}} */
179179

180+
/* {{{ OnUpdate_zlib_output_handler */
181+
static PHP_INI_MH(OnUpdate_zlib_output_handler)
182+
{
183+
if (stage == PHP_INI_STAGE_RUNTIME && SG(headers_sent) && !SG(request_info).no_headers) {
184+
php_error(E_WARNING, "Cannot change zlib.output_handler - headers already sent");
185+
return FAILURE;
186+
}
187+
188+
OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
189+
190+
return SUCCESS;
191+
}
192+
/* }}} */
193+
180194

181195
PHP_INI_BEGIN()
182196
STD_PHP_INI_BOOLEAN("zlib.output_compression", "0", PHP_INI_ALL, OnUpdate_zlib_output_compression, output_compression, zend_zlib_globals, zlib_globals)
183197
STD_PHP_INI_ENTRY("zlib.output_compression_level", "-1", PHP_INI_ALL, OnUpdate_zlib_output_compression_level, output_compression_level, zend_zlib_globals, zlib_globals)
198+
STD_PHP_INI_ENTRY("zlib.output_handler", "", PHP_INI_ALL, OnUpdate_zlib_output_handler, output_handler, zend_zlib_globals, zlib_globals)
184199
PHP_INI_END()
185200

186201
#ifdef ZTS
@@ -1000,6 +1015,9 @@ int php_enable_output_compression(int buffer_size TSRMLS_DC)
10001015

10011016
php_start_ob_buffer(NULL, buffer_size, 0 TSRMLS_CC);
10021017
php_ob_set_internal_handler(php_gzip_output_handler, buffer_size*1.5, "zlib output compression", 0 TSRMLS_CC);
1018+
if (ZLIBG(output_handler) && strlen(ZLIBG(output_handler))) {
1019+
php_start_ob_buffer_named(ZLIBG(output_handler), 0, 1 TSRMLS_CC);
1020+
}
10031021
return SUCCESS;
10041022
}
10051023
/* }}} */

Diff for: main/main.c

+1-7
Original file line numberDiff line numberDiff line change
@@ -813,13 +813,7 @@ int php_request_startup(TSRMLS_D)
813813
}
814814

815815
if (PG(output_handler) && PG(output_handler)[0]) {
816-
zval *output_handler;
817-
818-
ALLOC_INIT_ZVAL(output_handler);
819-
Z_STRLEN_P(output_handler) = strlen(PG(output_handler)); /* this can be optimized */
820-
Z_STRVAL_P(output_handler) = estrndup(PG(output_handler), Z_STRLEN_P(output_handler));
821-
Z_TYPE_P(output_handler) = IS_STRING;
822-
php_start_ob_buffer(output_handler, 0, 1 TSRMLS_CC);
816+
php_start_ob_buffer_named(PG(output_handler), 0, 1 TSRMLS_CC);
823817
}
824818
else if (PG(output_buffering)) {
825819
if (PG(output_buffering)>1) {

Diff for: main/output.c

+148-57
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
+----------------------------------------------------------------------+
1515
| Authors: Zeev Suraski <zeev@zend.com> |
1616
| Thies C. Arntzen <thies@thieso.net> |
17+
| Marcus Boerger <helly@php.net> |
1718
+----------------------------------------------------------------------+
1819
*/
1920

@@ -30,7 +31,7 @@ static int php_ub_body_write(const char *str, uint str_length TSRMLS_DC);
3031
static int php_ub_body_write_no_header(const char *str, uint str_length TSRMLS_DC);
3132
static int php_b_body_write(const char *str, uint str_length TSRMLS_DC);
3233

33-
static void php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC);
34+
static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC);
3435
static void php_ob_append(const char *text, uint text_length TSRMLS_DC);
3536
#if 0
3637
static void php_ob_prepend(const char *text, uint text_length);
@@ -114,16 +115,39 @@ PHPAPI int php_header_write(const char *str, uint str_length TSRMLS_DC)
114115
* Start output buffering */
115116
PHPAPI int php_start_ob_buffer(zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
116117
{
118+
uint initial_size, block_size;
119+
117120
if (OG(ob_lock)) {
121+
php_error(E_ERROR, "%s() Cannot use output buffering in output buffering display handlers", get_active_function_name(TSRMLS_C));
118122
return FAILURE;
119123
}
120124
if (chunk_size) {
121-
php_ob_init((chunk_size*3/2), chunk_size/2, output_handler, chunk_size, erase TSRMLS_CC);
125+
initial_size = (chunk_size*3/2);
126+
block_size = chunk_size/2;
122127
} else {
123-
php_ob_init(40*1024, 10*1024, output_handler, chunk_size, erase TSRMLS_CC);
128+
initial_size = 40*1024;
129+
block_size = 10*1024;
124130
}
125-
OG(php_body_write) = php_b_body_write;
126-
return SUCCESS;
131+
return php_ob_init(initial_size, block_size, output_handler, chunk_size, erase TSRMLS_CC);
132+
}
133+
/* }}} */
134+
135+
136+
/* {{{ php_start_ob_buffer_named
137+
* Start output buffering */
138+
PHPAPI int php_start_ob_buffer_named(const char *output_handler_name, uint chunk_size, zend_bool erase TSRMLS_DC)
139+
{
140+
zval *output_handler;
141+
int result;
142+
143+
ALLOC_INIT_ZVAL(output_handler);
144+
Z_STRLEN_P(output_handler) = strlen(output_handler_name); /* this can be optimized */
145+
Z_STRVAL_P(output_handler) = estrndup(output_handler_name, Z_STRLEN_P(output_handler));
146+
Z_TYPE_P(output_handler) = IS_STRING;
147+
result = php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC);
148+
zval_dtor(output_handler);
149+
FREE_ZVAL(output_handler);
150+
return result;
127151
}
128152
/* }}} */
129153

@@ -333,10 +357,43 @@ static inline void php_ob_allocate(TSRMLS_D)
333357
}
334358
/* }}} */
335359

336-
/* {{{ php_ob_init
360+
/* {{{ php_ob_init_conflict
361+
* Returns 1 if handler_set is already used and generates error message
337362
*/
338-
static void php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
363+
static int php_ob_init_conflict(char *handler_new, char *handler_set TSRMLS_DC)
339364
{
365+
if (php_ob_handler_used(handler_set TSRMLS_CC))
366+
{
367+
php_error(E_WARNING, "%s() output handler '%s' conflicts with '%s'", get_active_function_name(TSRMLS_C), handler_new, handler_set);
368+
return 1;
369+
}
370+
return 0;
371+
}
372+
/* }}} */
373+
374+
/* {{{ php_ob_init_named
375+
*/
376+
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)
377+
{
378+
int handler_gz, handler_mb, handler_ic;
379+
380+
/* check for specific handlers where rules apply */
381+
handler_gz = strcmp(handler_name, "ob_gzhandler");
382+
handler_mb = strcmp(handler_name, "mb_output_handler");
383+
handler_ic = strcmp(handler_name, "ob_iconv_handler");
384+
/* apply rules */
385+
if (!handler_gz || !handler_mb || !handler_ic) {
386+
if (php_ob_handler_used(handler_name TSRMLS_CC)) {
387+
php_error(E_WARNING, "%s() output handler '%s' cannot be used twice", get_active_function_name(TSRMLS_C), handler_name);
388+
return FAILURE;
389+
}
390+
if (!handler_gz && php_ob_init_conflict(handler_name, "zlib output compression" TSRMLS_CC))
391+
return FAILURE;
392+
if (!handler_mb && php_ob_init_conflict(handler_name, "ob_iconv_handler" TSRMLS_CC))
393+
return FAILURE;
394+
if (!handler_ic && php_ob_init_conflict(handler_name, "mb_output_handler" TSRMLS_CC))
395+
return FAILURE;
396+
}
340397
if (OG(ob_nesting_level)>0) {
341398
if (OG(ob_nesting_level)==1) { /* initialize stack */
342399
zend_stack_init(&OG(ob_buffers));
@@ -352,37 +409,95 @@ static void php_ob_init(uint initial_size, uint block_size, zval *output_handler
352409
OG(active_ob_buffer).chunk_size = chunk_size;
353410
OG(active_ob_buffer).status = 0;
354411
OG(active_ob_buffer).internal_output_handler = NULL;
412+
OG(active_ob_buffer).handler_name = estrdup(handler_name&&handler_name[0]?handler_name:"default output handler");
413+
OG(active_ob_buffer).erase = erase;
414+
OG(php_body_write) = php_b_body_write;
415+
return SUCCESS;
416+
}
417+
/* }}} */
418+
419+
/* {{{ php_ob_handler_from_string
420+
* Create zval output handler from string
421+
*/
422+
static zval* php_ob_handler_from_string(const char *handler_name TSRMLS_DC)
423+
{
424+
zval *output_handler;
425+
426+
ALLOC_INIT_ZVAL(output_handler);
427+
Z_STRLEN_P(output_handler) = strlen(handler_name); /* this can be optimized */
428+
Z_STRVAL_P(output_handler) = estrndup(handler_name, Z_STRLEN_P(output_handler));
429+
Z_TYPE_P(output_handler) = IS_STRING;
430+
return output_handler;
431+
}
432+
/* }}} */
433+
434+
/* {{{ php_ob_init
435+
*/
436+
static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
437+
{
438+
int res, result, len;
439+
char *handler_name, *next_handler_name;
440+
HashPosition pos;
441+
zval **tmp;
442+
zval *handler_zval;
443+
355444
if (output_handler && output_handler->type == IS_STRING) {
356-
OG(active_ob_buffer).handler_name = estrndup(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler));
445+
result = 0;
446+
handler_name = Z_STRVAL_P(output_handler);
447+
while ((next_handler_name=strchr(handler_name, ',')) != NULL) {
448+
len = next_handler_name-handler_name;
449+
next_handler_name = estrndup(handler_name, len);
450+
handler_zval = php_ob_handler_from_string(next_handler_name TSRMLS_CC);
451+
res = php_ob_init_named(initial_size, block_size, next_handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
452+
result &= res;
453+
if (!res==SUCCESS) {
454+
zval_dtor(handler_zval);
455+
FREE_ZVAL(handler_zval);
456+
}
457+
handler_name += len+1;
458+
efree(next_handler_name);
459+
}
460+
handler_zval = php_ob_handler_from_string(handler_name TSRMLS_CC);
461+
res = php_ob_init_named(initial_size, block_size, handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
462+
result &= res;
463+
if (!res==SUCCESS) {
464+
zval_dtor(handler_zval);
465+
FREE_ZVAL(handler_zval);
466+
}
467+
result = result ? SUCCESS : FAILURE;
357468
}
358469
else if (output_handler && output_handler->type == IS_ARRAY) {
359-
/* FIXME: Array type is not supported yet.
360-
See call_user_function_ex() for detials. */
361-
OG(active_ob_buffer).handler_name = estrdup("array is not supported yet");
470+
result = 0;
471+
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(output_handler), &pos);
472+
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(output_handler), (void **)&tmp, &pos) == SUCCESS) {
473+
result &= php_ob_init(initial_size, block_size, *tmp, chunk_size, erase TSRMLS_CC);
474+
zend_hash_move_forward_ex(Z_ARRVAL_P(output_handler), &pos);
475+
}
476+
result = result ? SUCCESS : FAILURE;
362477
}
363478
else {
364-
OG(active_ob_buffer).handler_name = estrdup("default output handler");
479+
if (output_handler) {
480+
SEPARATE_ZVAL(&output_handler);
481+
output_handler->refcount++;
482+
}
483+
result = php_ob_init_named(initial_size, block_size, "default output handler", output_handler, chunk_size, erase TSRMLS_CC);
365484
}
366-
OG(active_ob_buffer).erase = erase;
485+
return result;
367486
}
368487
/* }}} */
369488

370489
/* {{{ php_ob_list_each
371490
*/
372-
373491
static int php_ob_list_each(php_ob_buffer *ob_buffer, zval *ob_handler_array)
374492
{
375-
if (!strcmp(ob_buffer->handler_name, "zlib output compression") && ob_buffer->internal_output_handler) {
376-
add_next_index_string(ob_handler_array, "ob_gzhandler", 1);
377-
} else {
378-
add_next_index_string(ob_handler_array, ob_buffer->handler_name, 1);
379-
}
493+
add_next_index_string(ob_handler_array, ob_buffer->handler_name, 1);
380494
return 0;
381495
}
382496
/* }}} */
383497

384498
/* {{{ proto array ob_list_handlers()
385-
List all output_buffers in an array */
499+
* List all output_buffers in an array
500+
*/
386501
PHP_FUNCTION(ob_list_handlers)
387502
{
388503
if (ZEND_NUM_ARGS()!=0) {
@@ -404,12 +519,11 @@ PHP_FUNCTION(ob_list_handlers)
404519
/* }}} */
405520

406521
/* {{{ php_ob_used_each
407-
Sets handler_name to NULL is found
522+
* Sets handler_name to NULL is found
408523
*/
409524
static int php_ob_handler_used_each(php_ob_buffer *ob_buffer, char **handler_name)
410525
{
411-
if ((!strcmp(ob_buffer->handler_name, "zlib output compression") && ob_buffer->internal_output_handler && !strcmp("ob_gzhandler", *handler_name))
412-
|| !strcmp(ob_buffer->handler_name, *handler_name))
526+
if (!strcmp(ob_buffer->handler_name, *handler_name))
413527
{
414528
*handler_name = NULL;
415529
return 1;
@@ -419,7 +533,7 @@ static int php_ob_handler_used_each(php_ob_buffer *ob_buffer, char **handler_nam
419533
/* }}} */
420534

421535
/* {{{ php_ob_used
422-
returns 1 if given handler_name is used as output_handler
536+
* returns 1 if given handler_name is used as output_handler
423537
*/
424538
PHPAPI int php_ob_handler_used(char *handler_name TSRMLS_DC)
425539
{
@@ -574,7 +688,7 @@ static int php_ub_body_write(const char *str, uint str_length TSRMLS_DC)
574688
* HEAD support
575689
*/
576690

577-
/* {{{ proto void ob_start([ string user_function [, int chunk_size [, bool erase]]])
691+
/* {{{ proto boolean ob_start([ string|array user_function [, int chunk_size [, bool erase]]])
578692
Turn on Output Buffering (specifying an optional output handler). */
579693
PHP_FUNCTION(ob_start)
580694
{
@@ -585,20 +699,9 @@ PHP_FUNCTION(ob_start)
585699

586700
if (zend_parse_parameters(argc TSRMLS_CC, "|zlb", &output_handler,
587701
&chunk_size, &erase) == FAILURE)
588-
return;
702+
RETURN_FALSE;
589703

590-
if (output_handler) {
591-
SEPARATE_ZVAL(&output_handler);
592-
output_handler->refcount++;
593-
}
594704
if (php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC)==FAILURE) {
595-
if (SG(headers_sent) && !SG(request_info).headers_only) {
596-
OG(php_body_write) = php_ub_body_write_no_header;
597-
} else {
598-
OG(php_body_write) = php_ub_body_write;
599-
}
600-
OG(ob_nesting_level) = 0;
601-
php_error(E_ERROR, "Cannot use output buffering in output buffering display handlers");
602705
RETURN_FALSE;
603706
}
604707
RETURN_TRUE;
@@ -754,41 +857,29 @@ static int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result)
754857
}
755858

756859

757-
/* {{{ proto array ob_get_status([bool full_status])
758-
Return the nesting level of the output buffer */
860+
/* {{{ proto false|array ob_get_status([bool full_status])
861+
Return the status of the active or all output buffers */
759862
PHP_FUNCTION(ob_get_status)
760863
{
761864
int argc = ZEND_NUM_ARGS();
762865
zend_bool full_status = 0;
763866

764867
if (zend_parse_parameters(argc TSRMLS_CC, "|b", &full_status) == FAILURE )
765-
return;
868+
RETURN_FALSE;
766869

767870
if (array_init(return_value) == FAILURE) {
768871
RETURN_FALSE;
769872
}
770873

771874
if (full_status) {
772-
zval *elem;
773-
774-
zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *elem, void *))php_ob_buffer_status, return_value);
775-
776-
MAKE_STD_ZVAL(elem);
777-
if (array_init(elem))
778-
RETURN_FALSE;
779-
780-
if (OG(active_ob_buffer).internal_output_handler) {
781-
add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_INTERNAL);
875+
if (OG(ob_nesting_level)>1) {
876+
zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *elem, void *))php_ob_buffer_status, return_value);
782877
}
783-
else {
784-
add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_USER);
878+
if (OG(ob_nesting_level)>0 && php_ob_buffer_status(&OG(active_ob_buffer), return_value)==FAILURE) {
879+
RETURN_FALSE;
785880
}
786-
add_assoc_long(elem, "status", OG(active_ob_buffer).status);
787-
add_assoc_string(elem, "name", OG(active_ob_buffer).handler_name, 1);
788-
add_assoc_bool(elem, "del", OG(active_ob_buffer).erase);
789-
add_next_index_zval(return_value, elem);
790881
}
791-
else {
882+
else if (OG(ob_nesting_level)>0) {
792883
add_assoc_long(return_value, "level", OG(ob_nesting_level));
793884
if (OG(active_ob_buffer).internal_output_handler) {
794885
add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_INTERNAL);

0 commit comments

Comments
 (0)