How To Run A PHP Script In The Background
If you have a PHP script that takes a while to execute (for example, long database operations or file format conversions), you might want to run it in the background so that the rest of your page still loads fast. In this post I’ll describe two ways to do this.
Launching a background process
One approach is to launch a new instance of the PHP interpreter as a background process. On a UNIX-based system this can be done with a single line of code :
exec ("/usr/bin/php path/to/script.php >/dev/null &");
To launch a background process in an OS-independent fashion you’d can use the function below (snatched from this post) -
function launchBackgroundProcess($call) {
// Windows
if(is_windows()){
pclose(popen('start /b '.$call, 'r'));
}
// Some sort of UNIX
else {
pclose(popen($call.' > /dev/null &', 'r'));
}
return true;
}
function is_windows(){
if(PHP_OS == 'WINNT' || PHP_OS == 'WIN32'){
return true;
}
return false;
}
If you use one of these methods you can pass data to the PHP script using command line arguments.
The main disadvantage of this approach is that the command to execute ($call) is still platform-dependent. Also, the path to the PHP executable may be different on various servers.
Using an asynchronous HTTP request
If the PHP script that needs to be executed is available on the Web, you can easily run it in the background by requesting the URL and dropping the connection right away, so you don’t need to wait until it’s done processing. You can also pass information to the script in the usual way – as URL parameters.
Here’s a function that will submit an asynchronous POST query to the specified URL (this works similar to how a web form is submitted) -
function backgroundPost($url){
$parts=parse_url($url);
$fp = fsockopen($parts['host'],
isset($parts['port'])?$parts['port']:80,
$errno, $errstr, 30);
if (!$fp) {
return false;
} else {
$out = "POST ".$parts['path']." HTTP/1.1\r\n";
$out.= "Host: ".$parts['host']."\r\n";
$out.= "Content-Type: application/x-www-form-urlencoded\r\n";
$out.= "Content-Length: ".strlen($parts['query'])."\r\n";
$out.= "Connection: Close\r\n\r\n";
if (isset($parts['query'])) $out.= $parts['query'];
fwrite($fp, $out);
fclose($fp);
return true;
}
}
//Example of use
backgroundPost('http://example.com/slow.php?file='.
urlencode('some file.dat'));
Assuming that the target script is located on the same server as the calling script, this function is fast, plus it should work on any operating system.
By the way, I recommend to put a call to ignore_user_abort(true) right at the top of the backgroud script to make sure it’s not terminated when the connection is closed. Disabling the time limit with set_time_limit(0) can also be useful.
Need more?
If for some strange reason you can’t use the aforementioned tricks, you could also launch the script from the client side by using AJAX. This is what I do in my link checker plugin for WordPress. Another possibility is to store the tasks that need to be done in a database and periodically run the script using cron.
Related posts :
[...] Background Process in PHP http://w-shadow.com/blog/2007/10/16/how-to-run-a-php-script-in-the-background/ [...]
Thanks for this! I’ll check this site everyday and looking for some posts like this.
Wow, fantastic weblog format! How lengthy have you ever been running a blog for? you made running a blog look easy. The full glance of your website is great, let alone the content material!
Thank you for this information. Very useful!
Question: for the following code, who is $errno and $errstr?
$fp = fsockopen($parts['host'], isset($parts['port'])?$parts['port']:80, $errno, $errstr, 30);
Those are the output parameters for the error number and error message. See the fsockopen documentation for (slightly) more details.
Hi there…this is great! what edits would I need to make if I wanted to do this on an HTTPS request? On cURL I used to do SSL_VERFY_PEER set to false..anything I can do here?
As far as I know, there’s no easy way to do HTTPS with plain sockets (fsockopen). You’ll probably want to change the script to use cURL instead. With an appropriately low connection timeout, cURL should close the connection right away – just like the script above does.
Hi Janis,
is there a way to add a callback function/to be notified when the background process is completed? Thanks!
Thank you very much for this post.
I have been searching for weeks for a solution like this and am so grateful for your work!
@ Travis: If you use the first approach (the popen() function), you could modify the script to return the file pointer that it creates instead of closing it immediately, and then check its status by calling feof() on the pointer.
Something like this:
$fp = launchBackgroundProcess($command); //...other code... if (feof($fp)) { //The process has completed. }Note: One of the user comments on the popen() page claims that feof() will only ever return TRUE if you try to read from the file pointer at least once. I don’t know if this is true, but it’s something to keep in mind as a possible source of bugs.
[...] the first. If you want a very good tutorial on how to do the above two methods, then check out this tutorial on w-shadow.com. It gives you code samples and explains quite a bit of [...]