diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 6f345bc57f..2b8d92a7bc 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -261,7 +261,7 @@ class Process stream_set_blocking($pipe, false); } - $this->writePipes(false); + $this->writePipes(); $this->updateStatus(false); $this->checkTimeout(); } @@ -313,9 +313,9 @@ class Process if (null !== $callback) { $this->callback = $this->buildCallback($callback); } - while ($this->processInformation['running']) { - $this->updateStatus(true); + while ($this->pipes || (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles)) { $this->checkTimeout(); + $this->readPipes(true); } $this->updateStatus(false); if ($this->processInformation['signaled']) { @@ -1162,23 +1162,7 @@ class Process return; } - foreach ($r as $pipe) { - $type = array_search($pipe, $this->pipes); - $data = fread($pipe, 8192); - - if (strlen($data) > 0) { - // last exit code is output and caught to work around --enable-sigchild - if (3 == $type) { - $this->fallbackExitcode = (int) $data; - } else { - call_user_func($this->callback, $type == 1 ? self::OUT : self::ERR, $data); - } - } - if (false === $data || feof($pipe)) { - fclose($pipe); - unset($this->pipes[$type]); - } - } + $this->processReadPipes($r); } } @@ -1187,7 +1171,7 @@ class Process * * @param Boolean $blocking Whether to use blocking calls or not. */ - private function writePipes($blocking) + private function writePipes() { if ($this->tty) { $this->status = self::STATUS_TERMINATED; @@ -1208,7 +1192,11 @@ class Process $stdinOffset = 0; while ($writePipes) { - $r = array(); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->processFileHandles(); + } + + $r = $this->pipes; $w = $writePipes; $e = null; @@ -1235,6 +1223,34 @@ class Process $writePipes = null; } } + + $this->processReadPipes($r); + } + } + + /** + * Processes read pipes, executes callback on it. + * + * @param array $pipes + */ + private function processReadPipes(array $pipes) + { + foreach ($pipes as $pipe) { + $type = array_search($pipe, $this->pipes); + $data = fread($pipe, 8192); + + if (strlen($data) > 0) { + // last exit code is output and caught to work around --enable-sigchild + if (3 == $type) { + $this->fallbackExitcode = (int) $data; + } else { + call_user_func($this->callback, $type == 1 ? self::OUT : self::ERR, $data); + } + } + if (false === $data || feof($pipe)) { + fclose($pipe); + unset($this->pipes[$type]); + } } } } diff --git a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php index 53f79c5b55..c03a19eefb 100644 --- a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php +++ b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php @@ -399,7 +399,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase // Sleep doesn't work as it will allow the process to handle signals and close // file handles from the other end. - $process = $this->getProcess('php -r "sleep 4"'); + $process = $this->getProcess('php -r "while (true) {}"'); $process->start(); // PHP will deadlock when it tries to cleanup $process