diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 3d3c95667c9c..c4e464982c39 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -58,6 +58,7 @@ class Process private $status = self::STATUS_READY; private $incrementalOutputOffset; private $incrementalErrorOutputOffset; + private $tty; private $fileHandles; private $readBytes; @@ -239,35 +240,7 @@ public function start($callback = null) $this->incrementalOutputOffset = 0; $this->incrementalErrorOutputOffset = 0; $callback = $this->buildCallback($callback); - - //Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. - //Workaround for this problem is to use temporary files instead of pipes on Windows platform. - //@see https://bugs.php.net/bug.php?id=51800 - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->fileHandles = array( - self::STDOUT => tmpfile(), - ); - if (false === $this->fileHandles[self::STDOUT]) { - throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable'); - } - $this->readBytes = array( - self::STDOUT => 0, - ); - $descriptors = array(array('pipe', 'r'), $this->fileHandles[self::STDOUT], array('pipe', 'w')); - } else { - $descriptors = array( - array('pipe', 'r'), // stdin - array('pipe', 'w'), // stdout - array('pipe', 'w'), // stderr - ); - - if ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { - // last exit code is output on the fourth pipe and caught to work around --enable-sigchild - $descriptors = array_merge($descriptors, array(array('pipe', 'w'))); - - $this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code'; - } - } + $descriptors = $this->getDescriptors(); $commandline = $this->commandline; @@ -289,6 +262,12 @@ public function start($callback = null) stream_set_blocking($pipe, false); } + + if ($this->tty) { + $this->status = self::STATUS_TERMINATED; + return; + } + if (null === $this->stdin) { fclose($this->pipes[0]); unset($this->pipes[0]); @@ -887,6 +866,30 @@ public function setTimeout($timeout) return $this; } + /** + * Enable/Disable TTY mode + * + * @param boolean $tty If is enabled or not + * + * @return self The current Process instance + */ + public function setTTY($tty) + { + $this->tty = $tty; + + return $this; + } + + /** + * Gets if TTY is enabled/disabled + * + * @return string The current contents + */ + public function getTTY() + { + return $this->tty; + } + /** * Gets the working directory. * @@ -1062,6 +1065,54 @@ public function checkTimeout() } } + /** + * Create the descriptors needed by the proc_open + * + * @return array + */ + private function getDescriptors() + { + //Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. + //Workaround for this problem is to use temporary files instead of pipes on Windows platform. + //@see https://bugs.php.net/bug.php?id=51800 + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->fileHandles = array( + self::STDOUT => tmpfile(), + ); + if (false === $this->fileHandles[self::STDOUT]) { + throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable'); + } + $this->readBytes = array( + self::STDOUT => 0, + ); + + return array(array('pipe', 'r'), $this->fileHandles[self::STDOUT], array('pipe', 'w')); + } + + if ($this->tty) { + $descriptors = array( + array('file', '/dev/tty', 'r'), + array('file', '/dev/tty', 'w'), + array('file', '/dev/tty', 'w'), + ); + } else { + $descriptors = array( + array('pipe', 'r'), // stdin + array('pipe', 'w'), // stdout + array('pipe', 'w'), // stderr + ); + } + + if ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { + // last exit code is output on the fourth pipe and caught to work around --enable-sigchild + $descriptors = array_merge($descriptors, array(array('pipe', 'w'))); + + $this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code'; + } + + return $descriptors; + } + /** * Builds up the callback used by wait(). * diff --git a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php index 3d1bcf37e4db..a18ec20952ad 100644 --- a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php +++ b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php @@ -185,6 +185,19 @@ public function testExitCodeCommandFailed() $this->assertGreaterThan(0, $process->getExitCode()); } + public function testTTYCommand() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does have /dev/tty support'); + } + + $process = $this->getProcess('echo "foo" >> /dev/null'); + $process->setTTY(true); + $process->run(); + + $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus()); + } + public function testExitCodeText() { $process = $this->getProcess('');