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 :
Hi there,
I have a background task running in exact the same way you have in your example. Though the point is that I wan’t the script to popup a page or dialog or whatever when it’s done.
1. So a visitor clicks and genarates a request which is being executed on the background.
2. The visitor keeps surfing on different pages.
3. No matter wich page he is, when the script is done he should see a popup or whatever that tells him/her that (in my case) the pdf is created and can be downloaded.
What would be the best way to do this…is there a way to do this?
Thank you!
I think the easiest solution in this case is to use AJAX. Assuming that …
1. All requests are logged somewhere (e.g. a database).
2. You know which request corresponds to which user (e.g. using a cookie or session variables).
3. The background script can set some kind of “completed” flag for the request when it’s done, or the completeness can be verified in some other way.
You can create a small PHP script that accepts the request’s ID (or somesuch) as a parameter, checks whether it’s done and outputs 1 if yes and 0 if no (just an example, you could use other values, JSON or even XML).
Then add a small piece of JavaScript to every page that will query the aforementioned PHP script and notify the user if the processing is done. It could also display a new window, redirect to a page etc. I’d recommend putting this script in the footer of the page and only using it when there is a request being processed.
If you’re not familiar with AJAX, here are some useful links –
* What is AJAX? [Wikipedia]
* The jQuery library (with some useful AJAX functions)
* jQuery tutorials
Thank you White Shadow,
That should be the best way indeed. The whole website is kinda ajax so, it should not be a problem to implement.
Hi White Shadow
Thanks for the script works v well and is the closest thing I’ve found so far to what I need (maybe i’m just not searching the right terminology).
I basically need a similar script but where the php file is called locally using a relative path – i want to preserve session id and data (it is in a secure CMS) without having to have the user login again etc. Using your method it is treated as if another computer/person is accessing the file and php creates a new session id for the request.
If you’ve got any advice would be greatly appreciated!
Its me again (my name was Hi White Shadow)
Just on a side note… what would do/be good enough is if I could load the php page outside the webroot as this would provide enough security…
Thanks !
Hmm… You could probably grab the cookies ($_COOKIE, I think) and use them with an asynchronous request (google it; sending cookies in a HTTP request), but I don’t know if it would work. The IP address of the script would be that of your server – not of the logged-in user, so the session id might be considered invalid by whatever CMS you’re using.
AJAX would get around this, but might not be sufficiently secure/flexible for what you’re trying to do.
Hi,
I have written a php fork using pcntl option, can I execute that page using this way?
I think that would probably work.
Hi,
I was hoping I could use this to take advantage of the Private Message system that’s part of our vbulletin forum (to send responses from a custom program that handles special access requests)… but most of the parameters are expected as POST parameters, and I’m having difficulty figuring out exactly how to properly modify the header to include them. I’ve put them into a variable, and tried adding them to $out (before AND after $parts[‘query’]), but no luck. Can you help?
Thanks in advance!
Maybe I’m being a bit dense here, but I don’t get what, exactly, is your problem.
I assume there are two possibilities :
* If the form is normally submitted via POST, send the variables like this :
^ It would probably be a good idea to encode values with urlencode() before appending them to the URL.
* If the form is submitted via GET you will need to modify the function, i.e. rename it to backgroundGet and make a few changes :
Replace $out = “POST “.$parts[‘path’].” HTTP/1.1\r\n”; with
Leave the next line (setting the “Host”) unchanged, but comment out the four lines after that (ending with the line that begins with “if (isset(….)”).
Then call the function the same way as in the first example.
Sorry, I guess I was confused by: the fact that the action=URL (in the form I am trying to mimick) has some parameters embedded in it (as if it were a GET); my lack of understanding of the parse_url function, and; my unfamiliarity with the syntax involved in creating headers (as your code was already creating POST headers). I was also missing a security-related parameter. I have a bare-bones version of the form working, but the same parameters, submitted as part of the URL to your function, do not produce the same results.
Is there something that could be added to, or changed, in the function to still submit the POST query in the same manner, but show me the output/results so I could debug?
Thanks for all your help
Maybe the “real” form expects a login cookie to be set or something like that? My script doesn’t support cookies.
For debugging, I don’t have the time to test it, but inserting something like this before “fclose(…)” should work :
Thanks for the debugging while/echo – it worked perfectly!
I think you are correct (about the cookie). I’ve tried adding it, but I’m not sure if I have all the syntax right. I even tried cURL, thinknig that would be easier, but I ran into the same problem.
Thanks again for all your help. I was really hoping this was going to be the answer for me. If you have time and are interested in looking at the code I’ve written, you can read the thread I started on my forum.
Cheers.
Eh, I think I’ll just go and write the script/function myself, that will be simpler than trying to figure out and then explain (in great detail) how to do it 😉 Should be done by tomorrow.
It is done.
Thanks for the quick turn-around on this!
Its a nice script , great job .
I wanna do a PHP/JS/Ajax/Comet chat application, but not with the usual ‘check every n seconds’ logic. Simple environment: single server, possibly win32, possibly *nix. Gonna be Open Source. I’ve looked hard, nothing drops out, so here I am.
Client-side, Comet (AKA xhr slow-load) looks like part of the solution.
PHP’s sockets function look like candidate for the server-side user-to-user data exchanges. I’ve seen some daemons around, but they seem hard to test debug, so I’ve looked at yr threads as a possible solution. Have you done anything like this I might peek at? Or, … ? Thanks a lot for a super/cool/awesome/useful site!
I haven’t done anything complex with these thread scripts, or written any chat servers. I also have to confess my ignorance about Comet (though the wiki page looks interesting).
Unfortunately I probably can’t help you in this particular case.
I couldn’t get the HTTP request to work. I echo’d the output and get:
POST /quote/quotePricing.php HTTP/1.1 Host: dev.quote2bill.com Content-Type: application/x-www-form-urlencoded Content-Length: 7 Connection: Close quote=1
… but the /quote/quotePricing.php script on dev.quote2bill.com does not seem to run for me. All the called script does at this point is insert into my DB as a test and it works stand alone.