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 4068bc9

Browse filesBrowse files
Extract dispatching console signal handling and include subscribers
1 parent 0fc8f7a commit 4068bc9
Copy full SHA for 4068bc9

File tree

2 files changed

+158
-38
lines changed
Filter options

2 files changed

+158
-38
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Application.php
+20-15Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -974,22 +974,31 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
974974
}
975975
}
976976

977-
if ($command instanceof SignalableCommandInterface && ($this->signalsToDispatchEvent || $command->getSubscribedSignals())) {
978-
if (!$this->signalRegistry) {
979-
throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
980-
}
977+
if ($this->signalsToDispatchEvent) {
978+
$commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];
979+
$dispatchSignals = $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::SIGNAL);
981980

982-
if (Terminal::hasSttyAvailable()) {
983-
$sttyMode = shell_exec('stty -g');
981+
if ($commandSignals || $dispatchSignals) {
982+
if (!$this->signalRegistry) {
983+
throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
984+
}
984985

985-
foreach ([\SIGINT, \SIGTERM] as $signal) {
986-
$this->signalRegistry->register($signal, static function () use ($sttyMode) {
987-
shell_exec('stty '.$sttyMode);
988-
});
986+
if (Terminal::hasSttyAvailable()) {
987+
$sttyMode = shell_exec('stty -g');
988+
989+
foreach ([\SIGINT, \SIGTERM] as $signal) {
990+
$this->signalRegistry->register($signal, static function () use ($sttyMode) {
991+
shell_exec('stty '.$sttyMode);
992+
});
993+
}
994+
}
995+
996+
foreach ($commandSignals as $signal) {
997+
$this->signalRegistry->register($signal, [$command, 'handleSignal']);
989998
}
990999
}
9911000

992-
if ($this->dispatcher) {
1001+
if ($dispatchSignals) {
9931002
foreach ($this->signalsToDispatchEvent as $signal) {
9941003
$event = new ConsoleSignalEvent($command, $input, $output, $signal);
9951004

@@ -1005,10 +1014,6 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
10051014
});
10061015
}
10071016
}
1008-
1009-
foreach ($command->getSubscribedSignals() as $signal) {
1010-
$this->signalRegistry->register($signal, [$command, 'handleSignal']);
1011-
}
10121017
}
10131018

10141019
if (null === $this->dispatcher) {

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Tests/ApplicationTest.php
+138-23Lines changed: 138 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
2222
use Symfony\Component\Console\Event\ConsoleCommandEvent;
2323
use Symfony\Component\Console\Event\ConsoleErrorEvent;
24+
use Symfony\Component\Console\Event\ConsoleSignalEvent;
2425
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
2526
use Symfony\Component\Console\Exception\CommandNotFoundException;
2627
use Symfony\Component\Console\Exception\NamespaceNotFoundException;
@@ -42,6 +43,8 @@
4243
use Symfony\Component\Console\Tester\ApplicationTester;
4344
use Symfony\Component\DependencyInjection\ContainerBuilder;
4445
use Symfony\Component\EventDispatcher\EventDispatcher;
46+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
47+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
4548
use Symfony\Component\Process\Process;
4649

4750
class ApplicationTest extends TestCase
@@ -1843,39 +1846,107 @@ public function testCommandNameMismatchWithCommandLoaderKeyThrows()
18431846
/**
18441847
* @requires extension pcntl
18451848
*/
1846-
public function testSignal()
1849+
public function testSignalListenerNotCalledByDefault(): void
18471850
{
1848-
$command = new SignableCommand();
1851+
$command = new SignableCommand(false);
18491852

18501853
$dispatcherCalled = false;
18511854
$dispatcher = new EventDispatcher();
18521855
$dispatcher->addListener('console.signal', function () use (&$dispatcherCalled) {
18531856
$dispatcherCalled = true;
18541857
});
18551858

1856-
$application = new Application();
1857-
$application->setAutoExit(false);
1858-
$application->setDispatcher($dispatcher);
1859-
$application->setSignalsToDispatchEvent(\SIGALRM);
1860-
$application->add(new LazyCommand('signal', [], '', false, function () use ($command) { return $command; }, true));
1861-
1862-
$this->assertFalse($command->signaled);
1863-
$this->assertFalse($dispatcherCalled);
1859+
$application = $this->createSignalableApplication($command, $dispatcher);
18641860

18651861
$this->assertSame(0, $application->run(new ArrayInput(['signal'])));
18661862
$this->assertFalse($command->signaled);
18671863
$this->assertFalse($dispatcherCalled);
1864+
}
1865+
1866+
/**
1867+
* @requires extension pcntl
1868+
*/
1869+
public function testSignalListener(): void
1870+
{
1871+
$command = new SignableCommand();
1872+
1873+
$dispatcherCalled = false;
1874+
$dispatcher = new EventDispatcher();
1875+
$dispatcher->addListener('console.signal', function () use (&$dispatcherCalled) {
1876+
$dispatcherCalled = true;
1877+
});
1878+
1879+
$application = $this->createSignalableApplication($command, $dispatcher);
18681880

1869-
$command->loop = 100000;
1870-
pcntl_alarm(1);
18711881
$this->assertSame(1, $application->run(new ArrayInput(['signal'])));
1872-
$this->assertTrue($command->signaled);
18731882
$this->assertTrue($dispatcherCalled);
1883+
$this->assertTrue($command->signaled);
1884+
}
1885+
1886+
/**
1887+
* @requires extension pcntl
1888+
*/
1889+
public function testSignalSubscriberNotCalledByDefault(): void
1890+
{
1891+
$command = new BaseSignableCommand(false);
1892+
1893+
$subscriber = new SignalEventSubscriber();
1894+
$dispatcher = new EventDispatcher();
1895+
$dispatcher->addSubscriber($subscriber);
1896+
1897+
$application = $this->createSignalableApplication($command, $dispatcher);
1898+
1899+
$this->assertSame(0, $application->run(new ArrayInput(['signal'])));
1900+
$this->assertFalse($subscriber->signaled);
1901+
}
1902+
1903+
/**
1904+
* @requires extension pcntl
1905+
*/
1906+
public function testSignalSubscriber(): void
1907+
{
1908+
$command = new BaseSignableCommand();
1909+
1910+
$subscriber1 = new SignalEventSubscriber();
1911+
$subscriber2 = new SignalEventSubscriber();
1912+
1913+
$dispatcher = new EventDispatcher();
1914+
$dispatcher->addSubscriber($subscriber1);
1915+
$dispatcher->addSubscriber($subscriber2);
1916+
1917+
$application = $this->createSignalableApplication($command, $dispatcher);
1918+
1919+
$this->assertSame(1, $application->run(new ArrayInput(['signal'])));
1920+
$this->assertTrue($subscriber1->signaled);
1921+
$this->assertTrue($subscriber2->signaled);
1922+
}
1923+
1924+
/**
1925+
* @requires extension pcntl
1926+
*/
1927+
public function testSetSignalsToDispatchEvent(): void
1928+
{
1929+
$command = new BaseSignableCommand();
1930+
1931+
$subscriber = new SignalEventSubscriber();
1932+
1933+
$dispatcher = new EventDispatcher();
1934+
$dispatcher->addSubscriber($subscriber);
1935+
1936+
$application = $this->createSignalableApplication($command, $dispatcher);
1937+
$application->setSignalsToDispatchEvent(\SIGUSR2);
1938+
$this->assertSame(0, $application->run(new ArrayInput(['signal'])));
1939+
$this->assertFalse($subscriber->signaled);
1940+
1941+
$application = $this->createSignalableApplication($command, $dispatcher);
1942+
$application->setSignalsToDispatchEvent(\SIGUSR1);
1943+
$this->assertSame(1, $application->run(new ArrayInput(['signal'])));
1944+
$this->assertTrue($subscriber->signaled);
18741945
}
18751946

18761947
public function testSignalableCommandInterfaceWithoutSignals()
18771948
{
1878-
$command = new SignableCommand();
1949+
$command = new SignableCommand(false);
18791950

18801951
$dispatcher = new EventDispatcher();
18811952
$application = new Application();
@@ -1917,6 +1988,18 @@ public function testSignalableRestoresStty()
19171988

19181989
$this->assertSame($previousSttyMode, $sttyMode);
19191990
}
1991+
1992+
private function createSignalableApplication(Command $command, ?EventDispatcherInterface $dispatcher): Application
1993+
{
1994+
$application = new Application();
1995+
$application->setAutoExit(false);
1996+
if ($dispatcher) {
1997+
$application->setDispatcher($dispatcher);
1998+
}
1999+
$application->add(new LazyCommand('signal', [], '', false, function () use ($command) { return $command; }, true));
2000+
2001+
return $application;
2002+
}
19202003
}
19212004

19222005
class CustomApplication extends Application
@@ -1971,25 +2054,26 @@ public function isEnabled(): bool
19712054
}
19722055
}
19732056

1974-
class SignableCommand extends Command implements SignalableCommandInterface
2057+
class BaseSignableCommand extends Command
19752058
{
19762059
public $signaled = false;
1977-
public $loop = 100;
2060+
public $loop = 1000;
2061+
private $emitsSignal;
19782062

19792063
protected static $defaultName = 'signal';
19802064

1981-
public function getSubscribedSignals(): array
2065+
public function __construct(bool $emitsSignal = true)
19822066
{
1983-
return SignalRegistry::isSupported() ? [\SIGALRM] : [];
1984-
}
1985-
1986-
public function handleSignal(int $signal): void
1987-
{
1988-
$this->signaled = true;
2067+
parent::__construct();
2068+
$this->emitsSignal = $emitsSignal;
19892069
}
19902070

19912071
protected function execute(InputInterface $input, OutputInterface $output): int
19922072
{
2073+
if ($this->emitsSignal) {
2074+
posix_kill(posix_getpid(), SIGUSR1);
2075+
}
2076+
19932077
for ($i = 0; $i < $this->loop; ++$i) {
19942078
usleep(100);
19952079
if ($this->signaled) {
@@ -2000,3 +2084,34 @@ protected function execute(InputInterface $input, OutputInterface $output): int
20002084
return 0;
20012085
}
20022086
}
2087+
2088+
class SignableCommand extends BaseSignableCommand implements SignalableCommandInterface
2089+
{
2090+
protected static $defaultName = 'signal';
2091+
2092+
public function getSubscribedSignals(): array
2093+
{
2094+
return SignalRegistry::isSupported() ? [\SIGUSR1] : [];
2095+
}
2096+
2097+
public function handleSignal(int $signal): void
2098+
{
2099+
$this->signaled = true;
2100+
}
2101+
}
2102+
2103+
class SignalEventSubscriber implements EventSubscriberInterface
2104+
{
2105+
public $signaled = false;
2106+
2107+
public function onSignal(ConsoleSignalEvent $event): void
2108+
{
2109+
$this->signaled = true;
2110+
$event->getCommand()->signaled = true;
2111+
}
2112+
2113+
public static function getSubscribedEvents(): array
2114+
{
2115+
return ['console.signal' => 'onSignal'];
2116+
}
2117+
}

0 commit comments

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