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