@@ -46,7 +46,7 @@ class Process
46
46
private $ timeout ;
47
47
private $ options ;
48
48
private $ exitcode ;
49
- private $ fallbackExitcode ;
49
+ private $ fallbackStatus = array () ;
50
50
private $ processInformation ;
51
51
private $ stdout ;
52
52
private $ stderr ;
@@ -65,6 +65,14 @@ class Process
65
65
private $ latestSignal ;
66
66
67
67
private static $ sigchild ;
68
+ private static $ posixSignals = array (
69
+ 1 => 1 , // SIGHUP
70
+ 2 => 2 , // SIGINT
71
+ 3 => 3 , // SIGQUIT
72
+ 6 => 6 , // SIGABRT
73
+ 14 => 14 , // SIGALRM
74
+ 15 => 15 , // SIGTERM
75
+ );
68
76
69
77
/**
70
78
* Exit codes translation table.
@@ -339,17 +347,9 @@ public function wait($callback = null)
339
347
* Returns the Pid (process identifier), if applicable.
340
348
*
341
349
* @return int|null The process id if running, null otherwise
342
- *
343
- * @throws RuntimeException In case --enable-sigchild is activated
344
350
*/
345
351
public function getPid ()
346
352
{
347
- if ($ this ->isSigchildEnabled ()) {
348
- throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. ' );
349
- }
350
-
351
- $ this ->updateStatus (false );
352
-
353
353
return $ this ->isRunning () ? $ this ->processInformation ['pid ' ] : null ;
354
354
}
355
355
@@ -361,7 +361,7 @@ public function getPid()
361
361
* @return Process
362
362
*
363
363
* @throws LogicException In case the process is not running
364
- * @throws RuntimeException In case --enable-sigchild is activated
364
+ * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
365
365
* @throws RuntimeException In case of failure
366
366
*/
367
367
public function signal ($ signal )
@@ -467,7 +467,9 @@ public function getIncrementalErrorOutput()
467
467
*/
468
468
public function getExitCode ()
469
469
{
470
- if ($ this ->isSigchildEnabled () && !$ this ->enhanceSigchildCompatibility ) {
470
+ if (!$ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
471
+ $ this ->stop (0 );
472
+
471
473
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. ' );
472
474
}
473
475
@@ -484,8 +486,6 @@ public function getExitCode()
484
486
*
485
487
* @return null|string A string representation for the exit status code, null if the Process is not terminated.
486
488
*
487
- * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
488
- *
489
489
* @see http://tldp.org/LDP/abs/html/exitcodes.html
490
490
* @see http://en.wikipedia.org/wiki/Unix_signal
491
491
*/
@@ -522,12 +522,12 @@ public function hasBeenSignaled()
522
522
{
523
523
$ this ->requireProcessIsTerminated (__FUNCTION__ );
524
524
525
- if ($ this ->isSigchildEnabled ()) {
525
+ if (!$ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
526
+ $ this ->stop (0 );
527
+
526
528
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
527
529
}
528
530
529
- $ this ->updateStatus (false );
530
-
531
531
return $ this ->processInformation ['signaled ' ];
532
532
}
533
533
@@ -545,12 +545,12 @@ public function getTermSignal()
545
545
{
546
546
$ this ->requireProcessIsTerminated (__FUNCTION__ );
547
547
548
- if ($ this ->isSigchildEnabled ()) {
548
+ if ($ this ->isSigchildEnabled () && (!$ this ->enhanceSigchildCompatibility || -1 === $ this ->processInformation ['termsig ' ])) {
549
+ $ this ->stop (0 );
550
+
549
551
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
550
552
}
551
553
552
- $ this ->updateStatus (false );
553
-
554
554
return $ this ->processInformation ['termsig ' ];
555
555
}
556
556
@@ -567,8 +567,6 @@ public function hasBeenStopped()
567
567
{
568
568
$ this ->requireProcessIsTerminated (__FUNCTION__ );
569
569
570
- $ this ->updateStatus (false );
571
-
572
570
return $ this ->processInformation ['stopped ' ];
573
571
}
574
572
@@ -585,8 +583,6 @@ public function getStopSignal()
585
583
{
586
584
$ this ->requireProcessIsTerminated (__FUNCTION__ );
587
585
588
- $ this ->updateStatus (false );
589
-
590
586
return $ this ->processInformation ['stopsig ' ];
591
587
}
592
588
@@ -660,7 +656,7 @@ public function stop($timeout = 10, $signal = null)
660
656
usleep (1000 );
661
657
} while ($ this ->isRunning () && microtime (true ) < $ timeoutMicro );
662
658
663
- if ($ this ->isRunning () && ! $ this -> isSigchildEnabled () ) {
659
+ if ($ this ->isRunning ()) {
664
660
// Avoid exception here: process is supposed to be running, but it might have stopped just
665
661
// after this line. In any case, let's silently discard the error, we cannot do anything.
666
662
$ this ->doSignal ($ signal ?: 9 , false );
@@ -998,9 +994,15 @@ private function getDescriptors()
998
994
999
995
if (!$ this ->useFileHandles && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1000
996
// last exit code is output on the fourth pipe and caught to work around --enable-sigchild
1001
- $ descriptors = array_merge ($ descriptors , array (array ('pipe ' , 'w ' )));
997
+ $ descriptors [3 ] = array ('pipe ' , 'w ' );
998
+
999
+ $ trap = '' ;
1000
+ foreach (self ::$ posixSignals as $ s ) {
1001
+ $ trap .= "trap 'echo s $ s >&3' $ s; " ;
1002
+ }
1002
1003
1003
- $ this ->commandline = '( ' .$ this ->commandline .') 3>/dev/null; code=$?; echo $code >&3; exit $code ' ;
1004
+ $ this ->commandline = $ trap .'{ ( ' .$ this ->commandline .') <&3 3<&- 3>/dev/null & } 3<&0; ' ;
1005
+ $ this ->commandline .= 'pid=$!; echo p$pid >&3; wait $pid; code=$?; echo x$code >&3; exit $code ' ;
1004
1006
}
1005
1007
1006
1008
return $ descriptors ;
@@ -1047,10 +1049,13 @@ protected function updateStatus($blocking)
1047
1049
}
1048
1050
1049
1051
$ this ->processInformation = proc_get_status ($ this ->process );
1050
- $ this ->captureExitCode ();
1051
1052
1052
1053
$ this ->readPipes ($ blocking , '\\' === DIRECTORY_SEPARATOR ? !$ this ->processInformation ['running ' ] : true );
1053
1054
1055
+ if ($ this ->fallbackStatus && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1056
+ $ this ->processInformation = $ this ->fallbackStatus + $ this ->processInformation ;
1057
+ }
1058
+
1054
1059
if (!$ this ->processInformation ['running ' ]) {
1055
1060
$ this ->close ();
1056
1061
}
@@ -1067,7 +1072,7 @@ protected function isSigchildEnabled()
1067
1072
return self ::$ sigchild ;
1068
1073
}
1069
1074
1070
- if (!function_exists ('phpinfo ' )) {
1075
+ if (!function_exists ('phpinfo ' ) || defined ( ' HHVM_VERSION ' ) ) {
1071
1076
return self ::$ sigchild = false ;
1072
1077
}
1073
1078
@@ -1093,24 +1098,24 @@ private function readPipes($blocking, $close)
1093
1098
1094
1099
$ callback = $ this ->callback ;
1095
1100
foreach ($ result as $ type => $ data ) {
1096
- if (3 == $ type ) {
1097
- $ this ->fallbackExitcode = (int ) $ data ;
1101
+ if (3 === $ type ) {
1102
+ foreach (explode ("\n" , substr ($ data , 0 , -1 )) as $ data ) {
1103
+ if ('p ' === $ data [0 ]) {
1104
+ $ this ->fallbackStatus ['pid ' ] = (int ) substr ($ data , 1 );
1105
+ } elseif ('s ' === $ data [0 ]) {
1106
+ $ this ->fallbackStatus ['signaled ' ] = true ;
1107
+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1108
+ $ this ->fallbackStatus ['termsig ' ] = (int ) substr ($ data , 1 );
1109
+ } elseif ('x ' === $ data [0 ] && !isset ($ this ->fallbackStatus ['signaled ' ])) {
1110
+ $ this ->fallbackStatus ['exitcode ' ] = (int ) substr ($ data , 1 );
1111
+ }
1112
+ }
1098
1113
} else {
1099
1114
$ callback ($ type === self ::STDOUT ? self ::OUT : self ::ERR , $ data );
1100
1115
}
1101
1116
}
1102
1117
}
1103
1118
1104
- /**
1105
- * Captures the exitcode if mentioned in the process information.
1106
- */
1107
- private function captureExitCode ()
1108
- {
1109
- if (isset ($ this ->processInformation ['exitcode ' ]) && -1 != $ this ->processInformation ['exitcode ' ]) {
1110
- $ this ->exitcode = $ this ->processInformation ['exitcode ' ];
1111
- }
1112
- }
1113
-
1114
1119
/**
1115
1120
* Closes process resource, closes file handles, sets the exitcode.
1116
1121
*
@@ -1120,19 +1125,19 @@ private function close()
1120
1125
{
1121
1126
$ this ->processPipes ->close ();
1122
1127
if (is_resource ($ this ->process )) {
1123
- $ exitcode = proc_close ($ this ->process );
1124
- } else {
1125
- $ exitcode = -1 ;
1128
+ proc_close ($ this ->process );
1126
1129
}
1127
-
1128
- $ this ->exitcode = -1 !== $ exitcode ? $ exitcode : (null !== $ this ->exitcode ? $ this ->exitcode : -1 );
1130
+ $ this ->exitcode = $ this ->processInformation ['exitcode ' ];
1129
1131
$ this ->status = self ::STATUS_TERMINATED ;
1130
1132
1131
- if (-1 === $ this ->exitcode && null !== $ this ->fallbackExitcode ) {
1132
- $ this ->exitcode = $ this ->fallbackExitcode ;
1133
- } elseif (-1 === $ this ->exitcode && $ this ->processInformation ['signaled ' ] && 0 < $ this ->processInformation ['termsig ' ]) {
1134
- // if process has been signaled, no exitcode but a valid termsig, apply Unix convention
1135
- $ this ->exitcode = 128 + $ this ->processInformation ['termsig ' ];
1133
+ if (-1 === $ this ->exitcode ) {
1134
+ if ($ this ->processInformation ['signaled ' ] && 0 < $ this ->processInformation ['termsig ' ]) {
1135
+ // if process has been signaled, no exitcode but a valid termsig, apply Unix convention
1136
+ $ this ->exitcode = 128 + $ this ->processInformation ['termsig ' ];
1137
+ } elseif ($ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1138
+ $ this ->processInformation ['signaled ' ] = true ;
1139
+ $ this ->processInformation ['termsig ' ] = -1 ;
1140
+ }
1136
1141
}
1137
1142
1138
1143
// Free memory from self-reference callback created by buildCallback
@@ -1151,7 +1156,7 @@ private function resetProcessData()
1151
1156
$ this ->starttime = null ;
1152
1157
$ this ->callback = null ;
1153
1158
$ this ->exitcode = null ;
1154
- $ this ->fallbackExitcode = null ;
1159
+ $ this ->fallbackStatus = array () ;
1155
1160
$ this ->processInformation = null ;
1156
1161
$ this ->stdout = null ;
1157
1162
$ this ->stderr = null ;
@@ -1171,7 +1176,7 @@ private function resetProcessData()
1171
1176
* @return bool True if the signal was sent successfully, false otherwise
1172
1177
*
1173
1178
* @throws LogicException In case the process is not running
1174
- * @throws RuntimeException In case --enable-sigchild is activated
1179
+ * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
1175
1180
* @throws RuntimeException In case of failure
1176
1181
*/
1177
1182
private function doSignal ($ signal , $ throwException )
@@ -1184,9 +1189,9 @@ private function doSignal($signal, $throwException)
1184
1189
return false ;
1185
1190
}
1186
1191
1187
- if ($ this ->isSigchildEnabled ()) {
1192
+ if ($ this ->enhanceSigchildCompatibility && $ this -> isSigchildEnabled () && ! isset ( self :: $ posixSignals [ $ signal ]) && !( function_exists ( ' posix_kill ' ) && @ posix_kill ( $ this -> getPid (), $ signal ) )) {
1188
1193
if ($ throwException ) {
1189
- throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. The process can not be signaled . ' );
1194
+ throw new RuntimeException ('This PHP has been compiled with --enable-sigchild and posix_kill() is not available . ' );
1190
1195
}
1191
1196
1192
1197
return false ;
@@ -1211,7 +1216,10 @@ private function doSignal($signal, $throwException)
1211
1216
return false ;
1212
1217
}
1213
1218
1214
- $ this ->latestSignal = $ signal ;
1219
+ $ this ->latestSignal = (int ) $ signal ;
1220
+ $ this ->fallbackStatus ['signaled ' ] = true ;
1221
+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1222
+ $ this ->fallbackStatus ['termsig ' ] = $ this ->latestSignal ;
1215
1223
1216
1224
return true ;
1217
1225
}
0 commit comments