diff --git a/Application.php b/Application.php index 1b15e7941..acd326878 100644 --- a/Application.php +++ b/Application.php @@ -36,7 +36,6 @@ use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\StreamableInputInterface; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -805,7 +804,7 @@ public function renderException(\Exception $e, OutputInterface $output) public function renderThrowable(\Throwable $e, OutputInterface $output): void { - if (__CLASS__ !== \get_class($this) && __CLASS__ === (new \ReflectionMethod($this, 'renderThrowable'))->getDeclaringClass()->getName() && __CLASS__ !== (new \ReflectionMethod($this, 'renderException'))->getDeclaringClass()->getName()) { + if (__CLASS__ !== static::class && __CLASS__ === (new \ReflectionMethod($this, 'renderThrowable'))->getDeclaringClass()->getName() && __CLASS__ !== (new \ReflectionMethod($this, 'renderException'))->getDeclaringClass()->getName()) { @trigger_error(sprintf('The "%s::renderException()" method is deprecated since Symfony 4.4, use "renderThrowable()" instead.', __CLASS__), E_USER_DEPRECATED); if (!$e instanceof \Exception) { @@ -844,7 +843,7 @@ protected function doRenderException(\Exception $e, OutputInterface $output) protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void { - if (__CLASS__ !== \get_class($this) && __CLASS__ === (new \ReflectionMethod($this, 'doRenderThrowable'))->getDeclaringClass()->getName() && __CLASS__ !== (new \ReflectionMethod($this, 'doRenderException'))->getDeclaringClass()->getName()) { + if (__CLASS__ !== static::class && __CLASS__ === (new \ReflectionMethod($this, 'doRenderThrowable'))->getDeclaringClass()->getName() && __CLASS__ !== (new \ReflectionMethod($this, 'doRenderException'))->getDeclaringClass()->getName()) { @trigger_error(sprintf('The "%s::doRenderException()" method is deprecated since Symfony 4.4, use "doRenderThrowable()" instead.', __CLASS__), E_USER_DEPRECATED); if (!$e instanceof \Exception) { @@ -947,16 +946,6 @@ protected function configureIO(InputInterface $input, OutputInterface $output) if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) { $input->setInteractive(false); - } elseif (\function_exists('posix_isatty')) { - $inputStream = null; - - if ($input instanceof StreamableInputInterface) { - $inputStream = $input->getStream(); - } - - if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { - $input->setInteractive(false); - } } switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) { diff --git a/Command/Command.php b/Command/Command.php index 00010d6db..84aceafc1 100644 --- a/Command/Command.php +++ b/Command/Command.php @@ -55,7 +55,7 @@ class Command */ public static function getDefaultName() { - $class = \get_called_class(); + $class = static::class; $r = new \ReflectionProperty($class, 'defaultName'); return $class === $r->class ? static::$defaultName : null; @@ -255,7 +255,7 @@ public function run(InputInterface $input, OutputInterface $output) $statusCode = $this->execute($input, $output); if (!\is_int($statusCode)) { - @trigger_error(sprintf('Return value of "%s::execute()" should always be of the type int since Symfony 4.4, %s returned.', \get_class($this), \gettype($statusCode)), E_USER_DEPRECATED); + @trigger_error(sprintf('Return value of "%s::execute()" should always be of the type int since Symfony 4.4, %s returned.', static::class, \gettype($statusCode)), E_USER_DEPRECATED); } } @@ -344,7 +344,7 @@ public function setDefinition($definition) public function getDefinition() { if (null === $this->definition) { - throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', \get_class($this))); + throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class)); } return $this->definition; diff --git a/Helper/Table.php b/Helper/Table.php index 24613bb99..e4a7423a1 100644 --- a/Helper/Table.php +++ b/Helper/Table.php @@ -601,7 +601,9 @@ private function calculateRowCount(): int ++$numberOfRows; // Add row for header separator } - ++$numberOfRows; // Add row for footer separator + if (\count($this->rows) > 0) { + ++$numberOfRows; // Add row for footer separator + } return $numberOfRows; } diff --git a/Style/SymfonyStyle.php b/Style/SymfonyStyle.php index 4f11b2074..5efe2d4b1 100644 --- a/Style/SymfonyStyle.php +++ b/Style/SymfonyStyle.php @@ -295,7 +295,7 @@ public function choice($question, array $choices, $default = null) { if (null !== $default) { $values = array_flip($choices); - $default = $values[$default]; + $default = isset($values[$default]) ? $values[$default] : $default; } return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); diff --git a/Tester/ApplicationTester.php b/Tester/ApplicationTester.php index ced56cff2..4f99da18d 100644 --- a/Tester/ApplicationTester.php +++ b/Tester/ApplicationTester.php @@ -59,19 +59,12 @@ public function run(array $input, $options = []) $this->input->setInteractive($options['interactive']); } - $shellInteractive = getenv('SHELL_INTERACTIVE'); - if ($this->inputs) { $this->input->setStream(self::createStream($this->inputs)); - putenv('SHELL_INTERACTIVE=1'); } $this->initOutput($options); - $this->statusCode = $this->application->run($this->input, $this->output); - - putenv($shellInteractive ? "SHELL_INTERACTIVE=$shellInteractive" : 'SHELL_INTERACTIVE'); - - return $this->statusCode; + return $this->statusCode = $this->application->run($this->input, $this->output); } } diff --git a/Tests/ApplicationTest.php b/Tests/ApplicationTest.php index 40a5eb835..cc68f596e 100644 --- a/Tests/ApplicationTest.php +++ b/Tests/ApplicationTest.php @@ -897,8 +897,7 @@ public function testRenderAnonymousException() $application = new Application(); $application->setAutoExit(false); $application->register('foo')->setCode(function () { - throw new class('') extends \InvalidArgumentException { - }; + throw new class('') extends \InvalidArgumentException { }; }); $tester = new ApplicationTester($application); @@ -908,8 +907,7 @@ public function testRenderAnonymousException() $application = new Application(); $application->setAutoExit(false); $application->register('foo')->setCode(function () { - throw new \InvalidArgumentException(sprintf('Dummy type "%s" is invalid.', \get_class(new class() { - }))); + throw new \InvalidArgumentException(sprintf('Dummy type "%s" is invalid.', \get_class(new class() { }))); }); $tester = new ApplicationTester($application); @@ -922,8 +920,7 @@ public function testRenderExceptionStackTraceContainsRootException() $application = new Application(); $application->setAutoExit(false); $application->register('foo')->setCode(function () { - throw new class('') extends \InvalidArgumentException { - }; + throw new class('') extends \InvalidArgumentException { }; }); $tester = new ApplicationTester($application); @@ -933,8 +930,7 @@ public function testRenderExceptionStackTraceContainsRootException() $application = new Application(); $application->setAutoExit(false); $application->register('foo')->setCode(function () { - throw new \InvalidArgumentException(sprintf('Dummy type "%s" is invalid.', \get_class(new class() { - }))); + throw new \InvalidArgumentException(sprintf('Dummy type "%s" is invalid.', \get_class(new class() { }))); }); $tester = new ApplicationTester($application); @@ -1710,6 +1706,31 @@ public function testAllExcludesDisabledLazyCommand() $this->assertArrayNotHasKey('disabled', $application->all()); } + public function testFindAlternativesDoesNotLoadSameNamespaceCommandsOnExactMatch() + { + $application = new Application(); + $application->setAutoExit(false); + + $loaded = []; + + $application->setCommandLoader(new FactoryCommandLoader([ + 'foo:bar' => function () use (&$loaded) { + $loaded['foo:bar'] = true; + + return (new Command('foo:bar'))->setCode(function () {}); + }, + 'foo' => function () use (&$loaded) { + $loaded['foo'] = true; + + return (new Command('foo'))->setCode(function () {}); + }, + ])); + + $application->run(new ArrayInput(['command' => 'foo']), new NullOutput()); + + $this->assertSame(['foo' => true], $loaded); + } + protected function getDispatcher($skipCommand = false) { $dispatcher = new EventDispatcher(); diff --git a/Tests/Helper/TableTest.php b/Tests/Helper/TableTest.php index 124309dd5..c4acc3fbf 100644 --- a/Tests/Helper/TableTest.php +++ b/Tests/Helper/TableTest.php @@ -951,6 +951,38 @@ public function testAppendRowWithoutSectionOutput() $table->appendRow(['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25']); } + public function testSectionOutputHandlesZeroRowsAfterRender() + { + $sections = []; + $stream = $this->getOutputStream(true); + $output = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter()); + $output->writeln('My Table'); + $table = new Table($output); + $table + ->setHeaders(['ISBN', 'Title', 'Author', 'Price']) + ->setRows([]); + + $table->render(); + + $table->appendRow(['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25']); + + $expected = + <<assertEquals($expected, $this->getOutputContent($output)); + } + public function testIsNotDefinedStyleException() { $this->expectException('Symfony\Component\Console\Exception\InvalidArgumentException'); diff --git a/Tests/phpt/uses_stdin_as_interactive_input.phpt b/Tests/phpt/uses_stdin_as_interactive_input.phpt new file mode 100644 index 000000000..db1bb4ce4 --- /dev/null +++ b/Tests/phpt/uses_stdin_as_interactive_input.phpt @@ -0,0 +1,28 @@ +--STDIN-- +Hello World +--FILE-- +register('app') + ->setCode(function(InputInterface $input, OutputInterface $output) { + $output->writeln((new QuestionHelper())->ask($input, $output, new Question('Foo?'))); + }) + ->getApplication() + ->setDefaultCommand('app', true) + ->run() +; +--EXPECT-- +Foo?Hello World