]> granicus.if.org Git - php/commitdiff
- new output control code
authorMichael Wallner <mike@php.net>
Fri, 2 Jun 2006 19:51:43 +0000 (19:51 +0000)
committerMichael Wallner <mike@php.net>
Fri, 2 Jun 2006 19:51:43 +0000 (19:51 +0000)
# scan README.NEW-OUTPUT-API to get a grasp
# tree has been tagged with BEFORE_NEW_OUTPUT_API
#
# TODO:
#  - improve existing output handlers
#  - move zlib.output_compression cruft from SAPI.c to zlib.c
#  - output_encoding handling was ambigious, resp. is undefined yet
#  - more tests

42 files changed:
README.NEW-OUTPUT-API [new file with mode: 0644]
ext/pgsql/pgsql.c
ext/session/session.c
ext/soap/soap.c
ext/standard/basic_functions.c
ext/standard/head.c
ext/standard/info.c
ext/standard/url_scanner_ex.c
ext/standard/url_scanner_ex.re
ext/standard/var.c
ext/tidy/tidy.c
ext/zlib/php_zlib.h
ext/zlib/zlib.c
main/SAPI.c
main/main.c
main/output.c
main/php.h
main/php_output.h
sapi/apache/mod_php5.c
sapi/apache/php_apache.c
sapi/apache2handler/php_functions.c
sapi/apache_hooks/mod_php5.c
sapi/apache_hooks/php_apache.c
sapi/cgi/cgi_main.c
sapi/cli/php_cli.c
sapi/milter/php_milter.c
sapi/nsapi/nsapi.c
tests/output/ob_001.phpt [new file with mode: 0644]
tests/output/ob_002.phpt [new file with mode: 0644]
tests/output/ob_003.phpt [new file with mode: 0644]
tests/output/ob_004.phpt [new file with mode: 0644]
tests/output/ob_005.phpt [new file with mode: 0644]
tests/output/ob_006.phpt [new file with mode: 0644]
tests/output/ob_007.phpt [new file with mode: 0644]
tests/output/ob_008.phpt [new file with mode: 0644]
tests/output/ob_009.phpt [new file with mode: 0644]
tests/output/ob_010.phpt [new file with mode: 0644]
tests/output/ob_011.phpt [new file with mode: 0644]
tests/output/ob_012.phpt [new file with mode: 0644]
tests/output/ob_013.phpt [new file with mode: 0644]
tests/output/ob_014.phpt [new file with mode: 0644]
tests/output/ob_015.phpt [new file with mode: 0644]

diff --git a/README.NEW-OUTPUT-API b/README.NEW-OUTPUT-API
new file mode 100644 (file)
index 0000000..a6b0fe2
--- /dev/null
@@ -0,0 +1,140 @@
+$Id$
+
+
+API adjustment to the old output control code:
+
+       Everything now resides beneath the php_output namespace, and there's an 
+       API call for every output handler op.  However, there's a thin 
+       compatibility layer unless PHP_OUTPUT_NOCOMPAT is defined.
+
+       Checking output control layers status:
+               // Using OG()
+               php_output_get_status();
+
+       Starting the default output handler:
+               // php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC);
+               php_output_start_default();
+
+       Starting an user handler by zval:
+               // php_start_ob_buffer(zhandler, chunk_size, erase TSRMLS_CC);
+               php_output_start_user(zhandler, chunk_size, flags);
+
+       Starting an internal handler whithout context:
+               // php_ob_set_internal_handler(my_php_output_handler_func_t, buffer_size, "output handler name", erase TSRMLS_CC);
+               php_output_start_internal(handler_name_zval, my_php_output_handler_func_t, chunk_size, flags);
+
+       Starting an internal handler with context:
+               // not possible with old API
+               php_output_handler *h;
+               h = php_output_handler_create_internal(handler_name_zval, my_php_output_handler_context_func_t, chunk_size, flags);
+               php_output_handler_set_context(h, my_context, my_context_dtor);
+               php_output_handler_start(h);
+
+       Testing whether a certain output handler has already been started:
+               // php_ob_handler_used("output handler name" TSRMLS_CC);
+               php_output_handler_started(handler_name_zval);
+
+       Flushing one output buffer:
+               // php_ob_end_buffer(1, 1 TSRMLS_CC);
+               php_output_flush();
+
+       Flushing all output buffers:
+               // not possible with old API
+               php_output_flush_all();
+
+       Cleaning one output buffer:
+               // php_ob_end_buffer(0, 1 TSRMLS_CC);
+               php_output_clean();
+
+       Cleaning all output buffers:
+               // not possible with old API
+               php_output_clean_all();
+
+       Discarding one output buffer:
+               // php_ob_end_buffer(0, 0 TSRMLS_CC);
+               php_output_discard();
+
+       Discarding all output buffers:
+               // php_ob_end_buffers(0 TSRMLS_CC);
+               php_output_discard_all();
+
+       Stopping (and dropping) one output buffer:
+               // php_ob_end_buffer(1, 0 TSRMLS_CC)
+               php_output_end();
+
+       Stopping (and dropping) all output buffers:
+               // php_ob_end_buffers(1, 0 TSRMLS_CC);
+               php_output_end_all();
+
+       Retrieving output buffers contents:
+               // php_ob_get_buffer(zstring TSRMLS_CC);
+               php_output_get_contents(zstring);
+
+       Retrieving output buffers length:
+               // php_ob_get_length(zlength TSRMLS_CC);
+               php_output_get_length(zlenght);
+
+       Retrieving output buffering level:
+               // OG(nesting_level);
+               php_output_get_level();
+
+       Issue a warning because of an output handler conflict:
+               // php_ob_init_conflict("to be started handler name", "to be tested if already started handler name" TSRMLS_CC);
+               php_output_handler_conflict(new_handler_name_zval, set_handler_name_zval);
+
+       Registering a conflict checking function, which will be checked prior starting the handler:
+               // not possible with old API, unless hardcoding into output.c
+               php_output_handler_conflict_register(handler_name_zval, my_php_output_handler_conflict_check_t);
+
+       Registering a reverse conflict checking function, which will be checked prior starting the specified foreign handler:
+               // not possible with old API
+               php_output_handler_reverse_conflict_register(foreign_handler_name_zval, my_php_output_handler_conflict_check_t);
+
+       Facilitating a context from within an output handler callable with ob_start():
+               // not possible with old API
+               php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ, (void *) &custom_ctx_ptr_ptr);
+
+       Disabling of the output handler by itself:
+               //not possible with old API
+               php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL);
+
+       Marking an output handler immutable by itself because of irreversibility of its operation:
+               // not possible with old API
+               php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
+
+       Restarting the output handler because of a CLEAN operation:
+               // not possible with old API
+               if (flags & PHP_OUTPUT_HANDLER_CLEAN) { ... }
+
+       Recognizing by the output handler itself if it gets discarded:
+               // not possible with old API
+               if ((flags & PHP_OUTPUT_HANDLER_CLEAN) && (flags & PHP_OUTPUT_HANDLER_FINAL)) { ... }
+
+
+Output handler hooks
+
+       The output handler can change its abilities at runtime. Eg. the gz handler can
+       remove the CLEANABLE and REMOVABLE bits when the first output has passed through it;
+       or handlers implemented in C to be used with ob_start() can contain a non-global
+       context:
+               PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ
+                       pass a void*** pointer as second arg to receive the address of a pointer
+                       pointer to the opaque field of the output handler context
+               PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS
+                       pass a int* pointer as second arg to receive the flags set for the output handler
+               PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE
+                       the second arg is ignored; marks the output handler to be neither cleanable
+                       nor removable
+               PHP_OUTPUT_HANDLER_HOOK_DISABLE
+                       the second arg is ignored; marks the output handler as disabled
+
+
+Open questions
+
+       Should the userland API be adjusted and unified?
+       
+       Many bits of the manual (and very first implementation) do not comply
+       with the behaviour of the current (to be obsoleted) code, thus should
+       the manual or the behaviour be adjusted?
+
+END
index 423e006fe8aa61f2d18306c455432d2ef810c442..90f9d76b121a0ae751fed6e7be8392e576d5a479 100644 (file)
@@ -2858,7 +2858,7 @@ PHP_FUNCTION(pg_lo_read_all)
 
        tbytes = 0;
        while ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, buf, PGSQL_LO_READ_BUF_SIZE))>0) {
-               php_body_write(buf, nbytes TSRMLS_CC);
+               PHPWRITE(buf, nbytes);
                tbytes += nbytes;
        }
        RETURN_LONG(tbytes);
index 3073406deaadfd53488b51f5030ecde0dd475e44..76394ba080a1c60c13f894a72db891703fc698bc 100644 (file)
@@ -855,8 +855,8 @@ static int php_session_cache_limiter(TSRMLS_D)
        if (PS(cache_limiter)[0] == '\0') return 0;
        
        if (SG(headers_sent)) {
-               char *output_start_filename = php_get_output_start_filename(TSRMLS_C);
-               int output_start_lineno = php_get_output_start_lineno(TSRMLS_C);
+               char *output_start_filename = php_output_get_start_filename();
+               int output_start_lineno = php_output_get_start_lineno();
 
                if (output_start_filename) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cache limiter - headers already sent (output started at %s:%d)",
@@ -889,8 +889,8 @@ static void php_session_send_cookie(TSRMLS_D)
        char *date_fmt = NULL;
 
        if (SG(headers_sent)) {
-               char *output_start_filename = php_get_output_start_filename(TSRMLS_C);
-               int output_start_lineno = php_get_output_start_lineno(TSRMLS_C);
+               char *output_start_filename = php_output_get_start_filename();
+               int output_start_lineno = php_output_get_start_lineno();
 
                if (output_start_filename) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cookie - headers already sent by (output started at %s:%d)",
index 8d9c856295f3be55e58e02a08875615190f2cc83..7d23ccf307b6d6088ed6588967c3b99fc246d784 100644 (file)
@@ -1482,7 +1482,7 @@ PHP_METHOD(SoapServer, handle)
                }
        }
 
-       if (php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC) != SUCCESS) {
+       if (php_output_start_default() != SUCCESS) {
                php_error_docref(NULL TSRMLS_CC, E_ERROR,"ob_start failed");
        }
 
@@ -1609,7 +1609,7 @@ PHP_METHOD(SoapServer, handle)
                                        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error calling constructor");
                                }
                                if (EG(exception)) {
-                                       php_end_ob_buffer(0, 0 TSRMLS_CC);
+                                       php_output_discard();
                                        if (Z_TYPE_P(EG(exception)) == IS_OBJECT &&
                                            instanceof_function(Z_OBJCE_P(EG(exception)), soap_fault_class_entry TSRMLS_CC)) {
                                                soap_server_fault_ex(function, EG(exception), NULL TSRMLS_CC);
@@ -1642,7 +1642,7 @@ PHP_METHOD(SoapServer, handle)
                                        }
 #ifdef ZEND_ENGINE_2
                                        if (EG(exception)) {
-                                               php_end_ob_buffer(0, 0 TSRMLS_CC);
+                                               php_output_discard();
                                                if (Z_TYPE_P(EG(exception)) == IS_OBJECT &&
                                                    instanceof_function(Z_OBJCE_P(EG(exception)), soap_fault_class_entry TSRMLS_CC)) {
                                                        soap_server_fault_ex(function, EG(exception), NULL TSRMLS_CC);
@@ -1721,14 +1721,14 @@ PHP_METHOD(SoapServer, handle)
                                            Z_TYPE_PP(tmp) != IS_NULL) {
                                                headerfault = *tmp;
                                        }
-                                       php_end_ob_buffer(0, 0 TSRMLS_CC);
+                                       php_output_discard();
                                        soap_server_fault_ex(function, &h->retval, h TSRMLS_CC);
                                        efree(fn_name);
                                        if (soap_obj) {zval_ptr_dtor(&soap_obj);}
                                        goto fail;
 #ifdef ZEND_ENGINE_2
                                } else if (EG(exception)) {
-                                       php_end_ob_buffer(0, 0 TSRMLS_CC);
+                                       php_output_discard();
                                        if (Z_TYPE_P(EG(exception)) == IS_OBJECT &&
                                            instanceof_function(Z_OBJCE_P(EG(exception)), soap_fault_class_entry TSRMLS_CC)) {
                                                zval *headerfault = NULL, **tmp;
@@ -1774,7 +1774,7 @@ PHP_METHOD(SoapServer, handle)
 
 #ifdef ZEND_ENGINE_2
        if (EG(exception)) {
-               php_end_ob_buffer(0, 0 TSRMLS_CC);
+               php_output_discard();
                if (Z_TYPE_P(EG(exception)) == IS_OBJECT &&
                    instanceof_function(Z_OBJCE_P(EG(exception)), soap_fault_class_entry TSRMLS_CC)) {
                        soap_server_fault_ex(function, EG(exception), NULL TSRMLS_CC);
@@ -1794,7 +1794,7 @@ PHP_METHOD(SoapServer, handle)
 
                if (Z_TYPE(retval) == IS_OBJECT &&
                    instanceof_function(Z_OBJCE(retval), soap_fault_class_entry TSRMLS_CC)) {
-                       php_end_ob_buffer(0, 0 TSRMLS_CC);
+                       php_output_discard();
                        soap_server_fault_ex(function, &retval, NULL TSRMLS_CC);
                        goto fail;
                }
@@ -1815,7 +1815,7 @@ PHP_METHOD(SoapServer, handle)
        }
 
        /* Flush buffer */
-       php_end_ob_buffer(0, 0 TSRMLS_CC);
+       php_output_discard();
 
        if (doc_return) {
                /* xmlDocDumpMemoryEnc(doc_return, &buf, &size, XML_CHAR_ENCODING_UTF8); */
@@ -2083,11 +2083,11 @@ static void soap_error_handler(int error_num, const char *error_filename, const
                                code = "Server";
                        }
                        /* Get output buffer and send as fault detials */
-                       if (php_ob_get_length(&outbuflen TSRMLS_CC) != FAILURE && Z_LVAL(outbuflen) != 0) {
+                       if (php_output_get_length(&outbuflen) != FAILURE && Z_LVAL(outbuflen) != 0) {
                                ALLOC_INIT_ZVAL(outbuf);
-                   php_ob_get_buffer(outbuf TSRMLS_CC);
+                               php_output_get_contents(outbuf TSRMLS_CC);
                        }
-                       php_end_ob_buffer(0, 0 TSRMLS_CC);
+                       php_output_discard();
 
                        INIT_ZVAL(fault_obj);
                        set_soap_fault(&fault_obj, NULL, code, buffer, NULL, outbuf, NULL TSRMLS_CC);
index ace8ebd32a383eee34eeff382e65f6f40c1c01f0..9756f6f683f879f8513cc3df01c635841509489e 100644 (file)
@@ -2406,7 +2406,7 @@ PHP_FUNCTION(highlight_file)
        }
 
        if (i) {
-               php_start_ob_buffer (NULL, 0, 1 TSRMLS_CC);
+               php_output_start_default();
        }
 
        php_get_highlight_struct(&syntax_highlighter_ini);
@@ -2416,8 +2416,8 @@ PHP_FUNCTION(highlight_file)
        }
 
        if (i) {
-               php_ob_get_buffer (return_value TSRMLS_CC);
-               php_end_ob_buffer (0, 0 TSRMLS_CC);
+               php_output_get_contents(return_value);
+               php_output_discard();
        } else {
                RETURN_TRUE;
        }
@@ -2437,7 +2437,7 @@ PHP_FUNCTION(php_strip_whitespace)
                RETURN_FALSE;
        }
 
-       php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC);
+       php_output_start_default();
 
        file_handle.type = ZEND_HANDLE_FILENAME;
        file_handle.filename = filename;
@@ -2453,8 +2453,8 @@ PHP_FUNCTION(php_strip_whitespace)
        zend_destroy_file_handle(&file_handle TSRMLS_CC);
        zend_restore_lexical_state(&original_lex_state TSRMLS_CC);
 
-       php_ob_get_buffer(return_value TSRMLS_CC);
-       php_end_ob_buffer(0, 0 TSRMLS_CC);
+       php_output_get_contents(return_value);
+       php_output_discard();
 
        return;
 }
@@ -2476,7 +2476,7 @@ PHP_FUNCTION(highlight_string)
        convert_to_string(expr);
 
        if (i) {
-               php_start_ob_buffer (NULL, 0, 1 TSRMLS_CC);
+               php_output_start_default();
        }
 
        EG(error_reporting) = E_ERROR;
@@ -2494,8 +2494,8 @@ PHP_FUNCTION(highlight_string)
        EG(error_reporting) = old_error_reporting;
 
        if (i) {
-               php_ob_get_buffer (return_value TSRMLS_CC);
-               php_end_ob_buffer (0, 0 TSRMLS_CC);
+               php_output_get_contents(return_value);
+               php_output_discard();
        } else {
                RETURN_TRUE;
        }
@@ -2739,14 +2739,14 @@ PHP_FUNCTION(print_r)
        }
        
        if (i) {
-               php_start_ob_buffer (NULL, 0, 1 TSRMLS_CC);
+               php_output_start_default();
        }
 
        zend_print_zval_r(var, 0 TSRMLS_CC);
 
        if (i) {
-               php_ob_get_buffer (return_value TSRMLS_CC);
-               php_end_ob_buffer (0, 0 TSRMLS_CC);
+               php_output_get_contents(return_value);
+               php_output_discard();
        } else {
                RETURN_TRUE;
        }
index 6097a95deba5487a75393a719107c6f575a5f524..9754a2281f5b5b79eb9b52a0bda9a5b47cb94b8a 100644 (file)
@@ -200,8 +200,8 @@ PHP_FUNCTION(headers_sent)
                return;
 
        if (SG(headers_sent)) {
-               line = php_get_output_start_lineno(TSRMLS_C);
-               file = php_get_output_start_filename(TSRMLS_C);
+               line = php_output_get_start_lineno();
+               file = php_output_get_start_filename();
        }
 
        switch(ZEND_NUM_ARGS()) {
index 7fb61bc69deb5eaaa0496fb559bdad4cf845ae00..83da26918ed6ccf5d8963fdb907169bddfaa3ed7 100644 (file)
@@ -70,7 +70,7 @@ static int php_info_write_wrapper(const char *str, uint str_length)
 
        elem_esc = php_escape_html_entities((char *)str, str_length, &new_len, 0, ENT_QUOTES, NULL TSRMLS_CC);
 
-       written = php_body_write(elem_esc, new_len TSRMLS_CC);
+       written = PHPWRITE(elem_esc, new_len);
 
        efree(elem_esc);
 
@@ -351,7 +351,7 @@ PHPAPI void php_print_info_htmlhead(TSRMLS_D)
        }
 
 #if HAVE_MBSTRING
-       if (php_ob_handler_used("mb_output_handler" TSRMLS_CC)) {
+       if (php_output_handler_started("mb_output_handler")) {
                if (MBSTRG(current_http_output_encoding) == mbfl_no_encoding_pass) {
                        charset = "US-ASCII";
                } else {
@@ -361,7 +361,7 @@ PHPAPI void php_print_info_htmlhead(TSRMLS_D)
 #endif   
 
 #if HAVE_ICONV
-       if (php_ob_handler_used("ob_iconv_handler" TSRMLS_CC)) {
+       if (php_output_handler_started("ob_iconv_handler")) {
                charset = ICONVG(output_encoding);
        }
 #endif
@@ -999,9 +999,9 @@ PHP_FUNCTION(phpinfo)
        }
 
        /* Andale!  Andale!  Yee-Hah! */
-       php_start_ob_buffer(NULL, 4096, 0 TSRMLS_CC);
+       php_output_start_default();
        php_print_info(flag TSRMLS_CC);
-       php_end_ob_buffer(1, 0 TSRMLS_CC);
+       php_output_end();
 
        RETURN_TRUE;
 }
index 2f84d5952212b3278e40dae6c37dfb5b41a208b4..33208ca1462bb98f69d652e91d4ae5012a221593 100644 (file)
@@ -109,51 +109,78 @@ static inline void append_modified_url(smart_str *url, smart_str *dest, smart_st
        q = (p = url->c) + url->len;
 
 scan:
-
-#line 114 "ext/standard/url_scanner_ex.c"
 {
-       YYCTYPE yych;
-       goto yy0;
-       ++YYCURSOR;
-yy0:
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       switch(yych){
-       case '#':       goto yy6;
-       case ':':       goto yy2;
-       case '?':       goto yy4;
-       default:        goto yy8;
-       }
-yy2:   ++YYCURSOR;
-       goto yy3;
-yy3:
+       static unsigned char yybm[] = {
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128,   0, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128,   0, 128, 128, 128, 128,   0, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+       };
+
+#line 149 "ext/standard/url_scanner_ex.c"
+       {
+               YYCTYPE yych;
+
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+               if(yybm[0+yych] & 128) {
+                       goto yy8;
+               }
+               if(yych <= '9') goto yy6;
+               if(yych >= ';') goto yy4;
+               ++YYCURSOR;
 #line 115 "ext/standard/url_scanner_ex.re"
-{ smart_str_append(dest, url); return; }
-#line 133 "ext/standard/url_scanner_ex.c"
-yy4:   ++YYCURSOR;
-       goto yy5;
-yy5:
+               { smart_str_append(dest, url); return; }
+#line 163 "ext/standard/url_scanner_ex.c"
+yy4:
+               ++YYCURSOR;
 #line 116 "ext/standard/url_scanner_ex.re"
-{ sep = separator; goto scan; }
-#line 139 "ext/standard/url_scanner_ex.c"
-yy6:   ++YYCURSOR;
-       goto yy7;
-yy7:
+               { sep = separator; goto scan; }
+#line 168 "ext/standard/url_scanner_ex.c"
+yy6:
+               ++YYCURSOR;
 #line 117 "ext/standard/url_scanner_ex.re"
-{ bash = p - 1; goto done; }
-#line 145 "ext/standard/url_scanner_ex.c"
-yy8:   ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy9;
-yy9:   switch(yych){
-       case '#':       case ':':       case '?':       goto yy10;
-       default:        goto yy8;
-       }
-yy10:
+               { bash = p - 1; goto done; }
+#line 173 "ext/standard/url_scanner_ex.c"
+yy8:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+               if(yybm[0+yych] & 128) {
+                       goto yy8;
+               }
 #line 118 "ext/standard/url_scanner_ex.re"
-{ goto scan; }
-#line 157 "ext/standard/url_scanner_ex.c"
+               { goto scan; }
+#line 183 "ext/standard/url_scanner_ex.c"
+       }
 }
 #line 119 "ext/standard/url_scanner_ex.re"
 
@@ -323,176 +350,138 @@ state_plain_begin:
        
 state_plain:
        start = YYCURSOR;
-
-#line 328 "ext/standard/url_scanner_ex.c"
 {
-       YYCTYPE yych;
-       goto yy11;
-       ++YYCURSOR;
-yy11:
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       switch(yych){
-       case '<':       goto yy13;
-       default:        goto yy15;
-       }
-yy13:  ++YYCURSOR;
-       goto yy14;
-yy14:
+       static unsigned char yybm[] = {
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128,   0, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+       };
+
+#line 390 "ext/standard/url_scanner_ex.c"
+       {
+               YYCTYPE yych;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+               if(yybm[0+yych] & 128) {
+                       goto yy15;
+               }
+               ++YYCURSOR;
 #line 287 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); STATE = STATE_TAG; goto state_tag; }
-#line 345 "ext/standard/url_scanner_ex.c"
-yy15:  ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy16;
-yy16:  switch(yych){
-       case '<':       goto yy17;
-       default:        goto yy15;
-       }
-yy17:
+               { passthru(STD_ARGS); STATE = STATE_TAG; goto state_tag; }
+#line 401 "ext/standard/url_scanner_ex.c"
+yy15:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+               if(yybm[0+yych] & 128) {
+                       goto yy15;
+               }
 #line 288 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); goto state_plain; }
-#line 357 "ext/standard/url_scanner_ex.c"
+               { passthru(STD_ARGS); goto state_plain; }
+#line 411 "ext/standard/url_scanner_ex.c"
+       }
 }
 #line 289 "ext/standard/url_scanner_ex.re"
 
 
 state_tag:     
        start = YYCURSOR;
-
-#line 365 "ext/standard/url_scanner_ex.c"
 {
-       YYCTYPE yych;
-       goto yy18;
-       ++YYCURSOR;
-yy18:
-       if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
-       yych = *YYCURSOR;
-       switch(yych){
-       case 'A':
-       case 'B':
-       case 'C':
-       case 'D':
-       case 'E':
-       case 'F':
-       case 'G':
-       case 'H':
-       case 'I':
-       case 'J':
-       case 'K':
-       case 'L':
-       case 'M':
-       case 'N':
-       case 'O':
-       case 'P':
-       case 'Q':
-       case 'R':
-       case 'S':
-       case 'T':
-       case 'U':
-       case 'V':
-       case 'W':
-       case 'X':
-       case 'Y':
-       case 'Z':       case 'a':
-       case 'b':
-       case 'c':
-       case 'd':
-       case 'e':
-       case 'f':
-       case 'g':
-       case 'h':
-       case 'i':
-       case 'j':
-       case 'k':
-       case 'l':
-       case 'm':
-       case 'n':
-       case 'o':
-       case 'p':
-       case 'q':
-       case 'r':
-       case 's':
-       case 't':
-       case 'u':
-       case 'v':
-       case 'w':
-       case 'x':
-       case 'y':
-       case 'z':       goto yy20;
-       default:        goto yy22;
-       }
-yy20:  ++YYCURSOR;
-       yych = *YYCURSOR;
-       goto yy25;
+       static unsigned char yybm[] = {
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128,   0,   0,   0,   0,   0, 
+                 0, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+       };
+
+#line 455 "ext/standard/url_scanner_ex.c"
+       {
+               YYCTYPE yych;
+               if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+               yych = *YYCURSOR;
+               if(yych <= '@') goto yy22;
+               if(yych <= 'Z') goto yy20;
+               if(yych <= '`') goto yy22;
+               if(yych >= '{') goto yy22;
+yy20:
+               ++YYCURSOR;
+               yych = *YYCURSOR;
+               goto yy25;
 yy21:
 #line 294 "ext/standard/url_scanner_ex.re"
-{ handle_tag(STD_ARGS); /* Sets STATE */; passthru(STD_ARGS); if (STATE == STATE_PLAIN) goto state_plain; else goto state_next_arg; }
-#line 433 "ext/standard/url_scanner_ex.c"
-yy22:  ++YYCURSOR;
-       goto yy23;
-yy23:
+               { handle_tag(STD_ARGS); /* Sets STATE */; passthru(STD_ARGS); if (STATE == STATE_PLAIN) goto state_plain; else goto state_next_arg; }
+#line 471 "ext/standard/url_scanner_ex.c"
+yy22:
+               ++YYCURSOR;
 #line 295 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); goto state_plain_begin; }
-#line 439 "ext/standard/url_scanner_ex.c"
-yy24:  ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy25;
-yy25:  switch(yych){
-       case 'A':
-       case 'B':
-       case 'C':
-       case 'D':
-       case 'E':
-       case 'F':
-       case 'G':
-       case 'H':
-       case 'I':
-       case 'J':
-       case 'K':
-       case 'L':
-       case 'M':
-       case 'N':
-       case 'O':
-       case 'P':
-       case 'Q':
-       case 'R':
-       case 'S':
-       case 'T':
-       case 'U':
-       case 'V':
-       case 'W':
-       case 'X':
-       case 'Y':
-       case 'Z':       case 'a':
-       case 'b':
-       case 'c':
-       case 'd':
-       case 'e':
-       case 'f':
-       case 'g':
-       case 'h':
-       case 'i':
-       case 'j':
-       case 'k':
-       case 'l':
-       case 'm':
-       case 'n':
-       case 'o':
-       case 'p':
-       case 'q':
-       case 'r':
-       case 's':
-       case 't':
-       case 'u':
-       case 'v':
-       case 'w':
-       case 'x':
-       case 'y':
-       case 'z':       goto yy24;
-       default:        goto yy21;
+               { passthru(STD_ARGS); goto state_plain_begin; }
+#line 476 "ext/standard/url_scanner_ex.c"
+yy24:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+yy25:
+               if(yybm[0+yych] & 128) {
+                       goto yy24;
+               }
+               goto yy21;
        }
 }
 #line 296 "ext/standard/url_scanner_ex.re"
@@ -503,107 +492,98 @@ state_next_arg_begin:
        
 state_next_arg:
        start = YYCURSOR;
-
-#line 508 "ext/standard/url_scanner_ex.c"
 {
-       YYCTYPE yych;
-       goto yy26;
-       ++YYCURSOR;
-yy26:
-       if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
-       yych = *YYCURSOR;
-       switch(yych){
-       case 0x09:
-       case 0x0A:
-       case 0x0B:      case 0x0D:      case ' ':       goto yy30;
-       case '>':       goto yy28;
-       case 'A':
-       case 'B':
-       case 'C':
-       case 'D':
-       case 'E':
-       case 'F':
-       case 'G':
-       case 'H':
-       case 'I':
-       case 'J':
-       case 'K':
-       case 'L':
-       case 'M':
-       case 'N':
-       case 'O':
-       case 'P':
-       case 'Q':
-       case 'R':
-       case 'S':
-       case 'T':
-       case 'U':
-       case 'V':
-       case 'W':
-       case 'X':
-       case 'Y':
-       case 'Z':       case 'a':
-       case 'b':
-       case 'c':
-       case 'd':
-       case 'e':
-       case 'f':
-       case 'g':
-       case 'h':
-       case 'i':
-       case 'j':
-       case 'k':
-       case 'l':
-       case 'm':
-       case 'n':
-       case 'o':
-       case 'p':
-       case 'q':
-       case 'r':
-       case 's':
-       case 't':
-       case 'u':
-       case 'v':
-       case 'w':
-       case 'x':
-       case 'y':
-       case 'z':       goto yy32;
-       default:        goto yy34;
-       }
-yy28:  ++YYCURSOR;
-       goto yy29;
-yy29:
+       static unsigned char yybm[] = {
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0, 128, 128, 128,   0, 128,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+               128,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+       };
+
+#line 532 "ext/standard/url_scanner_ex.c"
+       {
+               YYCTYPE yych;
+               if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+               yych = *YYCURSOR;
+               if(yych <= ' ') {
+                       if(yych <= 0x0C) {
+                               if(yych <= 0x08) goto yy34;
+                               if(yych <= 0x0B) goto yy30;
+                               goto yy34;
+                       } else {
+                               if(yych <= 0x0D) goto yy30;
+                               if(yych <= 0x1F) goto yy34;
+                               goto yy30;
+                       }
+               } else {
+                       if(yych <= '@') {
+                               if(yych != '>') goto yy34;
+                       } else {
+                               if(yych <= 'Z') goto yy32;
+                               if(yych <= '`') goto yy34;
+                               if(yych <= 'z') goto yy32;
+                               goto yy34;
+                       }
+               }
+               ++YYCURSOR;
 #line 304 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); handle_form(STD_ARGS); goto state_plain_begin; }
-#line 579 "ext/standard/url_scanner_ex.c"
-yy30:  ++YYCURSOR;
-       yych = *YYCURSOR;
-       goto yy37;
+               { passthru(STD_ARGS); handle_form(STD_ARGS); goto state_plain_begin; }
+#line 560 "ext/standard/url_scanner_ex.c"
+yy30:
+               ++YYCURSOR;
+               yych = *YYCURSOR;
+               goto yy37;
 yy31:
 #line 305 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); goto state_next_arg; }
-#line 586 "ext/standard/url_scanner_ex.c"
-yy32:  ++YYCURSOR;
-       goto yy33;
-yy33:
+               { passthru(STD_ARGS); goto state_next_arg; }
+#line 568 "ext/standard/url_scanner_ex.c"
+yy32:
+               ++YYCURSOR;
 #line 306 "ext/standard/url_scanner_ex.re"
-{ --YYCURSOR; STATE = STATE_ARG; goto state_arg; }
-#line 592 "ext/standard/url_scanner_ex.c"
-yy34:  ++YYCURSOR;
-       goto yy35;
-yy35:
+               { --YYCURSOR; STATE = STATE_ARG; goto state_arg; }
+#line 573 "ext/standard/url_scanner_ex.c"
+yy34:
+               ++YYCURSOR;
 #line 307 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); goto state_plain_begin; }
-#line 598 "ext/standard/url_scanner_ex.c"
-yy36:  ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy37;
-yy37:  switch(yych){
-       case 0x09:
-       case 0x0A:
-       case 0x0B:      case 0x0D:      case ' ':       goto yy36;
-       default:        goto yy31;
+               { passthru(STD_ARGS); goto state_plain_begin; }
+#line 578 "ext/standard/url_scanner_ex.c"
+yy36:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+yy37:
+               if(yybm[0+yych] & 128) {
+                       goto yy36;
+               }
+               goto yy31;
        }
 }
 #line 308 "ext/standard/url_scanner_ex.re"
@@ -611,139 +591,73 @@ yy37:    switch(yych){
 
 state_arg:
        start = YYCURSOR;
-
-#line 616 "ext/standard/url_scanner_ex.c"
 {
-       YYCTYPE yych;
-       goto yy38;
-       ++YYCURSOR;
-yy38:
-       if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
-       yych = *YYCURSOR;
-       switch(yych){
-       case 'A':
-       case 'B':
-       case 'C':
-       case 'D':
-       case 'E':
-       case 'F':
-       case 'G':
-       case 'H':
-       case 'I':
-       case 'J':
-       case 'K':
-       case 'L':
-       case 'M':
-       case 'N':
-       case 'O':
-       case 'P':
-       case 'Q':
-       case 'R':
-       case 'S':
-       case 'T':
-       case 'U':
-       case 'V':
-       case 'W':
-       case 'X':
-       case 'Y':
-       case 'Z':       case 'a':
-       case 'b':
-       case 'c':
-       case 'd':
-       case 'e':
-       case 'f':
-       case 'g':
-       case 'h':
-       case 'i':
-       case 'j':
-       case 'k':
-       case 'l':
-       case 'm':
-       case 'n':
-       case 'o':
-       case 'p':
-       case 'q':
-       case 'r':
-       case 's':
-       case 't':
-       case 'u':
-       case 'v':
-       case 'w':
-       case 'x':
-       case 'y':
-       case 'z':       goto yy40;
-       default:        goto yy42;
-       }
-yy40:  ++YYCURSOR;
-       yych = *YYCURSOR;
-       goto yy45;
+       static unsigned char yybm[] = {
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0, 128,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128,   0,   0,   0,   0,   0, 
+                 0, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128, 128, 128, 128, 128, 128, 
+               128, 128, 128,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+       };
+
+#line 631 "ext/standard/url_scanner_ex.c"
+       {
+               YYCTYPE yych;
+               if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+               yych = *YYCURSOR;
+               if(yych <= '@') goto yy42;
+               if(yych <= 'Z') goto yy40;
+               if(yych <= '`') goto yy42;
+               if(yych >= '{') goto yy42;
+yy40:
+               ++YYCURSOR;
+               yych = *YYCURSOR;
+               goto yy45;
 yy41:
 #line 313 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); handle_arg(STD_ARGS); STATE = STATE_BEFORE_VAL; goto state_before_val; }
-#line 684 "ext/standard/url_scanner_ex.c"
-yy42:  ++YYCURSOR;
-       goto yy43;
-yy43:
+               { passthru(STD_ARGS); handle_arg(STD_ARGS); STATE = STATE_BEFORE_VAL; goto state_before_val; }
+#line 647 "ext/standard/url_scanner_ex.c"
+yy42:
+               ++YYCURSOR;
 #line 314 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); STATE = STATE_NEXT_ARG; goto state_next_arg; }
-#line 690 "ext/standard/url_scanner_ex.c"
-yy44:  ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy45;
-yy45:  switch(yych){
-       case '-':       case 'A':
-       case 'B':
-       case 'C':
-       case 'D':
-       case 'E':
-       case 'F':
-       case 'G':
-       case 'H':
-       case 'I':
-       case 'J':
-       case 'K':
-       case 'L':
-       case 'M':
-       case 'N':
-       case 'O':
-       case 'P':
-       case 'Q':
-       case 'R':
-       case 'S':
-       case 'T':
-       case 'U':
-       case 'V':
-       case 'W':
-       case 'X':
-       case 'Y':
-       case 'Z':       case 'a':
-       case 'b':
-       case 'c':
-       case 'd':
-       case 'e':
-       case 'f':
-       case 'g':
-       case 'h':
-       case 'i':
-       case 'j':
-       case 'k':
-       case 'l':
-       case 'm':
-       case 'n':
-       case 'o':
-       case 'p':
-       case 'q':
-       case 'r':
-       case 's':
-       case 't':
-       case 'u':
-       case 'v':
-       case 'w':
-       case 'x':
-       case 'y':
-       case 'z':       goto yy44;
-       default:        goto yy41;
+               { passthru(STD_ARGS); STATE = STATE_NEXT_ARG; goto state_next_arg; }
+#line 652 "ext/standard/url_scanner_ex.c"
+yy44:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+yy45:
+               if(yybm[0+yych] & 128) {
+                       goto yy44;
+               }
+               goto yy41;
        }
 }
 #line 315 "ext/standard/url_scanner_ex.re"
@@ -751,61 +665,90 @@ yy45:     switch(yych){
 
 state_before_val:
        start = YYCURSOR;
-
-#line 756 "ext/standard/url_scanner_ex.c"
 {
-       YYCTYPE yych;
-       unsigned int yyaccept = 0;
-       goto yy46;
-       ++YYCURSOR;
-yy46:
-       if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
-       yych = *YYCURSOR;
-       switch(yych){
-       case ' ':       goto yy48;
-       case '=':       goto yy50;
-       default:        goto yy52;
-       }
-yy48:  yyaccept = 0;
-       yych = *(YYMARKER = ++YYCURSOR);
-       switch(yych){
-       case ' ':       goto yy55;
-       case '=':       goto yy53;
-       default:        goto yy49;
-       }
+       static unsigned char yybm[] = {
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+               128,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+                 0,   0,   0,   0,   0,   0,   0,   0, 
+       };
+
+#line 705 "ext/standard/url_scanner_ex.c"
+       {
+               YYCTYPE yych;
+               unsigned int yyaccept = 0;
+               if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+               yych = *YYCURSOR;
+               if(yych == ' ') goto yy48;
+               if(yych == '=') goto yy50;
+               goto yy52;
+yy48:
+               yyaccept = 0;
+               yych = *(YYMARKER = ++YYCURSOR);
+               if(yych == ' ') goto yy55;
+               if(yych == '=') goto yy53;
 yy49:
 #line 321 "ext/standard/url_scanner_ex.re"
-{ --YYCURSOR; goto state_next_arg_begin; }
-#line 780 "ext/standard/url_scanner_ex.c"
-yy50:  ++YYCURSOR;
-       yych = *YYCURSOR;
-       goto yy54;
+               { --YYCURSOR; goto state_next_arg_begin; }
+#line 722 "ext/standard/url_scanner_ex.c"
+yy50:
+               ++YYCURSOR;
+               yych = *YYCURSOR;
+               goto yy54;
 yy51:
 #line 320 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); STATE = STATE_VAL; goto state_val; }
-#line 787 "ext/standard/url_scanner_ex.c"
-yy52:  yych = *++YYCURSOR;
-       goto yy49;
-yy53:  ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy54;
-yy54:  switch(yych){
-       case ' ':       goto yy53;
-       default:        goto yy51;
-       }
-yy55:  ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy56;
-yy56:  switch(yych){
-       case ' ':       goto yy55;
-       case '=':       goto yy53;
-       default:        goto yy57;
-       }
-yy57:  YYCURSOR = YYMARKER;
-       switch(yyaccept){
-       case 0: goto yy49;
+               { passthru(STD_ARGS); STATE = STATE_VAL; goto state_val; }
+#line 730 "ext/standard/url_scanner_ex.c"
+yy52:
+               yych = *++YYCURSOR;
+               goto yy49;
+yy53:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+yy54:
+               if(yybm[0+yych] & 128) {
+                       goto yy53;
+               }
+               goto yy51;
+yy55:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+               if(yych == ' ') goto yy55;
+               if(yych == '=') goto yy53;
+               YYCURSOR = YYMARKER;
+               switch(yyaccept){
+               case 0: goto yy49;
+               }
        }
 }
 #line 322 "ext/standard/url_scanner_ex.re"
@@ -814,121 +757,161 @@ yy57:   YYCURSOR = YYMARKER;
 
 state_val:
        start = YYCURSOR;
-
-#line 819 "ext/standard/url_scanner_ex.c"
 {
-       YYCTYPE yych;
-       unsigned int yyaccept = 0;
-       goto yy58;
-       ++YYCURSOR;
-yy58:
-       if((YYLIMIT - YYCURSOR) < 3) YYFILL(3);
-       yych = *YYCURSOR;
-       switch(yych){
-       case 0x09:
-       case 0x0A:      case 0x0D:      case ' ':       case '>':       goto yy64;
-       case '"':       goto yy60;
-       case '\'':      goto yy62;
-       default:        goto yy63;
-       }
-yy60:  yyaccept = 0;
-       yych = *(YYMARKER = ++YYCURSOR);
-       goto yy77;
+       static unsigned char yybm[] = {
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 160, 160, 248, 248, 160, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               160, 248,  56, 248, 248, 248, 248, 200, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248,   0, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+               248, 248, 248, 248, 248, 248, 248, 248, 
+       };
+
+#line 797 "ext/standard/url_scanner_ex.c"
+       {
+               YYCTYPE yych;
+               unsigned int yyaccept = 0;
+               if((YYLIMIT - YYCURSOR) < 3) YYFILL(3);
+               yych = *YYCURSOR;
+               if(yych <= ' ') {
+                       if(yych <= 0x0C) {
+                               if(yych <= 0x08) goto yy63;
+                               if(yych <= 0x0A) goto yy64;
+                               goto yy63;
+                       } else {
+                               if(yych <= 0x0D) goto yy64;
+                               if(yych <= 0x1F) goto yy63;
+                               goto yy64;
+                       }
+               } else {
+                       if(yych <= '&') {
+                               if(yych != '"') goto yy63;
+                       } else {
+                               if(yych <= '\'') goto yy62;
+                               if(yych == '>') goto yy64;
+                               goto yy63;
+                       }
+               }
+               yyaccept = 0;
+               yych = *(YYMARKER = ++YYCURSOR);
+               goto yy77;
 yy61:
 #line 330 "ext/standard/url_scanner_ex.re"
-{ handle_val(STD_ARGS, 0, ' '); goto state_next_arg_begin; }
-#line 841 "ext/standard/url_scanner_ex.c"
-yy62:  yyaccept = 0;
-       yych = *(YYMARKER = ++YYCURSOR);
-       goto yy69;
-yy63:  yych = *++YYCURSOR;
-       goto yy67;
-yy64:  ++YYCURSOR;
-       goto yy65;
-yy65:
+               { handle_val(STD_ARGS, 0, ' '); goto state_next_arg_begin; }
+#line 828 "ext/standard/url_scanner_ex.c"
+yy62:
+               yyaccept = 0;
+               yych = *(YYMARKER = ++YYCURSOR);
+               goto yy69;
+yy63:
+               yych = *++YYCURSOR;
+               goto yy67;
+yy64:
+               ++YYCURSOR;
 #line 331 "ext/standard/url_scanner_ex.re"
-{ passthru(STD_ARGS); goto state_next_arg_begin; }
-#line 852 "ext/standard/url_scanner_ex.c"
-yy66:  ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy67;
-yy67:  switch(yych){
-       case 0x09:
-       case 0x0A:      case 0x0D:      case ' ':       case '>':       goto yy61;
-       default:        goto yy66;
-       }
-yy68:  yyaccept = 0;
-       YYMARKER = ++YYCURSOR;
-       if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
-       yych = *YYCURSOR;
-       goto yy69;
-yy69:  switch(yych){
-       case 0x09:
-       case 0x0A:      case 0x0D:      case ' ':       goto yy72;
-       case '\'':      goto yy70;
-       case '>':       goto yy61;
-       default:        goto yy68;
-       }
-yy70:  ++YYCURSOR;
-       switch((yych = *YYCURSOR)) {
-       case 0x09:
-       case 0x0A:      case 0x0D:      case ' ':       case '>':       goto yy71;
-       default:        goto yy66;
-       }
+               { passthru(STD_ARGS); goto state_next_arg_begin; }
+#line 840 "ext/standard/url_scanner_ex.c"
+yy66:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+yy67:
+               if(yybm[0+yych] & 8) {
+                       goto yy66;
+               }
+               goto yy61;
+yy68:
+               yyaccept = 0;
+               YYMARKER = ++YYCURSOR;
+               if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+               yych = *YYCURSOR;
+yy69:
+               if(yybm[0+yych] & 16) {
+                       goto yy68;
+               }
+               if(yych <= '&') goto yy72;
+               if(yych >= '(') goto yy61;
+               ++YYCURSOR;
+               if(yybm[0+(yych = *YYCURSOR)] & 8) {
+                       goto yy66;
+               }
 yy71:
 #line 329 "ext/standard/url_scanner_ex.re"
-{ handle_val(STD_ARGS, 1, '\''); goto state_next_arg_begin; }
-#line 883 "ext/standard/url_scanner_ex.c"
-yy72:  ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy73;
-yy73:  switch(yych){
-       case '\'':      goto yy75;
-       case '>':       goto yy74;
-       default:        goto yy72;
-       }
-yy74:  YYCURSOR = YYMARKER;
-       switch(yyaccept){
-       case 0: goto yy61;
-       }
-yy75:  yych = *++YYCURSOR;
-       goto yy71;
-yy76:  yyaccept = 0;
-       YYMARKER = ++YYCURSOR;
-       if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
-       yych = *YYCURSOR;
-       goto yy77;
-yy77:  switch(yych){
-       case 0x09:
-       case 0x0A:      case 0x0D:      case ' ':       goto yy80;
-       case '"':       goto yy78;
-       case '>':       goto yy61;
-       default:        goto yy76;
-       }
-yy78:  ++YYCURSOR;
-       switch((yych = *YYCURSOR)) {
-       case 0x09:
-       case 0x0A:      case 0x0D:      case ' ':       case '>':       goto yy79;
-       default:        goto yy66;
-       }
+               { handle_val(STD_ARGS, 1, '\''); goto state_next_arg_begin; }
+#line 868 "ext/standard/url_scanner_ex.c"
+yy72:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+               if(yybm[0+yych] & 32) {
+                       goto yy72;
+               }
+               if(yych <= '=') goto yy75;
+yy74:
+               YYCURSOR = YYMARKER;
+               switch(yyaccept){
+               case 0: goto yy61;
+               }
+yy75:
+               yych = *++YYCURSOR;
+               goto yy71;
+yy76:
+               yyaccept = 0;
+               YYMARKER = ++YYCURSOR;
+               if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+               yych = *YYCURSOR;
+yy77:
+               if(yybm[0+yych] & 64) {
+                       goto yy76;
+               }
+               if(yych <= '!') goto yy80;
+               if(yych >= '#') goto yy61;
+               ++YYCURSOR;
+               if(yybm[0+(yych = *YYCURSOR)] & 8) {
+                       goto yy66;
+               }
 yy79:
 #line 328 "ext/standard/url_scanner_ex.re"
-{ handle_val(STD_ARGS, 1, '"'); goto state_next_arg_begin; }
-#line 920 "ext/standard/url_scanner_ex.c"
-yy80:  ++YYCURSOR;
-       if(YYLIMIT == YYCURSOR) YYFILL(1);
-       yych = *YYCURSOR;
-       goto yy81;
-yy81:  switch(yych){
-       case '"':       goto yy82;
-       case '>':       goto yy74;
-       default:        goto yy80;
+               { handle_val(STD_ARGS, 1, '"'); goto state_next_arg_begin; }
+#line 903 "ext/standard/url_scanner_ex.c"
+yy80:
+               ++YYCURSOR;
+               if(YYLIMIT == YYCURSOR) YYFILL(1);
+               yych = *YYCURSOR;
+               if(yybm[0+yych] & 128) {
+                       goto yy80;
+               }
+               if(yych >= '>') goto yy74;
+               ++YYCURSOR;
+               yych = *YYCURSOR;
+               goto yy79;
        }
-yy82:  ++YYCURSOR;
-       yych = *YYCURSOR;
-       goto yy79;
 }
 #line 332 "ext/standard/url_scanner_ex.re"
 
@@ -1055,7 +1038,7 @@ int php_url_scanner_add_var(char *name, int name_len, char *value, int value_len
        
        if (! BG(url_adapt_state_ex).active) {
                php_url_scanner_ex_activate(TSRMLS_C);
-               php_ob_set_internal_handler(php_url_scanner_output_handler, 0, "URL-Rewriter", 1 TSRMLS_CC);
+               php_output_start_internal("URL-Rewriter", php_url_scanner_output_handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
                BG(url_adapt_state_ex).active = 1;
        }
 
index e1d0a67dafe1ecc3555280c00da5e068544fab54..9593bbf6119e540052b8e70aeda45b3d223d0516 100644 (file)
@@ -453,7 +453,7 @@ int php_url_scanner_add_var(char *name, int name_len, char *value, int value_len
        
        if (! BG(url_adapt_state_ex).active) {
                php_url_scanner_ex_activate(TSRMLS_C);
-               php_ob_set_internal_handler(php_url_scanner_output_handler, 0, "URL-Rewriter", 1 TSRMLS_CC);
+               php_output_start_internal("URL-Rewriter", php_url_scanner_output_handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
                BG(url_adapt_state_ex).active = 1;
        }
 
index 01a9e0daf15d8a6b1516f5c2feab529176037f80..ca91f49c00d106c1f3d763b9e48856e8c38cbacf 100644 (file)
@@ -12,7 +12,7 @@
    | obtain it through the world-wide-web, please send a note to          |
    | license@php.net so we can mail you a copy immediately.               |
    +----------------------------------------------------------------------+
-   | Authors: Jani Lehtimäki <jkl@njet.net>                               |
+   | Authors: Jani Lehtim�i <jkl@njet.net>                               |
    |          Thies C. Arntzen <thies@thieso.net>                         |
    |          Sascha Schumann <sascha@schumann.cx>                        |
    +----------------------------------------------------------------------+
@@ -602,7 +602,7 @@ PHP_FUNCTION(var_export)
        }
        
        if (return_output) {
-               php_start_ob_buffer (NULL, 0, 1 TSRMLS_CC);
+               php_output_start_default();
        }
        
        /* UTODO
@@ -614,8 +614,8 @@ PHP_FUNCTION(var_export)
        php_var_export(&var, 1 TSRMLS_CC);
 
        if (return_output) {
-               php_ob_get_buffer (return_value TSRMLS_CC);
-               php_end_ob_buffer (0, 0 TSRMLS_CC);
+               php_output_get_contents(return_value);
+               php_output_discard();
        }
 }
 /* }}} */
index 71da10f2efdd826d6d565d2a94c66b052d5f2ee8..86c46c57692778d01ab5a8deeb7e0aef37ecf44e 100644 (file)
@@ -985,9 +985,13 @@ PHP_MINIT_FUNCTION(tidy)
 PHP_RINIT_FUNCTION(tidy)
 {
        if (INI_BOOL("tidy.clean_output") == TRUE) {
-               if (php_start_ob_buffer_named("ob_tidyhandler", 0, 1 TSRMLS_CC) == FAILURE) {
+               zval *name;
+               MAKE_STD_ZVAL(name);
+               ZVAL_ASCII_STRINGL(name, "ob_tidyhandler", sizeof("ob_tidyhandler"), ZSTR_DUPLICATE);
+               if (php_output_start_user(name, 0, PHP_OUTPUT_HANDLER_STDFLAGS) == FAILURE) {
                        zend_error(E_NOTICE, "Failure installing Tidy output buffering.");
                }
+               zval_ptr_dtor(&name);
        }
 
        return SUCCESS;
index 1e9c0b5cffd9ae70a7be64d7cdfe1418f9d24e25..093a740c5e44fa1284c3152554505d07e205ab88 100644 (file)
@@ -13,7 +13,7 @@
    | license@php.net so we can mail you a copy immediately.               |
    +----------------------------------------------------------------------+
    | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
-   |          Stefan Röhrich <sr@linux.de>                                |
+   |          Stefan R�rich <sr@linux.de>                                |
    +----------------------------------------------------------------------+
 */
 
@@ -55,7 +55,7 @@ PHP_FUNCTION(ob_gzhandler);
 PHP_FUNCTION(zlib_get_coding_type);
 
 int php_enable_output_compression(int buffer_size TSRMLS_DC);
-int php_ob_gzhandler_check(TSRMLS_D);
+int php_ob_gzhandler_check(zval *handler_name TSRMLS_DC);
 
 php_stream *php_stream_gzopen(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
 extern php_stream_ops php_stream_gzio_ops;
index cdeab4263c31040d7a34ae5b7fb2033210ffcae7..7d86ce122455ea085eab3bb4e3f7923da6018867 100644 (file)
@@ -13,7 +13,7 @@
    | license@php.net so we can mail you a copy immediately.               |
    +----------------------------------------------------------------------+
    | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
-   |          Stefan Röhrich <sr@linux.de>                                |
+   |          Stefan R�rich <sr@linux.de>                                |
    |          Zeev Suraski <zeev@zend.com>                                |
    |          Jade Nicoletti <nicoletti@nns.ch>                           |
    +----------------------------------------------------------------------+
@@ -211,11 +211,15 @@ static void php_zlib_init_globals(zend_zlib_globals *zlib_globals_p TSRMLS_DC)
  */
 PHP_MINIT_FUNCTION(zlib)
 {
+       zval tmp;
 #ifdef ZTS
        ts_allocate_id(&zlib_globals_id, sizeof(zend_zlib_globals), (ts_allocate_ctor) php_zlib_init_globals, NULL);
 #endif
        php_register_url_stream_wrapper("compress.zlib", &php_stream_gzip_wrapper TSRMLS_CC);
        php_stream_filter_register_factory("zlib.*", &php_zlib_filter_factory TSRMLS_CC);
+       INIT_PZVAL(&tmp);
+       ZVAL_ASCII_STRINGL(&tmp, "ob_gzhandler", sizeof("ob_gzhandler")-1, 0);
+       php_output_handler_conflict_register(&tmp, php_ob_gzhandler_check);
 
        REGISTER_LONG_CONSTANT("FORCE_GZIP", CODING_GZIP, CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("FORCE_DEFLATE", CODING_DEFLATE, CONST_CS | CONST_PERSISTENT);
@@ -828,30 +832,41 @@ PHP_FUNCTION(gzencode)
 
 /* {{{ php_ob_gzhandler_check
  */
-int php_ob_gzhandler_check(TSRMLS_D)
+int php_ob_gzhandler_check(zval *handler_name TSRMLS_DC)
 {
        /* check for wrong usages */
-       if (OG(ob_nesting_level > 0)) {
-               if (php_ob_handler_used("ob_gzhandler" TSRMLS_CC)) {
+       if (php_output_get_level() > 0) {
+               zval tmp;
+               
+               if (php_output_handler_started(handler_name)) {
                        php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler 'ob_gzhandler' cannot be used twice");
                        return FAILURE;
                }
-               if (php_ob_handler_used("mb_output_handler" TSRMLS_CC)) {
+               INIT_PZVAL(&tmp);
+               ZVAL_ASCII_STRINGL(&tmp, "mb_output_handler", sizeof("mb_output_handler")-1, ZSTR_DUPLICATE);
+               if (php_output_handler_started(&tmp)) {
+                       zval_dtor(&tmp);
                        php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler 'ob_gzhandler' cannot be used after 'mb_output_handler'");
                        return FAILURE;
                }
-               if (php_ob_handler_used("URL-Rewriter" TSRMLS_CC)) {
+               zval_dtor(&tmp);
+               ZVAL_ASCII_STRINGL(&tmp, "URL-Reqriter", sizeof("URL-Rewriter")-1, ZSTR_DUPLICATE);
+               if (php_output_handler_started(&tmp)) {
+                       zval_dtor(&tmp);
                        php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler 'ob_gzhandler' cannot be used after 'URL-Rewriter'");
                        return FAILURE;
                }
-               if (php_ob_init_conflict("ob_gzhandler", "zlib output compression" TSRMLS_CC)) {
+               zval_dtor(&tmp);
+               ZVAL_ASCII_STRINGL(&tmp, "zlib output compression", sizeof("zlib output compression")-1, ZSTR_DUPLICATE);
+               if (php_output_handler_conflict(handler_name, &tmp)) {
+                       zval_dtor(&tmp);
                        return FAILURE;
                }
+               zval_dtor(&tmp);
        }
 
        return SUCCESS;
 }
-
 /* }}} */
 
 /* {{{ proto string ob_gzhandler(string str, int mode)
@@ -869,8 +884,9 @@ PHP_FUNCTION(ob_gzhandler)
                return;
        }
 
-       if(ZLIBG(ob_gzhandler_status) == -1)
+       if(ZLIBG(ob_gzhandler_status) == -1) {
                RETURN_FALSE;
+       }
 
        zend_is_auto_global("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC);
 
@@ -878,16 +894,20 @@ PHP_FUNCTION(ob_gzhandler)
                || zend_hash_find(PG(http_globals)[TRACK_VARS_SERVER]->value.ht, "HTTP_ACCEPT_ENCODING", sizeof("HTTP_ACCEPT_ENCODING"), (void **) &a_encoding) == FAILURE
        ) {
                ZLIBG(ob_gzhandler_status) = -1;
-               RETURN_FALSE;
-       }
-
-       convert_to_string_ex(a_encoding);
-       if (php_memnstr(Z_STRVAL_PP(a_encoding), "gzip", 4, Z_STRVAL_PP(a_encoding) + Z_STRLEN_PP(a_encoding))) {
-               ZLIBG(compression_coding) = CODING_GZIP;
-       } else if (php_memnstr(Z_STRVAL_PP(a_encoding), "deflate", 7, Z_STRVAL_PP(a_encoding) + Z_STRLEN_PP(a_encoding))) {
-               ZLIBG(compression_coding) = CODING_DEFLATE;
        } else {
-               ZLIBG(ob_gzhandler_status) = -1;
+               convert_to_string_ex(a_encoding);
+               if (php_memnstr(Z_STRVAL_PP(a_encoding), "gzip", 4, Z_STRVAL_PP(a_encoding) + Z_STRLEN_PP(a_encoding))) {
+                       ZLIBG(compression_coding) = CODING_GZIP;
+               } else if (php_memnstr(Z_STRVAL_PP(a_encoding), "deflate", 7, Z_STRVAL_PP(a_encoding) + Z_STRLEN_PP(a_encoding))) {
+                       ZLIBG(compression_coding) = CODING_DEFLATE;
+               } else {
+                       ZLIBG(ob_gzhandler_status) = -1;
+               }
+       }
+       
+       if (ZLIBG(ob_gzhandler_status == -1)) {
+               /* don't call this handler any more */
+               php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL);
                RETURN_FALSE;
        }
        
@@ -931,8 +951,13 @@ PHP_FUNCTION(ob_gzhandler)
        }
 
        if (return_original) {
+               /* don't call this handler any more */
+               php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL);
                /* return the original string */
                RETURN_STRINGL(string, string_len, 1);
+       } else {
+               /* don't allow cleaning and removing any longer */
+               php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
        }
 }
 /* }}} */
@@ -959,7 +984,7 @@ static void php_gzip_output_handler(char *output, uint output_len, char **handle
  */
 int php_enable_output_compression(int buffer_size TSRMLS_DC)
 {
-       zval **a_encoding;
+       zval **a_encoding, *output_handler;
 
        zend_is_auto_global("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC);
 
@@ -979,10 +1004,16 @@ int php_enable_output_compression(int buffer_size TSRMLS_DC)
                return FAILURE;
        }
 
-       php_ob_set_internal_handler(php_gzip_output_handler, (uint)buffer_size, "zlib output compression", 0 TSRMLS_CC);
-
-       if (ZLIBG(output_handler) && strlen(ZLIBG(output_handler))) {
-               php_start_ob_buffer_named(ZLIBG(output_handler), 0, 1 TSRMLS_CC);
+       MAKE_STD_ZVAL(output_handler);
+       ZVAL_ASCII_STRINGL(output_handler, "zlib output compression", sizeof("zlib output compression")-1, ZSTR_DUPLICATE);
+       php_output_start_internal(output_handler, php_gzip_output_handler, buffer_size, PHP_OUTPUT_HANDLER_STDFLAGS);
+       zval_ptr_dtor(&output_handler);
+       
+       if (ZLIBG(output_handler) && *ZLIBG(output_handler)) {
+               MAKE_STD_ZVAL(output_handler);
+               ZVAL_ASCII_STRING(output_handler, ZLIBG(output_handler), ZSTR_DUPLICATE);
+               php_output_start_user(output_handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
+               zval_ptr_dtor(&output_handler);
        }
        return SUCCESS;
 }
index 4a5ea83521c109743a1123c15e443bc00be79f43..6b7e1ef4aefc3a9e83d1e6eff0850447fc489cf7 100644 (file)
@@ -553,8 +553,8 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC)
        int http_response_code;
        
        if (SG(headers_sent) && !SG(request_info).no_headers) {
-               char *output_start_filename = php_get_output_start_filename(TSRMLS_C);
-               int output_start_lineno = php_get_output_start_lineno(TSRMLS_C);
+               char *output_start_filename = php_output_get_start_filename();
+               int output_start_lineno = php_output_get_start_lineno();
 
                if (output_start_filename) {
                        sapi_module.sapi_error(E_WARNING, "Cannot modify header information - headers already sent by (output started at %s:%d)",
@@ -720,6 +720,7 @@ SAPI_API int sapi_send_headers(TSRMLS_D)
        }
 
 #if HAVE_ZLIB
+       /* TODO: move to zlib.c */
        /* Add output compression headers at this late stage in order to make
           it possible to switch it off inside the script. */
 
index d8e2ff32c709be9b59c9502c356a0546440437eb..b8fd867fa892e585d5b6e16c975ef52a0632a8e3 100644 (file)
@@ -234,8 +234,8 @@ static ZEND_INI_MH(OnUpdateOutputEncoding)
 
 #define PHP_INI_OPTION_HEADERS_SENT(option_name)                                                                                                                       \
                if (SG(headers_sent)) {                                                                                                                                 \
-                       char *output_start_filename = php_get_output_start_filename(TSRMLS_C);                                                                              \
-                       int output_start_lineno = php_get_output_start_lineno(TSRMLS_C);                                                                                    \
+                       char *output_start_filename = php_output_get_start_filename();                                                                              \
+                       int output_start_lineno = php_output_get_start_lineno();                                                                                    \
                        if (output_start_filename) {                                                                                                                        \
                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Option " #option_name " cannot be changed after headers have been sent (output started at %s:%d)", \
                                                                                                                                                                        output_start_filename, output_start_lineno);                                \
@@ -870,14 +870,7 @@ static void php_error_cb(int type, const char *error_filename, const uint error_
                        php_log_err(log_buffer TSRMLS_CC);
                        efree(log_buffer);
                }
-               if (PG(display_errors)
-                       && ((module_initialized && !PG(during_request_startup))
-                               || (PG(display_startup_errors)
-                                       && (OG(php_body_write)==php_default_output_func || OG(php_body_write)==php_ub_body_write_no_header || OG(php_body_write)==php_ub_body_write)
-                                       )
-                               )
-                       ) {
-
+               if (PG(display_errors) && ((module_initialized && !PG(during_request_startup)) || (PG(display_startup_errors)))) {
                        if (PG(xmlrpc_errors)) {
                                php_printf("<?xml version=\"1.0\"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><int>%ld</int></value></member><member><name>faultString</name><value><string>%s:%s in %s on line %d</string></value></member></struct></value></fault></methodResponse>", PG(xmlrpc_error_number), error_type_str, buffer, error_filename, error_lineno);
                        } else {
@@ -1182,7 +1175,7 @@ int php_request_startup(TSRMLS_D)
        zend_try {
                PG(during_request_startup) = 1;
 
-               php_output_activate(TSRMLS_C);
+               php_output_activate();
 
                /* initialize global variables */
                PG(modules_activated) = 0;
@@ -1208,15 +1201,16 @@ int php_request_startup(TSRMLS_D)
                }
 
                if (PG(output_handler) && PG(output_handler)[0]) {
-                       php_start_ob_buffer_named(PG(output_handler), 0, 1 TSRMLS_CC);
+                       zval *oh;
+                       
+                       MAKE_STD_ZVAL(oh);
+                       ZVAL_ASCII_STRING(oh, PG(output_handler), ZSTR_DUPLICATE);
+                       php_output_start_user(oh, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
+                       zval_ptr_dtor(&oh);
                } else if (PG(output_buffering)) {
-                       if (PG(output_buffering)>1) {
-                               php_start_ob_buffer(NULL, PG(output_buffering), 1 TSRMLS_CC);
-                       } else {
-                               php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC);
-                       }
+                       php_output_start_user(NULL, PG(output_buffering) > 1 ? PG(output_buffering) : 0, PHP_OUTPUT_HANDLER_STDFLAGS);
                } else if (PG(implicit_flush)) {
-                       php_start_implicit_flush(TSRMLS_C);
+                       php_output_set_implicit_flush(1);
                }
 
                /* We turn this off in php_execute_script() */
@@ -1246,13 +1240,12 @@ int php_request_startup(TSRMLS_D)
                return FAILURE;
        }
 
-       php_output_activate(TSRMLS_C);
+       php_output_activate();
        sapi_activate(TSRMLS_C);
        php_hash_environment(TSRMLS_C);
 
        zend_try {
                PG(during_request_startup) = 1;
-               php_output_activate(TSRMLS_C);
                if (PG(expose_php)) {
                        sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1);
                }
@@ -1279,7 +1272,7 @@ int php_request_startup_for_hook(TSRMLS_D)
                return FAILURE;
        }
 
-       php_output_activate(TSRMLS_C);
+       php_output_activate();
        sapi_activate_headers_only(TSRMLS_C);
        php_hash_environment(TSRMLS_C);
 
@@ -1372,7 +1365,12 @@ void php_request_shutdown(void *dummy)
 
        /* 3. Flush all output buffers */
        zend_try {
-               php_end_ob_buffers((zend_bool)(SG(request_info).headers_only?0:1) TSRMLS_CC);
+               if (SG(request_info).headers_only) {
+                       php_output_discard_all();
+               } else {
+                       php_output_end_all();
+               }
+               php_output_deactivate();
        } zend_end_try();
 
        /* 4. Send the set HTTP headers (note: This must be done AFTER php_end_ob_buffers() !!) */
@@ -1432,12 +1430,12 @@ void php_request_shutdown(void *dummy)
 /* }}} */
 
 
-/* {{{ php_body_write_wrapper
+/* {{{ php_output_wrapper
  */
-static int php_body_write_wrapper(const char *str, uint str_length)
+static int php_output_wrapper(const char *str, uint str_length)
 {
        TSRMLS_FETCH();
-       return php_body_write(str, str_length TSRMLS_CC);
+       return php_output_write(str, str_length);
 }
 /* }}} */
 
@@ -1523,7 +1521,7 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod
 
        zuf.error_function = php_error_cb;
        zuf.printf_function = php_printf;
-       zuf.write_function = php_body_write_wrapper;
+       zuf.write_function = php_output_wrapper;
        zuf.fopen_function = php_fopen_wrapper_for_zend;
        zuf.message_handler = php_message_handler_for_zend;
        zuf.block_interruptions = sapi_module.block_interruptions;
@@ -1667,7 +1665,7 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod
        REGISTER_MAIN_STRINGL_CONSTANT("PHP_EOL", PHP_EOL, sizeof(PHP_EOL)-1, CONST_PERSISTENT | CONST_CS);
        REGISTER_MAIN_LONG_CONSTANT("PHP_INT_MAX", LONG_MAX, CONST_PERSISTENT | CONST_CS);
        REGISTER_MAIN_LONG_CONSTANT("PHP_INT_SIZE", sizeof(long), CONST_PERSISTENT | CONST_CS);
-       php_output_register_constants(TSRMLS_C);
+       php_output_register_constants();
        php_rfc1867_register_constants(TSRMLS_C);
 
        if (php_startup_ticks(TSRMLS_C) == FAILURE) {
@@ -1785,6 +1783,8 @@ void php_module_shutdown(TSRMLS_D)
        zend_ini_global_shutdown(TSRMLS_C);
 #endif
 
+       php_output_shutdown();
+
        module_initialized = 0;
        if (PG(last_error_message)) {
                free(PG(last_error_message));
@@ -1946,7 +1946,7 @@ PHPAPI void php_handle_aborted_connection(void)
        TSRMLS_FETCH();
 
        PG(connection_status) = PHP_CONNECTION_ABORTED;
-       php_output_set_status(0 TSRMLS_CC);
+       php_output_set_status(PHP_OUTPUT_DISABLED);
 
        if (!PG(ignore_user_abort)) {
                zend_bailout();
index f7f216fa108b521091af22f7aec8194650a56b90..fcc9968fc73a5251c86e59bcc2e86faeb3fdeb58 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    +----------------------------------------------------------------------+
    | PHP Version 5                                                        |
    +----------------------------------------------------------------------+
    | Authors: Zeev Suraski <zeev@zend.com>                                |
    |          Thies C. Arntzen <thies@thieso.net>                         |
    |          Marcus Boerger <helly@php.net>                              |
+   | New API: Michael Wallner <mike@php.net>                              |
    +----------------------------------------------------------------------+
 */
 
 /* $Id$ */
 
+#ifndef PHP_OUTPUT_DEBUG
+#      define PHP_OUTPUT_DEBUG 0
+#endif
+#ifndef PHP_OUTPUT_NOINLINE
+#      define PHP_OUTPUT_NOINLINE 0
+#endif
+
 #include "php.h"
 #include "ext/standard/head.h"
-#include "ext/standard/basic_functions.h"
 #include "ext/standard/url_scanner_ex.h"
-#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
-#include "ext/zlib/php_zlib.h"
-#endif
 #include "SAPI.h"
+#include "zend_stack.h"
+#include "php_output.h"
 
-#define OB_DEFAULT_HANDLER_NAME "default output handler"
-
-/* output functions */
-static int php_b_body_write(const char *str, uint str_length TSRMLS_DC);
+ZEND_DECLARE_MODULE_GLOBALS(output);
 
-static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC);
-static void php_ob_append(const char *text, uint text_length TSRMLS_DC);
-#if 0
-static void php_ob_prepend(const char *text, uint text_length);
+#if PHP_OUTPUT_NOINLINE || PHP_OUTPUT_DEBUG
+#      undef   inline
+#      define  inline
 #endif
 
-#ifdef ZTS
-int output_globals_id;
-#else
-php_output_globals output_globals;
-#endif
+/* {{{ aliases, conflict and reverse conflict hash tables */
+static HashTable php_output_handler_aliases;
+static HashTable php_output_handler_conflicts;
+static HashTable php_output_handler_reverse_conflicts;
+/* }}} */
+
+/* {{{ forward declarations */
+static inline int php_output_lock_error(int op TSRMLS_DC);
+static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC);
+
+static inline php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags);
+static inline int 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 discard, int shutdown TSRMLS_DC);
 
-/* {{{ php_default_output_func */
-PHPAPI int php_default_output_func(const char *str, uint str_len 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)
 {
-       fwrite(str, 1, str_len, stderr);
-       return str_len;
+       memset(G, 0, sizeof(*G));
 }
 /* }}} */
 
-/* {{{ php_output_init_globals */
-static void php_output_init_globals(php_output_globals *output_globals_p TSRMLS_DC)
+/* {{{ void php_output_startup(void)
+       Set up module globals and initalize the conflict and reverse conflict hash tables */
+PHPAPI void php_output_startup(void)
 {
-       OG(php_body_write) = php_default_output_func;
-       OG(php_header_write) = php_default_output_func;
-       OG(implicit_flush) = 0;
-       OG(output_start_filename) = NULL;
-       OG(output_start_lineno) = 0;
+       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);
 }
 /* }}} */
 
+/* {{{ void php_output_shutdown(void)
+       Destroy module globals and the conflict and reverse conflict hash tables */
+PHPAPI void php_output_shutdown(void)
+{
+       zend_hash_destroy(&php_output_handler_aliases);
+       zend_hash_destroy(&php_output_handler_conflicts);
+       zend_hash_destroy(&php_output_handler_reverse_conflicts);
+}
+/* }}} */
 
-/* {{{ php_output_startup
-   Start output layer */
-PHPAPI void php_output_startup(void)
+/* {{{ 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
-       ts_allocate_id(&output_globals_id, sizeof(php_output_globals), (ts_allocate_ctor) php_output_init_globals, NULL);
-#else 
-       php_output_init_globals(&output_globals TSRMLS_CC);
+       memset((*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)], 0, sizeof(zend_output_globals));
+#else
+       memset(&output_globals, 0, sizeof(zend_output_globals));
 #endif
+       if (SUCCESS != zend_stack_init(&OG(handlers))) {
+               return FAILURE;
+       }
+       
+       MAKE_STD_ZVAL(OG(default_output_handler_name));
+       ZVAL_ASCII_STRINGL(OG(default_output_handler_name), "default output handler", sizeof("default output handler")-1, ZSTR_DUPLICATE);
+       MAKE_STD_ZVAL(OG(devnull_output_handler_name));
+       ZVAL_ASCII_STRINGL(OG(devnull_output_handler_name), "null output handler", sizeof("null output handler")-1, ZSTR_DUPLICATE);
+       
+       return SUCCESS;
 }
 /* }}} */
 
-
-/* {{{ php_output_activate
-   Initilize output global for activation */
-PHPAPI void php_output_activate(TSRMLS_D)
+/* {{{ void php_output_deactivate(TSRMLS_D) 
+       Destroy the output handler stack */
+PHPAPI void _php_output_deactivate(TSRMLS_D)
 {
-       OG(php_body_write) = php_ub_body_write;
-       OG(php_header_write) = sapi_module.ub_write;
-       OG(ob_nesting_level) = 0;
-       OG(ob_lock) = 0;
-       OG(disable_output) = 0;
-       OG(output_start_filename) = NULL;
-       OG(output_start_lineno) = 0;
+       php_output_handler **handler = NULL;
+       
+       OG(active) = NULL;
+       OG(running) = NULL;
+       /* release all output handlers */
+       while (SUCCESS == zend_stack_top(&OG(handlers), (void *) &handler)) {
+               php_output_handler_free(handler);
+               zend_stack_del_top(&OG(handlers));
+       }
+       zend_stack_destroy(&OG(handlers));
+       
+       zval_ptr_dtor(&OG(default_output_handler_name));
+       zval_ptr_dtor(&OG(devnull_output_handler_name));
 }
 /* }}} */
 
+/* {{{ void _php_output_register_constants() */
+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_WRITE", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT);
+       REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSH", PHP_OUTPUT_HANDLER_FLUSH, CONST_CS | CONST_PERSISTENT);
+       REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEAN", PHP_OUTPUT_HANDLER_CLEAN, CONST_CS | CONST_PERSISTENT);
+       REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FINAL", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT);
+       REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT);
+       REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT);
+
+       REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEANABLE", PHP_OUTPUT_HANDLER_CLEANABLE, CONST_CS | CONST_PERSISTENT);
+       REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_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
-   Toggle output status.  Do NOT use in application code, only in SAPIs where appropriate. */
-PHPAPI void php_output_set_status(zend_bool status TSRMLS_DC)
+/* {{{ void php_output_set_status(int status)
+       Used by SAPIs to disable output */
+PHPAPI void _php_output_set_status(int status TSRMLS_DC)
 {
-       OG(disable_output) = !status;
+       OG(flags) = status & 0xf;
 }
 /* }}} */
 
-/* {{{ php_output_register_constants */
-void php_output_register_constants(TSRMLS_D)
+/* {{{ int php_output_get_status()
+       Get output control status */
+PHPAPI int _php_output_get_status(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_CONT", PHP_OUTPUT_HANDLER_CONT, CONST_CS | CONST_PERSISTENT);
-       REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_END, CONST_CS | CONST_PERSISTENT);
+       return OG(flags)
+                       | (OG(active) ? PHP_OUTPUT_ACTIVE : 0)
+                       | (OG(running)? PHP_OUTPUT_LOCKED : 0);
 }
 /* }}} */
 
+/* {{{ zval *php_output_get_default_handler_name() */
+PHPAPI zval *_php_output_get_default_handler_name(TSRMLS_D)
+{
+       return OG(default_output_handler_name);
+}
+/* }}} */
 
-/* {{{ php_body_wirte
- * Write body part */
-PHPAPI int php_body_write(const char *str, uint str_length TSRMLS_DC)
+/* {{{ zval *php_output_get_devnull_handler_name() */
+PHPAPI zval *_php_output_get_devnull_handler_name(TSRMLS_D)
 {
-       return OG(php_body_write)(str, str_length TSRMLS_CC);   
+       return OG(devnull_output_handler_name);
 }
 /* }}} */
 
-/* {{{ php_header_wirte
* Write HTTP header */
-PHPAPI int php_header_write(const char *str, uint str_length TSRMLS_DC)
+/* {{{ int php_output_write_unbuffered(const char *str, size_t len)
      Unbuffered write */
+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;
-       } else {
-               return OG(php_header_write)(str, str_length TSRMLS_CC);
        }
+       return sapi_module.ub_write(str, len TSRMLS_CC);
 }
 /* }}} */
 
-/* {{{ php_start_ob_buffer
* Start output buffering */
-PHPAPI int php_start_ob_buffer(zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
+/* {{{ int php_output_write(const char *str, size_t len)
      Buffered write */
+PHPAPI int _php_output_write(const char *str, size_t len TSRMLS_DC)
 {
-       uint initial_size, block_size;
-
-       if (OG(ob_lock)) {
-               if (SG(headers_sent) && !SG(request_info).headers_only) {
-                       OG(php_body_write) = php_ub_body_write_no_header;
-               } else {
-                       OG(php_body_write) = php_ub_body_write;
-               }
-               OG(ob_nesting_level) = 0;
-               php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers");
-               return FAILURE;
+       if (OG(flags) & PHP_OUTPUT_DISABLED) {
+               return 0;
        }
-       if (chunk_size > 0) {
-               if (chunk_size==1) {
-                       chunk_size = 4096;
+       php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len TSRMLS_CC);
+       return (int) len;
+}
+/* }}} */
+
+/* {{{ void php_output_flush()
+       Flush the most recent output handlers buffer */
+PHPAPI void _php_output_flush(TSRMLS_D)
+{
+       php_output_context context;
+       
+       if (OG(active)) {
+               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);
+                       zend_stack_push(&OG(handlers), &OG(active), sizeof(php_output_handler *));
                }
-               initial_size = (chunk_size*3/2);
-               block_size = chunk_size/2;
-       } else {
-               initial_size = 40*1024;
-               block_size = 10*1024;
+               php_output_context_dtor(&context);
        }
-       return php_ob_init(initial_size, block_size, output_handler, chunk_size, erase TSRMLS_CC);
 }
 /* }}} */
 
-/* {{{ php_start_ob_buffer_named
* Start output buffering */
-PHPAPI int php_start_ob_buffer_named(const char *output_handler_name, uint chunk_size, zend_bool erase TSRMLS_DC)
+/* {{{ void php_output_flush_all()
      Flush all output buffers subsequently */
+PHPAPI void _php_output_flush_all(TSRMLS_D)
 {
-       zval *output_handler;
-       int result;
-
-       ALLOC_INIT_ZVAL(output_handler);
-       Z_STRLEN_P(output_handler) = strlen(output_handler_name);       /* this can be optimized */
-       Z_STRVAL_P(output_handler) = estrndup(output_handler_name, Z_STRLEN_P(output_handler));
-       Z_TYPE_P(output_handler) = IS_STRING;
-       result = php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC);
-       zval_dtor(output_handler);
-       FREE_ZVAL(output_handler);
-       return result;
+       if (OG(active)) {
+               php_output_op(PHP_OUTPUT_HANDLER_FLUSH, NULL, 0 TSRMLS_CC);
+       }
 }
 /* }}} */
 
-/* {{{ php_end_ob_buffer
* End output buffering (one level) */
-PHPAPI void php_end_ob_buffer(zend_bool send_buffer, zend_bool just_flush TSRMLS_DC)
+/* {{{ SUCCESS|FAILURE php_output_clean()
      Cleans the most recent output handlers buffer if the handler is cleanable */
+PHPAPI int _php_output_clean(TSRMLS_D)
 {
-       char *final_buffer=NULL;
-       unsigned int final_buffer_length=0;
-       zval *alternate_buffer=NULL;
-       char *to_be_destroyed_buffer, *to_be_destroyed_handler_name;
-       char *to_be_destroyed_handled_output[2] = { 0, 0 };
-       int status;
-       php_ob_buffer *prev_ob_buffer_p=NULL;
-       php_ob_buffer orig_ob_buffer;
-
-       if (OG(ob_nesting_level)==0) {
-               return;
-       }
-       status = 0;
-       if (!OG(active_ob_buffer).status & PHP_OUTPUT_HANDLER_START) {
-               /* our first call */
-               status |= PHP_OUTPUT_HANDLER_START;
-       }
-       if (just_flush) {
-               status |= PHP_OUTPUT_HANDLER_CONT;
-       } else {
-               status |= PHP_OUTPUT_HANDLER_END;
-       }
+       php_output_context context;
+       
+       if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_CLEANABLE)) {
+               OG(active)->buffer.used = 0;
+               php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC);
+               php_output_handler_op(OG(active), &context);
+               php_output_context_dtor(&context);
+               return SUCCESS;
+       }
+       return FAILURE;
+}
+/* }}} */
 
-#if 0
- {
-        FILE *fp;
-        fp = fopen("/tmp/ob_log", "a");
-        fprintf(fp, "NestLevel: %d  ObStatus: %d  HandlerName: %s\n", OG(ob_nesting_level), status, OG(active_ob_buffer).handler_name.s);
-        fclose(fp);
- }
-#endif
+/* {{{ void php_output_clean_all()
+       Cleans all output handler buffers, without regard whether the handler is cleanable */
+PHPAPI void _php_output_clean_all(TSRMLS_D)
+{
+       php_output_context context;
        
-       if (OG(active_ob_buffer).internal_output_handler) {
-               final_buffer = OG(active_ob_buffer).internal_output_handler_buffer;
-               final_buffer_length = OG(active_ob_buffer).internal_output_handler_buffer_size;
-               OG(active_ob_buffer).internal_output_handler(OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, &final_buffer, &final_buffer_length, status TSRMLS_CC);
-       } else if (OG(active_ob_buffer).output_handler) {
-               zval **params[2];
-               zval *orig_buffer;
-               zval *z_status;
-
-               ALLOC_INIT_ZVAL(orig_buffer);
-               ZVAL_U_STRINGL(ZEND_U_CONVERTER(UG(output_encoding_conv)), orig_buffer, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1);
-               orig_buffer->refcount=2;        /* don't let call_user_function() destroy our buffer */
-               orig_buffer->is_ref=1;
-
-               ALLOC_INIT_ZVAL(z_status);
-               ZVAL_LONG(z_status, status);
-
-               params[0] = &orig_buffer;
-               params[1] = &z_status;
-               OG(ob_lock) = 1;
-
-               if (call_user_function_ex(CG(function_table), NULL, OG(active_ob_buffer).output_handler, &alternate_buffer, 2, params, 1, NULL TSRMLS_CC)==SUCCESS) {
-                       if (alternate_buffer && !(Z_TYPE_P(alternate_buffer)==IS_BOOL && Z_BVAL_P(alternate_buffer)==0)) {
-                               convert_to_string_ex(&alternate_buffer);
-                               final_buffer = Z_STRVAL_P(alternate_buffer);
-                               final_buffer_length = Z_STRLEN_P(alternate_buffer);
-                       }
-               }
-               OG(ob_lock) = 0;
-               if (!just_flush) {
-                       zval_ptr_dtor(&OG(active_ob_buffer).output_handler);
-               }
-               orig_buffer->refcount -=2;
-               if (orig_buffer->refcount <= 0) { /* free the zval */
-                       zval_dtor(orig_buffer);
-                       FREE_ZVAL(orig_buffer);
-               }
-               zval_ptr_dtor(&z_status);
+       if (OG(active)) {
+               php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC);
+               zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_clean, &context);
        }
+}
 
-       if (!final_buffer) {
-               final_buffer = OG(active_ob_buffer).buffer;
-               final_buffer_length = OG(active_ob_buffer).text_length;
+/* {{{ SUCCESS|FAILURE php_output_end()
+       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(0, 0 TSRMLS_CC)) {
+               return SUCCESS;
        }
+       return FAILURE;
+}
+/* }}} */
 
-       if (OG(ob_nesting_level)==1) { /* end buffering */
-               if (SG(headers_sent) && !SG(request_info).headers_only) {
-                       OG(php_body_write) = php_ub_body_write_no_header;
-               } else {
-                       OG(php_body_write) = php_ub_body_write;
-               }
-       }
+/* {{{ void php_output_end_all()
+       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(0, 1 TSRMLS_CC));
+}
+/* }}} */
 
-       to_be_destroyed_buffer = OG(active_ob_buffer).buffer;
-       /* FIXME: unicode support??? */
-       to_be_destroyed_handler_name = OG(active_ob_buffer).handler_name.s;
-       if (OG(active_ob_buffer).internal_output_handler
-               && (final_buffer != OG(active_ob_buffer).internal_output_handler_buffer)
-               && (final_buffer != OG(active_ob_buffer).buffer)) {
-               to_be_destroyed_handled_output[0] = final_buffer;
+/* {{{ SUCCESS|FAILURE php_output_discard()
+       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(1, 0 TSRMLS_CC)) {
+               return SUCCESS;
        }
+       return FAILURE;
+}
+/* }}} */
 
-       if (!just_flush) {
-               if (OG(active_ob_buffer).internal_output_handler) {
-                       to_be_destroyed_handled_output[1] = OG(active_ob_buffer).internal_output_handler_buffer;
-               }
-       }
-       if (OG(ob_nesting_level)>1) { /* restore previous buffer */
-               zend_stack_top(&OG(ob_buffers), (void **) &prev_ob_buffer_p);
-               orig_ob_buffer = OG(active_ob_buffer);
-               OG(active_ob_buffer) = *prev_ob_buffer_p;
-               zend_stack_del_top(&OG(ob_buffers));
-               if (!just_flush && OG(ob_nesting_level)==2) { /* destroy the stack */
-                       zend_stack_destroy(&OG(ob_buffers));
-               }
+/* {{{ void php_output_discard_all()
+       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(1, 1 TSRMLS_CC);
        }
-       OG(ob_nesting_level)--;
+}
+/* }}} */
 
-       if (send_buffer) {
-               if (just_flush) { /* if flush is called prior to proper end, ensure presence of NUL */
-                       final_buffer[final_buffer_length] = '\0';
-               }
-               OG(php_body_write)(final_buffer, final_buffer_length TSRMLS_CC);
+/* {{{ int php_output_get_level()
+       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)
+       Get the contents of the active output handlers buffer */
+PHPAPI int _php_output_get_contents(zval *p TSRMLS_DC)
+{
+       if (OG(active)) {
+               ZVAL_STRINGL(p, OG(active)->buffer.data, OG(active)->buffer.used, 1);
+               return SUCCESS;
+       } else {
+               ZVAL_NULL(p);
+               return FAILURE;
        }
+}
 
-       if (just_flush) { /* we restored the previous ob, return to the current */
-               if (prev_ob_buffer_p) {
-                       zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer));
-                       OG(active_ob_buffer) = orig_ob_buffer;
-               }
-               OG(ob_nesting_level)++;
+/* {{{ SUCCESS|FAILURE php_output_get_length(zval *z)
+       Get the length of the active output handlers buffer */
+PHPAPI int _php_output_get_length(zval *p TSRMLS_DC)
+{
+       if (OG(active)) {
+               ZVAL_LONG(p, OG(active)->buffer.used);
+               return SUCCESS;
+       } else {
+               ZVAL_NULL(p);
+               return FAILURE;
        }
+}
+/* }}} */
 
-       if (alternate_buffer) {
-               zval_ptr_dtor(&alternate_buffer);
+/* {{{ SUCCESS|FAILURE php_output_handler_start_default()
+       Start a "default output handler" */
+PHPAPI int _php_output_start_default(TSRMLS_D)
+{
+       php_output_handler *handler;
+       
+       handler = php_output_handler_create_internal(OG(default_output_handler_name), php_output_handler_default_func, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
+       if (SUCCESS == php_output_handler_start(handler)) {
+               return SUCCESS;
        }
+       php_output_handler_free(&handler);
+       return FAILURE;
+}
+/* }}} */
 
-       if (status & PHP_OUTPUT_HANDLER_END) {
-               efree(to_be_destroyed_handler_name);
+/* {{{ SUCCESS|FAILURE php_output_handler_start_devnull()
+       Start a "null output handler" */
+PHPAPI int _php_output_start_devnull(TSRMLS_D)
+{
+       php_output_handler *handler;
+       
+       handler = php_output_handler_create_internal(OG(devnull_output_handler_name), php_output_handler_devnull_func, PHP_OUTPUT_HANDLER_DEFAULT_SIZE, 0);
+       if (SUCCESS == php_output_handler_start(handler)) {
+               return SUCCESS;
        }
-       if (!just_flush) {
-               efree(to_be_destroyed_buffer);
+       php_output_handler_free(&handler);
+       return FAILURE;
+}
+/* }}} */
+
+/* {{{ SUCCESS|FAILURE php_output_start_user(zval *handler, size_t chunk_size, int flags)
+       Start a user level output handler */
+PHPAPI int _php_output_start_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC)
+{
+       php_output_handler *handler;
+       
+       if (output_handler) {
+               handler = php_output_handler_create_user(output_handler, chunk_size, flags);
        } else {
-               OG(active_ob_buffer).text_length = 0;
-               OG(active_ob_buffer).status |= PHP_OUTPUT_HANDLER_START;
-               OG(php_body_write) = php_b_body_write;
-       }
-       if (to_be_destroyed_handled_output[0]) {
-               efree(to_be_destroyed_handled_output[0]);
+               handler = php_output_handler_create_internal(OG(default_output_handler_name), php_output_handler_default_func, chunk_size, flags);
        }
-       if (to_be_destroyed_handled_output[1]) {
-               efree(to_be_destroyed_handled_output[1]);
+       if (SUCCESS == php_output_handler_start(handler)) {
+               return SUCCESS;
        }
+       php_output_handler_free(&handler);
+       return FAILURE;
 }
 /* }}} */
 
-/* {{{ php_end_ob_buffers
* End output buffering (all buffers) */
-PHPAPI void php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC)
+/* {{{ SUCCESS|FAILURE php_output_start_internal(const char *name, php_output_handler_func_t handler, size_t chunk_size, int flags)
      Start an internal output handler that does not have to maintain a non-global state */
+PHPAPI int _php_output_start_internal(zval *name, php_output_handler_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC)
 {
-       while (OG(ob_nesting_level)!=0) {
-               php_end_ob_buffer(send_buffer, 0 TSRMLS_CC);
+       php_output_handler *handler;
+       
+       handler = php_output_handler_create_internal(name, php_output_handler_compat_func, chunk_size, flags);
+       php_output_handler_set_context(handler, output_handler, NULL);
+       if (SUCCESS == php_output_handler_start(handler)) {
+               return SUCCESS;
        }
+       php_output_handler_free(&handler);
+       return FAILURE;
 }
 /* }}} */
 
-/* {{{ php_start_implicit_flush
- */
-PHPAPI void php_start_implicit_flush(TSRMLS_D)
+/* {{{ php_output_handler *php_output_handler_create_user(zval *handler, size_t chunk_size, int flags)
      Create a user level output handler */
+PHPAPI php_output_handler *_php_output_handler_create_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC)
 {
-       OG(implicit_flush)=1;
+       zval *handler_name = NULL;
+       php_output_handler *handler = NULL;
+       php_output_handler_context_func_t *internal = NULL;
+       
+       switch (Z_TYPE_P(output_handler)) {
+               case IS_NULL:
+                       break;
+               case IS_STRING:
+               case IS_UNICODE:
+                       if (Z_UNILEN_P(output_handler) && (internal = php_output_handler_alias(output_handler))) {
+                               return php_output_handler_create_internal(output_handler, *internal, chunk_size, flags);
+                       }
+               default:
+                       MAKE_STD_ZVAL(handler_name);
+                       ZVAL_NULL(handler_name);
+                       if (zend_is_callable(output_handler, 0, handler_name)) {
+                               handler = php_output_handler_init(handler_name, chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_USER);
+                               ZVAL_ADDREF(output_handler);
+                               handler->user = output_handler;
+                       }
+                       zval_ptr_dtor(&handler_name);
+                       return handler;
+       }
+       
+       return php_output_handler_create_internal(OG(default_output_handler_name), php_output_handler_default_func, chunk_size, flags);
 }
 /* }}} */
 
-/* {{{ php_end_implicit_flush
- */
-PHPAPI void php_end_implicit_flush(TSRMLS_D)
+/* {{{ php_output_handler *php_output_handler_create_internal(const char *name, php_output_handler_context_func_t handler, size_t chunk_size, int flags)
      Create an internal output handler that can maintain a non-global state */
+PHPAPI php_output_handler *_php_output_handler_create_internal(zval *name, php_output_handler_context_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC)
 {
-       OG(implicit_flush)=0;
+       php_output_handler *handler;
+       
+       handler = php_output_handler_init(name, chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_INTERNAL);
+       handler->internal = output_handler;
+       
+       return handler;
 }
-/* }}} */
 
-/* {{{ php_ob_set_internal_handler
- */
-PHPAPI void php_ob_set_internal_handler(php_output_handler_func_t internal_output_handler, uint buffer_size, char *handler_name, zend_bool erase TSRMLS_DC)
+/* {{{ void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* 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)
 {
-       /* FIXME: Unicode support??? */
-       if (OG(ob_nesting_level)==0 || OG(active_ob_buffer).internal_output_handler || strcmp(OG(active_ob_buffer).handler_name.s, OB_DEFAULT_HANDLER_NAME)) {
-               php_start_ob_buffer(NULL, buffer_size, erase TSRMLS_CC);
+       if (handler->dtor && handler->opaq) {
+               handler->dtor(handler->opaq TSRMLS_CC);
        }
+       handler->dtor = dtor;
+       handler->opaq = opaq;
+}
+/* }}} */
 
-       OG(active_ob_buffer).internal_output_handler = internal_output_handler;
-       OG(active_ob_buffer).internal_output_handler_buffer = (char *) emalloc(buffer_size);
-       OG(active_ob_buffer).internal_output_handler_buffer_size = buffer_size;
-       if (OG(active_ob_buffer).handler_name.s) {
-               efree(OG(active_ob_buffer).handler_name.s);
+/* {{{ SUCCESS|FAILURE php_output_handler_start(php_output_handler *handler)
+       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)
+{
+       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_u_hash_find(&php_output_handler_conflicts, Z_TYPE_P(handler->name), Z_UNIVAL_P(handler->name), Z_UNILEN_P(handler->name), (void *) &conflict)) {
+               if (SUCCESS != (*conflict)(handler->name TSRMLS_CC)) {
+                       return FAILURE;
+               }
        }
-       OG(active_ob_buffer).handler_name.s = estrdup(handler_name);
-       OG(active_ob_buffer).erase = erase;
+       if (SUCCESS == zend_u_hash_find(&php_output_handler_reverse_conflicts, Z_TYPE_P(handler->name), Z_UNIVAL_P(handler->name), Z_UNILEN_P(handler->name), (void *) &rconflicts)) {
+               for (   zend_hash_internal_pointer_reset(rconflicts);
+                               zend_hash_get_current_data(rconflicts, (void *) &conflict) == SUCCESS;
+                               zend_hash_move_forward(rconflicts)) {
+                       if (SUCCESS != (*conflict)(handler->name TSRMLS_CC)) {
+                               return FAILURE;
+                       }
+               }
+       }
+       /* zend_stack_push never returns SUCCESS but FAILURE or stack level */
+       if (FAILURE == (handler->level = zend_stack_push(&OG(handlers), &handler, sizeof(php_output_handler *)))) {
+               return FAILURE;
+       }
+       OG(active) = handler;
+       return SUCCESS;
 }
 /* }}} */
 
-/*
- * Output buffering - implementation
- */
-
-/* {{{ php_ob_allocate
- */
-static inline void php_ob_allocate(uint text_length TSRMLS_DC)
+/* {{{ int php_output_handler_started(const char *name)
+       Check whether a certain output handler is in use */
+PHPAPI int _php_output_handler_started(zval *name TSRMLS_DC)
 {
-       uint new_len = OG(active_ob_buffer).text_length + text_length;
-
-       if (OG(active_ob_buffer).size < new_len) {
-               uint buf_size = OG(active_ob_buffer).size;
-               while (buf_size <= new_len) {
-                       buf_size += OG(active_ob_buffer).block_size;
+       php_output_handler **handlers;
+       int i, count = php_output_get_level();
+       
+       if (count) {
+               handlers = *(php_output_handler ***) zend_stack_base(&OG(handlers));
+               
+               for (i = 0; i < count; ++i) {
+                       if (!zend_binary_zval_strcmp(handlers[i]->name, name)) {
+                               return 1;
+                       }
                }
-
-               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;
+       
+       return 0;
 }
 /* }}} */
 
-/* {{{ php_ob_init_conflict
- * Returns 1 if handler_set is already used and generates error message
- */
-PHPAPI int php_ob_init_conflict(char *handler_new, char *handler_set TSRMLS_DC)
+/* {{{ int php_output_handler_conflict(const char *handler_new, const char *handler_old)
+       Check whether a certain handler is in use and issue a warning that the new handler would conflict with the already used one */
+PHPAPI int _php_output_handler_conflict(zval *handler_new, zval *handler_set TSRMLS_DC)
 {
-       if (php_ob_handler_used(handler_set TSRMLS_CC)) {
-               php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set);
+       if (php_output_handler_started(handler_set)) {
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%v' conflicts with '%v'", Z_UNIVAL_P(handler_new), Z_UNIVAL_P(handler_set));
                return 1;
        }
        return 0;
 }
 /* }}} */
 
-/* {{{ php_ob_init_named
- */
-static int php_ob_init_named(uint initial_size, uint block_size, zend_uchar type, zstr handler_name, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
+/* {{{ SUCCESS|FAILURE php_output_handler_conflict_register(const char name[], php_output_handler_conflict_check_t check_func)
      Register a conflict checking function on MINIT */
+PHPAPI int _php_output_handler_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC)
 {
-       int handler_len;
+       if (!EG(current_module)) {
+               zend_error(E_ERROR, "Cannot register an output handler conflict outside of MINIT");
+               return FAILURE;
+       }
+       return zend_u_hash_update(&php_output_handler_conflicts, Z_TYPE_P(name), Z_UNIVAL_P(name), Z_UNILEN_P(name), &check_func, sizeof(php_output_handler_conflict_check_t *), NULL);
+}
+/* }}} */
 
-       if (output_handler && !zend_is_callable(output_handler, 0, NULL)) {
+/* {{{ SUCCESS|FAILURE php_output_handler_reverse_conflict_register(const char name[], php_output_handler_conflict_check_t check_func)
+       Register a reverse conflict checking function on MINIT */
+PHPAPI int _php_output_handler_reverse_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC)
+{
+       HashTable rev, *rev_ptr = NULL;
+       
+       if (!EG(current_module)) {
+               zend_error(E_ERROR, "Cannot register a reverse output handler conflict outside of MINIT");
                return FAILURE;
        }
-       if (type == IS_UNICODE) {
-               handler_len = u_strlen(handler_name.u);
+       if (SUCCESS == zend_u_hash_find(&php_output_handler_reverse_conflicts, Z_TYPE_P(name), Z_UNIVAL_P(name), Z_UNILEN_P(name), (void *) &rev_ptr)) {
+               return zend_hash_next_index_insert(rev_ptr, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL);
        } else {
-               handler_len = strlen(handler_name.s);
-       }
-       if (OG(ob_nesting_level)>0) {
-#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
-               if ((handler_len == sizeof("ob_gzhandler")-1) && 
-                   (ZEND_U_EQUAL(type, handler_name, handler_len, "ob_gzhandler", sizeof("ob_gzhandler"))) &&
-                   php_ob_gzhandler_check(TSRMLS_C)) {
+               zend_hash_init(&rev, 1, NULL, NULL, 1);
+               if (SUCCESS != zend_hash_next_index_insert(&rev, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL)) {
+                       zend_hash_destroy(&rev);
                        return FAILURE;
                }
-#endif
-               if (OG(ob_nesting_level)==1) { /* initialize stack */
-                       zend_stack_init(&OG(ob_buffers));
+               if (SUCCESS != zend_u_hash_update(&php_output_handler_reverse_conflicts, Z_TYPE_P(name), Z_UNIVAL_P(name), Z_UNILEN_P(name), &rev, sizeof(HashTable), NULL)) {
+                       zend_hash_destroy(&rev);
+                       return FAILURE;
                }
-               zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer));
-       }
-       OG(ob_nesting_level)++;
-       OG(active_ob_buffer).block_size = block_size;
-       OG(active_ob_buffer).size = initial_size;
-       OG(active_ob_buffer).buffer = (char *) emalloc(initial_size+1);
-       OG(active_ob_buffer).text_length = 0;
-       OG(active_ob_buffer).output_handler = output_handler;
-       OG(active_ob_buffer).chunk_size = chunk_size;
-       OG(active_ob_buffer).status = 0;
-       OG(active_ob_buffer).internal_output_handler = NULL;
-       if (type == IS_UNICODE) {
-               /* FIXME: Unicode support??? */
-               OG(active_ob_buffer).handler_name.u = eustrdup((handler_name.u && handler_name.u[0])?handler_name.u:(UChar*)OB_DEFAULT_HANDLER_NAME);
-       } else {
-               OG(active_ob_buffer).handler_name.s = estrdup((handler_name.s && handler_name.s[0])?handler_name.s:OB_DEFAULT_HANDLER_NAME);
+               return SUCCESS;
        }
-       OG(active_ob_buffer).erase = erase;
-       OG(php_body_write) = php_b_body_write;
-       return SUCCESS;
 }
 /* }}} */
 
-/* {{{ php_ob_handler_from_string
- * Create zval output handler from string 
- */
-static zval* php_ob_handler_from_string(const char *handler_name, int len TSRMLS_DC)
+/* {{{ php_output_handler_context_func_t php_output_handler_alias(const char[] name)
+       Get an internal output handler for a user handler if it exists */
+PHPAPI php_output_handler_context_func_t *_php_output_handler_alias(zval *name TSRMLS_DC)
 {
-       zval *output_handler;
-
-       ALLOC_INIT_ZVAL(output_handler);
-       Z_STRLEN_P(output_handler) = len;
-       Z_STRVAL_P(output_handler) = estrndup(handler_name, len);
-       Z_TYPE_P(output_handler) = IS_STRING;
-       return output_handler;
+       php_output_handler_context_func_t *func = NULL;
+       
+       zend_u_hash_find(&php_output_handler_aliases, Z_TYPE_P(name), Z_UNIVAL_P(name), Z_UNILEN_P(name), (void *) &func);
+       return func;
 }
 /* }}} */
 
-/* {{{ php_ob_handler_from_unicode
- * Create zval output handler from unicode 
- */
-static zval* php_ob_handler_from_unicode(const UChar *handler_name, int len TSRMLS_DC)
+/* {{{ SUCCESS|FAILURE php_output_handler_alias_register(const char[] name, php_output_handler_context_func_t func)
+       Registers an internal output handler as alias for a user handler */
+PHPAPI int _php_output_handler_alias_register_ex(zval *name, php_output_handler_context_func_t func TSRMLS_DC)
 {
-       zval *output_handler;
-
-       ALLOC_INIT_ZVAL(output_handler);
-       Z_USTRLEN_P(output_handler) = len;
-       Z_USTRVAL_P(output_handler) = eustrndup(handler_name, len);
-       Z_TYPE_P(output_handler) = IS_UNICODE;
-       return output_handler;
+       if (!EG(current_module)) {
+               zend_error(E_ERROR, "Cannot register an output handler alias outside of MINIT");
+               return FAILURE;
+       }
+       return zend_u_hash_update(&php_output_handler_aliases, Z_TYPE_P(name), Z_UNIVAL_P(name), Z_UNILEN_P(name), &func, sizeof(php_output_handler_context_func_t *), NULL);
 }
 /* }}} */
 
-/* {{{ php_ob_init
- */
-static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
-{
-       int result = FAILURE, handler_len, len;
-       HashPosition pos;
-       zval **tmp;
-       zval *handler_zval;
-
-       if (output_handler && output_handler->type == IS_STRING) {
-               zstr next_handler_name;
-               zstr handler_name = Z_UNIVAL_P(output_handler);
-               handler_len  = Z_UNILEN_P(output_handler);
-
-               result = SUCCESS;
-               if (handler_len && handler_name.s[0] != '\0') {
-                       while ((next_handler_name.s=strchr(handler_name.s, ',')) != NULL) {
-                               len = next_handler_name.s-handler_name.s;
-                               next_handler_name.s = estrndup(handler_name.s, len);
-                               handler_zval = php_ob_handler_from_string(next_handler_name.s, len TSRMLS_CC);
-                               result = php_ob_init_named(initial_size, block_size, IS_STRING, next_handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
-                               if (result != SUCCESS) {
-                                       zval_dtor(handler_zval);
-                                       FREE_ZVAL(handler_zval);
-                               }
-                               handler_name.s += len+1;
-                               handler_len -= len+1;
-                               efree(next_handler_name.s);
-                       }
-               }
-               if (result == SUCCESS) {
-                       handler_zval = php_ob_handler_from_string(handler_name.s, handler_len TSRMLS_CC);
-                       result = php_ob_init_named(initial_size, block_size, IS_STRING, handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
-                       if (result != SUCCESS) {
-                               zval_dtor(handler_zval);
-                               FREE_ZVAL(handler_zval);
-                       }
-               }
-       } else if (output_handler && output_handler->type == IS_UNICODE) {
-               zstr next_handler_name;
-               zstr handler_name = Z_UNIVAL_P(output_handler);
-               handler_len  = Z_UNILEN_P(output_handler);
-
-               result = SUCCESS;
-               if (handler_len && handler_name.u[0] != 0) {
-                       while ((next_handler_name.u=u_strchr(handler_name.u, ',')) != NULL) {
-                               len = next_handler_name.u-handler_name.u;
-                               next_handler_name.u = eustrndup(handler_name.u, len);
-                               handler_zval = php_ob_handler_from_unicode(next_handler_name.u, len TSRMLS_CC);
-                               result = php_ob_init_named(initial_size, block_size, IS_UNICODE, next_handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
-                               if (result != SUCCESS) {
-                                       zval_dtor(handler_zval);
-                                       FREE_ZVAL(handler_zval);
-                               }
-                               handler_name.u += len+1;
-                               handler_len -= len+1;
-                               efree(next_handler_name.u);
-                       }
-               }
-               if (result == SUCCESS) {
-                       handler_zval = php_ob_handler_from_unicode(handler_name.u, handler_len TSRMLS_CC);
-                       result = php_ob_init_named(initial_size, block_size, IS_UNICODE, handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
-                       if (result != SUCCESS) {
-                               zval_dtor(handler_zval);
-                               FREE_ZVAL(handler_zval);
-                       }
-               }
-       } else if (output_handler && output_handler->type == IS_ARRAY) {
-               zval handler_name;
-
-               /* do we have array(object,method) */
-               if (zend_is_callable(output_handler, 0, &handler_name)) {
-                       SEPARATE_ZVAL(&output_handler);
-                       output_handler->refcount++;
-                       result = php_ob_init_named(initial_size, block_size, Z_TYPE(handler_name), Z_UNIVAL(handler_name), output_handler, chunk_size, erase TSRMLS_CC);
-                       zval_dtor(&handler_name);
-               } else {
-                       zval_dtor(&handler_name);
-                       /* init all array elements recursively */
-                       zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(output_handler), &pos);
-                       while (zend_hash_get_current_data_ex(Z_ARRVAL_P(output_handler), (void **)&tmp, &pos) == SUCCESS) {
-                               result = php_ob_init(initial_size, block_size, *tmp, chunk_size, erase TSRMLS_CC);
-                               if (result == FAILURE) {
-                                       break;
-                               }
-                               zend_hash_move_forward_ex(Z_ARRVAL_P(output_handler), &pos);
-                       }
+/* {{{ SUCCESS|FAILURE php_output_handler_hook(int type, void *arg) 
+       Output handler hook for output handler functions to check/modify the current handlers abilities */
+PHPAPI int _php_output_handler_hook(int 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_IMMUTABLE:
+                               OG(running)->flags &= ~PHP_OUTPUT_HANDLER_STDFLAGS;
+                               return SUCCESS;
+                       case PHP_OUTPUT_HANDLER_HOOK_DISABLE:
+                               OG(running)->flags |= PHP_OUTPUT_HANDLER_DISABLED;
+                               return SUCCESS;
                }
-       } else if (output_handler && output_handler->type == IS_OBJECT) {
-               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 %v to use as output handler", Z_OBJCE_P(output_handler)->name);
-               result = FAILURE;
-       } else {
-               zstr z_name;
-
-               z_name.s = OB_DEFAULT_HANDLER_NAME;
-               result = php_ob_init_named(initial_size, block_size, IS_STRING, z_name, NULL, chunk_size, erase TSRMLS_CC);
        }
-       return result;
+       return FAILURE;
 }
 /* }}} */
 
-/* {{{ php_ob_list_each
- */
-static int php_ob_list_each(php_ob_buffer *ob_buffer, zval *ob_handler_array) 
+/* {{{ void php_output_handler_dtor(php_output_handler *handler)
      Destroy an output handler */
+PHPAPI void _php_output_handler_dtor(php_output_handler *handler TSRMLS_DC)
 {
-       TSRMLS_FETCH();
-
-       add_next_index_text(ob_handler_array, ob_buffer->handler_name, 1);
-       return 0;
+       zval_ptr_dtor(&handler->name);
+       STR_FREE(handler->buffer.data);
+       if (handler->flags & PHP_OUTPUT_HANDLER_USER) {
+               zval_ptr_dtor(&handler->user);
+       }
+       if (handler->dtor && handler->opaq) {
+               handler->dtor(handler->opaq TSRMLS_CC);
+       }
+       memset(handler, 0, sizeof(*handler));
 }
 /* }}} */
 
-/* {{{ proto false|array ob_list_handlers()
- *  List all output_buffers in an array 
- */
-PHP_FUNCTION(ob_list_handlers)
+/* {{{ void php_output_handler_free(php_output_handler **handler)
+       Destroy and free an output handler */
+PHPAPI void _php_output_handler_free(php_output_handler **h TSRMLS_DC)
 {
-       if (ZEND_NUM_ARGS()!=0) {
-               ZEND_WRONG_PARAM_COUNT();
-               RETURN_FALSE;
+       if (*h) {
+               php_output_handler_dtor(*h);
+               efree(*h);
+               *h = NULL;
        }
+}
+/* }}} */
 
-       array_init(return_value);
-       if (OG(ob_nesting_level)) {
-               if (OG(ob_nesting_level)>1) {
-                       zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_list_each, return_value);
-               }
-               php_ob_list_each(&OG(active_ob_buffer), return_value);
+/* void php_output_set_implicit_flush(int enabled)
+       Enable or disable implicit flush */
+PHPAPI void _php_output_set_implicit_flush(int flush TSRMLS_DC)
+{
+       if (flush) {
+               OG(flags) |= PHP_OUTPUT_IMPLICITFLUSH;
+       } else {
+               OG(flags) &= ~PHP_OUTPUT_IMPLICITFLUSH;
        }
 }
 /* }}} */
 
-/* {{{ php_ob_used_each
- *  Sets handler_name to NULL is found
- */
-static int php_ob_handler_used_each(php_ob_buffer *ob_buffer, char **handler_name) 
+/* {{{ char *php_output_get_start_filename()
+       Get the file name where output has started */
+PHPAPI char *_php_output_get_start_filename(TSRMLS_D)
+{
+       return OG(output_start_filename);
+}
+/* }}} */
+
+/* {{{ int php_output_get_start_lineno()
+       Get the line number where output has started */
+PHPAPI int _php_output_get_start_lineno(TSRMLS_D)
+{
+       return OG(output_start_lineno);
+}
+/* }}} */
+
+/* {{{ static int php_output_lock_error(int op TSRMLS_DC)
+       Checks whether an unallowed operation is attempted from within the output handler and issues a fatal error */
+static inline int php_output_lock_error(int op TSRMLS_DC)
 {
-       /* FIXME: Unicode support??? */
-       if (!strcmp(ob_buffer->handler_name.s, *handler_name)) {
-               *handler_name = NULL;
+       /* if there's no ob active, ob has been stopped */
+       if (op && OG(active) && OG(running)) {
+               /* fatal error */
+               php_output_deactivate();
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers");
                return 1;
        }
        return 0;
 }
 /* }}} */
 
-/* {{{ php_ob_used
- * returns 1 if given handler_name is used as output_handler
- */
-PHPAPI int php_ob_handler_used(char *handler_name TSRMLS_DC)
+/* {{{ static php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC)
+       Initialize a new output context */
+static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC)
 {
-       /* FIXME: Unicode support??? */
-       char *tmp = handler_name;
-
-       if (OG(ob_nesting_level)) {
-               if (!strcmp(OG(active_ob_buffer).handler_name.s, handler_name)) {
-                       return 1;
-               }
-               if (OG(ob_nesting_level)>1) {
-                       zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_handler_used_each, &tmp);
-               }
+       if (!context) {
+               context = emalloc(sizeof(php_output_context));
        }
-       return tmp ? 0 : 1;
+       
+       memset(context, 0, sizeof(php_output_context));
+       TSRMLS_SET_CTX(context->tsrm_ls);
+       context->op = op;
+       
+       return context;
 }
 /* }}} */
 
-/* {{{ php_ob_append
- */
-static inline void php_ob_append(const char *text, uint text_length TSRMLS_DC)
+/* {{{ static void php_output_context_reset(php_output_context *context)
      Reset an output context */
+static inline void php_output_context_reset(php_output_context *context)
 {
-       char *target;
-       int original_ob_text_length;
+       int op = context->op;
+       php_output_context_dtor(context);
+       memset(context, 0, sizeof(php_output_context));
+       context->op = op;
+}
+/* }}} */
 
-       original_ob_text_length=OG(active_ob_buffer).text_length;
+/* {{{ static void php_output_context_swap(php_output_context *context)
+       Swap output contexts buffers */
+static inline void php_output_context_swap(php_output_context *context)
+{
+       if (context->in.free && context->in.data) {
+               efree(context->in.data);
+       }
+       context->in.data = context->out.data;
+       context->in.used = context->out.used;
+       context->in.free = context->out.free;
+       context->in.size = context->out.size;
+       context->out.data = NULL;
+       context->out.used = 0;
+       context->out.size = 0;
+}
+/* }}} */
 
-       php_ob_allocate(text_length TSRMLS_CC);
-       target = OG(active_ob_buffer).buffer+original_ob_text_length;
-       memcpy(target, text, text_length);
-       target[text_length]=0;
+/* {{{ static void php_output_context_pass(php_output_context *context)
+       Pass input to output buffer */
+static inline void php_output_context_pass(php_output_context *context)
+{
+       context->out.data = context->in.data;
+       context->out.used = context->in.used;
+       context->out.size = context->in.size;
+       context->out.free = context->in.free;
+       context->in.data = NULL;
+       context->in.used = 0;
+       context->in.size = 0;
+}
+/* }}} */
 
-       /* If implicit_flush is On or chunked buffering, send contents to next buffer and return. */
-       if (OG(active_ob_buffer).chunk_size
-               && OG(active_ob_buffer).text_length >= OG(active_ob_buffer).chunk_size) {
-               
-               php_end_ob_buffer(1, 1 TSRMLS_CC);
-               return;
+/* {{{ static void php_output_context_dtor(php_output_context *context)
+       Destroy the contents of an output context */
+static inline void php_output_context_dtor(php_output_context *context)
+{
+       if (context->in.free && context->in.data) {
+               efree(context->in.data);
+       }
+       if (context->out.free && context->out.data) {
+               efree(context->out.data);
        }
 }
 /* }}} */
 
-#if 0
-static inline void php_ob_prepend(const char *text, uint text_length)
+/* {{{ static php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags)
+       Allocates and initializes a php_output_handler structure */
+static inline php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags)
 {
-       char *p, *start;
-       TSRMLS_FETCH();
-
-       php_ob_allocate(text_length TSRMLS_CC);
-
-       /* php_ob_allocate() may change OG(ob_buffer), so we can't initialize p&start earlier */
-       p = OG(ob_buffer)+OG(ob_text_length);
-       start = OG(ob_buffer);
+       php_output_handler *handler;
+        
+       handler = ecalloc(1, sizeof(php_output_handler));
+       ZVAL_ADDREF(name);
+       handler->name = name;
+       handler->size = chunk_size;
+       handler->flags = flags;
+       handler->buffer.size = PHP_OUTPUT_HANDLER_INITBUF_SIZE(chunk_size);
+       handler->buffer.data = emalloc(handler->buffer.size);
+       
+       return handler;
+}
+/* }}} */
 
-       while (--p>=start) {
-               p[text_length] = *p;
+/* {{{ static int php_output_handler_appen(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC)
+       Appends input to the output handlers buffer and indicates whether the buffer does not have to be processed by the output handler */
+static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC)
+{
+       /* 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 + 1 - (handler->buffer.size - handler->buffer.used));
+               size_t grow_max = MAX(grow_int, grow_buf);
+               
+               handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size += grow_max);
+       }
+       memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used);
+       handler->buffer.used += buf->used;
+       handler->buffer.data[handler->buffer.used] = '\0';
+       
+       /* chunked buffering */
+       if (handler->size && (handler->buffer.used >= handler->size)) {
+               /* store away errors and/or any intermediate output */
+               return OG(running) ? 1 : 0;
        }
-       memcpy(OG(ob_buffer), text, text_length);
-       OG(ob_buffer)[OG(active_ob_buffer).text_length]=0;
+       return 1;
 }
+/* }}} */
+
+/* {{{ static PHP_OUTPUT_HANDLER_(SUCCESS|FAILURE|NO_DATA) php_output_handler_op(php_output_handler *handler, php_output_context *context)
+       Output handler operation dispatcher, applying context op to the php_output_handler handler */
+static inline int php_output_handler_op(php_output_handler *handler, php_output_context *context)
+{
+       int status, op;
+       PHP_OUTPUT_TSRMLS(context);
+       
+#if PHP_OUTPUT_DEBUG
+       fprintf(stderr, ">>> op(%d, "
+                                       "handler=%p, "
+                                       "name=%s, "
+                                       "flags=%d, "
+                                       "buffer.data=%s, "
+                                       "buffer.used=%lu, "
+                                       "buffer.size=%lu, "
+                                       "in.data=%s, "
+                                       "in.used=%lu)\n",
+                       context->op,
+                       handler,
+                       handler->name,
+                       handler->flags,
+                       handler->buffer.data,
+                       handler->buffer.used,
+                       handler->buffer.size,
+                       context->in.data,
+                       context->in.used
+       );
 #endif
+       
+       op = context->op;
+       if (php_output_lock_error(op TSRMLS_CC)) {
+               /* fatal error */
+               return PHP_OUTPUT_HANDLER_FAILURE;
+       }
+       
+       /* storable? */
+       if (php_output_handler_append(handler, &context->in TSRMLS_CC) && !op) {
+               status = PHP_OUTPUT_HANDLER_NO_DATA;
+       } else {
+               /* need to start? */
+               if (!(handler->flags & PHP_OUTPUT_HANDLER_STARTED)) {
+                       handler->flags |= PHP_OUTPUT_HANDLER_STARTED;
+                       op |= PHP_OUTPUT_HANDLER_START;
+               }
+               
+               OG(running) = handler;
+               if (handler->flags & PHP_OUTPUT_HANDLER_USER) {
+                       zval *retval = NULL, **params[2], *flags, *input;
+                       
+                       MAKE_STD_ZVAL(input);
+                       ZVAL_STRINGL(input, handler->buffer.data, handler->buffer.used, 1);
+                       MAKE_STD_ZVAL(flags);
+                       ZVAL_LONG(flags, (long) op);
+                       params[0] = &input;
+                       params[1] = &flags;
+                       
+                       if (    (SUCCESS == call_user_function_ex(CG(function_table), NULL, handler->user, &retval, 2, params, 1, NULL TSRMLS_CC)) &&
+                                               retval && (Z_TYPE_P(retval) != IS_NULL) && (Z_TYPE_P(retval) != IS_BOOL || Z_BVAL_P(retval))) {
+                               /* user handler may have returned TRUE */
+                               status = PHP_OUTPUT_HANDLER_NO_DATA;
+                               if (Z_TYPE_P(retval) != IS_BOOL) {
+                                       convert_to_string_ex(&retval);
+                                       if (Z_STRLEN_P(retval)) {
+                                               context->out.data = estrndup(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
+                                               context->out.used = Z_STRLEN_P(retval);
+                                               context->out.free = 1;
+                                               status = PHP_OUTPUT_HANDLER_SUCCESS;
+                                       }
+                               }
+                       } else {
+                               /* call failed, pass internal buffer along */
+                               status = PHP_OUTPUT_HANDLER_FAILURE;
+                       }
+                       if (retval) {
+                               zval_ptr_dtor(&retval);
+                       }
+                       zval_ptr_dtor(&input);
+                       zval_ptr_dtor(&flags);
+               } else {
+                       
+                       context->in.data = handler->buffer.data;
+                       context->in.used = handler->buffer.used;
+                       context->in.free = 0;
+                       
+                       if (SUCCESS == handler->internal(&handler->opaq, context)) {
+                               if (context->out.used) {
+                                       status = PHP_OUTPUT_HANDLER_SUCCESS;
+                               } else {
+                                       status = PHP_OUTPUT_HANDLER_NO_DATA;
+                               }
+                       } else {
+                               status = PHP_OUTPUT_HANDLER_FAILURE;
+                       }
+               }
+               OG(running) = NULL;
+       }
+       
+       switch (status) {
+               case PHP_OUTPUT_HANDLER_FAILURE:
+                       /* disable this handler */
+                       handler->flags |= PHP_OUTPUT_HANDLER_DISABLED;
+                       /* returns handlers buffer */
+                       context->out.data = handler->buffer.data;
+                       context->out.used = handler->buffer.used;
+                       context->out.free = 1;
+                       handler->buffer.data = NULL;
+                       handler->buffer.used = 0;
+                       handler->buffer.size = 0;
+                       break;
+               case PHP_OUTPUT_HANDLER_SUCCESS:
+                       /* no more buffered data */
+                       handler->buffer.used = 0;
+                       break;
+               case PHP_OUTPUT_HANDLER_NO_DATA:
+                       /* handler ate all */
+                       php_output_context_reset(context);
+                       break;
+       }
+       
+       return status;
+}
+/* }}} */
 
 
-/* {{{ php_ob_get_buffer
* Return the current output buffer */
-PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC)
+/* {{{ static void php_output_op(int op, const char *str, size_t len TSRMLS_DC)
      Output op dispatcher, passes input and output handlers output through the output handler stack until it gets written to the SAPI */
+static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC)
 {
-       if (OG(ob_nesting_level)==0) {
-               return FAILURE;
+       php_output_context context;
+       php_output_handler **active;
+       int obh_cnt;
+       
+       if (php_output_lock_error(op TSRMLS_CC)) {
+               return;
        }
-       ZVAL_U_STRINGL(ZEND_U_CONVERTER(UG(output_encoding_conv)), p, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1);
-       return SUCCESS;
+       
+       php_output_context_init(&context, op TSRMLS_CC);
+       
+       /*
+        * broken up for better performance:
+        *  - apply op to the one active handler; note that OG(active) might be popped off the stack on a flush
+        *  - or apply op to the handler stack
+        */
+       if (OG(active) && (obh_cnt = zend_stack_count(&OG(handlers)))) {
+               context.in.data = (char *) str;
+               context.in.used = len;
+               
+               if (obh_cnt > 1) {
+                       zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_op, &context);
+               } else if ((SUCCESS == zend_stack_top(&OG(handlers), (void *) &active)) && (!((*active)->flags & PHP_OUTPUT_HANDLER_DISABLED))) {
+                       php_output_handler_op(*active, &context);
+               } else {
+                       php_output_context_pass(&context);
+               }
+       } else {
+               context.out.data = (char *) str;
+               context.out.used = len;
+       }
+       
+       if (context.out.data) {
+               if (context.out.used) {
+#if PHP_OUTPUT_DEBUG
+                       fprintf(stderr, "::: sapi_write('%s', %lu)\n", context.out.data, context.out.used);
+#endif
+                       if (!SG(headers_sent) && php_header(TSRMLS_C)) {
+                               if (zend_is_compiling(TSRMLS_C)) {
+                                       OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C);
+                                       OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C);
+                               } else if (zend_is_executing(TSRMLS_C)) {
+                                       OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C);
+                                       OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C);
+                               }
+#if PHP_OUTPUT_DEBUG
+                               fprintf(stderr, "!!! output started at: %s (%d)\n", OG(output_start_filename), OG(output_start_lineno));
+#endif
+                       }
+                       sapi_module.ub_write(context.out.data, context.out.used TSRMLS_CC);
+                       if (OG(flags) & PHP_OUTPUT_IMPLICITFLUSH) {
+                               sapi_flush(TSRMLS_C);
+                       }
+               }
+       }
+       php_output_context_dtor(&context);
 }
 /* }}} */
 
-/* {{{ php_ob_get_length
* Return the size of the current output buffer */
-PHPAPI int php_ob_get_length(zval *p TSRMLS_DC)
+/* {{{ static int php_output_stack_apply_op(void *h, void *c)
      Operation callback for the stack apply function */
+static int php_output_stack_apply_op(void *h, void *c)
 {
-       if (OG(ob_nesting_level) == 0) {
-               return FAILURE;
+       int status = PHP_OUTPUT_HANDLER_FAILURE, was_disabled;
+       php_output_handler *handler = *(php_output_handler **) h;
+       php_output_context *context = (php_output_context *) c;
+       
+       if (!(was_disabled = (handler->flags & PHP_OUTPUT_HANDLER_DISABLED))) {
+               status = php_output_handler_op(handler, context);
+       }
+       
+       /*
+        * handler ate all => break
+        * handler returned data or failed resp. is disabled => continue
+        */
+       switch (status) {
+               case PHP_OUTPUT_HANDLER_NO_DATA:
+                       return 1;
+                       
+               case PHP_OUTPUT_HANDLER_SUCCESS:
+                       /* swap contexts buffers, unless this is the last handler in the stack */
+                       if (handler->level) {
+                               php_output_context_swap(context);
+                       }
+                       return 0;
+                       
+               case PHP_OUTPUT_HANDLER_FAILURE:
+               default:
+                       if (was_disabled) {
+                               /* pass input along, if it's the last handler in the stack */
+                               if (!handler->level) {
+                                       php_output_context_pass(context);
+                               }
+                       } else {
+                               /* swap buffers, unless this is the last handler */
+                               if (handler->level) {
+                                       php_output_context_swap(context);
+                               }
+                       }
+                       return 0;
        }
-       ZVAL_LONG(p, OG(active_ob_buffer).text_length);
-       return SUCCESS;
 }
 /* }}} */
 
-/*
- * Wrapper functions - implementation
- */
-
-
-/* buffered output function */
-static int php_b_body_write(const char *str, uint str_length TSRMLS_DC)
+/* {{{ static int php_output_stack_apply_clean(void *h, void *c)
+       Clean callback for the stack apply function */
+static int php_output_stack_apply_clean(void *h, void *c)
 {
-       php_ob_append(str, str_length TSRMLS_CC);
-       return str_length;
+       php_output_handler *handler = *(php_output_handler **) h;
+       php_output_context *context = (php_output_context *) c;
+       
+       handler->buffer.used = 0;
+       php_output_handler_op(handler, context);
+       php_output_context_reset(context);
+       return 0;
 }
+/* }}} */
 
-/* {{{ php_ub_body_write_no_header
- */
-PHPAPI int php_ub_body_write_no_header(const char *str, uint str_length TSRMLS_DC)
+/* {{{ static int php_output_stack_apply_list(void *h, void *z)
      List callback for the stack apply function */
+static int php_output_stack_apply_list(void *h, void *z)
 {
-       int result;
-
-       if (OG(disable_output)) {
-               return 0;
-       }               
-
-       result = OG(php_header_write)(str, str_length TSRMLS_CC);
-
-       if (OG(implicit_flush)) {
-               sapi_flush(TSRMLS_C);
+       php_output_handler *handler = *(php_output_handler **) h;
+       zval *array = (zval *) z;
+       
+       if (Z_TYPE_P(handler->name) == IS_UNICODE) {
+               add_next_index_unicodel(array, Z_USTRVAL_P(handler->name), Z_USTRLEN_P(handler->name), 1);
+       } else {
+               add_next_index_stringl(array, Z_STRVAL_P(handler->name), Z_STRLEN_P(handler->name), 1);
        }
+       return 0;
+}
+/* }}} */
 
-       return result;
+/* {{{ static int php_output_stack_apply_status(void *h, void *z)
+       Status callback for the stack apply function */
+static int php_output_stack_apply_status(void *h, void *z)
+{
+       php_output_handler *handler = *(php_output_handler **) h;
+       zval *array = (zval *) z;
+       
+       add_next_index_zval(array, php_output_handler_status(handler, NULL));
+       
+       return 0;
 }
 /* }}} */
 
-/* {{{ php_ub_body_write
- */
-PHPAPI int php_ub_body_write(const char *str, uint str_length TSRMLS_DC)
+/* {{{ static zval *php_output_handler_status(php_output_handler *handler, zval *entry)
      Returns an array with the status of the output handler */
+static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry)
 {
-       int result = 0;
+       if (!entry) {
+               MAKE_STD_ZVAL(entry);
+               array_init(entry);
+       }
+       
+       if (Z_TYPE_P(handler->name) == IS_UNICODE) {
+               add_assoc_unicodel(entry, "name", Z_USTRVAL_P(handler->name), Z_USTRLEN_P(handler->name), 1);
+       } else {
+               add_assoc_stringl(entry, "name", Z_STRVAL_P(handler->name), Z_STRLEN_P(handler->name), 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;
+}
+/* }}} */
 
-       if (SG(request_info).headers_only) {
-               if(SG(headers_sent)) {
-                       return 0;
+/* {{{ static int php_output_stack_pop(int discard, int shutdown TSRMLS_DC)
+       Pops an output handler off the stack, ignores whether the handler is removable if shutdown==1, discards the handlers output if discard==1 */
+static inline int php_output_stack_pop(int discard, int shutdown TSRMLS_DC)
+{
+       php_output_context context;
+       php_output_handler **current, *orphan = OG(active);
+       
+       if (!orphan) {
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete.");
+               return 0;
+       } else if (!shutdown && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) {
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", orphan->name);
+               return 0;
+       } else {
+               php_output_context_init(&context, PHP_OUTPUT_HANDLER_FINAL TSRMLS_CC);
+               
+               /* don't run the output handler if it's disabled */
+               if (!(orphan->flags & PHP_OUTPUT_HANDLER_DISABLED)) {
+                       /* didn't it start yet? */
+                       if (!(orphan->flags & PHP_OUTPUT_HANDLER_STARTED)) {
+                               context.op |= PHP_OUTPUT_HANDLER_START;
+                       }
+                       /* signal that we're cleaning up */
+                       if (discard) {
+                               context.op |= PHP_OUTPUT_HANDLER_CLEAN;
+                       }
+                       php_output_handler_op(orphan, &context);
+               }
+               
+               /* pop it off the stack */
+               zend_stack_del_top(&OG(handlers));
+               if (SUCCESS == zend_stack_top(&OG(handlers), (void *) &current)) {
+                       OG(active) = *current;
+               } else {
+                       OG(active) = NULL;
                }
-               php_header(TSRMLS_C);
-               zend_bailout();
-       }
-       if (php_header(TSRMLS_C)) {
-               if (zend_is_compiling(TSRMLS_C)) {
-                       OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C);
-                       OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C);
-               } else if (zend_is_executing(TSRMLS_C)) {
-                       OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C);
-                       OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C);
+               
+               /* pass output along */
+               if (context.out.data && context.out.used && !discard) {
+                       php_output_write(context.out.data, context.out.used);
                }
+               
+               /* destroy the handler (after write!) */
+               php_output_handler_free(&orphan);
+               php_output_context_dtor(&context);
+               
+               return 1;
+       }
+}
+/* }}} */
 
-               OG(php_body_write) = php_ub_body_write_no_header;
-               result = php_ub_body_write_no_header(str, str_length TSRMLS_CC);
+/* {{{ static SUCCESS|FAILURE php_output_handler_compat_func(void *ctx, int op, const char *in, size_t in_len, char **out, size_t *out_len TSRMLS_DC)
+       php_output_handler_context_func_t for php_output_handler_func_t output handlers */
+static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context)
+{
+       php_output_handler_func_t func = *(php_output_handler_func_t *) handler_context;
+       PHP_OUTPUT_TSRMLS(output_context);
+       
+       if (func) {
+               func(output_context->in.data, output_context->in.used, &output_context->out.data, &output_context->out.used, output_context->op TSRMLS_CC);
+               output_context->out.free = 1;
+               return SUCCESS;
        }
+       return FAILURE;
+}
+/* }}} */
+
+/* {{{ static SUCCESS|FAILURE php_output_handler_default_func(void *ctx, int op, const char *in, size_t in_len, char **out, size_t *out_len TSRMLS_DC)
+       Default output handler */
+static int php_output_handler_default_func(void **handler_context, php_output_context *output_context)
+{
+       output_context->out.data = output_context->in.data;
+       output_context->out.used = output_context->in.used;
+       output_context->out.free = output_context->in.free;
+       output_context->in.data = NULL;
+       output_context->in.used = 0;
+       return SUCCESS;
+}
+/* }}} */
 
-       return result;
+/* {{{ static SUCCESS|FAILURE php_output_handler_devnull_func(void *ctx, int op, const char *in, size_t in_len, char **out, size_t *out_len TSRMLS_DC)
+       Null output handler */
+static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context)
+{
+       return SUCCESS;
 }
 /* }}} */
 
 /*
- * HEAD support
+ * 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). */
 PHP_FUNCTION(ob_start)
 {
-       zval *output_handler=NULL;
-       long chunk_size=0;
-       zend_bool erase=1;
-       int argc = ZEND_NUM_ARGS();
+       zval *output_handler = NULL;
+       long chunk_size = 0;
+       long flags = PHP_OUTPUT_HANDLER_CLEANABLE|PHP_OUTPUT_HANDLER_REMOVABLE;
        
-       if (zend_parse_parameters(argc TSRMLS_CC, "|zlb", &output_handler, &chunk_size, &erase) == FAILURE) {
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zlb", &output_handler, &chunk_size, &flags)) {
                RETURN_FALSE;
        }
-
-       if (chunk_size < 0)
+       if (chunk_size < 0) {
                chunk_size = 0;
+       }
        
-       if (php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC)==FAILURE) {
+       if (SUCCESS != php_output_start_user(output_handler, chunk_size, flags)) {
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to create buffer.");
                RETURN_FALSE;
        }
        RETURN_TRUE;
@@ -820,16 +1228,16 @@ PHP_FUNCTION(ob_start)
    Flush (send) contents of the output buffer. The last buffer content is sent to next buffer */
 PHP_FUNCTION(ob_flush)
 {
-       if (ZEND_NUM_ARGS() != 0) {
+       if (ZEND_NUM_ARGS()) {
                ZEND_WRONG_PARAM_COUNT();
        }
-
-       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.");
                RETURN_FALSE;
        }
        
-       php_end_ob_buffer(1, 1 TSRMLS_CC);
+       php_output_flush();
        RETURN_TRUE;
 }
 /* }}} */
@@ -839,21 +1247,18 @@ PHP_FUNCTION(ob_flush)
    Clean (delete) the current output buffer */
 PHP_FUNCTION(ob_clean)
 {
-       if (ZEND_NUM_ARGS() != 0) {
+       if (ZEND_NUM_ARGS()) {
                ZEND_WRONG_PARAM_COUNT();
        }
        
-       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.");
                RETURN_FALSE;
        }
-
-       if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
-               php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active_ob_buffer).handler_name);
+       if (SUCCESS != php_output_clean()) {
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active)->name);
                RETURN_FALSE;
        }
-       
-       php_end_ob_buffer(0, 1 TSRMLS_CC);
        RETURN_TRUE;
 }
 /* }}} */
@@ -862,20 +1267,18 @@ PHP_FUNCTION(ob_clean)
    Flush (send) the output buffer, and delete current output buffer */
 PHP_FUNCTION(ob_end_flush)
 {
-       if (ZEND_NUM_ARGS() != 0) {
+       if (ZEND_NUM_ARGS()) {
                ZEND_WRONG_PARAM_COUNT();
        }
        
-       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.");
                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);
+       if (SUCCESS != php_output_end()) {
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active)->name);
                RETURN_FALSE;
        }
-       
-       php_end_ob_buffer(1, 0 TSRMLS_CC);
        RETURN_TRUE;
 }
 /* }}} */
@@ -884,20 +1287,18 @@ PHP_FUNCTION(ob_end_flush)
    Clean the output buffer, and delete current output buffer */
 PHP_FUNCTION(ob_end_clean)
 {
-       if (ZEND_NUM_ARGS() != 0) {
+       if (ZEND_NUM_ARGS()) {
                ZEND_WRONG_PARAM_COUNT();
        }
-               
-       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.");
                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);
+       if (SUCCESS != php_output_discard()) {
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active)->name);
                RETURN_FALSE;
        }
-       
-       php_end_ob_buffer(0, 0 TSRMLS_CC);
        RETURN_TRUE;
 }
 /* }}} */
@@ -906,25 +1307,17 @@ PHP_FUNCTION(ob_end_clean)
    Get current buffer contents, flush (send) the output buffer, and delete current output buffer */
 PHP_FUNCTION(ob_get_flush)
 {
-       if (ZEND_NUM_ARGS() != 0) {
+       if (ZEND_NUM_ARGS()) {
                ZEND_WRONG_PARAM_COUNT();
        }
-
-       /* get contents */
-       if (php_ob_get_buffer(return_value TSRMLS_CC)==FAILURE) {
-               RETURN_FALSE;
-       }
-       /* error checks */
-       if (!OG(ob_nesting_level)) {
+       
+       if (SUCCESS != php_output_get_contents(return_value)) {
                php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush.");
                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);
-               RETURN_FALSE;
+       if (SUCCESS != php_output_end()) {
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active)->name);
        }
-       /* flush */
-       php_end_ob_buffer(1, 0 TSRMLS_CC);
 }
 /* }}} */
 
@@ -932,24 +1325,17 @@ PHP_FUNCTION(ob_get_flush)
    Get current buffer contents and delete current output buffer */
 PHP_FUNCTION(ob_get_clean)
 {
-       if (ZEND_NUM_ARGS() != 0)
-                       ZEND_WRONG_PARAM_COUNT();
-               
-       /* get contents */
-       if (php_ob_get_buffer(return_value TSRMLS_CC)==FAILURE) {
-               RETURN_FALSE;
+       if (ZEND_NUM_ARGS()) {
+               ZEND_WRONG_PARAM_COUNT();
        }
-       /* error checks */
-       if (!OG(ob_nesting_level)) {
+       
+       if (SUCCESS != php_output_get_contents(return_value)) {
                php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete.");
                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);
-               RETURN_FALSE;
+       if (SUCCESS != php_output_discard()) {
+               php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s.", OG(active)->name);
        }
-       /* delete buffer */
-       php_end_ob_buffer(0, 0 TSRMLS_CC);
 }
 /* }}} */
 
@@ -957,11 +1343,10 @@ PHP_FUNCTION(ob_get_clean)
    Return the contents of the output buffer */
 PHP_FUNCTION(ob_get_contents)
 {
-       if (ZEND_NUM_ARGS() != 0) {
+       if (ZEND_NUM_ARGS()) {
                ZEND_WRONG_PARAM_COUNT();
        }
-               
-       if (php_ob_get_buffer(return_value TSRMLS_CC)==FAILURE) {
+       if (SUCCESS != php_output_get_contents(return_value)) {
                RETURN_FALSE;
        }
 }
@@ -971,11 +1356,10 @@ PHP_FUNCTION(ob_get_contents)
    Return the nesting level of the output buffer */
 PHP_FUNCTION(ob_get_level)
 {
-       if (ZEND_NUM_ARGS() != 0) {
+       if (ZEND_NUM_ARGS()) {
                ZEND_WRONG_PARAM_COUNT();
        }
-               
-       RETURN_LONG (OG(ob_nesting_level));
+       RETURN_LONG(php_output_get_level());
 }
 /* }}} */
 
@@ -983,130 +1367,66 @@ PHP_FUNCTION(ob_get_level)
    Return the length of the output buffer */
 PHP_FUNCTION(ob_get_length)
 {
-       if (ZEND_NUM_ARGS() != 0) {
+       if (ZEND_NUM_ARGS()) {
                ZEND_WRONG_PARAM_COUNT();
        }
-               
-       if (php_ob_get_length(return_value TSRMLS_CC)==FAILURE) {
+       if (SUCCESS != php_output_get_length(return_value)) {
                RETURN_FALSE;
        }
 }
 /* }}} */
 
-/* {{{ int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result) */
-static int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result) 
+/* {{{ proto false|array ob_list_handlers()
+ *  List all output_buffers in an array 
+ */
+PHP_FUNCTION(ob_list_handlers)
 {
-       zval *elem;
-       TSRMLS_FETCH();
-
-       MAKE_STD_ZVAL(elem);
-       array_init(elem);
-
-       add_assoc_long(elem, "chunk_size", ob_buffer->chunk_size);
-       if (!ob_buffer->chunk_size) {
-               add_assoc_long(elem, "size", ob_buffer->size);
-               add_assoc_long(elem, "block_size", ob_buffer->block_size);
-       }
-       if (ob_buffer->internal_output_handler) {
-               add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_INTERNAL);
-               add_assoc_long(elem, "buffer_size", ob_buffer->internal_output_handler_buffer_size);
+       if (ZEND_NUM_ARGS()) {
+               ZEND_WRONG_PARAM_COUNT();
        }
-       else {
-               add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_USER);
+       if (!OG(active)) {
+               RETURN_FALSE;
        }
-       add_assoc_long(elem, "status", ob_buffer->status);
-       add_assoc_text(elem, "name", ob_buffer->handler_name, 1);
-       add_assoc_bool(elem, "del", ob_buffer->erase);
-       add_next_index_zval(result, elem);
-
-       return SUCCESS;
+       
+       array_init(return_value);
+       zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_list, return_value);
 }
 /* }}} */
 
-
 /* {{{ proto false|array ob_get_status([bool full_status])
    Return the status of the active or all output buffers */
 PHP_FUNCTION(ob_get_status)
 {
-       int argc = ZEND_NUM_ARGS();
        zend_bool full_status = 0;
        
-       if (zend_parse_parameters(argc TSRMLS_CC, "|b", &full_status) == FAILURE )
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &full_status)) {
                RETURN_FALSE;
+       }
+       if (!OG(active)) {
+               RETURN_FALSE;
+       }
        
        array_init(return_value);
-
        if (full_status) {
-               if (OG(ob_nesting_level)>1) {
-                       zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *elem, void *))php_ob_buffer_status, return_value);
-               }
-               if (OG(ob_nesting_level)>0 && php_ob_buffer_status(&OG(active_ob_buffer), return_value)==FAILURE) {
-                       RETURN_FALSE;
-               }
-       } else if (OG(ob_nesting_level)>0) {
-               add_assoc_long(return_value, "level", OG(ob_nesting_level));
-               if (OG(active_ob_buffer).internal_output_handler) {
-                       add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_INTERNAL);
-               } else {
-                       add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_USER);
-               }
-               add_assoc_long(return_value, "status", OG(active_ob_buffer).status);
-               add_assoc_text(return_value, "name", OG(active_ob_buffer).handler_name, 1);
-               add_assoc_bool(return_value, "del", OG(active_ob_buffer).erase);
+               zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_status, return_value);
+       } else {
+               php_output_handler_status(OG(active), return_value);
        }
 }
 /* }}} */
 
-
 /* {{{ proto void ob_implicit_flush([int flag])
    Turn implicit flush on/off and is equivalent to calling flush() after every output call */
 PHP_FUNCTION(ob_implicit_flush)
 {
-       zval **zv_flag;
-       int flag;
-
-       switch(ZEND_NUM_ARGS()) {
-               case 0:
-                       flag = 1;
-                       break;
-               case 1:
-                       if (zend_get_parameters_ex(1, &zv_flag)==FAILURE) {
-                               RETURN_FALSE;
-                       }
-                       convert_to_long_ex(zv_flag);
-                       flag = Z_LVAL_PP(zv_flag);
-                       break;
-               default:
-                       ZEND_WRONG_PARAM_COUNT();
-                       break;
-       }
-       if (flag) {
-               php_start_implicit_flush(TSRMLS_C);
-       } else {
-               php_end_implicit_flush(TSRMLS_C);
+       long flag = 1;
+       
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flag)) {
+               php_output_set_implicit_flush(flag);
        }
 }
 /* }}} */
 
-
-/* {{{ char *php_get_output_start_filename(TSRMLS_D)
-   Return filename start output something */
-PHPAPI char *php_get_output_start_filename(TSRMLS_D)
-{
-       return OG(output_start_filename);
-}
-/* }}} */
-
-
-/* {{{ char *php_get_output_start_lineno(TSRMLS_D)
-   Return line number start output something */
-PHPAPI int php_get_output_start_lineno(TSRMLS_D)
-{
-       return OG(output_start_lineno);
-}
-/* }}} */
-
-
 /* {{{ proto bool output_reset_rewrite_vars(void)
    Reset(clear) URL rewriter values */
 PHP_FUNCTION(output_reset_rewrite_vars)
@@ -1119,7 +1439,6 @@ PHP_FUNCTION(output_reset_rewrite_vars)
 }
 /* }}} */
 
-
 /* {{{ proto bool output_add_rewrite_var(string name, string value)
    Add URL rewriter values */
 PHP_FUNCTION(output_add_rewrite_var)
index 9a543ac53ad094bb76365558ba452334a79dda1e..a0a475adbdf60c90c629b6f2185a46fe873dc621 100644 (file)
@@ -373,20 +373,7 @@ END_EXTERN_C()
 
 /* Output support */
 #include "main/php_output.h"
-#define PHPWRITE(str, str_len)         php_body_write((str), (str_len) TSRMLS_CC)
-#define PUTS(str)                                      do {                    \
-       const char *__str = (str);                                              \
-       php_body_write(__str, strlen(__str) TSRMLS_CC); \
-} while (0)
-
-#define PUTC(c)                                                (php_body_write(&(c), 1 TSRMLS_CC), (c))
-#define PHPWRITE_H(str, str_len)       php_header_write((str), (str_len) TSRMLS_CC)
-#define PUTS_H(str)                                    do {                            \
-       const char *__str = (str);                                                      \
-       php_header_write(__str, strlen(__str) TSRMLS_CC);       \
-} while (0)
-
-#define PUTC_H(c)                                      (php_header_write(&(c), 1 TSRMLS_CC), (c))
+
 
 #ifdef ZTS
 #define VIRTUAL_DIR
index 579cae503ce826b3018c34488fd74980a0c55e64..d7488f82160463a9c0027929b84516ec33654a62 100644 (file)
@@ -12,7 +12,7 @@
    | obtain it through the world-wide-web, please send a note to          |
    | license@php.net so we can mail you a copy immediately.               |
    +----------------------------------------------------------------------+
-   | Author: Zeev Suraski <zeev@zend.com>                                 |
+   | Author: Michael Wallner <mike@php.net>                               |
    +----------------------------------------------------------------------+
 */
 
 #ifndef PHP_OUTPUT_H
 #define PHP_OUTPUT_H
 
+#define PHP_OUTPUT_NEWAPI 1
+
+/* handler ops */
+#define PHP_OUTPUT_HANDLER_WRITE       0x00    /* standard passthru */
+#define PHP_OUTPUT_HANDLER_START       0x01    /* start */
+#define PHP_OUTPUT_HANDLER_CLEAN       0x02    /* restart */
+#define PHP_OUTPUT_HANDLER_FLUSH       0x04    /* pass along as much as possible */
+#define PHP_OUTPUT_HANDLER_FINAL       0x08    /* finalize */
+#define PHP_OUTPUT_HANDLER_CONT                PHP_OUTPUT_HANDLER_WRITE
+#define PHP_OUTPUT_HANDLER_END         PHP_OUTPUT_HANDLER_FINAL
+
+/* handler types */
+#define PHP_OUTPUT_HANDLER_INTERNAL            0x0000
+#define PHP_OUTPUT_HANDLER_USER                        0x0001
+
+/* handler ability flags */
+#define PHP_OUTPUT_HANDLER_CLEANABLE   0x0010
+#define PHP_OUTPUT_HANDLER_REMOVABLE   0x0020
+#define PHP_OUTPUT_HANDLER_STDFLAGS            0x0030
+
+/* handler status flags */
+#define PHP_OUTPUT_HANDLER_STARTED             0x1000
+#define PHP_OUTPUT_HANDLER_DISABLED            0x2000
+
+/* handler op return values */
+#define PHP_OUTPUT_HANDLER_FAILURE             0
+#define PHP_OUTPUT_HANDLER_SUCCESS             1
+#define PHP_OUTPUT_HANDLER_NO_DATA             2
+
+/* real global flags */
+#define PHP_OUTPUT_IMPLICITFLUSH               0x01
+#define PHP_OUTPUT_DISABLED                            0x02
+/* supplementary flags for php_output_get_status() */
+#define PHP_OUTPUT_ACTIVE                              0x10
+#define PHP_OUTPUT_LOCKED                              0x20
+
+/* handler hooks */
+#define PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ       1
+#define PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS      2
+#define PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE      3
+#define PHP_OUTPUT_HANDLER_HOOK_DISABLE                4
+
+#define PHP_OUTPUT_HANDLER_INITBUF_SIZE(s) \
+( (s) ? \
+       (s) + PHP_OUTPUT_HANDLER_ALIGNTO_SIZE - ((s) % (PHP_OUTPUT_HANDLER_ALIGNTO_SIZE >> 2)) : \
+       PHP_OUTPUT_HANDLER_DEFAULT_SIZE \
+)
+#define PHP_OUTPUT_HANDLER_ALIGNTO_SIZE                0x1000
+#define PHP_OUTPUT_HANDLER_DEFAULT_SIZE                0x4000
+#define PHP_OUTPUT_HANDLER_DEFAULT_NAME                "default output handler"
+#define PHP_OUTPUT_HANDLER_DEVNULL_NAME                "null output handler"
+
+PHPAPI zval php_output_handler_default;
+PHPAPI zval php_output_handler_devnull;
+
+typedef struct _php_output_buffer {
+       char *data;
+       size_t size;
+       size_t used;
+       uint free:1;
+       uint _res:31;
+} php_output_buffer;
+
+typedef struct _php_output_context {
+       int op;
+       php_output_buffer in;
+       php_output_buffer out;
+#ifdef ZTS
+       void ***tsrm_ls;
+#endif
+} php_output_context;
+
+#define PHP_OUTPUT_TSRMLS(ctx) TSRMLS_FETCH_FROM_CTX((ctx)->tsrm_ls)
+
+/* old-style, stateless callback */
 typedef void (*php_output_handler_func_t)(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC);
+/* new-style, opaque context callback */
+typedef int (*php_output_handler_context_func_t)(void **handler_context, php_output_context *output_context);
+/* conflict check callback */
+typedef int (*php_output_handler_conflict_check_t)(zval *handler_name TSRMLS_DC);
+
+typedef struct _php_output_handler {
+       zval *name;
+       int flags;
+       int level;
+       size_t size;
+       php_output_buffer buffer;
+       
+       void *opaq;
+       void (*dtor)(void *opaq TSRMLS_DC);
+       
+       union {
+               zval *user;
+               php_output_handler_context_func_t internal;
+       };
+} php_output_handler;
+
+ZEND_BEGIN_MODULE_GLOBALS(output)
+       int flags;
+       zend_stack handlers;
+       php_output_handler *active;
+       php_output_handler *running;
+       char *output_start_filename;
+       int output_start_lineno;
+       zval *default_output_handler_name;
+       zval *devnull_output_handler_name;
+ZEND_END_MODULE_GLOBALS(output);
+
+/* there should not be a need to use OG() from outside of output.c */
+#ifdef ZTS
+#define OG(v) TSRMG(output_globals_id, zend_output_globals *, v)
+#else
+#define OG(v) (output_globals.v)
+#endif
+
+/* convenience macros */
+#define PHPWRITE(str, str_len)         php_output_write((str), (str_len))
+#define PHPWRITE_H(str, str_len)       php_output_write_unbuffered((str), (str_len))
+
+#define PUTC(c)                                                (php_output_write(&(c), 1), (c))
+#define PUTC_H(c)                                      (php_output_write_unbuffered(&(c), 1), (c))
+
+#define PUTS(str)                                      do {                            \
+       const char *__str = (str);                                                      \
+       php_output_write(__str, strlen(__str));                         \
+} while (0)
+#define PUTS_H(str)                                    do {                            \
+       const char *__str = (str);                                                      \
+       php_output_write_unbuffered(__str, strlen(__str));      \
+} while (0)
+
 
 BEGIN_EXTERN_C()
+#define php_output_tearup() \
+       php_output_startup(); \
+       php_output_activate()
+#define php_output_teardown() \
+       php_output_end_all(); \
+       php_output_deactivate(); \
+       php_output_shutdown()
+
+/* MINIT */
 PHPAPI void php_output_startup(void);
-PHPAPI void php_output_activate(TSRMLS_D);
-PHPAPI void php_output_set_status(zend_bool status TSRMLS_DC);
-PHPAPI void php_output_register_constants(TSRMLS_D);
-PHPAPI int  php_default_output_func(const char *str, uint str_len TSRMLS_DC);
-PHPAPI int  php_ub_body_write(const char *str, uint str_length TSRMLS_DC);
-PHPAPI int  php_ub_body_write_no_header(const char *str, uint str_length TSRMLS_DC);
-PHPAPI int  php_body_write(const char *str, uint str_length TSRMLS_DC);
-PHPAPI int  php_header_write(const char *str, uint str_length TSRMLS_DC);
-PHPAPI int php_start_ob_buffer(zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC);
-PHPAPI int php_start_ob_buffer_named(const char *output_handler_name, uint chunk_size, zend_bool erase TSRMLS_DC);
-PHPAPI void php_end_ob_buffer(zend_bool send_buffer, zend_bool just_flush TSRMLS_DC);
-PHPAPI void php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC);
-PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC);
-PHPAPI int php_ob_get_length(zval *p TSRMLS_DC);
-PHPAPI void php_start_implicit_flush(TSRMLS_D);
-PHPAPI void php_end_implicit_flush(TSRMLS_D);
-PHPAPI char *php_get_output_start_filename(TSRMLS_D);
-PHPAPI int php_get_output_start_lineno(TSRMLS_D);
-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);
-PHPAPI int php_ob_handler_used(char *handler_name TSRMLS_DC);
-PHPAPI int php_ob_init_conflict(char *handler_new, char *handler_set TSRMLS_DC);
-PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC);
-PHPAPI int php_ob_get_length(zval *p TSRMLS_DC);
+/* MSHUTDOWN */
+PHPAPI void php_output_shutdown(void);
+
+#define php_output_register_constants() _php_output_register_constants(TSRMLS_C)
+PHPAPI void _php_output_register_constants(TSRMLS_D);
+
+/* RINIT */
+#define php_output_activate() _php_output_activate(TSRMLS_C)
+PHPAPI int _php_output_activate(TSRMLS_D);
+/* RSHUTDOWN */
+#define php_output_deactivate() _php_output_deactivate(TSRMLS_C)
+PHPAPI void _php_output_deactivate(TSRMLS_D);
+
+
+#define php_output_get_default_handler_name() _php_output_get_default_handler_name(TSRMLS_C)
+PHPAPI zval *_php_output_get_default_handler_name(TSRMLS_D);
+
+#define php_output_get_devnull_handler_name() _php_output_get_devnull_handler_name(TSRMLS_C)
+PHPAPI zval *_php_output_get_devnull_handler_name(TSRMLS_D);
+
+
+#define php_output_set_status(s) _php_output_set_status((s) TSRMLS_CC)
+PHPAPI void _php_output_set_status(int status TSRMLS_DC);
+
+#define php_output_get_status() _php_output_get_status(TSRMLS_C)
+PHPAPI int _php_output_get_status(TSRMLS_D);
+
+#define php_output_write_unbuffered(s, l) _php_output_write_unbuffered((s), (l) TSRMLS_CC)
+PHPAPI int _php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC);
+
+#define php_output_write(s, l) _php_output_write((s), (l) TSRMLS_CC)
+PHPAPI int _php_output_write(const char *str, size_t len TSRMLS_DC);
+
+#define php_output_flush() _php_output_flush(TSRMLS_C)
+PHPAPI void _php_output_flush(TSRMLS_D);
+
+#define php_output_flush_all() _php_output_flush_all(TSRMLS_C)
+PHPAPI void _php_output_flush_all(TSRMLS_D);
+
+#define php_output_clean() _php_output_clean(TSRMLS_C)
+PHPAPI int _php_output_clean(TSRMLS_D);
+
+#define php_output_clean_all() _php_output_clean_all(TSRMLS_C)
+PHPAPI void _php_output_clean_all(TSRMLS_D);
+
+#define php_output_end() _php_output_end(TSRMLS_C)
+PHPAPI int _php_output_end(TSRMLS_D);
+
+#define php_output_end_all() _php_output_end_all(TSRMLS_C)
+PHPAPI void _php_output_end_all(TSRMLS_D);
+
+#define php_output_discard() _php_output_discard(TSRMLS_C)
+PHPAPI int _php_output_discard(TSRMLS_D);
+
+#define php_output_discard_all() _php_output_discard_all(TSRMLS_C)
+PHPAPI void _php_output_discard_all(TSRMLS_D);
+
+
+#define php_output_get_contents(p) _php_output_get_contents((p) TSRMLS_CC)
+PHPAPI int _php_output_get_contents(zval *p TSRMLS_DC);
+
+#define php_output_get_length(p) _php_output_get_length((p) TSRMLS_CC)
+PHPAPI int _php_output_get_length(zval *TSRMLS_DC);
+
+#define php_output_get_level() _php_output_get_level(TSRMLS_C)
+PHPAPI int _php_output_get_level(TSRMLS_D);
+
+
+#define php_output_start_default() _php_output_start_default(TSRMLS_C)
+PHPAPI int _php_output_start_default(TSRMLS_D);
+
+#define php_output_start_devnull() _php_output_start_devnull(TSRMLS_C)
+PHPAPI int _php_output_start_devnull(TSRMLS_D);
+
+#define php_output_start_user(h, s, f) _php_output_start_user((h), (s), (f) TSRMLS_CC)
+PHPAPI int _php_output_start_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC);
+
+#define php_output_start_internal(n, h, s, f) _php_output_start_internal((n), (h), (s), (f) TSRMLS_CC)
+PHPAPI int _php_output_start_internal(zval *name, php_output_handler_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC);
+
+#define php_output_handler_create_user(h, s, f) _php_output_handler_create_user((h), (s), (f) TSRMLS_CC)
+PHPAPI php_output_handler *_php_output_handler_create_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC);
+
+#define php_output_handler_create_internal(n, h, s, f) _php_output_handler_create_internal((n), (h), (s), (f) TSRMLS_CC)
+PHPAPI 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);
+
+#define php_output_handler_set_context(h, c, d) _php_output_handler_set_context((h), (c), (d) TSRMLS_CC)
+PHPAPI void _php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC);
+
+#define php_output_handler_start(h) _php_output_handler_start((h) TSRMLS_CC)
+PHPAPI int _php_output_handler_start(php_output_handler *handler TSRMLS_DC);
+
+#define php_output_handler_started(n) _php_output_handler_started((n) TSRMLS_CC)
+PHPAPI int _php_output_handler_started(zval *name TSRMLS_DC);
+
+#define php_output_handler_hook(t, a) _php_output_handler_hook((t), (a) TSRMLS_CC)
+PHPAPI int _php_output_handler_hook(int type, void *arg TSRMLS_DC);
+
+#define php_output_handler_dtor(h) _php_output_handler_dtor((h) TSRMLS_CC)
+PHPAPI void _php_output_handler_dtor(php_output_handler *handler TSRMLS_DC);
+
+#define php_output_handler_free(h) _php_output_handler_free((h) TSRMLS_CC)
+PHPAPI void _php_output_handler_free(php_output_handler **handler TSRMLS_DC);
+
+
+#define php_output_set_implicit_flush(f) _php_output_set_implicit_flush((f) TSRMLS_CC)
+PHPAPI void _php_output_set_implicit_flush(int flush TSRMLS_DC);
+
+#define php_output_get_start_filename() _php_output_get_start_filename(TSRMLS_C)
+PHPAPI char *_php_output_get_start_filename(TSRMLS_D);
+
+#define php_output_get_start_lineno() _php_output_get_start_lineno(TSRMLS_C)
+PHPAPI int _php_output_get_start_lineno(TSRMLS_D);
+
+
+#define php_output_handler_conflict(n, s) _php_output_handler_conflict((n), (s) TSRMLS_CC)
+PHPAPI int _php_output_handler_conflict(zval *handler_new, zval *handler_set TSRMLS_DC);
+
+#define php_output_handler_conflict_register(n, f) _php_output_handler_conflict_register((n), (f) TSRMLS_CC)
+PHPAPI int _php_output_handler_conflict_register(zval *handler_name, php_output_handler_conflict_check_t check_func TSRMLS_DC);
+
+#define php_output_handler_reverse_conflict_register(n, f) _php_output_handler_reverse_conflict_register((n), (f) TSRMLS_CC)
+PHPAPI int _php_output_handler_reverse_conflict_register(zval *handler_name, php_output_handler_conflict_check_t check_func TSRMLS_DC);
+
+#define php_output_handler_alias(n) _php_output_handler_alias((n) TSRMLS_CC)
+PHPAPI php_output_handler_context_func_t *_php_output_handler_alias(zval *handler_name TSRMLS_DC);
+
+#define php_output_handler_alias_register(n, f) _php_output_handler_alias_register((n), (f) TSRMLS_CC)
+PHPAPI int _php_output_handler_alias_register(zval *handler_name, php_output_handler_context_func_t func TSRMLS_DC);
+
 END_EXTERN_C()
 
+
 PHP_FUNCTION(ob_start);
 PHP_FUNCTION(ob_flush);
 PHP_FUNCTION(ob_clean);
@@ -64,51 +311,16 @@ PHP_FUNCTION(ob_get_status);
 PHP_FUNCTION(ob_implicit_flush);
 PHP_FUNCTION(ob_list_handlers);
 
-typedef struct _php_ob_buffer {
-       char *buffer;
-       uint size;
-       uint text_length;
-       int block_size;
-       uint chunk_size;
-       int status;
-       zval *output_handler;
-       php_output_handler_func_t internal_output_handler;
-       char *internal_output_handler_buffer;
-       uint internal_output_handler_buffer_size;
-       zstr handler_name;
-       zend_bool erase;
-} php_ob_buffer;
-
-typedef struct _php_output_globals {
-       int (*php_body_write)(const char *str, uint str_length TSRMLS_DC);              /* string output */
-       int (*php_header_write)(const char *str, uint str_length TSRMLS_DC);    /* unbuffer string output */
-       php_ob_buffer active_ob_buffer;
-       unsigned char implicit_flush;
-       char *output_start_filename;
-       int output_start_lineno;
-       zend_stack ob_buffers;
-       int ob_nesting_level;
-       zend_bool ob_lock;
-       zend_bool disable_output;
-} php_output_globals;
-
-#ifdef ZTS
-#define OG(v) TSRMG(output_globals_id, php_output_globals *, v)
-ZEND_API extern int output_globals_id;
-#else
-#define OG(v) (output_globals.v)
-ZEND_API extern php_output_globals output_globals;
-#endif
-
-#define PHP_OUTPUT_HANDLER_START               (1<<0)
-#define PHP_OUTPUT_HANDLER_CONT                        (1<<1)
-#define PHP_OUTPUT_HANDLER_END                 (1<<2)
-
-#define PHP_OUTPUT_HANDLER_INTERNAL     0
-#define PHP_OUTPUT_HANDLER_USER        1
-
 PHP_FUNCTION(output_add_rewrite_var);
 PHP_FUNCTION(output_reset_rewrite_vars);
 
+#endif
 
-#endif /* PHP_OUTPUT_H */
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
index d2f2cde2efdcd56f8f6ea3a870d590d69b8a2609..e9b853a4ad0266a54659c0e807ad58a09554fe2e 100644 (file)
@@ -315,7 +315,7 @@ static void php_apache_request_shutdown(void *dummy)
 {
        TSRMLS_FETCH();
 
-       php_output_set_status(0 TSRMLS_CC);
+       php_output_set_status(PHP_OUTPUT_DISABLED);
        if (AP(in_request)) {
                AP(in_request) = 0;
                php_request_shutdown(dummy);
index bbe63e7e5251cb6eef23481d63b9455f03b5e83b..864b6c2f5093e1db8bbee173385b20fafd35e2e4 100644 (file)
@@ -329,7 +329,7 @@ PHP_FUNCTION(virtual)
                RETURN_FALSE;
        }
 
-       php_end_ob_buffers(1 TSRMLS_CC);
+       php_output_end_all();
        php_header(TSRMLS_C);
 
        if (run_sub_req(rr)) {
index 1e9b60abeff59b17e59a16030d69d713be73d72d..517ef6df11f3d5943c2cdb6b6a0aee6c6860074b 100644 (file)
@@ -93,7 +93,7 @@ PHP_FUNCTION(virtual)
        }
 
        /* Flush everything. */
-       php_end_ob_buffers(1 TSRMLS_CC);
+       php_output_end_all();
        php_header(TSRMLS_C);
 
        /* Ensure that the ap_r* layer for the main request is flushed, to
index c23dfc1d5ce29a4a1a9f49562c258400e48d8edf..558274d5da6bd80dba67e75bc321633011d24d87 100644 (file)
@@ -434,7 +434,7 @@ static void php_apache_request_shutdown(void *dummy)
 {
        TSRMLS_FETCH();
        AP(current_hook) = AP_CLEANUP;
-       php_output_set_status(0 TSRMLS_CC);
+       php_output_set_status(PHP_OUTPUT_DISABLED);
        SG(server_context) = NULL; /* The server context (request) is invalid by the time run_cleanups() is called */
        if(SG(sapi_started)) {
                php_request_shutdown(dummy);
index 0e9fc4d5f74b99191193d3ec16b6f8a92aa74446..da68b57225c3c0c923a37e8113b65f426ad0d05d 100644 (file)
@@ -1731,7 +1731,7 @@ PHP_FUNCTION(virtual)
                RETURN_FALSE;
        }
 
-       php_end_ob_buffers(1 TSRMLS_CC);
+       php_output_end_all();
        php_header(TSRMLS_C);
 
        if (run_sub_req(rr)) {
index 41b35818c1c1ecf9a426e8a79e5323ef80f4e8ae..5642c5bbf59b49c29f2272dec12f2c4f4c9273ed 100644 (file)
@@ -1266,11 +1266,10 @@ consult the installation file that came with this distribution, or visit \n\
                                case 'h':
                                case '?':
                                        no_headers = 1;
-                                       php_output_startup();
-                                       php_output_activate(TSRMLS_C);
+                                       php_output_tearup();
                                        SG(headers_sent) = 1;
                                        php_cgi_usage(argv[0]);
-                                       php_end_ob_buffers(1 TSRMLS_CC);
+                                       php_output_teardown();
                                        exit(1);
                                        break;
                        }
@@ -1299,11 +1298,10 @@ consult the installation file that came with this distribution, or visit \n\
                        if (!cgi && !fastcgi) {
                                if (cgi_sapi_module.php_ini_path_override && cgi_sapi_module.php_ini_ignore) {
                                        no_headers = 1;
-                                       php_output_startup();
-                                       php_output_activate(TSRMLS_C);
+                                       php_output_tearup();
                                        SG(headers_sent) = 1;
                                        php_printf("You cannot use both -n and -c switch. Use -h for help.\n");
-                                       php_end_ob_buffers(1 TSRMLS_CC);
+                                       php_output_teardown();
                                        exit(1);
                                }
 
@@ -1349,7 +1347,7 @@ consult the installation file that came with this distribution, or visit \n\
                                                                SG(request_info).no_headers = 1;
                                                        }
                                                        php_print_info(0xFFFFFFFF TSRMLS_CC);
-                                                       php_end_ob_buffers(1 TSRMLS_CC);
+                                                       php_output_teardown();
                                                        exit(0);
                                                        break;
 
@@ -1359,15 +1357,14 @@ consult the installation file that came with this distribution, or visit \n\
                                                        break;
 
                                                case 'm': /* list compiled in modules */
-                                                       php_output_startup();
-                                                       php_output_activate(TSRMLS_C);
+                                                       php_output_tearup();
                                                        SG(headers_sent) = 1;
                                                        php_printf("[PHP Modules]\n");
                                                        print_modules(TSRMLS_C);
                                                        php_printf("\n[Zend Modules]\n");
                                                        print_extensions(TSRMLS_C);
                                                        php_printf("\n");
-                                                       php_end_ob_buffers(1 TSRMLS_CC);
+                                                       php_output_teardown();
                                                        exit(0);
                                                        break;
 
@@ -1400,7 +1397,7 @@ consult the installation file that came with this distribution, or visit \n\
 #else
                                                        php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2006 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
 #endif
-                                                       php_end_ob_buffers(1 TSRMLS_CC);
+                                                       php_output_teardown();
                                                        exit(0);
                                                        break;
 
@@ -1562,7 +1559,7 @@ consult the installation file that came with this distribution, or visit \n\
                                        if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
                                                zend_strip(TSRMLS_C);
                                                fclose(file_handle.handle.fp);
-                                               php_end_ob_buffers(1 TSRMLS_CC);
+                                               php_output_teardown();
                                        }
                                        return SUCCESS;
                                        break;
@@ -1574,7 +1571,7 @@ consult the installation file that came with this distribution, or visit \n\
                                                        php_get_highlight_struct(&syntax_highlighter_ini);
                                                        zend_highlight(&syntax_highlighter_ini TSRMLS_CC);
                                                        fclose(file_handle.handle.fp);
-                                                       php_end_ob_buffers(1 TSRMLS_CC);
+                                                       php_output_teardown();
                                                }
                                                return SUCCESS;
                                        }
@@ -1585,6 +1582,7 @@ consult the installation file that came with this distribution, or visit \n\
                                        open_file_for_scanning(&file_handle TSRMLS_CC);
                                        zend_indent();
                                        fclose(file_handle.handle.fp);
+                                       php_output_teardown();
                                        return SUCCESS;
                                        break;
 #endif
index c4b05786d7939513592df7efdd60f4493067b2da..3deaab28046298a95e767b15bd7658308c4759ac 100644 (file)
@@ -719,7 +719,7 @@ int main(int argc, char *argv[])
                                        goto err;
                                }
                                php_cli_usage(argv[0]);
-                               php_end_ob_buffers(1 TSRMLS_CC);
+                               php_output_end_all();
                                exit_status=0;
                                goto out;
 
@@ -728,7 +728,7 @@ int main(int argc, char *argv[])
                                        goto err;
                                }
                                php_print_info(0xFFFFFFFF TSRMLS_CC);
-                               php_end_ob_buffers(1 TSRMLS_CC);
+                               php_output_end_all();
                                exit_status=0;
                                goto out;
 
@@ -741,7 +741,7 @@ int main(int argc, char *argv[])
                                php_printf("\n[Zend Modules]\n");
                                print_extensions(TSRMLS_C);
                                php_printf("\n");
-                               php_end_ob_buffers(1 TSRMLS_CC);
+                               php_output_end_all();
                                exit_status=0;
                                goto out;
 
@@ -763,7 +763,7 @@ int main(int argc, char *argv[])
 #endif
                                        get_zend_version()
                                );
-                               php_end_ob_buffers(1 TSRMLS_CC);
+                               php_output_end_all();
                                exit_status=0;
                                goto out;
 
index 17bb63e38b59b5b21c4cb2244b3ab1993d347105..9240c8e02bb10d9534eef85182d1051c920a30d2 100644 (file)
@@ -966,11 +966,10 @@ int main(int argc, char *argv[])
                while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
                        switch (c) {
                        case '?':
-                               php_output_startup();
-                               php_output_activate(TSRMLS_C);
+                               php_output_tearup();
                                SG(headers_sent) = 1;
                                php_milter_usage(argv[0]);
-                               php_end_ob_buffers(1 TSRMLS_CC);
+                               php_output_teardown();
                                exit(1);
                                break;
                        }
@@ -1021,11 +1020,10 @@ int main(int argc, char *argv[])
 
                        case 'h': /* help & quit */
                        case '?':
-                               php_output_startup();
-                               php_output_activate(TSRMLS_C);
+                               php_output_tearup();
                                SG(headers_sent) = 1;
                                php_milter_usage(argv[0]);
-                               php_end_ob_buffers(1 TSRMLS_CC);
+                               php_output_teardown();
                                exit(1);
                                break;
 
@@ -1045,7 +1043,7 @@ int main(int argc, char *argv[])
                                SG(headers_sent) = 1;
                                SG(request_info).no_headers = 1;
                                php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2006 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
-                               php_end_ob_buffers(1 TSRMLS_CC);
+                               php_output_teardown();
                                exit(1);
                                break;
 
index 566934e315865e90e48bdd693dd523191af6a687..454020713fe93c26e5e92a47a5bc8f9d1c0df092 100644 (file)
@@ -347,7 +347,7 @@ PHP_FUNCTION(nsapi_virtual)
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Sub-requests do not work with zlib.output_compression", (*uri)->value.str.val);
                RETURN_FALSE;
        } else {
-               php_end_ob_buffers(1 TSRMLS_CC);
+               php_output_end_all();
                php_header(TSRMLS_C);
 
                /* do the sub-request */
diff --git a/tests/output/ob_001.phpt b/tests/output/ob_001.phpt
new file mode 100644 (file)
index 0000000..09b5c22
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+output buffering - nothing
+--FILE--
+<?php
+echo "foo\n";
+?>
+--EXPECTF--
+foo
diff --git a/tests/output/ob_002.phpt b/tests/output/ob_002.phpt
new file mode 100644 (file)
index 0000000..94f515b
--- /dev/null
@@ -0,0 +1,9 @@
+--TEST--
+output buffering - ob_start
+--FILE--
+<?php
+ob_start();
+echo "foo\n";
+?>
+--EXPECT--
+foo
diff --git a/tests/output/ob_003.phpt b/tests/output/ob_003.phpt
new file mode 100644 (file)
index 0000000..988d197
--- /dev/null
@@ -0,0 +1,13 @@
+--TEST--
+output buffering - ob_flush
+--FILE--
+<?php
+ob_start();
+echo "foo\n";
+ob_flush();
+echo "bar\n";
+ob_flush();
+?>
+--EXPECT--
+foo
+bar
diff --git a/tests/output/ob_004.phpt b/tests/output/ob_004.phpt
new file mode 100644 (file)
index 0000000..a089a8c
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+output buffering - ob_clean
+--FILE--
+<?php
+ob_start();
+echo "foo\n";
+ob_clean();
+echo "bar\n";
+?>
+--EXPECT--
+bar
diff --git a/tests/output/ob_005.phpt b/tests/output/ob_005.phpt
new file mode 100644 (file)
index 0000000..bbe807d
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+output buffering - ob_end_clean
+--FILE--
+<?php
+ob_start();
+echo "foo\n";
+ob_start();
+echo "bar\n";
+ob_end_clean();
+echo "baz\n";
+?>
+--EXPECT--
+foo
+baz
diff --git a/tests/output/ob_006.phpt b/tests/output/ob_006.phpt
new file mode 100644 (file)
index 0000000..aec3cfc
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+output buffering - ob_end_flush
+--FILE--
+<?php
+ob_start();
+echo "foo\n";
+ob_end_flush();
+var_dump(ob_get_level());
+?>
+--EXPECT--
+foo
+int(0)
diff --git a/tests/output/ob_007.phpt b/tests/output/ob_007.phpt
new file mode 100644 (file)
index 0000000..059925c
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+output buffering - ob_get_clean
+--FILE--
+<?php
+ob_start();
+echo "foo\n";
+var_dump(ob_get_clean());
+?>
+--EXPECT--
+string(4) "foo
+"
diff --git a/tests/output/ob_008.phpt b/tests/output/ob_008.phpt
new file mode 100644 (file)
index 0000000..17a8081
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+output buffering - ob_get_contents
+--FILE--
+<?php
+ob_start();
+echo "foo\n";
+echo ob_get_contents();
+?>
+--EXPECT--
+foo
+foo
diff --git a/tests/output/ob_009.phpt b/tests/output/ob_009.phpt
new file mode 100644 (file)
index 0000000..80edb46
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+output buffering - ob_get_flush
+--FILE--
+<?php
+ob_start();
+echo "foo\n";
+var_dump(ob_get_flush());
+?>
+--EXPECT--
+foo
+string(4) "foo
+"
diff --git a/tests/output/ob_010.phpt b/tests/output/ob_010.phpt
new file mode 100644 (file)
index 0000000..f83af73
--- /dev/null
@@ -0,0 +1,13 @@
+--TEST--
+output buffering - fatalism
+--FILE--
+<?php
+function obh($s)
+{
+       print_r($s, 1);
+}
+ob_start("obh");
+echo "foo\n";
+?>
+--EXPECTF--
+Fatal error: print_r(): Cannot use output buffering in output buffering display handlers in %s/ob_010.php on line %d
diff --git a/tests/output/ob_011.phpt b/tests/output/ob_011.phpt
new file mode 100644 (file)
index 0000000..87e7be1
--- /dev/null
@@ -0,0 +1,13 @@
+--TEST--
+output buffering - fatalism
+--FILE--
+<?php
+function obh($s)
+{
+       return ob_get_flush();
+}
+ob_start("obh");
+echo "foo\n";
+?>
+--EXPECTF--
+Fatal error: ob_get_flush(): Cannot use output buffering in output buffering display handlers in %s/ob_011.php on line %d
diff --git a/tests/output/ob_012.phpt b/tests/output/ob_012.phpt
new file mode 100644 (file)
index 0000000..9e6e885
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+output buffering - multiple
+--FILE--
+<?php
+echo 0;
+       ob_start();
+               ob_start();
+                       ob_start();
+                               ob_start();
+                                       echo 1;
+                               ob_end_flush();
+                               echo 2;
+                       $ob = ob_get_clean();
+               echo 3;
+               ob_flush();
+               ob_end_clean();
+       echo 4;
+       ob_end_flush();
+echo $ob;
+?>
+--EXPECT--
+03412
diff --git a/tests/output/ob_013.phpt b/tests/output/ob_013.phpt
new file mode 100644 (file)
index 0000000..0a17972
--- /dev/null
@@ -0,0 +1,105 @@
+--TEST--
+output buffering - handlers/status
+--FILE--
+<?php
+function a($s){return $s;}
+function b($s){return $s;}
+function c($s){return $s;}
+function d($s){return $s;}
+
+ob_start();
+ob_start('a');
+ob_start('b');
+ob_start('c');
+ob_start('d');
+ob_start();
+
+echo "foo\n";
+
+ob_flush();
+ob_end_clean();
+ob_flush();
+
+print_r(ob_list_handlers());
+print_r(ob_get_status());
+print_r(ob_get_status(true));
+
+?>
+--EXPECT--
+foo
+Array
+(
+    [0] => default output handler
+    [1] => a
+    [2] => b
+    [3] => c
+    [4] => d
+)
+Array
+(
+    [name] => d
+    [type] => 1
+    [flags] => 4145
+    [level] => 4
+    [chunk_size] => 0
+    [buffer_size] => 16384
+    [buffer_used] => 96
+)
+Array
+(
+    [0] => Array
+        (
+            [name] => default output handler
+            [type] => 0
+            [flags] => 48
+            [level] => 0
+            [chunk_size] => 0
+            [buffer_size] => 16384
+            [buffer_used] => 0
+        )
+
+    [1] => Array
+        (
+            [name] => a
+            [type] => 1
+            [flags] => 49
+            [level] => 1
+            [chunk_size] => 0
+            [buffer_size] => 16384
+            [buffer_used] => 0
+        )
+
+    [2] => Array
+        (
+            [name] => b
+            [type] => 1
+            [flags] => 49
+            [level] => 2
+            [chunk_size] => 0
+            [buffer_size] => 16384
+            [buffer_used] => 0
+        )
+
+    [3] => Array
+        (
+            [name] => c
+            [type] => 1
+            [flags] => 49
+            [level] => 3
+            [chunk_size] => 0
+            [buffer_size] => 16384
+            [buffer_used] => 4
+        )
+
+    [4] => Array
+        (
+            [name] => d
+            [type] => 1
+            [flags] => 4145
+            [level] => 4
+            [chunk_size] => 0
+            [buffer_size] => 16384
+            [buffer_used] => 248
+        )
+
+)
diff --git a/tests/output/ob_014.phpt b/tests/output/ob_014.phpt
new file mode 100644 (file)
index 0000000..696c91a
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+output buffering - failure
+--FILE--
+<?php
+/*
+ * apparently the error handler cannot get the current function name on shutdown
+ */
+ob_start("str_rot13");
+echo "foo\n";
+?>
+--EXPECTF--
+foo
+
+Warning: Wrong parameter count for (null)() in %s on line %d
diff --git a/tests/output/ob_015.phpt b/tests/output/ob_015.phpt
new file mode 100644 (file)
index 0000000..53d3acd
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+output buffering - failure
+--FILE--
+<?php
+ob_start("str_rot13", 1);
+echo "foo\n";
+?>
+--EXPECTF--
+foo
+
+Warning: Wrong parameter count for str_rot13() in %s on line %d