Read standard output and standard error of child process using stream_select. (#1253)
If a process filled up the buffer of the standard error pipe before closing the standard output pipe, it would hang. As a result, the call to stream_get_contents for the standard output pipe would not return, as the standard error pipe needs to be read from in order for the process to continue. This change uses stream_select to read from both pipes whenever data becomes available.
This commit is contained in:
parent
b861071402
commit
5c10fca905
2 changed files with 53 additions and 5 deletions
|
|
@ -94,14 +94,13 @@ abstract class BaseCommandExecutor implements CommandExecutor
|
|||
$pipes = [];
|
||||
$process = proc_open($command, $descriptorSpec, $pipes, $this->buildPath, null);
|
||||
|
||||
$this->lastOutput = '';
|
||||
$this->lastError = '';
|
||||
|
||||
if (is_resource($process)) {
|
||||
fclose($pipes[0]);
|
||||
|
||||
$this->lastOutput = stream_get_contents($pipes[1]);
|
||||
$this->lastError = stream_get_contents($pipes[2]);
|
||||
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
list($this->lastOutput, $this->lastError) = $this->readAlternating([$pipes[1], $pipes[2]]);
|
||||
|
||||
$status = proc_close($process);
|
||||
}
|
||||
|
|
@ -127,6 +126,36 @@ abstract class BaseCommandExecutor implements CommandExecutor
|
|||
return $rtn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from array of streams as data becomes available.
|
||||
* @param array $descriptors
|
||||
* @return string[] data read from each descriptor
|
||||
*/
|
||||
private function readAlternating(array $descriptors)
|
||||
{
|
||||
$outputs = [];
|
||||
foreach ($descriptors as $key => $descriptor) {
|
||||
stream_set_blocking($descriptor, false);
|
||||
$outputs[$key] = '';
|
||||
}
|
||||
do {
|
||||
$read = $descriptors;
|
||||
$write = null;
|
||||
$except = null;
|
||||
stream_select($read, $write, $except, null);
|
||||
foreach ($read as $descriptor) {
|
||||
$key = array_search($descriptor, $descriptors);
|
||||
if (feof($descriptor)) {
|
||||
fclose($descriptor);
|
||||
unset($descriptors[$key]);
|
||||
} else {
|
||||
$outputs[$key] .= fgets($descriptor);
|
||||
}
|
||||
}
|
||||
} while (count($descriptors) > 0);
|
||||
return $outputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output from the last command run.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue