diff --git a/Command/CompleteCommand.php b/Command/CompleteCommand.php index 11ada4e44..0e35143c3 100644 --- a/Command/CompleteCommand.php +++ b/Command/CompleteCommand.php @@ -155,10 +155,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int throw $e; } - return self::FAILURE; + return 2; } - return self::SUCCESS; + return 0; } private function createCompletionInput(InputInterface $input): CompletionInput diff --git a/Command/DumpCompletionCommand.php b/Command/DumpCompletionCommand.php index 6f809e2f1..eaf22be1a 100644 --- a/Command/DumpCompletionCommand.php +++ b/Command/DumpCompletionCommand.php @@ -85,7 +85,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($input->getOption('debug')) { $this->tailDebugLog($commandName, $output); - return self::SUCCESS; + return 0; } $shell = $input->getArgument('shell') ?? self::guessShell(); @@ -102,12 +102,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln(sprintf('Shell not detected, Symfony shell completion only supports "%s").', implode('", "', $supportedShells))); } - return self::INVALID; + return 2; } $output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, $this->getApplication()->getVersion()], file_get_contents($completionFile))); - return self::SUCCESS; + return 0; } private static function guessShell(): string diff --git a/Helper/QuestionHelper.php b/Helper/QuestionHelper.php index 10602038c..e236be92a 100644 --- a/Helper/QuestionHelper.php +++ b/Helper/QuestionHelper.php @@ -128,7 +128,18 @@ private function doAsk(OutputInterface $output, Question $question) } if (false === $ret) { + $isBlocked = stream_get_meta_data($inputStream)['blocked'] ?? true; + + if (!$isBlocked) { + stream_set_blocking($inputStream, true); + } + $ret = $this->readInput($inputStream, $question); + + if (!$isBlocked) { + stream_set_blocking($inputStream, false); + } + if (false === $ret) { throw new MissingInputException('Aborted.'); } @@ -500,13 +511,11 @@ private function isInteractiveInput($inputStream): bool return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r')); } - if (!\function_exists('exec')) { + if (!\function_exists('shell_exec')) { return self::$stdinIsInteractive = true; } - exec('stty 2> /dev/null', $output, $status); - - return self::$stdinIsInteractive = 1 !== $status; + return self::$stdinIsInteractive = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null')); } /** diff --git a/Input/InputArgument.php b/Input/InputArgument.php index ecfcdad58..8a64f7ac8 100644 --- a/Input/InputArgument.php +++ b/Input/InputArgument.php @@ -32,7 +32,7 @@ class InputArgument /** * @param string $name The argument name - * @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param int|null $mode The argument mode: a bit mask of self::REQUIRED, self::OPTIONAL and self::IS_ARRAY * @param string $description A description text * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) * diff --git a/Terminal.php b/Terminal.php index 04653d3bd..b91e8afc5 100644 --- a/Terminal.php +++ b/Terminal.php @@ -64,14 +64,12 @@ public static function hasSttyAvailable(): bool return self::$stty; } - // skip check if exec function is disabled - if (!\function_exists('exec')) { + // skip check if shell_exec function is disabled + if (!\function_exists('shell_exec')) { return false; } - exec('stty 2>&1', $output, $exitcode); - - return self::$stty = 0 === $exitcode; + return self::$stty = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null')); } private static function initDimensions() diff --git a/Tests/Helper/QuestionHelperTest.php b/Tests/Helper/QuestionHelperTest.php index 9761e8f97..74315d898 100644 --- a/Tests/Helper/QuestionHelperTest.php +++ b/Tests/Helper/QuestionHelperTest.php @@ -430,7 +430,7 @@ public function testAskHiddenResponse() $this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("8AM\n")), $this->createOutputInterface(), $question)); } - public function testAskHiddenResponseTrimmed() + public function testAskHiddenResponseNotTrimmed() { if ('\\' === \DIRECTORY_SEPARATOR) { $this->markTestSkipped('This test is not supported on Windows'); @@ -442,7 +442,7 @@ public function testAskHiddenResponseTrimmed() $question->setHidden(true); $question->setTrimmable(false); - $this->assertEquals(' 8AM', $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream(' 8AM')), $this->createOutputInterface(), $question)); + $this->assertEquals(' 8AM'.\PHP_EOL, $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream(' 8AM'.\PHP_EOL)), $this->createOutputInterface(), $question)); } public function testAskMultilineResponseWithEOF() diff --git a/Tests/TerminalTest.php b/Tests/TerminalTest.php index c2d36fe0a..34d32b08e 100644 --- a/Tests/TerminalTest.php +++ b/Tests/TerminalTest.php @@ -77,8 +77,8 @@ public function testSttyOnWindows() $this->markTestSkipped('Must be on windows'); } - $sttyString = exec('(stty -a | grep columns) 2>&1', $output, $exitcode); - if (0 !== $exitcode) { + $sttyString = shell_exec('(stty -a | grep columns) 2> NUL'); + if (!$sttyString) { $this->markTestSkipped('Must have stty support'); }