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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy