8000 [Console] Rework the signal integration · symfony/symfony@df57119 · GitHub
[go: up one dir, main page]

Skip to content

Commit df57119

Browse files
committed
[Console] Rework the signal integration
1 parent ae677cc commit df57119

File tree

4 files changed

+77
-28
lines changed

4 files changed

+77
-28
lines changed

src/Symfony/Component/Console/Application.php

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Console\Command\Command;
1515
use Symfony\Component\Console\Command\HelpCommand;
1616
use Symfony\Component\Console\Command\ListCommand;
17+
use Symfony\Component\Console\Command\SignalableCommandInterface;
1718
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
1819
use Symfony\Component\Console\Event\ConsoleCommandEvent;
1920
use Symfony\Component\Console\Event\ConsoleErrorEvent;
@@ -79,13 +80,18 @@ class Application implements ResetInterface
7980
private $singleCommand = false;
8081
private $initialized;
8182
private $signalRegistry;
83+
private $signalsToDispatchEvent = [];
8284

8385
public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN')
8486
{
8587
$this->name = $name;
8688
$this->version = $version;
8789
$this->terminal = new Terminal();
8890
$this->defaultCommand = 'list';
91+
$this->signalRegistry = new SignalRegistry();
92+
if (\defined('SIGINT')) {
93+
$this->signalsToDispatchEvent = [SIGINT, SIGTERM, SIGUSR1, SIGUSR2];
94+
}
8995
}
9096

9197
/**
@@ -101,9 +107,14 @@ public function setCommandLoader(CommandLoaderInterface $commandLoader)
101107
$this->commandLoader = $commandLoader;
102108
}
103109

104-
public function setSignalRegistry(SignalRegistry $signalRegistry)
110+
public function getSignalRegistry(): SignalRegistry
105111
{
106-
$this->signalRegistry = $signalRegistry;
112+
return $this->signalRegistry;
113+
}
114+
115+
public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent)
116+
{
117+
$this->signalsToDispatchEvent = $signalsToDispatchEvent;
107118
}
108119

109120
/**
@@ -268,14 +279,20 @@ public function doRun(InputInterface $input, OutputInterface $output)
268279
$command = $this->find($alternative);
269280
}
270281

271-
if ($this->signalRegistry) {
272-
foreach ($this->signalRegistry->getHandlingSignals() as $handlingSignal) {
273-
$event = new ConsoleSignalEvent($command, $input, $output, $handlingSignal);
274-
$onSignalHandler = function () use ($event) {
282+
if ($this->dispatcher) {
283+
foreach ($this->signalsToDispatchEvent as $signal) {
284+
$event = new ConsoleSignalEvent($command, $input, $output, $signal);
285+
286+
$this->signalRegistry->register($signal, function ($signal, $hasNext) use ($event) {
275287
$this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL);
276-
};
277288

278-
$this->signalRegistry->register($handlingSignal, $onSignalHandler);
289+
// No more handlers, we try to simulate PHP default behavior
290+
if (!$hasNext) {
291+
if (!\in_array($signal, [SIGUSR1, SIGUSR2], true)) {
292+
exit(0);
293+
}
294+
}
295+
});
279296
}
280297
}
281298

@@ -926,6 +943,12 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
926943
}
927944
}
928945

946+
if ($command instanceof SignalableCommandInterface) {
947+
foreach ($command->getSubscribedSignals() as $signal) {
948+
$this->signalRegistry->register($signal, [$command, 'handleSignal']);
949+
}
950+
}
951+
929952
if (null === $this->dispatcher) {
930953
return $command->run($input, $output);
931954
}

src/Symfony/Component/Console/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ CHANGELOG
77
* Added `SingleCommandApplication::setAutoExit()` to allow testing via `CommandTester`
88
* added support for multiline responses to questions through `Question::setMultiline()`
99
and `Question::isMultiline()`
10+
* Added `SignalRegistry` class to stack signals handlers
11+
* Added support for signals:
12+
* Added `Application::getSignalRegistry()` and `Application::setSignalsToDispatchEvent()` methods
13+
* Added `SignalableCommandInterface` interface
1014

1115
5.1.0
1216
-----
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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\Command;
13+
14+
/**
15+
* Interface for command reacting to signal.
16+
*
17+
* @author Grégoire Pineau <lyrixx@lyrix.info>
18+
*/
19+
interface SignalableCommandInterface
20+
{
21+
/**
22+
* Returns the list of signals to subscribe.
23+
*/
24+
public function getSubscribedSignals(): array;
25+
26+
/**
27+
* The method will be called when the application is signaled.
28+
*/
29+
public function handleSignal(int $signal): void;
30+
}

src/Symfony/Component/Console/SignalRegistry/SignalRegistry.php

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,27 @@
1313

1414
final class SignalRegistry
1515
{
16-
private $registeredSignals = [];
17-
18-
private $handlingSignals = [];
16+
private $signalHandlers = [];
1917

2018
public function __construct()
2119
{
22-
pcntl_async_signals(true);
20+
if (\function_exists('pcntl_async_signals')) {
21+
pcntl_async_signals(true);
22+
}
2323
}
2424

2525
public function register(int $signal, callable $signalHandler): void
2626
{
27-
if (!isset($this->registeredSignals[$signal])) {
27+
if (!isset($this->signalHandlers[$signal])) {
2828
$previousCallback = pcntl_signal_get_handler($signal);
2929

3030
if (\is_callable($previousCallback)) {
31-
$this->registeredSignals[$signal][] = $previousCallback;
31+
$this->signalHandlers[$signal][] = $previousCallback;
3232
}
3333
}
3434

35-
$this->registeredSignals[$signal][] = $signalHandler;
35+
$this->signalHandlers[$signal][] = $signalHandler;
36+
3637
pcntl_signal($signal, [$this, 'handle']);
3738
}
3839

@@ -41,20 +42,11 @@ public function register(int $signal, callable $signalHandler): void
4142
*/
4243
public function handle(int $signal): void
4344
{
44-
foreach ($this->registeredSignals[$signal] as $signalHandler) {
45-
$signalHandler($signal);
46-
}
47-
}
45+
$count = \count($this->signalHandlers[$signal]);
4846

49-
public function addHandlingSignals(int ...$signals): void
50-
{
51-
foreach ($signals as $signal) {
52-
$this->handlingSignals[$signal] = true;
47+
foreach ($this->signalHandlers[$signal] as $i => $signalHandler) {
48+
$hasNext = $i !== $count - 1;
49+
$signalHandler($signal, $hasNext);
5350
}
5451
}
55-
56-
public function getHandlingSignals(): array
57-
{
58-
return array_keys($this->handlingSignals);
59-
}
6052
}

0 commit comments

Comments
 (0)
0