Search code examples
phpsocketsoutput-buffering

detecting user abort with output buffering enabled in PHP


The Notes section in the function documentation of ignore_user_abort() suggest that PHP cannot detect that a user has aborted the request if no data is sent to the client. This is true for the function connection_status(), too. Unfortunately, I need to detect a user abort (an ajax request that can be cancelled) on a page which makes use of output buffering (and I cannot change that easily.)

Is there any other way of detecting a user abort except using the function connection_status()? Or any specific way to make that function return the correct value? PHP should actually know that the request was aborted, since a FIN packet is received from the client.

I have already tried analyzing the stream metadata of php://input, php://output, php://stdin and php://stdout after reading/writing data in a blocking and non-blocking manner before and after the connection was aborted, but that didn't provide any useful state changes.


Solution

  • Silly suggestion, but ... have you tried sending some data to the client, followed by a flush() before you start the output buffering? The only other solution I can think of is to escape the buffer(s), but I can imagine how it could be quite troublesome, as you said.

    Maybe a helper to assist with breaking the buffer ...

    function OBWanCallback($buffer)
    {
        if( OBWan::$isFinished )
        {
            // -- Actual callbacks go here ...
        }
    
       return $buffer;
    }
    
    OBWan::startbuffer('OBWanCallback');
    [ // -- Example functionality
        self::$callback = $callback;
        ob_start(self::$callback);
    ]
    
    // -- in some code far, far away ...
    
    OBWan::suspendbuffer();
    [ // -- Example functionality
        self::$buffercache = ob_get_clean();
    ]
    
    echo " ";
    flush();
    
    OBWan::resumebuffer();
    [ // -- Example functionality
        ob_start(self::$callback);
        echo self::$buffercache;
        self::$buffercache = "";
    ]
    
    // -- in some code far, far away ...
    
    OBWan::outputbuffer();
    [
        self::$isFinished = true;
        return ob_get_clean();
    ]
    

    with something to account for the depth of buffers you've implemented, if you have implemented depth.