please after reading this question , do not say that it is copied.I have already searched on web but none of the solution worked for me.
Wha
Here's my go at it (reading both stdout and stderr (hopefully) without deadlocks):
class ExecResult {
public $returnValue;
public $stdoutBuffer;
public $stderrBuffer;
}
class WtfPhpWhyIHaveToDoEverythingMyself {
public static function exec($proc, $argv) {
$cwd = getcwd();
$env = [];
$procAndArgv = count($argv) > 0 ?
$proc . ' ' . implode(' ', self::escapeArgvs($argv)) :
$proc;
$pipes = null; // will get filled by proc_open()
$result = new ExecResult();
$processHandle = proc_open(
$procAndArgv,
[
0 => ['pipe', 'r'], // read/write is from child process's perspective
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
],
$pipes,
$cwd,
$env);
$stdin = $pipes[0];
$stdout = $pipes[1];
$stderr = $pipes[2];
fclose($stdin);
stream_set_blocking($stdout, false);
stream_set_blocking($stderr, false);
$outEof = false;
$errEof = false;
do {
$read = [ $stdout, $stderr ]; // [1]
$write = null; // [1]
$except = null; // [1]
// [1] need to be as variables because only vars can be passed by reference
stream_select(
$read,
$write,
$except,
1, // seconds
0); // microseconds
$outEof = $outEof || feof($stdout);
$errEof = $errEof || feof($stderr);
if (!$outEof) {
$result->stdoutBuffer .= fgets($stdout);
}
if (!$errEof) {
$result->stderrBuffer .= fgets($stderr);
}
} while(!$outEof || !$errEof);
fclose($stdout);
fclose($stderr);
$result->returnValue = proc_close($processHandle);
return $result;
}
private static function escapeArgvs($argv) {
return array_map(function ($item){
return escapeshellarg($item);
}, $argv);
}
}
$result = WtfPhpWhyIHaveToDoEverythingMyself::exec('/var/www/html/hello.sh', [
'arg 1',
'arg 2',
]);
var_dump($result);
/*
object(ExecResult)#1 (3) {
["returnValue"]=> int(0)
["stdoutBuffer"]=>
string(45) "var1 = arg 1
var2 = arg 2
"
["stderrBuffer"]=>
string(0) ""
}
*/
I am really disappointed that with PHP, you have to write all the plumbing yourself. For example node.js has got your back covered with simple child_process.spawn()