views:

6423

answers:

9

I've got a PHP script that needs to invoke a shell script but doesn't care at all about the output. The shell script makes a number of SOAP calls and is slow to complete, so I don't want to slow down the PHP request while it waits for a reply. In fact, the PHP request should be able to exit without terminating the shell process.

I've looked into the various exec(), shell_exec(), pcntl_fork(), etc. functions, but none of them seem to offer exactly what I want. (Or, if they do, it's not clear to me how.) Any suggestions?

Thanks in advance.

+6  A: 

If it "doesn't care about the output", couldn't the exec to the script be called with the & to background the process?

EDIT - incorporating what @AdamTheHut commented to this post, you can add this to a call to exec:

"> /dev/null 2>/dev/null &"

That will redirect both stdio (first >) and stderr (2>) to /dev/null and run in the background.

There are other ways to do the same thing, but this is the simplest to read.


An alternative to the above double-redirect:

" &> /dev/null &"
warren
AdamTheHutt
that redirects stdio to /dev/null and stderr to /dev/null .. good catch on the addition to the call
warren
Definitely the way to go if you want fire and forget with php and apache. A lot of production Apache and PHP environments will have pcntl_fork() disabled.
method
+3  A: 

I used at for this, as it is really startingan independent process.

<?php
    `echo "the command"|at now`;
?>
Czimi
in some situations this is absolutely the best solution. it was the only one that worked for me to release a "sudo reboot" ("echo 'sleep 3; sudo reboot' | at now") from a webgui AND finish rendering the page .. on openbsd
Kaii
+2  A: 

On linux you can do the following:

$cmd = 'nohup nice -n 10 /usr/bin/php -c /path/to/php.ini -f /path/to/php/file.php action=generate var1_id=23 var2_id=35 gen_id=535 > /path/to/log/file.log & echo $!';
$pid = shell_exec($cmd);

This will execute the command at the command prompty and then just return the PID, which you can check for > 0 to ensure it worked.

This question is similar: http://stackoverflow.com/questions/209774/does-php-have-threading

Darryl Hein
+1  A: 

php-execute-a-background-process has some good suggestions. I think mine is pretty good, but I'm biased :)

Mark Biek
Nice answer in that thread :)
dcousineau
A: 

In Linux, you can start a process in a new independent thread by appending an ampersand at the end of the command

mycommand -someparam somevalue &

In Windows, you can use the "start" DOS command

start mycommand -someparam somevalue
Leo
A: 

You can also run the php script as daemon or cronjob... #!/usr/bin/php -q

Ronald Conco
A: 

the right way(!) to do it is to

  1. fork()
  2. setsid()
  3. execve()

fork forks, setsid tell the current process to become a master one (no parent), execve tell the calling process to be replaced by the called one. so that the parent can quit without affecting the child.

 $pid=pcntl_fork();
 if($pid==0)
 {
   posix_setsid();
   pcntl_exec($cmd,$args,$_ENV);
   // child becomes the standalone detached process
 }

 // parent's stuff
 exit();
The problem with pcntl_fork() is that you are not supposed to use it when running under a web server, as the OP does (besides, the OP have tried this already).
Guss
A: 

Use a named fifo.

#!/bin/sh
mkfifo trigger
while true; do
    read < trigger
    long_running_task
done

Then whenever you want to start the long running task, simply write a newline (nonblocking to the trigger file.

As long as your input is smaller than PIPE_BUF and it's a single write() operation, you can write arguments into the fifo and have them show up as $REPLY in the script.

geocar
A: 

I used this...

/** 
 * Asynchronously execute/include a PHP file. Does not record the output of the file anywhere.  
 * Relies on the PHP_PATH config constant.
 *
 * @param string $filename  file to execute
 * @param string $options   (optional) arguments to pass to file via the command line
 */ 
function asyncInclude($filename, $options = '') {
    exec(PHP_PATH . " -f {$filename} {$options} >> /dev/null &");
}

(where PHP_PATH is a const defined like define('PHP_PATH', '/opt/bin/php5') or similar)

It passes in arguments via the command line. To read them in PHP, see argv.

philfreo