1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Zeev Suraski <zeev@zend.com> |
16 | Thies C. Arntzen <thies@thieso.net> |
17 | Marcus Boerger <helly@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 /* $Id$ */
22
23 #include "php.h"
24 #include "ext/standard/head.h"
25 #include "ext/standard/basic_functions.h"
26 #include "ext/standard/url_scanner_ex.h"
27 #if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
28 #include "ext/zlib/php_zlib.h"
29 #endif
30 #include "SAPI.h"
31
32 #define OB_DEFAULT_HANDLER_NAME "default output handler"
33
34 /* output functions */
35 static int php_b_body_write(const char *str, uint str_length TSRMLS_DC);
36
37 static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC);
38 static void php_ob_append(const char *text, uint text_length TSRMLS_DC);
39 #if 0
40 static void php_ob_prepend(const char *text, uint text_length);
41 #endif
42
43 #ifdef ZTS
44 int output_globals_id;
45 #else
46 php_output_globals output_globals;
47 #endif
48
49 /* {{{ php_default_output_func */
php_default_output_func(const char * str,uint str_len TSRMLS_DC)50 PHPAPI int php_default_output_func(const char *str, uint str_len TSRMLS_DC)
51 {
52 fwrite(str, 1, str_len, stderr);
53 /* See http://support.microsoft.com/kb/190351 */
54 #ifdef PHP_WIN32
55 fflush(stderr);
56 #endif
57 return str_len;
58 }
59 /* }}} */
60
61 /* {{{ php_output_init_globals */
php_output_init_globals(php_output_globals * output_globals_p TSRMLS_DC)62 static void php_output_init_globals(php_output_globals *output_globals_p TSRMLS_DC)
63 {
64 OG(php_body_write) = php_default_output_func;
65 OG(php_header_write) = php_default_output_func;
66 OG(implicit_flush) = 0;
67 OG(output_start_filename) = NULL;
68 OG(output_start_lineno) = 0;
69 }
70 /* }}} */
71
72 /* {{{ php_output_startup
73 * Start output layer */
php_output_startup(void)74 PHPAPI void php_output_startup(void)
75 {
76 #ifdef ZTS
77 ts_allocate_id(&output_globals_id, sizeof(php_output_globals), (ts_allocate_ctor) php_output_init_globals, NULL);
78 #else
79 php_output_init_globals(&output_globals TSRMLS_CC);
80 #endif
81 }
82 /* }}} */
83
84 /* {{{ php_output_activate
85 * Initilize output global for activation */
php_output_activate(TSRMLS_D)86 PHPAPI void php_output_activate(TSRMLS_D)
87 {
88 OG(php_body_write) = php_ub_body_write;
89 OG(php_header_write) = sapi_module.ub_write;
90 OG(ob_nesting_level) = 0;
91 OG(ob_lock) = 0;
92 OG(disable_output) = 0;
93 OG(output_start_filename) = NULL;
94 OG(output_start_lineno) = 0;
95 }
96 /* }}} */
97
98 /* {{{ php_output_register_constants */
php_output_register_constants(TSRMLS_D)99 void php_output_register_constants(TSRMLS_D)
100 {
101 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_CS | CONST_PERSISTENT);
102 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_CONT, CONST_CS | CONST_PERSISTENT);
103 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_END, CONST_CS | CONST_PERSISTENT);
104 }
105 /* }}} */
106
107 /* {{{ php_output_set_status
108 * Toggle output status. Do NOT use in application code, only in SAPIs where appropriate. */
php_output_set_status(zend_bool status TSRMLS_DC)109 PHPAPI void php_output_set_status(zend_bool status TSRMLS_DC)
110 {
111 OG(disable_output) = !status;
112 }
113 /* }}} */
114
115 /* {{{ php_body_write
116 * Write body part */
php_body_write(const char * str,uint str_length TSRMLS_DC)117 PHPAPI int php_body_write(const char *str, uint str_length TSRMLS_DC)
118 {
119 return OG(php_body_write)(str, str_length TSRMLS_CC);
120 }
121 /* }}} */
122
123 /* {{{ php_header_write
124 * Write HTTP header */
php_header_write(const char * str,uint str_length TSRMLS_DC)125 PHPAPI int php_header_write(const char *str, uint str_length TSRMLS_DC)
126 {
127 if (OG(disable_output)) {
128 return 0;
129 } else {
130 return OG(php_header_write)(str, str_length TSRMLS_CC);
131 }
132 }
133 /* }}} */
134
135 /* {{{ php_start_ob_buffer
136 * Start output buffering */
php_start_ob_buffer(zval * output_handler,uint chunk_size,zend_bool erase TSRMLS_DC)137 PHPAPI int php_start_ob_buffer(zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
138 {
139 uint initial_size, block_size;
140
141 if (OG(ob_lock)) {
142 if (SG(headers_sent) && !SG(request_info).headers_only) {
143 OG(php_body_write) = php_ub_body_write_no_header;
144 } else {
145 OG(php_body_write) = php_ub_body_write;
146 }
147 OG(ob_nesting_level) = 0;
148 php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers");
149 return FAILURE;
150 }
151 if (chunk_size > 0) {
152 if (chunk_size==1) {
153 chunk_size = 4096;
154 }
155 initial_size = (chunk_size*3/2);
156 block_size = chunk_size/2;
157 } else {
158 initial_size = 40*1024;
159 block_size = 10*1024;
160 }
161 return php_ob_init(initial_size, block_size, output_handler, chunk_size, erase TSRMLS_CC);
162 }
163 /* }}} */
164
165 /* {{{ php_start_ob_buffer_named
166 * Start output buffering */
php_start_ob_buffer_named(const char * output_handler_name,uint chunk_size,zend_bool erase TSRMLS_DC)167 PHPAPI int php_start_ob_buffer_named(const char *output_handler_name, uint chunk_size, zend_bool erase TSRMLS_DC)
168 {
169 zval *output_handler;
170 int result;
171
172 ALLOC_INIT_ZVAL(output_handler);
173 Z_STRLEN_P(output_handler) = strlen(output_handler_name); /* this can be optimized */
174 Z_STRVAL_P(output_handler) = estrndup(output_handler_name, Z_STRLEN_P(output_handler));
175 Z_TYPE_P(output_handler) = IS_STRING;
176 result = php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC);
177 zval_dtor(output_handler);
178 FREE_ZVAL(output_handler);
179 return result;
180 }
181 /* }}} */
182
183 /* {{{ php_end_ob_buffer
184 * End output buffering (one level) */
php_end_ob_buffer(zend_bool send_buffer,zend_bool just_flush TSRMLS_DC)185 PHPAPI void php_end_ob_buffer(zend_bool send_buffer, zend_bool just_flush TSRMLS_DC)
186 {
187 char *final_buffer=NULL;
188 unsigned int final_buffer_length=0;
189 zval *alternate_buffer=NULL;
190 char *to_be_destroyed_buffer, *to_be_destroyed_handler_name;
191 char *to_be_destroyed_handled_output[2] = { 0, 0 };
192 int status;
193 php_ob_buffer *prev_ob_buffer_p=NULL;
194 php_ob_buffer orig_ob_buffer;
195
196 if (OG(ob_nesting_level)==0) {
197 return;
198 }
199 status = 0;
200 if (!OG(active_ob_buffer).status & PHP_OUTPUT_HANDLER_START) {
201 /* our first call */
202 status |= PHP_OUTPUT_HANDLER_START;
203 }
204 if (just_flush) {
205 status |= PHP_OUTPUT_HANDLER_CONT;
206 } else {
207 status |= PHP_OUTPUT_HANDLER_END;
208 }
209
210 #if 0
211 {
212 FILE *fp;
213 fp = fopen("/tmp/ob_log", "a");
214 fprintf(fp, "NestLevel: %d ObStatus: %d HandlerName: %s\n", OG(ob_nesting_level), status, OG(active_ob_buffer).handler_name);
215 fclose(fp);
216 }
217 #endif
218
219 if (OG(active_ob_buffer).internal_output_handler) {
220 final_buffer = OG(active_ob_buffer).internal_output_handler_buffer;
221 final_buffer_length = OG(active_ob_buffer).internal_output_handler_buffer_size;
222 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);
223 } else if (OG(active_ob_buffer).output_handler) {
224 zval **params[2];
225 zval *orig_buffer;
226 zval *z_status;
227
228 if(OG(ob_lock)) {
229 if (SG(headers_sent) && !SG(request_info).headers_only) {
230 OG(php_body_write) = php_ub_body_write_no_header;
231 } else {
232 OG(php_body_write) = php_ub_body_write;
233 }
234 OG(ob_nesting_level) = 0;
235 php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers");
236 return;
237 }
238
239 ALLOC_INIT_ZVAL(orig_buffer);
240 ZVAL_STRINGL(orig_buffer, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1);
241
242 ALLOC_INIT_ZVAL(z_status);
243 ZVAL_LONG(z_status, status);
244
245 params[0] = &orig_buffer;
246 params[1] = &z_status;
247 OG(ob_lock) = 1;
248
249 if (call_user_function_ex(CG(function_table), NULL, OG(active_ob_buffer).output_handler, &alternate_buffer, 2, params, 1, NULL TSRMLS_CC)==SUCCESS) {
250 if (alternate_buffer && !(Z_TYPE_P(alternate_buffer)==IS_BOOL && Z_BVAL_P(alternate_buffer)==0)) {
251 convert_to_string_ex(&alternate_buffer);
252 final_buffer = Z_STRVAL_P(alternate_buffer);
253 final_buffer_length = Z_STRLEN_P(alternate_buffer);
254 }
255 }
256 OG(ob_lock) = 0;
257 if (!just_flush) {
258 zval_ptr_dtor(&OG(active_ob_buffer).output_handler);
259 }
260 zval_ptr_dtor(&orig_buffer);
261 zval_ptr_dtor(&z_status);
262 }
263
264 if (!final_buffer) {
265 final_buffer = OG(active_ob_buffer).buffer;
266 final_buffer_length = OG(active_ob_buffer).text_length;
267 }
268
269 if (OG(ob_nesting_level)==1) { /* end buffering */
270 if (SG(headers_sent) && !SG(request_info).headers_only) {
271 OG(php_body_write) = php_ub_body_write_no_header;
272 } else {
273 OG(php_body_write) = php_ub_body_write;
274 }
275 }
276
277 to_be_destroyed_buffer = OG(active_ob_buffer).buffer;
278 to_be_destroyed_handler_name = OG(active_ob_buffer).handler_name;
279 if (OG(active_ob_buffer).internal_output_handler
280 && (final_buffer != OG(active_ob_buffer).internal_output_handler_buffer)
281 && (final_buffer != OG(active_ob_buffer).buffer)) {
282 to_be_destroyed_handled_output[0] = final_buffer;
283 }
284
285 if (!just_flush) {
286 if (OG(active_ob_buffer).internal_output_handler) {
287 to_be_destroyed_handled_output[1] = OG(active_ob_buffer).internal_output_handler_buffer;
288 }
289 }
290 if (OG(ob_nesting_level)>1) { /* restore previous buffer */
291 zend_stack_top(&OG(ob_buffers), (void **) &prev_ob_buffer_p);
292 orig_ob_buffer = OG(active_ob_buffer);
293 OG(active_ob_buffer) = *prev_ob_buffer_p;
294 zend_stack_del_top(&OG(ob_buffers));
295 if (!just_flush && OG(ob_nesting_level)==2) { /* destroy the stack */
296 zend_stack_destroy(&OG(ob_buffers));
297 }
298 }
299 OG(ob_nesting_level)--;
300
301 if (send_buffer) {
302 if (just_flush) { /* if flush is called prior to proper end, ensure presence of NUL */
303 final_buffer[final_buffer_length] = '\0';
304 }
305 OG(php_body_write)(final_buffer, final_buffer_length TSRMLS_CC);
306 }
307
308 if (just_flush) { /* we restored the previous ob, return to the current */
309 if (prev_ob_buffer_p) {
310 zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer));
311 OG(active_ob_buffer) = orig_ob_buffer;
312 }
313 OG(ob_nesting_level)++;
314 }
315
316 if (alternate_buffer) {
317 zval_ptr_dtor(&alternate_buffer);
318 }
319
320 if (status & PHP_OUTPUT_HANDLER_END) {
321 efree(to_be_destroyed_handler_name);
322 }
323 if (!just_flush) {
324 efree(to_be_destroyed_buffer);
325 } else {
326 OG(active_ob_buffer).text_length = 0;
327 OG(active_ob_buffer).status |= PHP_OUTPUT_HANDLER_START;
328 OG(php_body_write) = php_b_body_write;
329 }
330 if (to_be_destroyed_handled_output[0]) {
331 efree(to_be_destroyed_handled_output[0]);
332 }
333 if (to_be_destroyed_handled_output[1]) {
334 efree(to_be_destroyed_handled_output[1]);
335 }
336 }
337 /* }}} */
338
339 /* {{{ php_end_ob_buffers
340 * End output buffering (all buffers) */
php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC)341 PHPAPI void php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC)
342 {
343 while (OG(ob_nesting_level)!=0) {
344 php_end_ob_buffer(send_buffer, 0 TSRMLS_CC);
345 }
346 }
347 /* }}} */
348
349 /* {{{ php_start_implicit_flush
350 */
php_start_implicit_flush(TSRMLS_D)351 PHPAPI void php_start_implicit_flush(TSRMLS_D)
352 {
353 OG(implicit_flush) = 1;
354 }
355 /* }}} */
356
357 /* {{{ php_end_implicit_flush
358 */
php_end_implicit_flush(TSRMLS_D)359 PHPAPI void php_end_implicit_flush(TSRMLS_D)
360 {
361 OG(implicit_flush) = 0;
362 }
363 /* }}} */
364
365 /* {{{ char *php_get_output_start_filename(TSRMLS_D)
366 * Return filename start output something */
php_get_output_start_filename(TSRMLS_D)367 PHPAPI char *php_get_output_start_filename(TSRMLS_D)
368 {
369 return OG(output_start_filename);
370 }
371 /* }}} */
372
373 /* {{{ char *php_get_output_start_lineno(TSRMLS_D)
374 * Return line number start output something */
php_get_output_start_lineno(TSRMLS_D)375 PHPAPI int php_get_output_start_lineno(TSRMLS_D)
376 {
377 return OG(output_start_lineno);
378 }
379 /* }}} */
380
381 /* {{{ php_ob_set_internal_handler
382 */
php_ob_set_internal_handler(php_output_handler_func_t internal_output_handler,uint buffer_size,char * handler_name,zend_bool erase TSRMLS_DC)383 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)
384 {
385 if (OG(ob_nesting_level) == 0 || OG(active_ob_buffer).internal_output_handler || strcmp(OG(active_ob_buffer).handler_name, OB_DEFAULT_HANDLER_NAME)) {
386 php_start_ob_buffer(NULL, buffer_size, erase TSRMLS_CC);
387 }
388
389 OG(active_ob_buffer).internal_output_handler = internal_output_handler;
390 OG(active_ob_buffer).internal_output_handler_buffer = (char *) emalloc(buffer_size);
391 OG(active_ob_buffer).internal_output_handler_buffer_size = buffer_size;
392 if (OG(active_ob_buffer).handler_name) {
393 efree(OG(active_ob_buffer).handler_name);
394 }
395 OG(active_ob_buffer).handler_name = estrdup(handler_name);
396 OG(active_ob_buffer).erase = erase;
397 }
398 /* }}} */
399
400 /*
401 * Output buffering - implementation
402 */
403
404 /* {{{ php_ob_allocate
405 */
php_ob_allocate(uint text_length TSRMLS_DC)406 static inline void php_ob_allocate(uint text_length TSRMLS_DC)
407 {
408 uint new_len = OG(active_ob_buffer).text_length + text_length;
409
410 if (OG(active_ob_buffer).size < new_len) {
411 uint buf_size = OG(active_ob_buffer).size;
412 while (buf_size <= new_len) {
413 buf_size += OG(active_ob_buffer).block_size;
414 }
415
416 OG(active_ob_buffer).buffer = (char *) erealloc(OG(active_ob_buffer).buffer, buf_size+1);
417 OG(active_ob_buffer).size = buf_size;
418 }
419 OG(active_ob_buffer).text_length = new_len;
420 }
421 /* }}} */
422
423 /* {{{ php_ob_init_conflict
424 * Returns 1 if handler_set is already used and generates error message */
php_ob_init_conflict(char * handler_new,char * handler_set TSRMLS_DC)425 PHPAPI int php_ob_init_conflict(char *handler_new, char *handler_set TSRMLS_DC)
426 {
427 if (php_ob_handler_used(handler_set TSRMLS_CC)) {
428 php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set);
429 return 1;
430 }
431 return 0;
432 }
433 /* }}} */
434
435 /* {{{ php_ob_init_named
436 */
php_ob_init_named(uint initial_size,uint block_size,char * handler_name,zval * output_handler,uint chunk_size,zend_bool erase TSRMLS_DC)437 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)
438 {
439 php_ob_buffer tmp_buf;
440
441 if (output_handler && !zend_is_callable(output_handler, 0, NULL TSRMLS_CC)) {
442 return FAILURE;
443 }
444
445 tmp_buf.block_size = block_size;
446 tmp_buf.size = initial_size;
447 tmp_buf.buffer = (char *) emalloc(initial_size+1);
448 tmp_buf.text_length = 0;
449 tmp_buf.output_handler = output_handler;
450 tmp_buf.chunk_size = chunk_size;
451 tmp_buf.status = 0;
452 tmp_buf.internal_output_handler = NULL;
453 tmp_buf.internal_output_handler_buffer = NULL;
454 tmp_buf.internal_output_handler_buffer_size = 0;
455 tmp_buf.handler_name = estrdup(handler_name&&handler_name[0]?handler_name:OB_DEFAULT_HANDLER_NAME);
456 tmp_buf.erase = erase;
457
458 if (OG(ob_nesting_level)>0) {
459 #if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
460 if (!strncmp(handler_name, "ob_gzhandler", sizeof("ob_gzhandler")) && php_ob_gzhandler_check(TSRMLS_C)) {
461 return FAILURE;
462 }
463 #endif
464 if (OG(ob_nesting_level)==1) { /* initialize stack */
465 zend_stack_init(&OG(ob_buffers));
466 }
467 zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer));
468 }
469 OG(ob_nesting_level)++;
470 OG(active_ob_buffer) = tmp_buf;
471 OG(php_body_write) = php_b_body_write;
472 return SUCCESS;
473 }
474 /* }}} */
475
476 /* {{{ php_ob_handler_from_string
477 * Create zval output handler from string */
php_ob_handler_from_string(const char * handler_name,int len TSRMLS_DC)478 static zval* php_ob_handler_from_string(const char *handler_name, int len TSRMLS_DC)
479 {
480 zval *output_handler;
481
482 ALLOC_INIT_ZVAL(output_handler);
483 Z_STRLEN_P(output_handler) = len;
484 Z_STRVAL_P(output_handler) = estrndup(handler_name, len);
485 Z_TYPE_P(output_handler) = IS_STRING;
486 return output_handler;
487 }
488 /* }}} */
489
490 /* {{{ php_ob_init
491 */
php_ob_init(uint initial_size,uint block_size,zval * output_handler,uint chunk_size,zend_bool erase TSRMLS_DC)492 static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
493 {
494 int result = FAILURE, handler_len, len;
495 char *handler_name, *next_handler_name;
496 HashPosition pos;
497 zval **tmp;
498 zval *handler_zval;
499
500 if (output_handler && output_handler->type == IS_STRING) {
501 handler_name = Z_STRVAL_P(output_handler);
502 handler_len = Z_STRLEN_P(output_handler);
503
504 result = SUCCESS;
505 if (handler_len && handler_name[0] != '\0') {
506 while ((next_handler_name=strchr(handler_name, ',')) != NULL) {
507 len = next_handler_name-handler_name;
508 next_handler_name = estrndup(handler_name, len);
509 handler_zval = php_ob_handler_from_string(next_handler_name, len TSRMLS_CC);
510 result = php_ob_init_named(initial_size, block_size, next_handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
511 if (result != SUCCESS) {
512 zval_dtor(handler_zval);
513 FREE_ZVAL(handler_zval);
514 }
515 handler_name += len+1;
516 handler_len -= len+1;
517 efree(next_handler_name);
518 }
519 }
520 if (result == SUCCESS) {
521 handler_zval = php_ob_handler_from_string(handler_name, handler_len TSRMLS_CC);
522 result = php_ob_init_named(initial_size, block_size, handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
523 if (result != SUCCESS) {
524 zval_dtor(handler_zval);
525 FREE_ZVAL(handler_zval);
526 }
527 }
528 } else if (output_handler && output_handler->type == IS_ARRAY) {
529 /* do we have array(object,method) */
530 if (zend_is_callable(output_handler, 0, &handler_name TSRMLS_CC)) {
531 SEPARATE_ZVAL(&output_handler);
532 Z_ADDREF_P(output_handler);
533 result = php_ob_init_named(initial_size, block_size, handler_name, output_handler, chunk_size, erase TSRMLS_CC);
534 efree(handler_name);
535 } else {
536 efree(handler_name);
537 /* init all array elements recursively */
538 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(output_handler), &pos);
539 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(output_handler), (void **)&tmp, &pos) == SUCCESS) {
540 result = php_ob_init(initial_size, block_size, *tmp, chunk_size, erase TSRMLS_CC);
541 if (result == FAILURE) {
542 break;
543 }
544 zend_hash_move_forward_ex(Z_ARRVAL_P(output_handler), &pos);
545 }
546 }
547 } else if (output_handler && output_handler->type == IS_OBJECT) {
548 /* do we have callable object */
549 if (zend_is_callable(output_handler, 0, &handler_name TSRMLS_CC)) {
550 SEPARATE_ZVAL(&output_handler);
551 Z_ADDREF_P(output_handler);
552 result = php_ob_init_named(initial_size, block_size, handler_name, output_handler, chunk_size, erase TSRMLS_CC);
553 efree(handler_name);
554 } else {
555 efree(handler_name);
556 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);
557 result = FAILURE;
558 }
559 } else {
560 result = php_ob_init_named(initial_size, block_size, OB_DEFAULT_HANDLER_NAME, NULL, chunk_size, erase TSRMLS_CC);
561 }
562 return result;
563 }
564 /* }}} */
565
566 /* {{{ php_ob_list_each
567 */
php_ob_list_each(php_ob_buffer * ob_buffer,zval * ob_handler_array)568 static int php_ob_list_each(php_ob_buffer *ob_buffer, zval *ob_handler_array)
569 {
570 add_next_index_string(ob_handler_array, ob_buffer->handler_name, 1);
571 return 0;
572 }
573 /* }}} */
574
575 /* {{{ php_ob_used_each
576 Sets handler_name to NULL is found */
php_ob_handler_used_each(php_ob_buffer * ob_buffer,char ** handler_name)577 static int php_ob_handler_used_each(php_ob_buffer *ob_buffer, char **handler_name)
578 {
579 if (!strcmp(ob_buffer->handler_name, *handler_name)) {
580 *handler_name = NULL;
581 return 1;
582 }
583 return 0;
584 }
585 /* }}} */
586
587 /* {{{ php_ob_used
588 returns 1 if given handler_name is used as output_handler */
php_ob_handler_used(char * handler_name TSRMLS_DC)589 PHPAPI int php_ob_handler_used(char *handler_name TSRMLS_DC)
590 {
591 char *tmp = handler_name;
592
593 if (OG(ob_nesting_level)) {
594 if (!strcmp(OG(active_ob_buffer).handler_name, handler_name)) {
595 return 1;
596 }
597 if (OG(ob_nesting_level)>1) {
598 zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_handler_used_each, &tmp);
599 }
600 }
601 return tmp ? 0 : 1;
602 }
603 /* }}} */
604
605 /* {{{ php_ob_append
606 */
php_ob_append(const char * text,uint text_length TSRMLS_DC)607 static inline void php_ob_append(const char *text, uint text_length TSRMLS_DC)
608 {
609 char *target;
610 uint original_ob_text_length;
611
612 original_ob_text_length=OG(active_ob_buffer).text_length;
613
614 php_ob_allocate(text_length TSRMLS_CC);
615 target = OG(active_ob_buffer).buffer+original_ob_text_length;
616 memcpy(target, text, text_length);
617 target[text_length]=0;
618
619 /* If implicit_flush is On or chunked buffering, send contents to next buffer and return. */
620 if (OG(active_ob_buffer).chunk_size
621 && OG(active_ob_buffer).text_length >= OG(active_ob_buffer).chunk_size) {
622
623 php_end_ob_buffer(1, 1 TSRMLS_CC);
624 return;
625 }
626 }
627 /* }}} */
628
629 #if 0
630 static inline void php_ob_prepend(const char *text, uint text_length)
631 {
632 char *p, *start;
633 TSRMLS_FETCH();
634
635 php_ob_allocate(text_length TSRMLS_CC);
636
637 /* php_ob_allocate() may change OG(ob_buffer), so we can't initialize p&start earlier */
638 p = OG(ob_buffer)+OG(ob_text_length);
639 start = OG(ob_buffer);
640
641 while (--p>=start) {
642 p[text_length] = *p;
643 }
644 memcpy(OG(ob_buffer), text, text_length);
645 OG(ob_buffer)[OG(active_ob_buffer).text_length]=0;
646 }
647 #endif
648
649 /* {{{ php_ob_get_buffer
650 * Return the current output buffer */
php_ob_get_buffer(zval * p TSRMLS_DC)651 PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC)
652 {
653 if (OG(ob_nesting_level)==0) {
654 return FAILURE;
655 }
656 ZVAL_STRINGL(p, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1);
657 return SUCCESS;
658 }
659 /* }}} */
660
661 /* {{{ php_ob_get_length
662 * Return the size of the current output buffer */
php_ob_get_length(zval * p TSRMLS_DC)663 PHPAPI int php_ob_get_length(zval *p TSRMLS_DC)
664 {
665 if (OG(ob_nesting_level) == 0) {
666 return FAILURE;
667 }
668 ZVAL_LONG(p, OG(active_ob_buffer).text_length);
669 return SUCCESS;
670 }
671 /* }}} */
672
673 /*
674 * Wrapper functions - implementation
675 */
676
677 /* buffered output function */
php_b_body_write(const char * str,uint str_length TSRMLS_DC)678 static int php_b_body_write(const char *str, uint str_length TSRMLS_DC)
679 {
680 php_ob_append(str, str_length TSRMLS_CC);
681 return str_length;
682 }
683
684 /* {{{ php_ub_body_write_no_header
685 */
php_ub_body_write_no_header(const char * str,uint str_length TSRMLS_DC)686 PHPAPI int php_ub_body_write_no_header(const char *str, uint str_length TSRMLS_DC)
687 {
688 int result;
689
690 if (OG(disable_output)) {
691 return 0;
692 }
693
694 result = OG(php_header_write)(str, str_length TSRMLS_CC);
695
696 if (OG(implicit_flush)) {
697 sapi_flush(TSRMLS_C);
698 }
699
700 return result;
701 }
702 /* }}} */
703
704 /* {{{ php_ub_body_write
705 */
php_ub_body_write(const char * str,uint str_length TSRMLS_DC)706 PHPAPI int php_ub_body_write(const char *str, uint str_length TSRMLS_DC)
707 {
708 int result = 0;
709
710 if (SG(request_info).headers_only) {
711 if(SG(headers_sent)) {
712 return 0;
713 }
714 php_header(TSRMLS_C);
715 zend_bailout();
716 }
717 if (php_header(TSRMLS_C)) {
718 if (zend_is_compiling(TSRMLS_C)) {
719 OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C);
720 OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C);
721 } else if (zend_is_executing(TSRMLS_C)) {
722 OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C);
723 OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C);
724 }
725
726 OG(php_body_write) = php_ub_body_write_no_header;
727 result = php_ub_body_write_no_header(str, str_length TSRMLS_CC);
728 }
729
730 return result;
731 }
732 /* }}} */
733
734 /* {{{ int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result)
735 */
php_ob_buffer_status(php_ob_buffer * ob_buffer,zval * result)736 static int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result)
737 {
738 zval *elem;
739
740 MAKE_STD_ZVAL(elem);
741 array_init(elem);
742
743 add_assoc_long(elem, "chunk_size", ob_buffer->chunk_size);
744 if (!ob_buffer->chunk_size) {
745 add_assoc_long(elem, "size", ob_buffer->size);
746 add_assoc_long(elem, "block_size", ob_buffer->block_size);
747 }
748 if (ob_buffer->internal_output_handler) {
749 add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_INTERNAL);
750 add_assoc_long(elem, "buffer_size", ob_buffer->internal_output_handler_buffer_size);
751 } else {
752 add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_USER);
753 }
754 add_assoc_long(elem, "status", ob_buffer->status);
755 add_assoc_string(elem, "name", ob_buffer->handler_name, 1);
756 add_assoc_bool(elem, "del", ob_buffer->erase);
757 add_next_index_zval(result, elem);
758
759 return SUCCESS;
760 }
761 /* }}} */
762
763 /*
764 * USERLAND (nearly 1:1 of old output.c)
765 */
766
767 /* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, bool erase]]])
768 Turn on Output Buffering (specifying an optional output handler). */
PHP_FUNCTION(ob_start)769 PHP_FUNCTION(ob_start)
770 {
771 zval *output_handler = NULL;
772 long chunk_size = 0;
773 zend_bool erase = 1;
774
775 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/lb", &output_handler, &chunk_size, &erase) == FAILURE) {
776 return;
777 }
778
779 if (chunk_size < 0) {
780 chunk_size = 0;
781 }
782
783 if (php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC) == FAILURE) {
784 RETURN_FALSE;
785 }
786 RETURN_TRUE;
787 }
788 /* }}} */
789
790 /* {{{ proto bool ob_flush(void)
791 Flush (send) contents of the output buffer. The last buffer content is sent to next buffer */
PHP_FUNCTION(ob_flush)792 PHP_FUNCTION(ob_flush)
793 {
794 if (zend_parse_parameters_none() == FAILURE) {
795 return;
796 }
797
798 if (!OG(ob_nesting_level)) {
799 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush");
800 RETURN_FALSE;
801 }
802
803 if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
804 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer %s", OG(active_ob_buffer).handler_name);
805 RETURN_FALSE;
806 }
807
808 php_end_ob_buffer(1, 1 TSRMLS_CC);
809 RETURN_TRUE;
810 }
811 /* }}} */
812
813 /* {{{ proto bool ob_clean(void)
814 Clean (delete) the current output buffer */
PHP_FUNCTION(ob_clean)815 PHP_FUNCTION(ob_clean)
816 {
817 if (zend_parse_parameters_none() == FAILURE) {
818 return;
819 }
820
821 if (!OG(ob_nesting_level)) {
822 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
823 RETURN_FALSE;
824 }
825
826 if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
827 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
828 RETURN_FALSE;
829 }
830
831 php_end_ob_buffer(0, 1 TSRMLS_CC);
832 RETURN_TRUE;
833 }
834 /* }}} */
835
836 /* {{{ proto bool ob_end_flush(void)
837 Flush (send) the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_end_flush)838 PHP_FUNCTION(ob_end_flush)
839 {
840 if (zend_parse_parameters_none() == FAILURE) {
841 return;
842 }
843
844 if (!OG(ob_nesting_level)) {
845 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
846 RETURN_FALSE;
847 }
848
849 if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
850 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
851 RETURN_FALSE;
852 }
853
854 php_end_ob_buffer(1, 0 TSRMLS_CC);
855 RETURN_TRUE;
856 }
857 /* }}} */
858
859 /* {{{ proto bool ob_end_clean(void)
860 Clean the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_end_clean)861 PHP_FUNCTION(ob_end_clean)
862 {
863 if (zend_parse_parameters_none() == FAILURE) {
864 return;
865 }
866
867 if (!OG(ob_nesting_level)) {
868 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
869 RETURN_FALSE;
870 }
871
872 if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
873 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
874 RETURN_FALSE;
875 }
876
877 php_end_ob_buffer(0, 0 TSRMLS_CC);
878 RETURN_TRUE;
879 }
880 /* }}} */
881
882 /* {{{ proto bool ob_get_flush(void)
883 Get current buffer contents, flush (send) the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_get_flush)884 PHP_FUNCTION(ob_get_flush)
885 {
886 if (zend_parse_parameters_none() == FAILURE) {
887 return;
888 }
889
890 if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) {
891 RETURN_FALSE;
892 }
893
894 if (!OG(ob_nesting_level)) {
895 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
896 zval_dtor(return_value);
897 RETURN_FALSE;
898 }
899
900 if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
901 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
902 zval_dtor(return_value);
903 RETURN_FALSE;
904 }
905
906 php_end_ob_buffer(1, 0 TSRMLS_CC);
907 }
908 /* }}} */
909
910 /* {{{ proto bool ob_get_clean(void)
911 Get current buffer contents and delete current output buffer */
PHP_FUNCTION(ob_get_clean)912 PHP_FUNCTION(ob_get_clean)
913 {
914 if (zend_parse_parameters_none() == FAILURE) {
915 return;
916 }
917
918 if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) {
919 RETURN_FALSE;
920 }
921
922 if (!OG(ob_nesting_level)) {
923 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
924 zval_dtor(return_value);
925 RETURN_FALSE;
926 }
927 if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
928 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
929 zval_dtor(return_value);
930 RETURN_FALSE;
931 }
932
933 php_end_ob_buffer(0, 0 TSRMLS_CC);
934 }
935 /* }}} */
936
937 /* {{{ proto string ob_get_contents(void)
938 Return the contents of the output buffer */
PHP_FUNCTION(ob_get_contents)939 PHP_FUNCTION(ob_get_contents)
940 {
941 if (zend_parse_parameters_none() == FAILURE) {
942 return;
943 }
944
945 if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) {
946 RETURN_FALSE;
947 }
948 }
949 /* }}} */
950
951 /* {{{ proto int ob_get_level(void)
952 Return the nesting level of the output buffer */
PHP_FUNCTION(ob_get_level)953 PHP_FUNCTION(ob_get_level)
954 {
955 if (zend_parse_parameters_none() == FAILURE) {
956 return;
957 }
958
959 RETURN_LONG(OG(ob_nesting_level));
960 }
961 /* }}} */
962
963 /* {{{ proto int ob_get_length(void)
964 Return the length of the output buffer */
PHP_FUNCTION(ob_get_length)965 PHP_FUNCTION(ob_get_length)
966 {
967 if (zend_parse_parameters_none() == FAILURE) {
968 return;
969 }
970
971 if (php_ob_get_length(return_value TSRMLS_CC) == FAILURE) {
972 RETURN_FALSE;
973 }
974 }
975 /* }}} */
976
977 /* {{{ proto false|array ob_list_handlers()
978 List all output_buffers in an array */
PHP_FUNCTION(ob_list_handlers)979 PHP_FUNCTION(ob_list_handlers)
980 {
981 if (zend_parse_parameters_none() == FAILURE) {
982 return;
983 }
984
985 array_init(return_value);
986
987 if (OG(ob_nesting_level)) {
988 if (OG(ob_nesting_level) > 1) {
989 zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_list_each, return_value);
990 }
991 php_ob_list_each(&OG(active_ob_buffer), return_value);
992 }
993 }
994 /* }}} */
995
996 /* {{{ proto false|array ob_get_status([bool full_status])
997 Return the status of the active or all output buffers */
PHP_FUNCTION(ob_get_status)998 PHP_FUNCTION(ob_get_status)
999 {
1000 zend_bool full_status = 0;
1001
1002 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &full_status) == FAILURE) {
1003 return;
1004 }
1005
1006 array_init(return_value);
1007
1008 if (full_status) {
1009 if (OG(ob_nesting_level) > 1) {
1010 zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *elem, void *))php_ob_buffer_status, return_value);
1011 }
1012 if (OG(ob_nesting_level) > 0 && php_ob_buffer_status(&OG(active_ob_buffer), return_value) == FAILURE) {
1013 RETURN_FALSE;
1014 }
1015 } else if (OG(ob_nesting_level) > 0) {
1016 add_assoc_long(return_value, "level", OG(ob_nesting_level));
1017 if (OG(active_ob_buffer).internal_output_handler) {
1018 add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_INTERNAL);
1019 } else {
1020 add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_USER);
1021 }
1022 add_assoc_long(return_value, "status", OG(active_ob_buffer).status);
1023 add_assoc_string(return_value, "name", OG(active_ob_buffer).handler_name, 1);
1024 add_assoc_bool(return_value, "del", OG(active_ob_buffer).erase);
1025 }
1026 }
1027 /* }}} */
1028
1029 /* {{{ proto void ob_implicit_flush([int flag])
1030 Turn implicit flush on/off and is equivalent to calling flush() after every output call */
PHP_FUNCTION(ob_implicit_flush)1031 PHP_FUNCTION(ob_implicit_flush)
1032 {
1033 long flag = 1;
1034
1035 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flag) == FAILURE) {
1036 return;
1037 }
1038
1039 if (flag) {
1040 php_start_implicit_flush(TSRMLS_C);
1041 } else {
1042 php_end_implicit_flush(TSRMLS_C);
1043 }
1044 }
1045 /* }}} */
1046
1047 /* {{{ proto bool output_reset_rewrite_vars(void)
1048 Reset(clear) URL rewriter values */
PHP_FUNCTION(output_reset_rewrite_vars)1049 PHP_FUNCTION(output_reset_rewrite_vars)
1050 {
1051 if (php_url_scanner_reset_vars(TSRMLS_C) == SUCCESS) {
1052 RETURN_TRUE;
1053 } else {
1054 RETURN_FALSE;
1055 }
1056 }
1057 /* }}} */
1058
1059 /* {{{ proto bool output_add_rewrite_var(string name, string value)
1060 Add URL rewriter values */
PHP_FUNCTION(output_add_rewrite_var)1061 PHP_FUNCTION(output_add_rewrite_var)
1062 {
1063 char *name, *value;
1064 int name_len, value_len;
1065
1066 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &value, &value_len) == FAILURE) {
1067 return;
1068 }
1069
1070 if (php_url_scanner_add_var(name, name_len, value, value_len, 1 TSRMLS_CC) == SUCCESS) {
1071 RETURN_TRUE;
1072 } else {
1073 RETURN_FALSE;
1074 }
1075 }
1076 /* }}} */
1077
1078 /*
1079 * Local variables:
1080 * tab-width: 4
1081 * c-basic-offset: 4
1082 * End:
1083 * vim600: sw=4 ts=4 fdm=marker
1084 * vim<600: sw=4 ts=4
1085 */
1086