Skip to content

Navigation Menu

Sign in
Appearance settings
Sign up
Appearance settings

Commit fe42885

Browse filesBrowse the repository at this point in the historyBrowse files
Spomkynicolas-grekas
authored andcommitted
[Console][FrameworkBundle] Fix profiling a command stopped at ConsoleEvents::COMMAND
1 parent cb1b28d commit fe42885
Copy full SHA for fe42885

5 files changed

+151-7Lines changed: 151 additions & 7 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/Symfony/Bundle/FrameworkBundle/EventListener/ConsoleProfilerListener.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/EventListener/ConsoleProfilerListener.php
+11-2Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,17 @@ public function profile(ConsoleTerminateEvent $event): void
122122
}
123123
}
124124

125-
$request->command->exitCode = $event->getExitCode();
126-
$request->command->interruptedBySignal = $event->getInterruptingSignal();
125+
$command = $request->command;
126+
$command->exitCode = $event->getExitCode();
127+
$command->interruptedBySignal = $event->getInterruptingSignal();
128+
129+
if (!isset($command->input)) {
130+
// the command was stopped before it could run and record what it was given
131+
$command->input = $input = $event->getInput();
132+
$command->output = $event->getOutput();
133+
$command->arguments = $input->getArguments();
134+
$command->options = $input->getOptions();
135+
}
127136

128137
$profile = $this->profiler->collect($request, $request->getResponse(), $error);
129138
$this->profiles[$request] = $profile;
Collapse file
+108Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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\Bundle\FrameworkBundle\Tests\Functional;
13+
14+
use Symfony\Bundle\FrameworkBundle\Console\Application;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\ConsoleEvents;
17+
use Symfony\Component\Console\Event\ConsoleCommandEvent;
18+
use Symfony\Component\Console\Input\ArrayInput;
19+
use Symfony\Component\Console\Input\InputArgument;
20+
use Symfony\Component\Console\Output\NullOutput;
21+
use Symfony\Component\HttpKernel\Profiler\Profile;
22+
23+
class ConsoleProfilerTest extends AbstractWebTestCase
24+
{
25+
protected function setUp(): void
26+
{
27+
static::bootKernel(['test_case' => 'ConsoleProfiler', 'debug' => true]);
28+
static::getContainer()->get('public_profiler')->purge();
29+
}
30+
31+
public function testProfileCommandDisabledByListener()
32+
{
33+
$application = $this->createApplication();
34+
$this->stopCommandWith(static fn (ConsoleCommandEvent $event) => $event->disableCommand());
35+
36+
$exitCode = $application->run(new ArrayInput(['command' => 'app:profiled', 'name' => 'Fabien', '--profile' => true]), new NullOutput());
37+
38+
$this->assertSame(ConsoleCommandEvent::RETURN_CODE_DISABLED, $exitCode);
39+
40+
$profile = $this->loadProfile();
41+
42+
$this->assertStringContainsString('app:profiled', $profile->getUrl());
43+
$this->assertSame(ConsoleCommandEvent::RETURN_CODE_DISABLED, $profile->getStatusCode());
44+
$this->assertSame('Fabien', $profile->getCollector('command')->getArguments()['name']->getValue());
45+
}
46+
47+
public function testProfileCommandStoppedByThrowingListener()
48+
{
49+
$application = $this->createApplication();
50+
$this->stopCommandWith(static fn () => throw new \RuntimeException('Access denied.'));
51+
52+
try {
53+
$application->run(new ArrayInput(['command' => 'app:profiled', 'name' => 'Fabien', '--profile' => true]), new NullOutput());
54+
$this->fail('The listener should have stopped the command.');
55+
} catch (\RuntimeException $e) {
56+
$this->assertSame('Access denied.', $e->getMessage());
57+
}
58+
59+
$profile = $this->loadProfile();
60+
61+
$this->assertSame(1, $profile->getStatusCode());
62+
$this->assertSame('Fabien', $profile->getCollector('command')->getArguments()['name']->getValue());
63+
$this->assertTrue($profile->getCollector('exception')->hasException());
64+
}
65+
66+
public function testProfileCommandThatRuns()
67+
{
68+
$application = $this->createApplication();
69+
70+
$exitCode = $application->run(new ArrayInput(['command' => 'app:profiled', 'name' => 'Fabien', '--profile' => true]), new NullOutput());
71+
72+
$this->assertSame(Command::SUCCESS, $exitCode);
73+
74+
$profile = $this->loadProfile();
75+
76+
$this->assertSame(Command::SUCCESS, $profile->getStatusCode());
77+
$this->assertSame('Fabien', $profile->getCollector('command')->getArguments()['name']->getValue());
78+
}
79+
80+
private function createApplication(): Application
81+
{
82+
$command = new Command('app:profiled');
83+
$command->addArgument('name', InputArgument::REQUIRED);
84+
$command->setCode(static fn (): int => Command::SUCCESS);
85+
86+
$application = new Application(static::$kernel);
87+
$application->setAutoExit(false);
88+
$application->setCatchExceptions(false);
89+
$application->add($command);
90+
91+
return $application;
92+
}
93+
94+
private function stopCommandWith(callable $listener): void
95+
{
96+
static::getContainer()->get('event_dispatcher')->addListener(ConsoleEvents::COMMAND, $listener);
97+
}
98+
99+
private function loadProfile(): Profile
100+
{
101+
$profiler = static::getContainer()->get('public_profiler');
102+
$tokens = $profiler->find('', '', 2, '', '', '');
103+
104+
$this->assertCount(1, $tokens);
105+
106+
return $profiler->loadProfile($tokens[0]['token']);
107+
}
108+
}
Collapse file
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
13+
14+
return [
15+
new FrameworkBundle(),
16+
];
Collapse file
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
imports:
2+
- { resource: ../config/default.yml }
3+
4+
framework:
5+
http_method_override: false
6+
profiler:
7+
enabled: true
8+
collect: true
9+
10+
services:
11+
public_profiler:
12+
alias: profiler
13+
public: true
Collapse file

‎src/Symfony/Component/Console/Debug/CliRequest.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Debug/CliRequest.php
+3-5Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,12 @@ public function getMethod(): string
4949
public function getResponse(): Response
5050
{
5151
return new class($this->command->exitCode) extends Response {
52-
public function __construct(private readonly int $exitCode)
52+
public function __construct(int $exitCode)
5353
{
5454
parent::__construct();
55-
}
5655

57-
public function getStatusCode(): int
58-
{
59-
return $this->exitCode;
56+
// getStatusCode() is final and setStatusCode() rejects an exit code
57+
$this->statusCode = $exitCode;
6058
}
6159
};
6260
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.