2121use Symfony \Component \Console \DependencyInjection \AddConsoleCommandPass ;
2222use Symfony \Component \Console \Event \ConsoleCommandEvent ;
2323use Symfony \Component \Console \Event \ConsoleErrorEvent ;
24+ use Symfony \Component \Console \Event \ConsoleSignalEvent ;
2425use Symfony \Component \Console \Event \ConsoleTerminateEvent ;
2526use Symfony \Component \Console \Exception \CommandNotFoundException ;
2627use Symfony \Component \Console \Exception \NamespaceNotFoundException ;
4243use Symfony \Component \Console \Tester \ApplicationTester ;
4344use Symfony \Component \DependencyInjection \ContainerBuilder ;
4445use Symfony \Component \EventDispatcher \EventDispatcher ;
46+ use Symfony \Component \EventDispatcher \EventDispatcherInterface ;
47+ use Symfony \Component \EventDispatcher \EventSubscriberInterface ;
4548use Symfony \Component \Process \Process ;
4649
4750class 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
19222005class 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