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);
}
Jimmy Berry

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();

        // ...
    }
}
Published in #Living on the edge