PHP RFC: Continue output buffering despite aborted connection
- Version: 1.1
- Date: 2015-02-19
- Last-Modfied: 2015-02-20
- Author: Michael Wallner mike@php.net
- Status: Accepted
- First Published at: http://wiki.php.net/rfc/continue_ob
Introduction
The output buffering technique is used to speed up output, but also to capture intermediate output and use it elsewhere.
Often, particularly with functions which directly generate output, this is the only way to accumulate that data.
Current Status
As of PHP-5.4 and above, which might be a regression I introduced in the rewritten output layer.
ignore_user_abort = FALSE
The script ends as soon as an aborted connection is detected, either by an unbuffered write or an output exceeding its chunk size (which would result in an unbuffered write).
ignore_user_abort = TRUE
The script continues to run, but any already buffered output will be discarded after processing by any output handler, and more importantly, any output generated by the script is simply discarded, without ever hitting the output buffer or any output handler. Output buffers and handlers will just sit in the stack until the output layer is deactivated.
There are a few unimportant functions already broken now:
- phpinfo
- highlight_{file,string} with return_output=TRUE
- print_r, var_export with return_output=TRUE
- SoapServer::handle (unimportant because the connection is already broken)
Proposal
Let the output buffering functions stay usable after the connection was already aborted.
This will be effective only for scripts that do set ignore_user_abort, i.e. script execution will end when an aborted connection was detected and ignore_user_abort was not set, just as it used to.
ignore_user_abort = FALSE
Nothing changes.
ignore_user_abort = TRUE
The output buffer stack stays intact and usable. Data generated by a script will be passed to any output buffers, which will call respective output handlers to process the buffer.
Only now will any generated data of the output handler be discarded. That is, before the direct unbuffered write to the SAPI.
Bacon^WBenefits
Usage of ob_start([callback]) behaves the same, whether the connection was aborted or not. Still, you have to deliberately enable it with ignore_user_abort(TRUE).
Backward Incompatible Changes
Output handlers will be called when ignore_user_abort is TRUE.
Proposed PHP Version(s)
PHP 7.0
Unaffected PHP Functionality
Standard behavior will stay the same.
Vote
Vote will close on Sunday, March 22nd around 8:00UTC.
Implementation
diff --git a/main/output.c b/main/output.c index 22ac26a..a0a300b 100644 --- a/main/output.c +++ b/main/output.c @@ -242,9 +242,6 @@ PHPAPI int php_output_get_status(void) * Unbuffered write */ PHPAPI size_t php_output_write_unbuffered(const char *str, size_t len) { - if (OG(flags) & PHP_OUTPUT_DISABLED) { - return 0; - } if (OG(flags) & PHP_OUTPUT_ACTIVATED) { return sapi_module.ub_write(str, len); } @@ -256,13 +253,13 @@ PHPAPI size_t php_output_write_unbuffered(const char *str, size_t len) * Buffered write */ PHPAPI size_t php_output_write(const char *str, size_t len) { - if (OG(flags) & PHP_OUTPUT_DISABLED) { - return 0; - } if (OG(flags) & PHP_OUTPUT_ACTIVATED) { php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len); return len; } + if (OG(flags) & PHP_OUTPUT_DISABLED) { + return 0; + } return php_output_direct(str, len); } /* }}} */
References
A bug about the current behavior:
https://bugs.php.net/bug.php?id=67381
A bug about apparent pre-5.4 behavior:
https://bugs.php.net/bug.php?id=64152
Changelog
- 1.0 Proposed
- 1.1 Rewordings, list of broken internal functions