PHP - Redirect and continue (without abort)
The Problem
Sometimes, in PHP, we have to do things that take a long time (like converting user-submitted videos, say), but there's a problem with that: the user has to wait until we're done with whatever we're doing. The worst part is, it looks like their request was dropped because the browser just sits there "loading" forever and shows no sign of progress until the page is finally done loading or - *shudder* - times out.
Solutions
There are a few strategies to solve this problem:
- You could have your PHP script create a cron job on the server. This approach is overly complex in my opinion and has some problems: You have to be running your PHP code on a Linux server, and you have to have access to cron - not really an option for most people who don't host their own server.
- You could write a special PHP script that gets called via AJAX after the page is loaded and returns the results from the operation. That works just fine and is a great option for someone comfortable with AJAX. I've actually used this strategy myself, but it's still too complicated for my taste.
- My weapon of choice: The PHP redirect and continue.
Summary
It works like this: worker.php is the worker script. At the very beginning of worker.php, it redirects the user's browser to messageToUser.php, but worker.php keeps working. Then you can do anything you want in worker.php just like normal, and you can send the user a message when it's finished by whatever means you choose (you do already have a user message system, right?).
The Code
Say the user just finished uploading a video that needs to be converted. The page they should be taken to is worker.php, which instantly redirects them to messageToUser.php.
messageToUser.php:
1 2 3 4 5 6 7 8 9 10 |
worker.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <?PHP //Redirect to messageToUser.php header("Location: messageToUser.php"); //Erase the output buffer ob_end_clean(); //Tell the browser that the connection's closed header("Connection: close"); //Ignore the user's abort (which we caused with the redirect). ignore_user_abort(true); //Extend time limit to 30 minutes set_time_limit(1800); //Extend memory limit to 10MB ini_set("memory_limit","10M"); //Start output buffering again ob_start(); //Tell the browser we're serious... there's really //nothing else to receive from this page. header("Content-Length: 0"); //Send the output buffer and turn output buffering off. ob_end_flush(); //Yes... flush again. flush(); //Close the session. session_write_close(); //Do some work //Then notify the user that it's finished ?> |
Some notes about the last few lines:
- flush() has to be called even though we just called ob_end_flush() because:
flush() has no effect on the buffering scheme of your web server or the browser on the client side. Thus you need to call both ob_flush() and flush() to flush the output buffers. ~PHP.net flush() docs
- We need to call session_write_close() because session data is locked while in use, which means only one PHP script can access a single session at a time. So, since our script is going to take a while, we need to make sure other scripts can access the session instead of waiting for worker.php to finish. That would kinda defeat the whole purpose.
That's it. While this may seem like a complex fix to some, as long as you have PHP 4.0.4 or above, it Just Workstm, and you can use and reuse it anywhere you want. Just copy and paste. No AJAX. No cron jobs. Nothing more to deal with than PHP itself. I like it.



