Question

I am doing a minor experiment with PHP. I have a file named 'infinity.txt', and in that file, I write an incrementing number every 0.25 seconds.

while(true){

    file_put_contents('infinity.txt', ++$i.PHP_EOL, FILE_APPEND);
    usleep(250 * 1000);

}

This works fine and well, but when I close the tab in my browser, the script continues running. A browser request abort is no Ctrl. + C, so there's no surprise in that, but I was still wondering whether it is possible to abort an infinite loop when the user disconnects, or if there is any method to see if the user is still connected.

Calling register_shutdown_function beforehand is completely useless, by the way, even when the linked function has a die() within.

UPDATE: It seems to me that something might be done using the connection_aborted() function.

UPDATE 2: I have changed my code to look like this, but alas, the infinite loop does not cancel:

while(true){

    if(connection_aborted()){

        file_put_contents('infinity.txt', 'CONNECTION ABORTED.', FILE_APPEND);
        die();

    }

    file_put_contents('infinity.txt', ++$i.PHP_EOL, FILE_APPEND);
    usleep(250 * 1000);

}

UPDATE 3: I am now echoing and flushing some text in every iteration, yet still to no avail:

while(true){

    echo '0';
    ob_flush();
    flush(); // necessary for proper checking

    if(connection_aborted()){

        file_put_contents('infinity.txt', 'CONNECTION ABORTED.', FILE_APPEND);
        die();

    }

    file_put_contents('infinity.txt', ++$i.PHP_EOL, FILE_APPEND);
    usleep(250 * 1000);

}

UPDATE 4: The code shown in the previous update started working for some weird reason upon restarting Apache, without me changing any settings in the php.ini. What ultimately helped was adding an ignore_user_abort(true); at the very beginning.

Était-ce utile?

La solution

Do this way (I left explanation in comments):

ignore_user_abort(1); //we'll handle it by ourselves
header('Transfer-Encoding:chunked'); //HTTP 1.1: do not capture immediately (bin)
flush();
ob_flush();

$i = 0;
set_time_limit(0);
while(1)
{
    echo "0"; //do this: sending data to dead TCP connection will fail
    flush();
    ob_flush();
    if(connection_status()!=0)
    {

        file_put_contents('infinity.txt', 'CONNECTION ABORTED.', FILE_APPEND);
        echo "0\r\n\r\n"; //stream termination packet (double \r\n according to proto)
        flush();
        ob_flush();
        exit();

    }

    file_put_contents('infinity.txt', ++$i.PHP_EOL, FILE_APPEND);
    usleep(250 * 1000);

}

Autres conseils

See this comment on the same connection_aborted() page you linked.

The default procedure for a web server like PHP is to create the entire HTML page, and when finished, ship it to the browser. After the browser asks the server for the file, there is no further communication until the file is shipped.

You can, however, change this behavior with ob_flush() and related output control functions. This causes PHP to generate a certain amount of HTML, then ship it to the browser. This communication while processing allows your previously attempted connection_aborted() to work.

Edit to add: Glad you got it working! I also created some working test code for anybody else looking for this:

<?php 

@apache_setenv('no-gzip', 1);
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
ob_implicit_flush(1);
ignore_user_abort(true);
ob_start();

for ($i=1;$i<10;$i++) {
    sleep(2);
    echo "done pass ". $i . ".<br />";
    echo str_pad('',4096)."\n";          
    ob_flush();
    if(connection_aborted()){
        file_put_contents('test.txt', "CONNECTION ABORTED on pass $i.".PHP_EOL, FILE_APPEND);
        exit(); 
    }
    else {
        file_put_contents('test.txt', "successful pass    on pass $i.".PHP_EOL, FILE_APPEND);
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top