[Process] Added support for processes that need a TTY to run.

This commit is contained in:
Máximo Cuadros Ortiz 2013-04-05 12:35:05 +02:00
parent 0798800e88
commit 2d30fb3cd0
2 changed files with 93 additions and 29 deletions

View File

@ -53,6 +53,7 @@ class Process
private $status = self::STATUS_READY;
private $incrementalOutputOffset;
private $incrementalErrorOutputOffset;
private $tty;
private $fileHandles;
private $readBytes;
@ -233,35 +234,7 @@ class Process
$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;
@ -283,6 +256,12 @@ class Process
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]);
@ -825,6 +804,30 @@ class Process
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.
*
@ -982,6 +985,54 @@ class Process
return $this;
}
/**
* 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().
*

View File

@ -162,6 +162,19 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
$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('');