Executing a sub-process from PHP is a nightmare... of course, only if you want to get it right. There are so many things that can go wrong, and PHP itself does not help as it has many quirks depending on the platform you are using. That's why I created the Process component some years ago. And since then, many developers helped us enhancing and fixing the code to get it right.
For Symfony 2.2, we have made several improvements.
Contributed by
Romain Neutron
in #5731.
Getting incremental Output from a Process
Instead of using run()
to execute a process, you can start()
it:
run()
is blocking and waits for the process to finish, start()
creates
a background process. One way to get feedback from a running command is to
pass a callback when starting/running the process:
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\Component\Process\Process;
$process = new Process('ls -lsa');
// the same works when using start instead of run
$process->run(function ($type, $data) {
if (Process::ERR == $type) {
// $data was sent to the error output
} else {
// $data was sent to the standard output
}
});
But sometimes, using a callback is not possible. As of Symfony 2.2, you can
still get the incremental output data by using the getIncrementalOutput()
and getIncrementalErrorOutput()
methods; they return the new outputs since
the last call whereas getOutput()
and getErrorOutput()
return the
complete outputs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
use Symfony\Component\Process\Process;
$processes = array();
$processes[] = new Process('ls -lsa');
$processes[] = new Process('ps waux');
while (count($processes) > 0) {
foreach ($processes as $i => $process) {
if (!$process->isStarted()) {
echo "Process starts\n";
$process->start();
continue;
}
echo $process->getIncrementalOutput();
echo $process->getIncrementalErrorOutput();
if (!$process->isRunning()) {
echo "Process stopped\n";
unset($processes[$i]);
}
}
sleep(1);
}
Contributed by
Jimmy Berry
in #5456 and #5455.
Restarting a Process
In the previous example, you might want to restart one of the processes if it crashes; you can now possible:
1 2 3 4 5 6 7 8 9
use Symfony\Component\Process\ProcessBuilder;
if (!$process->isRunning()) {
if (!$process->isSuccessful()) {
$cloned = $process->restart();
// ...
}
}
Getting the Status of a running Process
When you run a set of processes, you might want to restart processes that died (like in the previous example). But when a process is not even able to start, there is no point in restarting it. As of 2.2, you can check the status of a process and act accordingly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
$process->isSuccessful();
$process->hasBeenSignaled();
$process->hasBeenStopped();
$process->isRunning();
// new in 2.2
$process->isStarted();
$process->isTerminated();
if (!$process->isRunning()) {
if ($process->isStarted() && !$process->isSuccessful()) {
$cloned = $process->restart();
// ...
}
}
I made a pull request for a method that returns the process id, any chance you could have a look?
I love this kind of news!