Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fd64a13

Browse filesBrowse files
committed
[Console] Fix issue with signal handling
1 parent 9b30b94 commit fd64a13
Copy full SHA for fd64a13

File tree

5 files changed

+119
-9
lines changed
Filter options

5 files changed

+119
-9
lines changed

‎src/Symfony/Component/Console/Application.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Application.php
+24-7Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,25 +1012,42 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
10121012
$sttyMode = shell_exec('stty -g');
10131013

10141014
foreach ([\SIGINT, \SIGTERM] as $signal) {
1015-
$this->signalRegistry->register($signal, static function () use ($sttyMode) {
1016-
shell_exec('stty '.$sttyMode);
1017-
});
1015+
$this->signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode));
10181016
}
10191017
}
10201018
}
10211019

1022-
if (null !== $this->dispatcher) {
1020+
if ($this->dispatcher) {
1021+
// We register application signal, so that we can dispatch the event
10231022
foreach ($this->signalsToDispatchEvent as $signal) {
1024-
$event = new ConsoleSignalEvent($command, $input, $output, $signal);
1023+
$event = new ConsoleSignalEvent($command, $input, $output, $signal, 0);
10251024

1026-
$this->signalRegistry->register($signal, function () use ($event) {
1025+
$this->signalRegistry->register($signal, function ($signal) use ($event, $command, $commandSignals) {
10271026
$this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL);
1027+
$exitCode = $event->getExitCode();
1028+
1029+
if (\in_array($signal, $commandSignals, true)) {
1030+
$command->handleSignal($signal);
1031+
$exitCode = $command->getExitCode($signal, $exitCode);
1032+
}
1033+
if (null !== $exitCode) {
1034+
exit($exitCode);
1035+
}
10281036
});
10291037
}
1038+
1039+
// then we register command signal, but not if already handled by the dispatcher
1040+
$commandSignals = array_diff($commandSignals, $this->signalsToDispatchEvent);
10301041
}
10311042

10321043
foreach ($commandSignals as $signal) {
1033-
$this->signalRegistry->register($signal, [$command, 'handleSignal']);
1044+
$this->signalRegistry->register($signal, function (int $signal) use ($command): void {
1045+
$command->handleSignal($signal);
1046+
$exitCode = $command->getExitCode($signal, 0);
1047+
if (null !== $exitCode) {
1048+
exit($exitCode);
1049+
}
1050+
});
10341051
}
10351052
}
10361053

‎src/Symfony/Component/Console/Command/SignalableCommandInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Command/SignalableCommandInterface.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
* Interface for command reacting to signal.
1616
*
1717
* @author Grégoire Pineau <lyrixx@lyrix.info>
18+
*
19+
* @method ?int getExitCode(int $signal, ?int $previousExitCode) Returns whether to automatically exit with $exitCode after command handling, or not with null.
1820
*/
1921
interface SignalableCommandInterface
2022
{

‎src/Symfony/Component/Console/Event/ConsoleSignalEvent.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Event/ConsoleSignalEvent.php
+20-1Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,34 @@
2121
final class ConsoleSignalEvent extends ConsoleEvent
2222
{
2323
private int $handlingSignal;
24+
private ?int $exitCode;
2425

25-
public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal)
26+
public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal, ?int $exitCode = 0)
2627
{
2728
parent::__construct($command, $input, $output);
2829
$this->handlingSignal = $handlingSignal;
30+
$this->exitCode = $exitCode;
2931
}
3032

3133
public function getHandlingSignal(): int
3234
{
3335
return $this->handlingSignal;
3436
}
37+
38+
/**
39+
* Sets whether to automatically exit with $exitCode after command handling, or not with null.
40+
*/
41+
public function setExitCode(?int $exitCode): void
42+
{
43+
if (null !== $exitCode && ($exitCode < 0 || $exitCode > 255)) {
44+
throw new \InvalidArgumentException('Exit code must be between 0 and 255 or null.');
45+
}
46+
47+
$this->exitCode = $exitCode;
48+
}
49+
50+
public function getExitCode(): ?int
51+
{
52+
return $this->exitCode;
53+
}
3554
}

‎src/Symfony/Component/Console/Tests/ApplicationTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Tests/ApplicationTest.php
+14-1Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1929,7 +1929,8 @@ public function testSignalListener()
19291929

19301930
$dispatcherCalled = false;
19311931
$dispatcher = new EventDispatcher();
1932-
$dispatcher->addListener('console.signal', function () use (&$dispatcherCalled) {
1932+
$dispatcher->addListener('console.signal', function ($e) use (&$dispatcherCalled) {
1933+
$e->setExitCode(null);
19331934
$dispatcherCalled = true;
19341935
});
19351936

@@ -2222,6 +2223,11 @@ public function handleSignal(int $signal): void
22222223
$this->signaled = true;
22232224
$this->signalHandlers[] = __CLASS__;
22242225
}
2226+
2227+
public function getExitCode(int $signal, ?int $previousExitCode): ?int
2228+
{
2229+
return null;
2230+
}
22252231
}
22262232

22272233
#[AsCommand(name: 'signal')]
@@ -2237,6 +2243,11 @@ public function handleSignal(int $signal): void
22372243
$this->signaled = true;
22382244
$this->signalHandlers[] = __CLASS__;
22392245
}
2246+
2247+
public function getExitCode(int $signal, ?int $previousExitCode): ?int
2248+
{
2249+
return null;
2250+
}
22402251
}
22412252

22422253
class SignalEventSubscriber implements EventSubscriberInterface
@@ -2248,6 +2259,8 @@ public function onSignal(ConsoleSignalEvent $event): void
22482259
$this->signaled = true;
22492260
$event->getCommand()->signaled = true;
22502261
$event->getCommand()->signalHandlers[] = __CLASS__;
2262+
2263+
$event->setExitCode(null);
22512264
}
22522265

22532266
public static function getSubscribedEvents(): array
+59Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
--TEST--
2+
Test command that exist
3+
--SKIPIF--
4+
<?php if (!\defined('SIGINT'));
5+
--FILE--
6+
<?php
7+
8+
use Symfony\Component\Console\Application;
9+
use Symfony\Component\Console\Command\Command;
10+
use Symfony\Component\Console\Command\SignalableCommandInterface;
11+
use Symfony\Component\Console\Helper\QuestionHelper;
12+
use Symfony\Component\Console\Input\InputInterface;
13+
use Symfony\Component\Console\Output\OutputInterface;
14+
use Symfony\Component\Console\Question\Question;
15+
16+
$vendor = __DIR__;
17+
while (!file_exists($vendor.'/vendor')) {
18+
$vendor = \dirname($vendor);
19+
}
20+
require $vendor.'/vendor/autoload.php';
21+
22+
class MyCommand extends Command implements SignalableCommandInterface
23+
{
24+
protected function execute(InputInterface $input, OutputInterface $output): int
25+
{
26+
posix_kill(posix_getpid(), \SIGINT);
27+
28+
$output->writeln('should not be displayed');
29+
30+
return 0;
31+
}
32+
33+
34+
public function getSubscribedSignals(): array
35+
{
36+
return [\SIGINT];
37+
}
38+
39+
public function handleSignal(int $signal): void
40+
{
41+
echo "Received signal!";
42+
}
43+
44+
public function getExitCode(int $signal, ?int $previousExitCode): ?int
45+
{
46+
return 12;
47+
}
48+
}
49+
50+
$app = new Application();
51+
$app->setDispatcher(new \Symfony\Component\EventDispatcher\EventDispatcher());
52+
$app->add(new MyCommand('foo'));
53+
54+
$app
55+
->setDefaultCommand('foo', true)
56+
->run()
57+
;
58+
--EXPECT--
59+
Received signal!

0 commit comments

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