Skip to content

Commit 6317c59

Browse files
bug #61138 [Console] fix profiler with overridden run() method (vinceAmstoutz)
This PR was squashed before being merged into the 6.4 branch. Discussion ---------- [Console] fix profiler with overridden `run()` method | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix #61134 | License | MIT When a command overrides the `run()` method, using the `--profile` option would bypass this custom logic. This was caused by `TraceableCommand` calling `parent::run()` instead of delegating the call to the decorated command. This change ensures `$this->command->run()` is called, preserving the overridden logic and allowing the profiler to work as expected. Commits ------- fb0cd65 [Console] fix profiler with overridden `run()` method
2 parents 1ce1a15 + fb0cd65 commit 6317c59

File tree

4 files changed

+136
-1
lines changed

4 files changed

+136
-1
lines changed

src/Symfony/Component/Console/Command/TraceableCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public function run(InputInterface $input, OutputInterface $output): int
283283
$event = $this->stopwatch->start($this->getName(), 'command');
284284

285285
try {
286-
$this->exitCode = parent::run($input, $output);
286+
$this->exitCode = $this->command->run($input, $output);
287287
} finally {
288288
$event->stop();
289289

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Tests\Command;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Console\Application;
16+
use Symfony\Component\Console\Command\TraceableCommand;
17+
use Symfony\Component\Console\Tester\CommandTester;
18+
use Symfony\Component\Console\Tests\Fixtures\LoopExampleCommand;
19+
use Symfony\Component\Stopwatch\Stopwatch;
20+
21+
class TraceableCommandTest extends TestCase
22+
{
23+
private Application $application;
24+
25+
protected function setUp(): void
26+
{
27+
$this->application = new Application();
28+
$this->application->add(new LoopExampleCommand());
29+
}
30+
31+
public function testRunIsOverriddenWithoutProfile()
32+
{
33+
$command = $this->application->find('app:loop:example');
34+
$commandTester = new CommandTester($command);
35+
$commandTester->execute([]);
36+
$commandTester->assertCommandIsSuccessful();
37+
38+
$output = $commandTester->getDisplay();
39+
$this->assertLoopOutputCorrectness($output);
40+
}
41+
42+
public function testRunIsNotOverriddenWithProfile()
43+
{
44+
// Simulate the bug environment by wrapping
45+
// our command in TraceableCommand, which is what Symfony does
46+
// when you use the --profile option.
47+
$command = new LoopExampleCommand();
48+
$traceableCommand = new TraceableCommand($command, new Stopwatch());
49+
50+
$this->application->add($traceableCommand);
51+
52+
$commandTester = new CommandTester($traceableCommand);
53+
$commandTester->execute([]);
54+
$commandTester->assertCommandIsSuccessful();
55+
56+
$output = $commandTester->getDisplay();
57+
$this->assertLoopOutputCorrectness($output);
58+
}
59+
60+
public function assertLoopOutputCorrectness(string $output)
61+
{
62+
self::assertMatchesRegularExpression('~3/3\s+\[▓+]\s+100%~u', $output);
63+
self::assertStringContainsString('Loop finished.', $output);
64+
self::assertEquals(3, substr_count($output, 'Hello world'));
65+
}
66+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Tests\Fixtures;
13+
14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
use Symfony\Component\Console\Style\SymfonyStyle;
18+
19+
abstract class AbstractLoopCommand extends Command
20+
{
21+
public function run(InputInterface $input, OutputInterface $output): int
22+
{
23+
$io = new SymfonyStyle($input, $output);
24+
$contexts = [1, 2, 3];
25+
$io->progressStart(count($contexts));
26+
$code = self::SUCCESS;
27+
28+
foreach ($contexts as $ignored) {
29+
$io->progressAdvance();
30+
try {
31+
parent::run($input, $output);
32+
} catch (\Throwable) {
33+
$code = self::FAILURE;
34+
}
35+
}
36+
$io->progressFinish();
37+
$output->writeln("\nLoop finished.");
38+
39+
return $code;
40+
}
41+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Tests\Fixtures;
13+
14+
use Symfony\Component\Console\Attribute\AsCommand;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Output\OutputInterface;
18+
19+
#[AsCommand('app:loop:example')]
20+
class LoopExampleCommand extends AbstractLoopCommand
21+
{
22+
protected function execute(InputInterface $input, OutputInterface $output): int
23+
{
24+
$output->writeln(' Hello world');
25+
26+
return Command::SUCCESS;
27+
}
28+
}

0 commit comments

Comments
 (0)