Skip to content

Commit bbcf7d6

Browse files
kbondfabpot
authored andcommitted
[Console][Messenger] add RunCommandMessage and RunCommandMessageHandler
1 parent 0eb64cd commit bbcf7d6

7 files changed

Lines changed: 252 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Add `SignalMap` to map signal value to its name
88
* Multi-line text in vertical tables is aligned properly
99
* The application can also catch errors with `Application::setCatchErrors(true)`
10+
* Add `RunCommandMessage` and `RunCommandMessageHandler`
1011

1112
6.3
1213
---
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
namespaceSymfony\Component\Console\Exception;
13+
14+
useSymfony\Component\Console\Messenger\RunCommandContext;
15+
16+
/**
17+
* @author Kevin Bond <kevinbond@gmail.com>
18+
*/
19+
finalclass RunCommandFailedException extends RuntimeException
20+
{
21+
publicfunction__construct(\Throwable|string$exception, publicreadonlyRunCommandContext$context)
22+
{
23+
parent::__construct(
24+
$exceptioninstanceof \Throwable ? $exception->getMessage() : $exception,
25+
$exceptioninstanceof \Throwable ? $exception->getCode() : 0,
26+
$exceptioninstanceof \Throwable ? $exception : null,
27+
);
28+
}
29+
}

‎Messenger/RunCommandContext.php‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
namespaceSymfony\Component\Console\Messenger;
13+
14+
/**
15+
* @author Kevin Bond <kevinbond@gmail.com>
16+
*/
17+
finalclass RunCommandContext extends RunCommandMessage
18+
{
19+
publicfunction__construct(RunCommandMessage$message, publicreadonlyint$exitCode, publicreadonlystring$output)
20+
{
21+
parent::__construct($message->input, $message->throwOnFailure, $message->catchExceptions);
22+
}
23+
}

‎Messenger/RunCommandMessage.php‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
namespaceSymfony\Component\Console\Messenger;
13+
14+
useSymfony\Component\Console\Exception\RunCommandFailedException;
15+
16+
/**
17+
* @author Kevin Bond <kevinbond@gmail.com>
18+
*/
19+
class RunCommandMessage implements \Stringable
20+
{
21+
/**
22+
* @param bool $throwOnFailure If the command has a non-zero exit code, throw {@see RunCommandFailedException}
23+
* @param bool $catchExceptions @see Application::setCatchExceptions()
24+
*/
25+
publicfunction__construct(
26+
publicreadonlystring$input,
27+
publicreadonlybool$throwOnFailure = true,
28+
publicreadonlybool$catchExceptions = false,
29+
) {
30+
}
31+
32+
publicfunction__toString(): string
33+
{
34+
return$this->input;
35+
}
36+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
namespaceSymfony\Component\Console\Messenger;
13+
14+
useSymfony\Component\Console\Application;
15+
useSymfony\Component\Console\Command\Command;
16+
useSymfony\Component\Console\Exception\RunCommandFailedException;
17+
useSymfony\Component\Console\Input\StringInput;
18+
useSymfony\Component\Console\Output\BufferedOutput;
19+
20+
/**
21+
* @author Kevin Bond <kevinbond@gmail.com>
22+
*/
23+
finalclass RunCommandMessageHandler
24+
{
25+
publicfunction__construct(privatereadonlyApplication$application)
26+
{
27+
}
28+
29+
publicfunction__invoke(RunCommandMessage$message): RunCommandContext
30+
{
31+
$input = newStringInput($message->input);
32+
$output = newBufferedOutput();
33+
34+
$this->application->setCatchExceptions($message->catchExceptions);
35+
36+
try {
37+
$exitCode = $this->application->run($input, $output);
38+
} catch (\Throwable$e) {
39+
thrownewRunCommandFailedException($e, newRunCommandContext($message, Command::FAILURE, $output->fetch()));
40+
}
41+
42+
if ($message->throwOnFailure && Command::SUCCESS !== $exitCode) {
43+
thrownewRunCommandFailedException(sprintf('Command "%s" exited with code "%s".', $message->input, $exitCode), newRunCommandContext($message, $exitCode, $output->fetch()));
44+
}
45+
46+
returnnewRunCommandContext($message, $exitCode, $output->fetch());
47+
}
48+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
namespaceSymfony\Component\Console\Tests\Messenger;
13+
14+
usePHPUnit\Framework\TestCase;
15+
useSymfony\Component\Console\Application;
16+
useSymfony\Component\Console\Command\Command;
17+
useSymfony\Component\Console\Exception\RunCommandFailedException;
18+
useSymfony\Component\Console\Input\InputInterface;
19+
useSymfony\Component\Console\Input\InputOption;
20+
useSymfony\Component\Console\Messenger\RunCommandMessage;
21+
useSymfony\Component\Console\Messenger\RunCommandMessageHandler;
22+
useSymfony\Component\Console\Output\OutputInterface;
23+
24+
/**
25+
* @author Kevin Bond <kevinbond@gmail.com>
26+
*/
27+
finalclass RunCommandMessageHandlerTest extends TestCase
28+
{
29+
publicfunctiontestExecutesCommand()
30+
{
31+
$handler = newRunCommandMessageHandler($this->createApplicationWithCommand());
32+
$context = $handler(newRunCommandMessage('test:command'));
33+
34+
$this->assertSame(0, $context->exitCode);
35+
$this->assertStringContainsString('some message', $context->output);
36+
}
37+
38+
publicfunctiontestExecutesCommandThatThrowsException()
39+
{
40+
$handler = newRunCommandMessageHandler($this->createApplicationWithCommand());
41+
42+
try {
43+
$handler(newRunCommandMessage('test:command --throw'));
44+
} catch (RunCommandFailedException$e) {
45+
$this->assertSame(1, $e->context->exitCode);
46+
$this->assertStringContainsString('some message', $e->context->output);
47+
$this->assertInstanceOf(\RuntimeException::class, $e->getPrevious());
48+
$this->assertSame('exception message', $e->getMessage());
49+
50+
return;
51+
}
52+
53+
$this->fail('Exception not thrown.');
54+
}
55+
56+
publicfunctiontestExecutesCommandThatCatchesThrownException()
57+
{
58+
$handler = newRunCommandMessageHandler($this->createApplicationWithCommand());
59+
$context = $handler(newRunCommandMessage('test:command --throw -v', throwOnFailure: false, catchExceptions: true));
60+
61+
$this->assertSame(1, $context->exitCode);
62+
$this->assertStringContainsString('[RuntimeException]', $context->output);
63+
$this->assertStringContainsString('exception message', $context->output);
64+
}
65+
66+
publicfunctiontestThrowOnNonSuccess()
67+
{
68+
$handler = newRunCommandMessageHandler($this->createApplicationWithCommand());
69+
70+
try {
71+
$handler(newRunCommandMessage('test:command --exit=1'));
72+
} catch (RunCommandFailedException$e) {
73+
$this->assertSame(1, $e->context->exitCode);
74+
$this->assertStringContainsString('some message', $e->context->output);
75+
$this->assertSame('Command "test:command --exit=1" exited with code "1".', $e->getMessage());
76+
$this->assertNull($e->getPrevious());
77+
78+
return;
79+
}
80+
81+
$this->fail('Exception not thrown.');
82+
}
83+
84+
privatefunctioncreateApplicationWithCommand(): Application
85+
{
86+
$application = newApplication();
87+
$application->setAutoExit(false);
88+
$application->addCommands([
89+
newclass() extends Command {
90+
publicfunctionconfigure(): void
91+
{
92+
$this
93+
->setName('test:command')
94+
->addOption('throw')
95+
->addOption('exit', null, InputOption::VALUE_REQUIRED, 0)
96+
;
97+
}
98+
99+
protectedfunctionexecute(InputInterface$input, OutputInterface$output): int
100+
{
101+
$output->write('some message');
102+
103+
if ($input->getOption('throw')) {
104+
thrownew \RuntimeException('exception message');
105+
}
106+
107+
return (int) $input->getOption('exit');
108+
}
109+
},
110+
]);
111+
112+
return$application;
113+
}
114+
}

‎composer.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"symfony/event-dispatcher": "^5.4|^6.0|^7.0",
2828
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
2929
"symfony/lock": "^5.4|^6.0|^7.0",
30+
"symfony/messenger": "^5.4|^6.0|^7.0",
3031
"symfony/process": "^5.4|^6.0|^7.0",
3132
"symfony/var-dumper": "^5.4|^6.0|^7.0",
3233
"psr/log": "^1|^2|^3"

0 commit comments

Comments
 (0)