@@ -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 ,
70
+ 2 => 2 ,
71
+ 3 => 3 ,
72
+ 6 => 6 ,
73
+ 14 => 14 ,
74
+ 15 => 15 ,
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 )
@@ -462,15 +461,9 @@ public function getIncrementalErrorOutput()
462
461
* Returns the exit code returned by the process.
463
462
*
464
463
* @return null|int The exit status code, null if the Process is not terminated
465
- *
466
- * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
467
464
*/
468
465
public function getExitCode ()
469
466
{
470
- if ($ this ->isSigchildEnabled () && !$ this ->enhanceSigchildCompatibility ) {
471
- throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. ' );
472
- }
473
-
474
467
$ this ->updateStatus (false );
475
468
476
469
return $ this ->exitcode ;
@@ -484,8 +477,6 @@ public function getExitCode()
484
477
*
485
478
* @return null|string A string representation for the exit status code, null if the Process is not terminated.
486
479
*
487
- * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
488
- *
489
480
* @see http://tldp.org/LDP/abs/html/exitcodes.html
490
481
* @see http://en.wikipedia.org/wiki/Unix_signal
491
482
*/
@@ -515,17 +506,12 @@ public function isSuccessful()
515
506
*
516
507
* @return bool
517
508
*
518
- * @throws RuntimeException In case --enable-sigchild is activated
519
- * @throws LogicException In case the process is not terminated
509
+ * @throws LogicException In case the process is not terminated
520
510
*/
521
511
public function hasBeenSignaled ()
522
512
{
523
513
$ this ->requireProcessIsTerminated (__FUNCTION__ );
524
514
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
515
$ this ->updateStatus (false );
530
516
531
517
return $ this ->processInformation ['signaled ' ];
@@ -538,17 +524,12 @@ public function hasBeenSignaled()
538
524
*
539
525
* @return int
540
526
*
541
- * @throws RuntimeException In case --enable-sigchild is activated
542
- * @throws LogicException In case the process is not terminated
527
+ * @throws LogicException In case the process is not terminated
543
528
*/
544
529
public function getTermSignal ()
545
530
{
546
531
$ this ->requireProcessIsTerminated (__FUNCTION__ );
547
532
548
- if ($ this ->isSigchildEnabled ()) {
549
- throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
550
- }
551
-
552
533
$ this ->updateStatus (false );
553
534
554
535
return $ this ->processInformation ['termsig ' ];
@@ -660,7 +641,7 @@ public function stop($timeout = 10, $signal = null)
660
641
usleep (1000 );
661
642
} while ($ this ->isRunning () && microtime (true ) < $ timeoutMicro );
662
643
663
- if ($ this ->isRunning () && ! $ this -> isSigchildEnabled () ) {
644
+ if ($ this ->isRunning ()) {
664
645
// Avoid exception here: process is supposed to be running, but it might have stopped just
665
646
// after this line. In any case, let's silently discard the error, we cannot do anything.
666
647
$ this ->doSignal ($ signal ?: 9 , false );
@@ -998,9 +979,15 @@ private function getDescriptors()
998
979
999
980
if (!$ this ->useFileHandles && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1000
981
// 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 ' )));
982
+ $ descriptors [3 ] = array ('pipe ' , 'w ' );
983
+
984
+ $ trap = '' ;
985
+ foreach (self ::$ posixSignals as $ s ) {
986
+ $ trap .= "trap 'echo s $ s >&3' $ s; " ;
987
+ }
1002
988
1003
- $ this ->commandline = '( ' .$ this ->commandline .') 3>/dev/null; code=$?; echo $code >&3; exit $code ' ;
989
+ $ this ->commandline = $ trap .'{ ( ' .$ this ->commandline .') <&3 3<&- 3>/dev/null & } 3<&0; ' ;
990
+ $ this ->commandline .= 'pid=$!; echo p$pid >&3; wait $pid; code=$?; echo x$code >&3; exit $code ' ;
1004
991
}
1005
992
1006
993
return $ descriptors ;
@@ -1047,10 +1034,13 @@ protected function updateStatus($blocking)
1047
1034
}
1048
1035
1049
1036
$ this ->processInformation = proc_get_status ($ this ->process );
1050
- $ this ->captureExitCode ();
1051
1037
1052
1038
$ this ->readPipes ($ blocking , '\\' === DIRECTORY_SEPARATOR ? !$ this ->processInformation ['running ' ] : true );
1053
1039
1040
+ if ($ this ->fallbackStatus && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1041
+ $ this ->processInformation = $ this ->fallbackStatus + $ this ->processInformation ;
1042
+ }
1043
+
1054
1044
if (!$ this ->processInformation ['running ' ]) {
1055
1045
$ this ->close ();
1056
1046
}
@@ -1067,7 +1057,7 @@ protected function isSigchildEnabled()
1067
1057
return self ::$ sigchild ;
1068
1058
}
1069
1059
1070
- if (!function_exists ('phpinfo ' )) {
1060
+ if (!function_exists ('phpinfo ' ) || defined ( ' HHVM_VERSION ' ) ) {
1071
1061
return self ::$ sigchild = false ;
1072
1062
}
1073
1063
@@ -1093,24 +1083,24 @@ private function readPipes($blocking, $close)
1093
1083
1094
1084
$ callback = $ this ->callback ;
1095
1085
foreach ($ result as $ type => $ data ) {
1096
- if (3 == $ type ) {
1097
- $ this ->fallbackExitcode = (int ) $ data ;
1086
+ if (3 === $ type ) {
1087
+ foreach (explode ("\n" , substr ($ data , 0 , -1 )) as $ data ) {
1088
+ if ('p ' === $ data [0 ]) {
1089
+ $ this ->fallbackStatus ['pid ' ] = (int ) substr ($ data , 1 );
1090
+ } elseif ('s ' === $ data [0 ]) {
1091
+ $ this ->fallbackStatus ['signaled ' ] = true ;
1092
+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1093
+ $ this ->fallbackStatus ['termsig ' ] = (int ) substr ($ data , 1 );
1094
+ } elseif ('x ' === $ data [0 ] && !isset ($ this ->fallbackStatus ['signaled ' ])) {
1095
+ $ this ->fallbackStatus ['exitcode ' ] = (int ) substr ($ data , 1 );
1096
+ }
1097
+ }
1098
1098
} else {
1099
1099
$ callback ($ type === self ::STDOUT ? self ::OUT : self ::ERR , $ data );
1100
1100
}
1101
1101
}
1102
1102
}
1103
1103
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
1104
/**
1115
1105
* Closes process resource, closes file handles, sets the exitcode.
1116
1106
*
@@ -1120,19 +1110,19 @@ private function close()
1120
1110
{
1121
1111
$ this ->processPipes ->close ();
1122
1112
if (is_resource ($ this ->process )) {
1123
- $ exitcode = proc_close ($ this ->process );
1124
- } else {
1125
- $ exitcode = -1 ;
1113
+ proc_close ($ this ->process );
1126
1114
}
1127
-
1128
- $ this ->exitcode = -1 !== $ exitcode ? $ exitcode : (null !== $ this ->exitcode ? $ this ->exitcode : -1 );
1115
+ $ this ->exitcode = $ this ->processInformation ['exitcode ' ];
1129
1116
$ this ->status = self ::STATUS_TERMINATED ;
1130
1117
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 ' ];
1118
+ if (-1 === $ this ->exitcode ) {
1119
+ if ($ this ->processInformation ['signaled ' ] && 0 < $ this ->processInformation ['termsig ' ]) {
1120
+ // if process has been signaled, no exitcode but a valid termsig, apply Unix convention
1121
+ $ this ->exitcode = 128 + $ this ->processInformation ['termsig ' ];
1122
+ } elseif ($ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1123
+ $ this ->processInformation ['signaled ' ] = true ;
1124
+ $ this ->processInformation ['termsig ' ] = -1 ;
1125
+ }
1136
1126
}
1137
1127
1138
1128
// Free memory from self-reference callback created by buildCallback
@@ -1151,7 +1141,7 @@ private function resetProcessData()
1151
1141
$ this ->starttime = null ;
1152
1142
$ this ->callback = null ;
1153
1143
$ this ->exitcode = null ;
1154
- $ this ->fallbackExitcode = null ;
1144
+ $ this ->fallbackStatus = array () ;
1155
1145
$ this ->processInformation = null ;
1156
1146
$ this ->stdout = null ;
1157
1147
$ this ->stderr = null ;
@@ -1171,7 +1161,7 @@ private function resetProcessData()
1171
1161
* @return bool True if the signal was sent successfully, false otherwise
1172
1162
*
1173
1163
* @throws LogicException In case the process is not running
1174
- * @throws RuntimeException In case --enable-sigchild is activated
1164
+ * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
1175
1165
* @throws RuntimeException In case of failure
1176
1166
*/
1177
1167
private function doSignal ($ signal , $ throwException )
@@ -1184,9 +1174,9 @@ private function doSignal($signal, $throwException)
1184
1174
return false ;
1185
1175
}
1186
1176
1187
- if ($ this ->isSigchildEnabled ()) {
1177
+ if ($ this ->enhanceSigchildCompatibility && $ this -> isSigchildEnabled () && ! isset ( self :: $ posixSignals [ $ signal ]) && !( function_exists ( ' posix_kill ' ) && @ posix_kill ( $ this -> getPid (), $ signal ) )) {
1188
1178
if ($ throwException ) {
1189
- throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. The process can not be signaled. ' );
1179
+ throw new RuntimeException (sprintf ( 'This PHP has been compiled with --enable-sigchild and posix_kill() is not available. ' , $ signal ) );
1190
1180
}
1191
1181
1192
1182
return false ;
@@ -1211,7 +1201,10 @@ private function doSignal($signal, $throwException)
1211
1201
return false ;
1212
1202
}
1213
1203
1214
- $ this ->latestSignal = $ signal ;
1204
+ $ this ->latestSignal = (int ) $ signal ;
1205
+ $ this ->fallbackStatus ['signaled ' ] = true ;
1206
+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1207
+ $ this ->fallbackStatus ['termsig ' ] = $ this ->latestSignal ;
1215
1208
1216
1209
return true ;
1217
1210
}
0 commit comments