4

This code example is supposed to redirect and continue processing a long operation, however it doesn't redirect until after the exec command is complete. I've tried multiple other ways of doing this and nothing works. Where am I going wrong?

ignore_user_abort() is enabled in my php.ini

<?php
set_time_limit ( 0 );
header ( 'Connection: close' );
ob_start ();
header ( 'Content-Length: 0' );
header ( 'Location: /redirect.php' );
ob_end_flush ();
flush ();
ignore_user_abort ( true );

exec('command that takes 5 minutes to process');
exit ();
?>

I appreciate the help in advance.

Mike
  • 23,542
  • 14
  • 76
  • 87
trump
  • 41
  • 2
  • what do you want? You want the redirect to happen and still process the code below? – Dharam Jul 01 '16 at 18:13
  • Yes I would like to redirect to a different page and have the exec() command that takes 5 minutes to process continue. – trump Jul 01 '16 at 18:14
  • Not sure if this is what's affecting it, but you have `ignore_user_abort ( true );` after your redirect. Are you sure you have the correct php.ini file? Sometimes there are multiple files. – Mike Jul 01 '16 at 18:15
  • Can you define "not working", please? – Mike Jul 01 '16 at 18:22
  • I'm sure it's the right php.ini because I previously edited the same one to make the `upload_max_filesize = 64M`, and setting `ignore_user_abort ( true );` above the redirect didn't fix it @Mike It's not redirecting until after `exec();` has finished – trump Jul 01 '16 at 18:24
  • Dont flush anything, generally the simple header location shall work. Did you try with the header location code only and your code being after that? – Dharam Jul 01 '16 at 18:24
  • What server OS are you using? – PaulH Jul 01 '16 at 18:26
  • @PaulH It's Debian 7 with VestaCP. And I'm not sure what you mean about tty Tried with `header();` only, but still the same. – trump Jul 01 '16 at 18:27
  • I'm running from the browser with http request. (edit): In tty it's the same. – trump Jul 01 '16 at 18:30
  • @PaulH What would you expect a `header("Location: ...");` header to do in the CLI? – Mike Jul 01 '16 at 18:31
  • Possible duplicate of [continue processing php after sending http response](http://stackoverflow.com/questions/15273570/continue-processing-php-after-sending-http-response) – Sven Tore Jul 01 '16 at 18:34
  • I just tried your code and it works fine for me using PHP 5.6.20-0+deb8u1. The redirect is done immediately. It does not wait until after the long executing command finishes (I substituted it with `sleep(10);`). I am unable to reproduce the behavior above. – Mike Jul 01 '16 at 18:39
  • @Mike, does't the `ignore_user_abort` documentation say "the script's tty goes away", does it work for http also? – PaulH Jul 01 '16 at 18:39
  • @PaulH Yes, it works for HTTP abort as well. – Mike Jul 01 '16 at 18:42
  • @Mike OK, forgive, my ignorance. – PaulH Jul 01 '16 at 18:42
  • @Mike I replaced `exec();` with `sleep(10);` and it still doesn't work. I'm going to try on my CentOS server. – trump Jul 01 '16 at 18:43
  • Still not working, it could be VestaCP because it uses nginx as a reverse proxy - http://stackoverflow.com/questions/26302354/php-redirect-but-continue-to-process-script-with-ignore-user-aborttrue-fails – trump Jul 01 '16 at 18:48
  • Maybe you can try the solution of Eduard Luca - http://stackoverflow.com/a/8910211/5962802 – IVO GELOV Aug 02 '16 at 12:42

1 Answers1

0

I recently had to write a webhook handler that was supposed to return a "200 OK" response immediately before continuing to process other long running tasks.

I have observed that the process of hanging up immediately was never possible when I the entire process was happening using the same web server. So if the client IP of connection where you need to send a response is the same as the server IP, you might never be able to do it. But it always worked when the incoming connected was a real world remote client.

With all that said, I was just returning a "200 OK" response. Which should not be different than sending back a redirection.

The code that worked for me was ...

public function respondASAP($responseCode = 200)
{
    // check if fastcgi_finish_request is callable
    if (is_callable('fastcgi_finish_request')) {
        /*
         * This works in Nginx but the next approach not
         */
        session_write_close();
        fastcgi_finish_request();

        return;
    }

    ignore_user_abort(true);

    ob_start();
    http_response_code($responseCode);
    header('Content-Encoding: none');
    header('Content-Length: '.ob_get_length());
    header('Connection: close');

    ob_end_flush();
    ob_flush();
    flush();
}

You could adapt it to your situation and make it set a redirection header instead of an "200 OK".

asiby
  • 3,229
  • 29
  • 32