Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds PTY mode & convenience method mustRun() #8655

Merged
merged 4 commits into from Feb 3, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Symfony/Component/Process/CHANGELOG.md
Expand Up @@ -5,6 +5,8 @@ CHANGELOG
-----

* added the ability to define an idle timeout
* added support for PTY mode
* added the convenience method "mustRun"

2.3.0
-----
Expand Down
79 changes: 79 additions & 0 deletions src/Symfony/Component/Process/Process.php
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\Process\Exception\InvalidArgumentException;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Exception\RuntimeException;

Expand Down Expand Up @@ -62,6 +63,7 @@ class Process
private $incrementalOutputOffset;
private $incrementalErrorOutputOffset;
private $tty;
private $pty;

private $fileHandles;
private $readBytes;
Expand Down Expand Up @@ -118,6 +120,33 @@ class Process
159 => 'Bad syscall',
);

/**
* Returns whether PTY is supported on the current operating system.
*
* @return Boolean
*/
public static function isPtySupported()
{
static $result;

if (null !== $result) {
return $result;
}

if (defined('PHP_WINDOWS_VERSION_BUILD')) {
return $result = false;
}

$proc = @proc_open('echo 1', array(array('pty'), array('pty'), array('pty')), $pipes);
if (is_resource($proc)) {
proc_close($proc);

return $result = true;
}

return $result = false;
}

/**
* Constructor.
*
Expand Down Expand Up @@ -156,6 +185,7 @@ public function __construct($commandline, $cwd = null, array $env = null, $stdin
}
$this->stdin = $stdin;
$this->setTimeout($timeout);
$this->pty = false;
$this->enhanceWindowsCompatibility = true;
$this->enhanceSigchildCompatibility = !defined('PHP_WINDOWS_VERSION_BUILD') && $this->isSigchildEnabled();
$this->options = array_replace(array('suppress_errors' => true, 'binary_pipes' => true), $options);
Expand Down Expand Up @@ -207,6 +237,25 @@ public function run($callback = null)
return $this->wait($callback);
}

/**
* Runs the process.
*
* This is identical to run() except that an exception is thrown if the process
* exits with a non-zero exit code.
*
* @param callable|null $callback
*
* @return self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it be $this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@throws ... ?

*/
public function mustRun($callback = null)
{
if (0 !== $this->run($callback)) {
throw new ProcessFailedException($this);
}

return $this;
}

/**
* Starts the process and returns after sending the STDIN.
*
Expand Down Expand Up @@ -915,6 +964,30 @@ public function isTty()
return $this->tty;
}

/**
* Sets PTY mode.
*
* @param Boolean $bool
*
* @return self
*/
public function setPty($bool)
{
$this->pty = (Boolean) $bool;

return $this;
}

/**
* Returns PTY state.
*
* @return Boolean
*/
public function isPty()
{
return $this->pty;
}

/**
* Gets the working directory.
*
Expand Down Expand Up @@ -1137,6 +1210,12 @@ private function getDescriptors()
array('file', '/dev/tty', 'w'),
array('file', '/dev/tty', 'w'),
);
} elseif ($this->pty && self::isPtySupported()) {
$descriptors = array(
array('pty'),
array('pty'),
array('pty'),
);
} else {
$descriptors = array(
array('pipe', 'r'), // stdin
Expand Down
39 changes: 39 additions & 0 deletions src/Symfony/Component/Process/Tests/AbstractProcessTest.php
Expand Up @@ -200,6 +200,45 @@ public function testTTYCommand()
$this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
}

/**
* @group pty
*/
public function testPTYCommand()
{
if ( ! Process::isPtySupported()) {
$this->markTestSkipped('PTY is not supported on this operating system.');
}

$process = $this->getProcess('echo "foo"');
$process->setPty(true);
$process->run();

$this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
$this->assertEquals("foo\r\n", $process->getOutput());
}

/**
* @group mustRun
*/
public function testMustRun()
{
$process = $this->getProcess('echo "foo"');

$this->assertSame($process, $process->mustRun());
$this->assertEquals("foo\n", $process->getOutput());
$this->assertEquals(0, $process->getExitCode());
}

/**
* @expectedException Symfony\Component\Process\Exception\ProcessFailedException
* @group mustRun
*/
public function testMustRunThrowsException()
{
$process = $this->getProcess('exit 1');
$process->mustRun();
}

public function testExitCodeText()
{
$process = $this->getProcess('');
Expand Down
Expand Up @@ -29,6 +29,24 @@ public function testExitCodeCommandFailed()
parent::testExitCodeCommandFailed();
}

/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @group mustRun
*/
public function testMustRun()
{
parent::testMustRun();
}

/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @group mustRun
*/
public function testMustRunThrowsException()
{
parent::testMustRunThrowsException();
}

/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
*/
Expand Down