mike

Michael Wallner

Contents

PHP RFC: Continue output buffering despite aborted connection

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.

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

Versions

Version Changed Date
1.1 Rewordings, list of broken internal functions
1.0 Proposed

Votes

An option needs 50%+1 votes to win

Continue output buffering on aborted connection? (100% approved)
User Vote
aharvey Yes
davey Yes
eliw Yes
frozenfire Yes
jedibc Yes
kinncj Yes
lcobucci Yes
mbeccati Yes
ramsey Yes
rdohms Yes
rmf Yes
salathe Yes
stas Yes
yohgaki Yes
yunosh Yes