From 055b605e309160f53479f366da129d6ace5baf77 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 1 Jul 2020 14:09:20 +0200 Subject: [PATCH] [Console] always use stty when possible to ask hidden questions --- .../Console/Helper/QuestionHelper.php | 85 +++++++------------ 1 file changed, 29 insertions(+), 56 deletions(-) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 1b60786c96..6efec5b5b6 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -34,6 +34,7 @@ class QuestionHelper extends Helper private $inputStream; private static $shell; private static $stty = true; + private static $stdinIsInteractive; /** * Asks a question to the user. @@ -419,33 +420,26 @@ class QuestionHelper extends Helper if (self::$stty && Terminal::hasSttyAvailable()) { $sttyMode = shell_exec('stty -g'); - shell_exec('stty -echo'); - $value = fgets($inputStream, 4096); + } elseif ($this->isInteractiveInput($inputStream)) { + throw new RuntimeException('Unable to hide the response.'); + } + + $value = fgets($inputStream, 4096); + + if (self::$stty && Terminal::hasSttyAvailable()) { shell_exec(sprintf('stty %s', $sttyMode)); - - if (false === $value) { - throw new MissingInputException('Aborted.'); - } - if ($trimmable) { - $value = trim($value); - } - $output->writeln(''); - - return $value; } - if (false !== $shell = $this->getShell()) { - $readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword'; - $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword' 2> /dev/null", $shell, $readCmd); - $sCommand = shell_exec($command); - $value = $trimmable ? rtrim($sCommand) : $sCommand; - $output->writeln(''); - - return $value; + if (false === $value) { + throw new MissingInputException('Aborted.'); } + if ($trimmable) { + $value = trim($value); + } + $output->writeln(''); - throw new RuntimeException('Unable to hide the response.'); + return $value; } /** @@ -473,56 +467,35 @@ class QuestionHelper extends Helper throw $e; } catch (\Exception $error) { } - - $attempts = $attempts ?? -(int) $this->askForever(); } throw $error; } - /** - * Returns a valid unix shell. - * - * @return string|bool The valid shell name, false in case no valid shell is found - */ - private function getShell() + private function isInteractiveInput($inputStream): bool { - if (null !== self::$shell) { - return self::$shell; + if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) { + return false; } - self::$shell = false; - - if (file_exists('/usr/bin/env')) { - // handle other OSs with bash/zsh/ksh/csh if available to hide the answer - $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; - foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) { - if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { - self::$shell = $sh; - break; - } - } - } - - return self::$shell; - } - - private function askForever(): bool - { - $inputStream = $this->inputStream ?: fopen('php://stdin', 'r'); - - if ('php://stdin' !== (stream_get_meta_data($inputStream)['url'] ?? null)) { - return true; + if (null !== self::$stdinIsInteractive) { + return self::$stdinIsInteractive; } if (\function_exists('stream_isatty')) { - return stream_isatty($inputStream); + return self::$stdinIsInteractive = stream_isatty(fopen('php://stdin', 'r')); } if (\function_exists('posix_isatty')) { - return posix_isatty($inputStream); + return self::$stdinIsInteractive = posix_isatty(fopen('php://stdin', 'r')); } - return true; + if (!\function_exists('exec')) { + return self::$stdinIsInteractive = true; + } + + exec('stty 2> /dev/null', $output, $status); + + return self::$stdinIsInteractive = 1 !== $status; } }