2222use Symfony \Component \Console \Completion \CompletionInput ;
2323use Symfony \Component \Console \Completion \CompletionSuggestions ;
2424use Symfony \Component \Console \Completion \Suggestion ;
25+ use Symfony \Component \Console \Event \ConsoleAlarmEvent ;
2526use Symfony \Component \Console \Event \ConsoleCommandEvent ;
2627use Symfony \Component \Console \Event \ConsoleErrorEvent ;
2728use Symfony \Component \Console \Event \ConsoleSignalEvent ;
@@ -88,6 +89,7 @@ class Application implements ResetInterface
8889 private bool $ initialized = false ;
8990 private ?SignalRegistry $ signalRegistry = null ;
9091 private array $ signalsToDispatchEvent = [];
92+ private ?int $ alarmInterval = null ;
9193
9294 public function __construct (
9395 private string $ name = 'UNKNOWN ' ,
@@ -97,7 +99,7 @@ public function __construct(
9799 $ this ->defaultCommand = 'list ' ;
98100 if (\defined ('SIGINT ' ) && SignalRegistry::isSupported ()) {
99101 $ this ->signalRegistry = new SignalRegistry ();
100- $ this ->signalsToDispatchEvent = [\SIGINT , \SIGQUIT , \SIGTERM , \SIGUSR1 , \SIGUSR2 ];
102+ $ this ->signalsToDispatchEvent = [\SIGINT , \SIGQUIT , \SIGTERM , \SIGUSR1 , \SIGUSR2 , \ SIGALRM ];
101103 }
102104 }
103105
@@ -128,6 +130,22 @@ public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent): void
128130 $ this ->signalsToDispatchEvent = $ signalsToDispatchEvent ;
129131 }
130132
133+ /**
134+ * Sets the interval to schedule a SIGALRM signal in seconds.
135+ */
136+ public function setAlarmInterval (?int $ seconds ): void
137+ {
138+ $ this ->alarmInterval = $ seconds ;
139+ $ this ->scheduleAlarm ();
140+ }
141+
142+ private function scheduleAlarm (): void
143+ {
144+ if (null !== $ this ->alarmInterval ) {
145+ $ this ->getSignalRegistry ()->scheduleAlarm ($ this ->alarmInterval );
146+ }
147+ }
148+
131149 /**
132150 * Runs the current application.
133151 *
@@ -981,34 +999,47 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
981999
9821000 $ commandSignals = $ command instanceof SignalableCommandInterface ? $ command ->getSubscribedSignals () : [];
9831001 if ($ commandSignals || $ this ->dispatcher && $ this ->signalsToDispatchEvent ) {
984- if (!$ this ->signalRegistry ) {
985- 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. ' );
986- }
1002+ $ signalRegistry = $ this ->getSignalRegistry ();
9871003
9881004 if (Terminal::hasSttyAvailable ()) {
9891005 $ sttyMode = shell_exec ('stty -g ' );
9901006
9911007 foreach ([\SIGINT , \SIGQUIT , \SIGTERM ] as $ signal ) {
992- $ this -> signalRegistry ->register ($ signal , static fn () => shell_exec ('stty ' .$ sttyMode ));
1008+ $ signalRegistry ->register ($ signal , static fn () => shell_exec ('stty ' .$ sttyMode ));
9931009 }
9941010 }
9951011
9961012 if ($ this ->dispatcher ) {
9971013 // We register application signals, so that we can dispatch the event
9981014 foreach ($ this ->signalsToDispatchEvent as $ signal ) {
999- $ event = new ConsoleSignalEvent ($ command , $ input , $ output , $ signal );
1000-
1001- $ this ->signalRegistry ->register ($ signal , function ($ signal ) use ($ event , $ command , $ commandSignals ) {
1002- $ this ->dispatcher ->dispatch ($ event , ConsoleEvents::SIGNAL );
1003- $ exitCode = $ event ->getExitCode ();
1015+ $ signalEvent = new ConsoleSignalEvent ($ command , $ input , $ output , $ signal );
1016+ $ alarmEvent = \SIGALRM === $ signal ? new ConsoleAlarmEvent ($ command , $ input , $ output ) : null ;
1017+
1018+ $ signalRegistry ->register ($ signal , function ($ signal ) use ($ signalEvent , $ alarmEvent , $ command , $ commandSignals , $ input , $ output ) {
1019+ $ this ->dispatcher ->dispatch ($ signalEvent , ConsoleEvents::SIGNAL );
1020+ $ exitCode = $ signalEvent ->getExitCode ();
1021+
1022+ if (null !== $ alarmEvent ) {
1023+ if (false !== $ exitCode ) {
1024+ $ alarmEvent ->setExitCode ($ exitCode );
1025+ } else {
1026+ $ alarmEvent ->abortExit ();
1027+ }
1028+ $ this ->dispatcher ->dispatch ($ alarmEvent );
1029+ $ exitCode = $ alarmEvent ->getExitCode ();
1030+ }
10041031
10051032 // If the command is signalable, we call the handleSignal() method
10061033 if (\in_array ($ signal , $ commandSignals , true )) {
10071034 $ exitCode = $ command ->handleSignal ($ signal , $ exitCode );
10081035 }
10091036
1037+ if (\SIGALRM === $ signal ) {
1038+ $ this ->scheduleAlarm ();
1039+ }
1040+
10101041 if (false !== $ exitCode ) {
1011- $ event = new ConsoleTerminateEvent ($ command , $ event -> getInput () , $ event -> getOutput () , $ exitCode , $ signal );
1042+ $ event = new ConsoleTerminateEvent ($ command , $ input , $ output , $ exitCode , $ signal );
10121043 $ this ->dispatcher ->dispatch ($ event , ConsoleEvents::TERMINATE );
10131044
10141045 exit ($ event ->getExitCode ());
@@ -1021,7 +1052,11 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
10211052 }
10221053
10231054 foreach ($ commandSignals as $ signal ) {
1024- $ this ->signalRegistry ->register ($ signal , function (int $ signal ) use ($ command ): void {
1055+ $ signalRegistry ->register ($ signal , function (int $ signal ) use ($ command ): void {
1056+ if (\SIGALRM === $ signal ) {
1057+ $ this ->scheduleAlarm ();
1058+ }
1059+
10251060 if (false !== $ exitCode = $ command ->handleSignal ($ signal )) {
10261061 exit ($ exitCode );
10271062 }
0 commit comments