From d9c2f5fc15eca9b77f3fe3dc7832324ab5682415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4dlich?= Date: Sun, 23 May 2021 17:28:21 +0200 Subject: [PATCH 1/5] [Console] Escape synopsis output --- Application.php | 2 +- Tests/ApplicationTest.php | 17 +++++++++++++++++ Tests/Command/CommandTest.php | 3 ++- .../application_rendersynopsis_escapesline.txt | 7 +++++++ 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 Tests/Fixtures/application_rendersynopsis_escapesline.txt diff --git a/Application.php b/Application.php index e71a16d10..ed6d00190 100644 --- a/Application.php +++ b/Application.php @@ -833,7 +833,7 @@ public function renderThrowable(\Throwable $e, OutputInterface $output): void private function finishRenderThrowableOrException(OutputInterface $output): void { if (null !== $this->runningCommand) { - $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); + $output->writeln(sprintf('%s', OutputFormatter::escape(sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET); $output->writeln('', OutputInterface::VERBOSITY_QUIET); } } diff --git a/Tests/ApplicationTest.php b/Tests/ApplicationTest.php index d293697d4..1634c0199 100644 --- a/Tests/ApplicationTest.php +++ b/Tests/ApplicationTest.php @@ -940,6 +940,23 @@ public function testRenderExceptionStackTraceContainsRootException() $this->assertStringContainsString('Dummy type "class@anonymous" is invalid.', $tester->getDisplay(true)); } + public function testRenderExceptionEscapesLinesOfSynopsis() + { + $application = new Application(); + $application->setAutoExit(false); + $application + ->register('foo') + ->setCode(function () { + throw new \Exception('some exception'); + }) + ->addArgument('info') + ; + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'foo'], ['decorated' => false]); + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_rendersynopsis_escapesline.txt', $tester->getDisplay(true), '->renderException() escapes lines containing formatting of synopsis'); + } + public function testRun() { $application = new Application(); diff --git a/Tests/Command/CommandTest.php b/Tests/Command/CommandTest.php index 10a476d19..7648b80ff 100644 --- a/Tests/Command/CommandTest.php +++ b/Tests/Command/CommandTest.php @@ -192,7 +192,8 @@ public function testGetSynopsis() $command = new \TestCommand(); $command->addOption('foo'); $command->addArgument('bar'); - $this->assertEquals('namespace:name [--foo] [--] []', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); + $command->addArgument('info'); + $this->assertEquals('namespace:name [--foo] [--] [ []]', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); } public function testAddGetUsages() diff --git a/Tests/Fixtures/application_rendersynopsis_escapesline.txt b/Tests/Fixtures/application_rendersynopsis_escapesline.txt new file mode 100644 index 000000000..a781326c6 --- /dev/null +++ b/Tests/Fixtures/application_rendersynopsis_escapesline.txt @@ -0,0 +1,7 @@ + +In ApplicationTest.php line %d: + + some exception + + +foo [] From c9824f0762622790408dc61ba11913cf42899c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Fri, 4 Jun 2021 00:48:40 +0200 Subject: [PATCH 2/5] [Console] Fix negated options not accessible --- Input/Input.php | 16 ++++++++++++++-- Tests/Input/InputTest.php | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Input/Input.php b/Input/Input.php index dd7658094..3b054c4b6 100644 --- a/Input/Input.php +++ b/Input/Input.php @@ -146,6 +146,14 @@ public function getOptions() */ public function getOption(string $name) { + if ($this->definition->hasNegation($name)) { + if (null === $value = $this->getOption($this->definition->negationToName($name))) { + return $value; + } + + return !$value; + } + if (!$this->definition->hasOption($name)) { throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); } @@ -158,7 +166,11 @@ public function getOption(string $name) */ public function setOption(string $name, $value) { - if (!$this->definition->hasOption($name)) { + if ($this->definition->hasNegation($name)) { + $this->options[$this->definition->negationToName($name)] = !$value; + + return; + } elseif (!$this->definition->hasOption($name)) { throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); } @@ -170,7 +182,7 @@ public function setOption(string $name, $value) */ public function hasOption(string $name) { - return $this->definition->hasOption($name); + return $this->definition->hasOption($name) || $this->definition->hasNegation($name); } /** diff --git a/Tests/Input/InputTest.php b/Tests/Input/InputTest.php index 48c287cd9..6547822fb 100644 --- a/Tests/Input/InputTest.php +++ b/Tests/Input/InputTest.php @@ -45,6 +45,20 @@ public function testOptions() $input = new ArrayInput(['--name' => 'foo', '--bar' => null], new InputDefinition([new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')])); $this->assertNull($input->getOption('bar'), '->getOption() returns null for options explicitly passed without value (or an empty value)'); $this->assertEquals(['name' => 'foo', 'bar' => null], $input->getOptions(), '->getOptions() returns all option values'); + + $input = new ArrayInput(['--name' => null], new InputDefinition([new InputOption('name', null, InputOption::VALUE_NEGATABLE)])); + $this->assertTrue($input->hasOption('name')); + $this->assertTrue($input->hasOption('no-name')); + $this->assertTrue($input->getOption('name')); + $this->assertFalse($input->getOption('no-name')); + + $input = new ArrayInput(['--no-name' => null], new InputDefinition([new InputOption('name', null, InputOption::VALUE_NEGATABLE)])); + $this->assertFalse($input->getOption('name')); + $this->assertTrue($input->getOption('no-name')); + + $input = new ArrayInput([], new InputDefinition([new InputOption('name', null, InputOption::VALUE_NEGATABLE)])); + $this->assertNull($input->getOption('name')); + $this->assertNull($input->getOption('no-name')); } public function testSetInvalidOption() From 9aa1eb46c1b12fada74dc0c529e93d1ccef22576 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sat, 5 Jun 2021 22:03:01 +0200 Subject: [PATCH 3/5] Fix incompatible implicit float-to-int conversions Signed-off-by: Alexander M. Turek --- Helper/ProgressBar.php | 4 ++-- Helper/Table.php | 2 +- Tests/Helper/ProgressBarTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Helper/ProgressBar.php b/Helper/ProgressBar.php index 5049c7dae..1de9b7b3c 100644 --- a/Helper/ProgressBar.php +++ b/Helper/ProgressBar.php @@ -193,7 +193,7 @@ public function getProgressPercent(): float public function getBarOffset(): int { - return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? min(5, $this->barWidth / 15) * $this->writeCount : $this->step) % $this->barWidth); + return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth); } public function setBarWidth(int $size) @@ -249,7 +249,7 @@ public function setFormat(string $format) /** * Sets the redraw frequency. * - * @param int|float $freq The frequency in steps + * @param int|null $freq The frequency in steps */ public function setRedrawFrequency(?int $freq) { diff --git a/Helper/Table.php b/Helper/Table.php index 329f24082..d51aee989 100644 --- a/Helper/Table.php +++ b/Helper/Table.php @@ -454,7 +454,7 @@ private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $tit $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...'); } - $titleStart = ($markupLength - $titleLength) / 2; + $titleStart = intdiv($markupLength - $titleLength, 2); if (false === mb_detect_encoding($markup, null, true)) { $markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength); } else { diff --git a/Tests/Helper/ProgressBarTest.php b/Tests/Helper/ProgressBarTest.php index 2d32e1c3d..9fdaa570d 100644 --- a/Tests/Helper/ProgressBarTest.php +++ b/Tests/Helper/ProgressBarTest.php @@ -535,7 +535,7 @@ public function testRedrawFrequencyIsAtLeastOneIfZeroGiven() public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven() { $bar = new ProgressBar($output = $this->getOutputStream(), 0, 0); - $bar->setRedrawFrequency(0.9); + $bar->setRedrawFrequency(0); $bar->start(); $bar->advance(); From ecc138be35272271a77bc8f384fb0c57be671bde Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 11 Jun 2021 15:29:40 +0200 Subject: [PATCH 4/5] [Console] fix managing signals when commands are lazy loaded --- Application.php | 4 ++++ Tests/ApplicationTest.php | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Application.php b/Application.php index 426b3c4d6..9cdcef88c 100644 --- a/Application.php +++ b/Application.php @@ -287,6 +287,10 @@ public function doRun(InputInterface $input, OutputInterface $output) $command = $this->find($alternative); } + if ($command instanceof LazyCommand) { + $command = $command->getCommand(); + } + $this->runningCommand = $command; $exitCode = $this->doRunCommand($command, $input, $output); $this->runningCommand = null; diff --git a/Tests/ApplicationTest.php b/Tests/ApplicationTest.php index b3958226b..361e174cd 100644 --- a/Tests/ApplicationTest.php +++ b/Tests/ApplicationTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\LazyCommand; use Symfony\Component\Console\Command\SignalableCommandInterface; use Symfony\Component\Console\CommandLoader\FactoryCommandLoader; use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass; @@ -1672,7 +1673,7 @@ public function testRunLazyCommandService() $container = new ContainerBuilder(); $container->addCompilerPass(new AddConsoleCommandPass()); $container - ->register('lazy-command', LazyCommand::class) + ->register('lazy-command', LazyTestCommand::class) ->addTag('console.command', ['command' => 'lazy:command']) ->addTag('console.command', ['command' => 'lazy:alias']) ->addTag('console.command', ['command' => 'lazy:alias2']); @@ -1847,7 +1848,7 @@ public function testSignal() $application->setAutoExit(false); $application->setDispatcher($dispatcher); $application->setSignalsToDispatchEvent(\SIGALRM); - $application->add($command); + $application->add(new LazyCommand('signal', [], '', false, function () use ($command) { return $command; }, true)); $this->assertFalse($command->signaled); $this->assertFalse($dispatcherCalled); @@ -1902,7 +1903,7 @@ public function __construct() } } -class LazyCommand extends Command +class LazyTestCommand extends Command { public function execute(InputInterface $input, OutputInterface $output): int { From 649730483885ff2ca99ca0560ef0e5f6b03f2ac1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 12 Jun 2021 11:39:52 +0200 Subject: [PATCH 5/5] [Console] Fix using #[AsCommand] without DI --- Command/Command.php | 13 ++++++++++++- Tests/Command/CommandTest.php | 7 +++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Command/Command.php b/Command/Command.php index e35ae51eb..71bf5d7c3 100644 --- a/Command/Command.php +++ b/Command/Command.php @@ -101,7 +101,18 @@ public function __construct(string $name = null) { $this->definition = new InputDefinition(); - if (null !== $name || null !== $name = static::getDefaultName()) { + if (null === $name && null !== $name = static::getDefaultName()) { + $aliases = explode('|', $name); + + if ('' === $name = array_shift($aliases)) { + $this->setHidden(true); + $name = array_shift($aliases); + } + + $this->setAliases($aliases); + } + + if (null !== $name) { $this->setName($name); } diff --git a/Tests/Command/CommandTest.php b/Tests/Command/CommandTest.php index 68c036621..839beac3f 100644 --- a/Tests/Command/CommandTest.php +++ b/Tests/Command/CommandTest.php @@ -414,6 +414,13 @@ public function testCommandAttribute() { $this->assertSame('|foo|f', Php8Command::getDefaultName()); $this->assertSame('desc', Php8Command::getDefaultDescription()); + + $command = new Php8Command(); + + $this->assertSame('foo', $command->getName()); + $this->assertSame('desc', $command->getDescription()); + $this->assertTrue($command->isHidden()); + $this->assertSame(['f'], $command->getAliases()); } }