Skip to content

Commit

Permalink
merged branch mcuadros/master (PR #7566)
Browse files Browse the repository at this point in the history
This PR was merged into the master branch.

Discussion
----------

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

Added support for processes that need a TTY to run. This can be useful in scenarios where we need to open an editor and wait for the user (like "crontab -e" or "git commit"). The new methos "setTTY" can be used to control this.

Regards

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

Commits
-------

2d30fb3 [Process] Added support for processes that need a TTY to run.
  • Loading branch information
fabpot committed Apr 20, 2013
2 parents e5af870 + 2d30fb3 commit ab43728
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 29 deletions.
109 changes: 80 additions & 29 deletions src/Symfony/Component/Process/Process.php
Expand Up @@ -58,6 +58,7 @@ class Process
private $status = self::STATUS_READY;
private $incrementalOutputOffset;
private $incrementalErrorOutputOffset;
private $tty;

private $fileHandles;
private $readBytes;
Expand Down Expand Up @@ -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;

Expand All @@ -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]);
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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().
*
Expand Down
13 changes: 13 additions & 0 deletions src/Symfony/Component/Process/Tests/AbstractProcessTest.php
Expand Up @@ -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('');
Expand Down

0 comments on commit ab43728

Please sign in to comment.