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