diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php
index efd05d22c..b2ecdf023 100644
--- a/src/Composer/EventDispatcher/EventDispatcher.php
+++ b/src/Composer/EventDispatcher/EventDispatcher.php
@@ -174,7 +174,7 @@ class EventDispatcher
$flags = $event->getFlags();
if (substr($callable, 0, 10) === '@composer ') {
$exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . ' ' . implode(' ', $args);
- if (0 !== ($exitCode = $this->process->execute($exec, $ignoredOutput, null, $this->io->isInteractive()))) {
+ if (0 !== ($exitCode = $this->executeTty($exec))) {
$this->io->writeError(sprintf('Script %s handling the %s event returned with error code '.$exitCode.'', $callable, $event->getName()), true, IOInterface::QUIET);
throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
@@ -248,7 +248,7 @@ class EventDispatcher
}
}
- if (0 !== ($exitCode = $this->process->execute($exec, $ignoredOutput, null, $this->io->isInteractive()))) {
+ if (0 !== ($exitCode = $this->executeTty($exec))) {
$this->io->writeError(sprintf('Script %s handling the %s event returned with error code '.$exitCode.'', $callable, $event->getName()), true, IOInterface::QUIET);
throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
@@ -265,6 +265,15 @@ class EventDispatcher
return $return;
}
+ protected function executeTty($exec)
+ {
+ if ($this->io->isInteractive()) {
+ return $this->process->executeTty($exec);
+ }
+
+ return $this->process->execute($exec);
+ }
+
protected function getPhpExecCommand()
{
$finder = new PhpExecutableFinder();
diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php
index 905d012e7..a30a04d15 100644
--- a/src/Composer/Util/ProcessExecutor.php
+++ b/src/Composer/Util/ProcessExecutor.php
@@ -15,6 +15,7 @@ namespace Composer\Util;
use Composer\IO\IOInterface;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessUtils;
+use Symfony\Component\Process\Exception\RuntimeException;
/**
* @author Robert Schönthal
@@ -41,7 +42,28 @@ class ProcessExecutor
* @param string $cwd the working directory
* @return int statuscode
*/
- public function execute($command, &$output = null, $cwd = null, $tty = false)
+ public function execute($command, &$output = null, $cwd = null)
+ {
+ if (func_num_args() > 1) {
+ return $this->doExecute($command, $cwd, false, $output);
+ }
+
+ return $this->doExecute($command, $cwd, false);
+ }
+
+ /**
+ * runs a process on the commandline in TTY mode
+ *
+ * @param string $command the command to execute
+ * @param string $cwd the working directory
+ * @return int statuscode
+ */
+ public function executeTty($command, $cwd = null)
+ {
+ return $this->doExecute($command, $cwd, true);
+ }
+
+ private function doExecute($command, $cwd, $tty, &$output = null)
{
if ($this->io && $this->io->isDebug()) {
$safeCommand = preg_replace_callback('{://(?P[^:/\s]+):(?P[^@\s/]+)@}i', function ($m) {
@@ -61,7 +83,7 @@ class ProcessExecutor
$cwd = realpath(getcwd());
}
- $this->captureOutput = func_num_args() > 1;
+ $this->captureOutput = func_num_args() > 3;
$this->errorOutput = null;
// TODO in v3, commands should be passed in as arrays of cmd + args
@@ -71,7 +93,11 @@ class ProcessExecutor
$process = new Process($command, $cwd, null, null, static::getTimeout());
}
if (!Platform::isWindows() && $tty) {
- $process->setTty(true);
+ try {
+ $process->setTty(true);
+ } catch (RuntimeException $e) {
+ // ignore TTY enabling errors
+ }
}
$callback = is_callable($output) ? $output : array($this, 'outputHandler');
diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
index 190d30f9d..1d6dcafe8 100644
--- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
+++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
@@ -538,6 +538,10 @@ class EventDispatcherTest extends TestCase
->willReturn('> exit 1');
$io->expects($this->at(2))
+ ->method('isInteractive')
+ ->willReturn(1);
+
+ $io->expects($this->at(3))
->method('writeError')
->with($this->equalTo('Script '.$code.' handling the post-install-cmd event returned with error code 1'));