@@ -46,7 +46,7 @@ class Process
4646 private $ timeout ;
4747 private $ options ;
4848 private $ exitcode ;
49- private $ fallbackExitcode ;
49+ private $ fallbackStatus = array () ;
5050 private $ processInformation ;
5151 private $ stdout ;
5252 private $ stderr ;
@@ -65,6 +65,14 @@ class Process
6565 private $ latestSignal ;
6666
6767 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+ );
6876
6977 /**
7078 * Exit codes translation table.
@@ -339,17 +347,9 @@ public function wait($callback = null)
339347 * Returns the Pid (process identifier), if applicable.
340348 *
341349 * @return int|null The process id if running, null otherwise
342- *
343- * @throws RuntimeException In case --enable-sigchild is activated
344350 */
345351 public function getPid ()
346352 {
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-
353353 return $ this ->isRunning () ? $ this ->processInformation ['pid ' ] : null ;
354354 }
355355
@@ -361,7 +361,7 @@ public function getPid()
361361 * @return Process
362362 *
363363 * @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
365365 * @throws RuntimeException In case of failure
366366 */
367367 public function signal ($ signal )
@@ -467,7 +467,9 @@ public function getIncrementalErrorOutput()
467467 */
468468 public function getExitCode ()
469469 {
470- if ($ this ->isSigchildEnabled () && !$ this ->enhanceSigchildCompatibility ) {
470+ if (!$ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
471+ $ this ->stop (0 );
472+
471473 throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. ' );
472474 }
473475
@@ -484,8 +486,6 @@ public function getExitCode()
484486 *
485487 * @return null|string A string representation for the exit status code, null if the Process is not terminated.
486488 *
487- * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
488- *
489489 * @see http://tldp.org/LDP/abs/html/exitcodes.html
490490 * @see http://en.wikipedia.org/wiki/Unix_signal
491491 */
@@ -522,12 +522,12 @@ public function hasBeenSignaled()
522522 {
523523 $ this ->requireProcessIsTerminated (__FUNCTION__ );
524524
525- if ($ this ->isSigchildEnabled ()) {
525+ if (!$ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
526+ $ this ->stop (0 );
527+
526528 throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
527529 }
528530
529- $ this ->updateStatus (false );
530-
531531 return $ this ->processInformation ['signaled ' ];
532532 }
533533
@@ -545,12 +545,12 @@ public function getTermSignal()
545545 {
546546 $ this ->requireProcessIsTerminated (__FUNCTION__ );
547547
548- if ($ this ->isSigchildEnabled ()) {
548+ if ($ this ->isSigchildEnabled () && (!$ this ->enhanceSigchildCompatibility || -1 === $ this ->processInformation ['termsig ' ])) {
549+ $ this ->stop (0 );
550+
549551 throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
550552 }
551553
552- $ this ->updateStatus (false );
553-
554554 return $ this ->processInformation ['termsig ' ];
555555 }
556556
@@ -567,8 +567,6 @@ public function hasBeenStopped()
567567 {
568568 $ this ->requireProcessIsTerminated (__FUNCTION__ );
569569
570- $ this ->updateStatus (false );
571-
572570 return $ this ->processInformation ['stopped ' ];
573571 }
574572
@@ -585,8 +583,6 @@ public function getStopSignal()
585583 {
586584 $ this ->requireProcessIsTerminated (__FUNCTION__ );
587585
588- $ this ->updateStatus (false );
589-
590586 return $ this ->processInformation ['stopsig ' ];
591587 }
592588
@@ -660,7 +656,7 @@ public function stop($timeout = 10, $signal = null)
660656 usleep (1000 );
661657 } while ($ this ->isRunning () && microtime (true ) < $ timeoutMicro );
662658
663- if ($ this ->isRunning () && ! $ this -> isSigchildEnabled () ) {
659+ if ($ this ->isRunning ()) {
664660 // Avoid exception here: process is supposed to be running, but it might have stopped just
665661 // after this line. In any case, let's silently discard the error, we cannot do anything.
666662 $ this ->doSignal ($ signal ?: 9 , false );
@@ -998,9 +994,15 @@ private function getDescriptors()
998994
999995 if (!$ this ->useFileHandles && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1000996 // 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+ }
10021003
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 ' ;
10041006 }
10051007
10061008 return $ descriptors ;
@@ -1047,10 +1049,13 @@ protected function updateStatus($blocking)
10471049 }
10481050
10491051 $ this ->processInformation = proc_get_status ($ this ->process );
1050- $ this ->captureExitCode ();
10511052
10521053 $ this ->readPipes ($ blocking , '\\' === DIRECTORY_SEPARATOR ? !$ this ->processInformation ['running ' ] : true );
10531054
1055+ if ($ this ->fallbackStatus && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1056+ $ this ->processInformation = $ this ->fallbackStatus + $ this ->processInformation ;
1057+ }
1058+
10541059 if (!$ this ->processInformation ['running ' ]) {
10551060 $ this ->close ();
10561061 }
@@ -1067,7 +1072,7 @@ protected function isSigchildEnabled()
10671072 return self ::$ sigchild ;
10681073 }
10691074
1070- if (!function_exists ('phpinfo ' )) {
1075+ if (!function_exists ('phpinfo ' ) || defined ( ' HHVM_VERSION ' ) ) {
10711076 return self ::$ sigchild = false ;
10721077 }
10731078
@@ -1093,24 +1098,24 @@ private function readPipes($blocking, $close)
10931098
10941099 $ callback = $ this ->callback ;
10951100 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+ }
10981113 } else {
10991114 $ callback ($ type === self ::STDOUT ? self ::OUT : self ::ERR , $ data );
11001115 }
11011116 }
11021117 }
11031118
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-
11141119 /**
11151120 * Closes process resource, closes file handles, sets the exitcode.
11161121 *
@@ -1120,19 +1125,19 @@ private function close()
11201125 {
11211126 $ this ->processPipes ->close ();
11221127 if (is_resource ($ this ->process )) {
1123- $ exitcode = proc_close ($ this ->process );
1124- } else {
1125- $ exitcode = -1 ;
1128+ proc_close ($ this ->process );
11261129 }
1127-
1128- $ this ->exitcode = -1 !== $ exitcode ? $ exitcode : (null !== $ this ->exitcode ? $ this ->exitcode : -1 );
1130+ $ this ->exitcode = $ this ->processInformation ['exitcode ' ];
11291131 $ this ->status = self ::STATUS_TERMINATED ;
11301132
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+ }
11361141 }
11371142
11381143 // Free memory from self-reference callback created by buildCallback
@@ -1151,7 +1156,7 @@ private function resetProcessData()
11511156 $ this ->starttime = null ;
11521157 $ this ->callback = null ;
11531158 $ this ->exitcode = null ;
1154- $ this ->fallbackExitcode = null ;
1159+ $ this ->fallbackStatus = array () ;
11551160 $ this ->processInformation = null ;
11561161 $ this ->stdout = null ;
11571162 $ this ->stderr = null ;
@@ -1171,7 +1176,7 @@ private function resetProcessData()
11711176 * @return bool True if the signal was sent successfully, false otherwise
11721177 *
11731178 * @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
11751180 * @throws RuntimeException In case of failure
11761181 */
11771182 private function doSignal ($ signal , $ throwException )
@@ -1184,9 +1189,9 @@ private function doSignal($signal, $throwException)
11841189 return false ;
11851190 }
11861191
1187- if ($ this ->isSigchildEnabled ()) {
1192+ if ($ this ->enhanceSigchildCompatibility && $ this -> isSigchildEnabled () && ! isset ( self :: $ posixSignals [ $ signal ]) && !( function_exists ( ' posix_kill ' ) && @ posix_kill ( $ this -> getPid (), $ signal ) )) {
11881193 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 . ' );
11901195 }
11911196
11921197 return false ;
@@ -1211,7 +1216,10 @@ private function doSignal($signal, $throwException)
12111216 return false ;
12121217 }
12131218
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 ;
12151223
12161224 return true ;
12171225 }
0 commit comments