Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0b7cd1d

Browse files
Extract dispatching console signal handling and include subscribers
1 parent 0fc8f7a commit 0b7cd1d

File tree

2 files changed

+157
-38
lines changed

2 files changed

+157
-38
lines changed

src/Symfony/Component/Console/Application.php

Lines 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

Lines changed: 137 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
1982-
{
1983-
return SignalRegistry::isSupported() ? [\SIGALRM] : [];
1984-
}
1985-
1986-
public function handleSignal(int $signal): void
2065+
public function __construct(bool $emitsSignal = true)
19872066
{
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,33 @@ 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+
public $signaled = false;
2105+
2106+
public function onSignal(ConsoleSignalEvent $event): void
2107+
{
2108+
$this->signaled = true;
2109+
$event->getCommand()->signaled = true;
2110+
}
2111+
2112+
public static function getSubscribedEvents(): array
2113+
{
2114+
return ['console.signal' => 'onSignal'];
2115+
}
2116+
}

0 commit comments

Comments
 (0)