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 :

74 Responses to “How To Run A PHP Script In The Background”

  1. Jumbo says:

    Hello

    I tried to run this comment:
    exec (“/usr/bin/php /public_html/test/test-exec.php >/dev/null &”);

    the test file test-exec.php should input something in my DB, but nothing happens…

    Is the syntax I’m using correct?

    Thanx

  2. Jumbo says:

    sorry i meant command, not comment 😀

  3. Jānis Elsts says:

    The syntax looks correct. Try running the command in the terminal/shell instead of PHP. It should tell you if there are any syntax errors.

  4. Brent F says:

    I think this is exactly what I am looking for but i can’t get it to run. I am using the following code:

    public function backgroundPost($url){
    echo ‘got here. URL = ‘.$url;
    ignore_user_abort(true);
    set_time_limit(0);

    $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;
    }
    }

    and am calling it this way:

    backgroundPost(‘http://www.example.com/cf.php’)

    It gets to the backgroundPost function but doesn’t seem to get to the url (e.g., cf.php file). I believe this is the case because I can hit the cf.php file manually and it does what i need it to but if I use the backgroundPost, nothing happens. I am not sure how to debug it.

    Any ideas?

    Brent

  5. Jānis Elsts says:

    Try placing that “echo” just before the fwrite() call. If it doesn’t get to that point, it probably can’t open a connection to the server for some reason. In that case $errno and $errstr should contain error information.

  6. Brent F says:

    I moved the echo and it displayed fine. I also echo’d the $errno and $errstr which returned 0 and blank respectively. So it appears to flow properly but it isn’t getting to the cf.php. I added a variable to the end of the url and tried to echo the variable on cf.php but nothing. So maybe I am not sure what is supposed to happen here. I through that this function was supposed to kick off a call to the url and run what ever code is triggered by visiting the url. Is that correct or am I missing something?

    Thanks Janis.

  7. Jānis Elsts says:

    As far as I can see, you’re not missing anything. I’m not sure why it doesn’t work.

    If you can run the script locally, try using Fiddler or Wireshark to monitor HTTP traffic and see if the request is actually sent.

  8. Brent F says:

    I am still looking for a way to do this. After many many hours of testing it appears to work once and then stop. So if I change the url to http://www.example.com and run it, i get an error obviously. When I change the url to the correct one it works once and then if I try to run it again it does process the script at the url, unless I first change it back to example and let it error.

    Any ideas why that would be? Is the socket left open or in some limbo state? I am not familiar with fsockopen so i am not sure where to turn.

    Any ideas? I am using a GoDaddy Shared host if that helps?

  9. Jānis Elsts says:

    No, the socket probably doesn’t stay open. If the function works, it will also close the socket. That’s what that fclose() does. And even if it did stay open, I don’t think it would stop working after the first run – each subsequent call would just open a new socket (which would waste some RAM, but that’s beside the point).

    Try adding a small delay (1-2 seconds) between runs. Also, does GoDaddy provide some kind of HTTP access log? You could check if your requests are showing up in there.

  10. Syam S says:

    @White Shadow . Awsome Post Thanks.

    @Brent F,

    Dont know you already found a resolution, I also came across the same issue and fixed it by changing single quotes to double quote in $out variable.
    Also i used some additional headers as below

                           $out = "POST" . $parts ['path']."?".$parts ['query']." HTTP/1.1\r\n";
    			$out .= "Host: " . $parts ['host'] . "\r\n";
    			$out .= "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:28.0)   Gecko/20100101 Firefox/28.0\r\n";
    			$out .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
    			$out .= "Accept-Language: en-US,en;q=0.5\r\n";
    			$out .= "Accept-Encoding: gzip, deflate\r\n";
    			$out .= "Connection: Close\r\n\r\n";
    

    Hope this will help

  11. Ben Haug says:

    I’ve use this script for years. But after upgrading PHP from 5.3 to 5.4.40 it have stopped working.

    Unfortunately there isn’t much info in the logs.

    Does anybody have an idea how I may get it running again?

  12. Jānis Elsts says:

    I’m not sure what could cause the script to stop working without logging any errors. Did you change your PHP configuration in anyway when upgrading to 5.4.x? Safe mode, open_basedir, Suhosin, disabled functions – anything like that?

  13. Ben Haug says:

    Thanks for fast reply Jānis.
    Good news: I rebuilt the script from scratch (with your code) and it works again.

    I have to apologize for disturbing you. The script is really awesome, I use it for running a series of SQL-scripts that pulls data from MS Dynamics NAV into a MySQL DB and thereafter re-indexing in the bacground.

    Thanks again!

  14. Hi,
    You are a legend. After hours trying to make exec work.

    Thanks heaps

Leave a Reply