@@ -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,7 @@ 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
471
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. ' );
472
472
}
473
473
@@ -484,8 +484,6 @@ public function getExitCode()
484
484
*
485
485
* @return null|string A string representation for the exit status code, null if the Process is not terminated.
486
486
*
487
- * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
488
- *
489
487
* @see http://tldp.org/LDP/abs/html/exitcodes.html
490
488
* @see http://en.wikipedia.org/wiki/Unix_signal
491
489
*/
@@ -522,12 +520,10 @@ public function hasBeenSignaled()
522
520
{
523
521
$ this ->requireProcessIsTerminated (__FUNCTION__ );
524
522
525
- if ($ this ->isSigchildEnabled ()) {
523
+ if (! $ this -> enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
526
524
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
527
525
}
528
526
529
- $ this ->updateStatus (false );
530
-
531
527
return $ this ->processInformation ['signaled ' ];
532
528
}
533
529
@@ -545,12 +541,10 @@ public function getTermSignal()
545
541
{
546
542
$ this ->requireProcessIsTerminated (__FUNCTION__ );
547
543
548
- if ($ this ->isSigchildEnabled ()) {
544
+ if ($ this ->isSigchildEnabled () && (! $ this -> enhanceSigchildCompatibility || - 1 === $ this -> processInformation [ ' termsig ' ]) ) {
549
545
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
550
546
}
551
547
552
- $ this ->updateStatus (false );
553
-
554
548
return $ this ->processInformation ['termsig ' ];
555
549
}
556
550
@@ -567,8 +561,6 @@ public function hasBeenStopped()
567
561
{
568
562
$ this ->requireProcessIsTerminated (__FUNCTION__ );
569
563
570
- $ this ->updateStatus (false );
571
-
572
564
return $ this ->processInformation ['stopped ' ];
573
565
}
574
566
@@ -585,8 +577,6 @@ public function getStopSignal()
585
577
{
586
578
$ this ->requireProcessIsTerminated (__FUNCTION__ );
587
579
588
- $ this ->updateStatus (false );
589
-
590
580
return $ this ->processInformation ['stopsig ' ];
591
581
}
592
582
@@ -660,7 +650,7 @@ public function stop($timeout = 10, $signal = null)
660
650
usleep (1000 );
661
651
} while ($ this ->isRunning () && microtime (true ) < $ timeoutMicro );
662
652
663
- if ($ this ->isRunning () && ! $ this -> isSigchildEnabled () ) {
653
+ if ($ this ->isRunning ()) {
664
654
// Avoid exception here: process is supposed to be running, but it might have stopped just
665
655
// after this line. In any case, let's silently discard the error, we cannot do anything.
666
656
$ this ->doSignal ($ signal ?: 9 , false );
@@ -998,9 +988,15 @@ private function getDescriptors()
998
988
999
989
if (!$ this ->useFileHandles && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1000
990
// 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 ' )));
991
+ $ descriptors [3 ] = array ('pipe ' , 'w ' );
992
+
993
+ $ trap = '' ;
994
+ foreach (self ::$ posixSignals as $ s ) {
995
+ $ trap .= "trap 'echo s $ s >&3' $ s; " ;
996
+ }
1002
997
1003
- $ this ->commandline = '( ' .$ this ->commandline .') 3>/dev/null; code=$?; echo $code >&3; exit $code ' ;
998
+ $ this ->commandline = $ trap .'{ ( ' .$ this ->commandline .') <&3 3<&- 3>/dev/null & } 3<&0; ' ;
999
+ $ this ->commandline .= 'pid=$!; echo p$pid >&3; wait $pid; code=$?; echo x$code >&3; exit $code ' ;
1004
1000
}
1005
1001
1006
1002
return $ descriptors ;
@@ -1047,10 +1043,13 @@ protected function updateStatus($blocking)
1047
1043
}
1048
1044
1049
1045
$ this ->processInformation = proc_get_status ($ this ->process );
1050
- $ this ->captureExitCode ();
1051
1046
1052
1047
$ this ->readPipes ($ blocking , '\\' === DIRECTORY_SEPARATOR ? !$ this ->processInformation ['running ' ] : true );
1053
1048
1049
+ if ($ this ->fallbackStatus && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1050
+ $ this ->processInformation = $ this ->fallbackStatus + $ this ->processInformation ;
1051
+ }
1052
+
1054
1053
if (!$ this ->processInformation ['running ' ]) {
1055
1054
$ this ->close ();
1056
1055
}
@@ -1067,7 +1066,7 @@ protected function isSigchildEnabled()
1067
1066
return self ::$ sigchild ;
1068
1067
}
1069
1068
1070
- if (!function_exists ('phpinfo ' )) {
1069
+ if (!function_exists ('phpinfo ' ) || defined ( ' HHVM_VERSION ' ) ) {
1071
1070
return self ::$ sigchild = false ;
1072
1071
}
1073
1072
@@ -1093,24 +1092,24 @@ private function readPipes($blocking, $close)
1093
1092
1094
1093
$ callback = $ this ->callback ;
1095
1094
foreach ($ result as $ type => $ data ) {
1096
- if (3 == $ type ) {
1097
- $ this ->fallbackExitcode = (int ) $ data ;
1095
+ if (3 === $ type ) {
1096
+ foreach (explode ("\n" , substr ($ data , 0 , -1 )) as $ data ) {
1097
+ if ('p ' === $ data [0 ]) {
1098
+ $ this ->fallbackStatus ['pid ' ] = (int ) substr ($ data , 1 );
1099
+ } elseif ('s ' === $ data [0 ]) {
1100
+ $ this ->fallbackStatus ['signaled ' ] = true ;
1101
+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1102
+ $ this ->fallbackStatus ['termsig ' ] = (int ) substr ($ data , 1 );
1103
+ } elseif ('x ' === $ data [0 ] && !isset ($ this ->fallbackStatus ['signaled ' ])) {
1104
+ $ this ->fallbackStatus ['exitcode ' ] = (int ) substr ($ data , 1 );
1105
+ }
1106
+ }
1098
1107
} else {
1099
1108
$ callback ($ type === self ::STDOUT ? self ::OUT : self ::ERR , $ data );
1100
1109
}
1101
1110
}
1102
1111
}
1103
1112
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
1113
/**
1115
1114
* Closes process resource, closes file handles, sets the exitcode.
1116
1115
*
@@ -1120,19 +1119,19 @@ private function close()
1120
1119
{
1121
1120
$ this ->processPipes ->close ();
1122
1121
if (is_resource ($ this ->process )) {
1123
- $ exitcode = proc_close ($ this ->process );
1124
- } else {
1125
- $ exitcode = -1 ;
1122
+ proc_close ($ this ->process );
1126
1123
}
1127
-
1128
- $ this ->exitcode = -1 !== $ exitcode ? $ exitcode : (null !== $ this ->exitcode ? $ this ->exitcode : -1 );
1124
+ $ this ->exitcode = $ this ->processInformation ['exitcode ' ];
1129
1125
$ this ->status = self ::STATUS_TERMINATED ;
1130
1126
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 ' ];
1127
+ if (-1 === $ this ->exitcode ) {
1128
+ if ($ this ->processInformation ['signaled ' ] && 0 < $ this ->processInformation ['termsig ' ]) {
1129
+ // if process has been signaled, no exitcode but a valid termsig, apply Unix convention
1130
+ $ this ->exitcode = 128 + $ this ->processInformation ['termsig ' ];
1131
+ } elseif ($ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1132
+ $ this ->processInformation ['signaled ' ] = true ;
1133
+ $ this ->processInformation ['termsig ' ] = -1 ;
1134
+ }
1136
1135
}
1137
1136
1138
1137
// Free memory from self-reference callback created by buildCallback
@@ -1151,7 +1150,7 @@ private function resetProcessData()
1151
1150
$ this ->starttime = null ;
1152
1151
$ this ->callback = null ;
1153
1152
$ this ->exitcode = null ;
1154
- $ this ->fallbackExitcode = null ;
1153
+ $ this ->fallbackStatus = array () ;
1155
1154
$ this ->processInformation = null ;
1156
1155
$ this ->stdout = null ;
1157
1156
$ this ->stderr = null ;
@@ -1171,7 +1170,7 @@ private function resetProcessData()
1171
1170
* @return bool True if the signal was sent successfully, false otherwise
1172
1171
*
1173
1172
* @throws LogicException In case the process is not running
1174
- * @throws RuntimeException In case --enable-sigchild is activated
1173
+ * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
1175
1174
* @throws RuntimeException In case of failure
1176
1175
*/
1177
1176
private function doSignal ($ signal , $ throwException )
@@ -1184,9 +1183,9 @@ private function doSignal($signal, $throwException)
1184
1183
return false ;
1185
1184
}
1186
1185
1187
- if ($ this ->isSigchildEnabled ()) {
1186
+ if ($ this ->enhanceSigchildCompatibility && $ this -> isSigchildEnabled () && ! isset ( self :: $ posixSignals [ $ signal ]) && !( function_exists ( ' posix_kill ' ) && @ posix_kill ( $ this -> getPid (), $ signal ) )) {
1188
1187
if ($ throwException ) {
1189
- throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. The process can not be signaled . ' );
1188
+ throw new RuntimeException ('This PHP has been compiled with --enable-sigchild and posix_kill() is not available . ' );
1190
1189
}
1191
1190
1192
1191
return false ;
@@ -1211,7 +1210,10 @@ private function doSignal($signal, $throwException)
1211
1210
return false ;
1212
1211
}
1213
1212
1214
- $ this ->latestSignal = $ signal ;
1213
+ $ this ->latestSignal = (int ) $ signal ;
1214
+ $ this ->fallbackStatus ['signaled ' ] = true ;
1215
+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1216
+ $ this ->fallbackStatus ['termsig ' ] = $ this ->latestSignal ;
1215
1217
1216
1218
return true ;
1217
1219
}
0 commit comments