3

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.

soulmerge
  • 73,842
  • 19
  • 118
  • 155
  • Abort is abort. It aborts. You can't do stuff after an abort. – Lightness Races in Orbit Mar 11 '11 at 13:58
  • I'm afraid that sounds like unsatisfiable requirements. At the very least you would have to `ob_get_clean` the output buffer and transmit some fill bytes (spaces). And I'm not even sure the aborted request is detected that reliably even under mod_php. – mario Mar 11 '11 at 14:03
  • @tomalak - actually, you can! Take a look at register_shutdown_function()... you can trap here to identify user abort, timeout, normal completion, etc... and perform some additional code logic – Mark Baker Mar 11 '11 at 14:05
  • @MarkBaker: Meh. You broke my sardonic response. – Lightness Races in Orbit Mar 11 '11 at 14:05
  • @tomalak - perhaps... knowing about register_shutdown_function() might also help the OP consider alternative approaches to his problem – Mark Baker Mar 11 '11 at 14:07
  • What about [`register_shutdown_function()`](http://php.net/manual/en/function.register-shutdown-function.php)? – seriousdev Mar 11 '11 at 14:14
  • @sexyprout: I need to send data to the client, `register_shutdown_function()` wouldn't help. – soulmerge Mar 11 '11 at 14:20
  • @Tomalak, @MarkBaker: With *abort* I mean the user hitting the stop button of his browser or calling abort() on the Javascript ajax request object (both of which send a FIN packet to the server, i.e. close the connection). Unfortunately PHP becomes aware of this situation only if it attempts to write something to the client, which I cannot do due to output buffering. In other words: I would be quite happy if "an abort was an abort" :-) – soulmerge Mar 11 '11 at 14:23
  • I'm not sure if you can send anything to the client after s/he chooses to close the connection explicitly. – Halil Özgür Mar 11 '11 at 15:29
  • @battal: No I cannot, nor do I want to (assuming the client has closed the connection, I would want to send something, if the connection is still open, though). I need to *try* to send something for PHP to be able to detect that the connection is closed. I must call `echo $atLeastOneCharacter` followed by `flush()` in order for `connection_status()` to return a non-zero value if the connection has closed. The `$atLeastOneCharacter` won't show up anywhere if the connection was closed. – soulmerge Mar 11 '11 at 16:04

1 Answers1

1

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.

Jeff Parker
  • 7,367
  • 1
  • 22
  • 25
  • I will use something like this if no other solution exists, but this has a nasty side-effect of triggering all callbacks that were registered with `ob_start()`. – soulmerge Mar 11 '11 at 14:19
  • If you use a helper, you could always be really evil, and have a callback relay function which checks to see if the final flush is being called. I'll try to update the example. – Jeff Parker Mar 11 '11 at 14:20
  • That looks really interesting, I think, I'll integrate that into our output buffer-manager class, thank you very much :-) – soulmerge Mar 11 '11 at 16:24