bug #37469 [Console] always use stty when possible to ask hidden questions (nicolas-grekas)
This PR was merged into the 4.4 branch.
Discussion
----------
[Console] always use stty when possible to ask hidden questions
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | Fix #36565, replaces #36590
| License | MIT
| Doc PR | -
The current code doesn't make much sense: we check `hasSttyAvailable()`, and if the answer is `false`, we still use `stty` directly.
This PR relies on `stream_isatty` and equivalent fallback checks to decide if the password can be hidden or not.
Best reviewed [ignoring whitespaces](https://github.com/symfony/symfony/pull/37469/files?w=1).
Commits
-------
055b605e30
[Console] always use stty when possible to ask hidden questions
This commit is contained in:
commit
fec23313e7
@ -34,6 +34,7 @@ class QuestionHelper extends Helper
|
|||||||
private $inputStream;
|
private $inputStream;
|
||||||
private static $shell;
|
private static $shell;
|
||||||
private static $stty = true;
|
private static $stty = true;
|
||||||
|
private static $stdinIsInteractive;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks a question to the user.
|
* Asks a question to the user.
|
||||||
@ -419,33 +420,26 @@ class QuestionHelper extends Helper
|
|||||||
|
|
||||||
if (self::$stty && Terminal::hasSttyAvailable()) {
|
if (self::$stty && Terminal::hasSttyAvailable()) {
|
||||||
$sttyMode = shell_exec('stty -g');
|
$sttyMode = shell_exec('stty -g');
|
||||||
|
|
||||||
shell_exec('stty -echo');
|
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));
|
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()) {
|
if (false === $value) {
|
||||||
$readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
|
throw new MissingInputException('Aborted.');
|
||||||
$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 ($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;
|
throw $e;
|
||||||
} catch (\Exception $error) {
|
} catch (\Exception $error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$attempts = $attempts ?? -(int) $this->askForever();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw $error;
|
throw $error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function isInteractiveInput($inputStream): bool
|
||||||
* Returns a valid unix shell.
|
|
||||||
*
|
|
||||||
* @return string|bool The valid shell name, false in case no valid shell is found
|
|
||||||
*/
|
|
||||||
private function getShell()
|
|
||||||
{
|
{
|
||||||
if (null !== self::$shell) {
|
if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) {
|
||||||
return self::$shell;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$shell = false;
|
if (null !== self::$stdinIsInteractive) {
|
||||||
|
return self::$stdinIsInteractive;
|
||||||
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 (\function_exists('stream_isatty')) {
|
if (\function_exists('stream_isatty')) {
|
||||||
return stream_isatty($inputStream);
|
return self::$stdinIsInteractive = stream_isatty(fopen('php://stdin', 'r'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\function_exists('posix_isatty')) {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user