22
22
use Symfony \Component \Console \Completion \CompletionInput ;
23
23
use Symfony \Component \Console \Completion \CompletionSuggestions ;
24
24
use Symfony \Component \Console \Completion \Suggestion ;
25
+ use Symfony \Component \Console \Event \ConsoleAlarmEvent ;
25
26
use Symfony \Component \Console \Event \ConsoleCommandEvent ;
26
27
use Symfony \Component \Console \Event \ConsoleErrorEvent ;
27
28
use Symfony \Component \Console \Event \ConsoleSignalEvent ;
@@ -88,6 +89,7 @@ class Application implements ResetInterface
88
89
private bool $ initialized =false ;
89
90
private ?SignalRegistry $ signalRegistry =null ;
90
91
private array $ signalsToDispatchEvent = [];
92
+ private ?int $ alarmInterval =null ;
91
93
92
94
public function __construct (
93
95
private string $ name ='UNKNOWN ' ,
@@ -97,7 +99,7 @@ public function __construct(
97
99
$ this ->defaultCommand ='list ' ;
98
100
if (\defined ('SIGINT ' ) && SignalRegistry::isSupported ()) {
99
101
$ this ->signalRegistry =new SignalRegistry ();
100
- $ this ->signalsToDispatchEvent = [\SIGINT , \SIGQUIT , \SIGTERM , \SIGUSR1 , \SIGUSR2 ];
102
+ $ this ->signalsToDispatchEvent = [\SIGINT , \SIGQUIT , \SIGTERM , \SIGUSR1 , \SIGUSR2 , \ SIGALRM ];
101
103
}
102
104
}
103
105
@@ -128,6 +130,22 @@ public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent): void
128
130
$ this ->signalsToDispatchEvent =$ signalsToDispatchEvent ;
129
131
}
130
132
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
+
131
149
/**
132
150
* Runs the current application.
133
151
*
@@ -981,34 +999,47 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
981
999
982
1000
$ commandSignals =$ commandinstanceof SignalableCommandInterface ?$ command ->getSubscribedSignals () : [];
983
1001
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 ();
987
1003
988
1004
if (Terminal::hasSttyAvailable ()) {
989
1005
$ sttyMode =shell_exec ('stty -g ' );
990
1006
991
1007
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 ));
993
1009
}
994
1010
}
995
1011
996
1012
if ($ this ->dispatcher ) {
997
1013
// We register application signals, so that we can dispatch the event
998
1014
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
+ }
1004
1031
1005
1032
// If the command is signalable, we call the handleSignal() method
1006
1033
if (\in_array ($ signal ,$ commandSignals ,true )) {
1007
1034
$ exitCode =$ command ->handleSignal ($ signal ,$ exitCode );
1008
1035
}
1009
1036
1037
+ if (\SIGALRM ===$ signal ) {
1038
+ $ this ->scheduleAlarm ();
1039
+ }
1040
+
1010
1041
if (false !==$ exitCode ) {
1011
- $ event =new ConsoleTerminateEvent ($ command ,$ event -> getInput () ,$ event -> getOutput () ,$ exitCode ,$ signal );
1042
+ $ event =new ConsoleTerminateEvent ($ command ,$ input ,$ output ,$ exitCode ,$ signal );
1012
1043
$ this ->dispatcher ->dispatch ($ event , ConsoleEvents::TERMINATE );
1013
1044
1014
1045
exit ($ event ->getExitCode ());
@@ -1021,7 +1052,11 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
1021
1052
}
1022
1053
1023
1054
foreach ($ commandSignalsas $ 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
+
1025
1060
if (false !==$ exitCode =$ command ->handleSignal ($ signal )) {
1026
1061
exit ($ exitCode );
1027
1062
}