Was revisiting this concept today and thought I'd add some additional information.
First, using the ampersand to background the process means you don't get the non-zero return_var if the PHP script throws an error. To demonstrate, I created this bad.php which throws an exception:
echo "so far so good BUT...\n";
throw new Exception("grrr! I am bad!");
Then I wrote this script, exec.php to execute it:
// example 1
$cmd = "/usr/bin/php /tmp/foo/bad.php > /tmp/foo/out.txt";
$cmd_output = NULL;
$cmd_result = NULL;
$cmd_return = exec($cmd, $cmd_output, $cmd_result); // $return will contain the last line from the result of the command which should be the PID of the process we have spawned
echo "=== CMD_RETURN ===\n";
var_dump($cmd_return);
echo "=== CMD_RESULT ===\n";
var_dump($cmd_result);
echo "=== CMD_OUTPUT ===\n";
var_dump($cmd_output);
echo "=== END OUTPUT ===\n";
Running exec.php from the command line yields this:
PHP Fatal error: Uncaught exception 'Exception' with message 'grrr! I am bad!' in /tmp/foo/bad.php:3
Stack trace:
#0 {main}
thrown in /tmp/foo/bad.php on line 3
=== CMD_RETURN ===
string(0) ""
=== CMD_RESULT ===
int(255)
=== CMD_OUTPUT ===
array(0) {
}
=== END OUTPUT ===
Note how $cmd_result has the integer value of 255. Command line scripts return zero when they run properly and non-zero values otherwise.
If you add the ampersand to background this process, then $cmd_result is zero even if bad.php encounters a fatal error. This:
// example 2
$cmd = "/usr/bin/php /tmp/foo/bad.php > /tmp/foo/out.txt &";
Yields this:
=== CMD_RETURN ===
string(0) ""
=== CMD_RESULT ===
int(0)
=== CMD_OUTPUT ===
array(0) {
}
=== END OUTPUT ===
sneakyimp@sneakyimp-ubuntu-14:/var/www/html$ PHP Fatal error: Uncaught exception 'Exception' with message 'grrr! I am bad!' in /tmp/foo/bad.php:3
Stack trace:
#0 {main}
thrown in /tmp/foo/bad.php on line 3
This presumably returns the successful zero $cmd_result value because we successfully launched an independent PHP process. Note also that the exception thrown comes AFTER the output of exec.php has finished and the command prompt has again been displayed. This is presumably because stderr was still being routed to our main process (exec.php) and, because we forked the process, it was somewhat delayed or something. I.e., the error in bad.php was routed to stdErr which was still somehow piped to exec.php and subsequently bubbled back up to the terminal window from which we invoked everything. The exception is not part of the output returned to $cmd_output. The text of an exception thrown in bad.php will not be available in exec.php.
Adding the echo $! at the end is how we get the PID of the forked process.
// example 3
$cmd = "/usr/bin/php /tmp/foo/bad.php > /tmp/foo/out.txt & echo \$!";
the result:
=== CMD_RETURN ===
string(4) "3036"
=== CMD_RESULT ===
int(0)
=== CMD_OUTPUT ===
array(1) {
[0] =>
string(4) "3036"
}
=== END OUTPUT ===
sneakyimp@sneakyimp-ubuntu-14:/var/www/html$ PHP Fatal error: Uncaught exception 'Exception' with message 'grrr! I am bad!' in /tmp/foo/bad.php:3
Stack trace:
#0 {main}
This is helpful if we need to log the PIDs of processes we are launching just in case we need to forcibly terminate them later or otherwise check up on them or check log files or something.
And finally, I would recommend routing stderr to stdout when we run this other script:
// example 4
$cmd = "/usr/bin/php /tmp/foo/bad.php > /tmp/foo/out.txt 2>&1 & echo \$!";
This routes stderr into our file along with stdout and also effects a more complete separation of bad.php from exec.php. I'm not really sure what tenuous link might hang around between exec.php and bad.php, but it just seems better to try and separate them more fully. The result is that the exception does not appear when we run exec.php but it does appear in the output file, /tmp/foo/out.txt:
=== CMD_RETURN ===
string(4) "3084"
=== CMD_RESULT ===
int(0)
=== CMD_OUTPUT ===
array(1) {
[0] =>
string(4) "3084"
}
=== END OUTPUT ===
Sadly, in that last variation, exec.php has NO IDEA if bad.php was successful or not. $cmd_return has the PID of the forked process, $cmd_result is zero, and $cmd_output also just has the PID.
I can't help but wonder where stdErr goes in examples 1-3 if we run exec.php via cron or apache?