@@ -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,6 @@ 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
365364 * @throws RuntimeException In case of failure
366365 */
367366 public function signal ($ signal )
@@ -467,7 +466,7 @@ public function getIncrementalErrorOutput()
467466 */
468467 public function getExitCode ()
469468 {
470- if ($ this ->isSigchildEnabled () && ! $ this ->enhanceSigchildCompatibility ) {
469+ if (! $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled () ) {
471470 throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. ' );
472471 }
473472
@@ -484,8 +483,6 @@ public function getExitCode()
484483 *
485484 * @return null|string A string representation for the exit status code, null if the Process is not terminated.
486485 *
487- * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
488- *
489486 * @see http://tldp.org/LDP/abs/html/exitcodes.html
490487 * @see http://en.wikipedia.org/wiki/Unix_signal
491488 */
@@ -522,12 +519,10 @@ public function hasBeenSignaled()
522519 {
523520 $ this ->requireProcessIsTerminated (__FUNCTION__ );
524521
525- if ($ this ->isSigchildEnabled ()) {
522+ if (! $ this -> enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
526523 throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
527524 }
528525
529- $ this ->updateStatus (false );
530-
531526 return $ this ->processInformation ['signaled ' ];
532527 }
533528
@@ -545,12 +540,10 @@ public function getTermSignal()
545540 {
546541 $ this ->requireProcessIsTerminated (__FUNCTION__ );
547542
548- if ($ this ->isSigchildEnabled ()) {
543+ if ($ this ->isSigchildEnabled () && (! $ this -> enhanceSigchildCompatibility || - 1 === $ this -> processInformation [ ' termsig ' ]) ) {
549544 throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
550545 }
551546
552- $ this ->updateStatus (false );
553-
554547 return $ this ->processInformation ['termsig ' ];
555548 }
556549
@@ -567,8 +560,6 @@ public function hasBeenStopped()
567560 {
568561 $ this ->requireProcessIsTerminated (__FUNCTION__ );
569562
570- $ this ->updateStatus (false );
571-
572563 return $ this ->processInformation ['stopped ' ];
573564 }
574565
@@ -585,8 +576,6 @@ public function getStopSignal()
585576 {
586577 $ this ->requireProcessIsTerminated (__FUNCTION__ );
587578
588- $ this ->updateStatus (false );
589-
590579 return $ this ->processInformation ['stopsig ' ];
591580 }
592581
@@ -660,7 +649,7 @@ public function stop($timeout = 10, $signal = null)
660649 usleep (1000 );
661650 } while ($ this ->isRunning () && microtime (true ) < $ timeoutMicro );
662651
663- if ($ this ->isRunning () && ! $ this -> isSigchildEnabled () ) {
652+ if ($ this ->isRunning ()) {
664653 // Avoid exception here: process is supposed to be running, but it might have stopped just
665654 // after this line. In any case, let's silently discard the error, we cannot do anything.
666655 $ this ->doSignal ($ signal ?: 9 , false );
@@ -998,9 +987,15 @@ private function getDescriptors()
998987
999988 if (!$ this ->useFileHandles && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1000989 // 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+ }
1002996
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 ' ;
1004999 }
10051000
10061001 return $ descriptors ;
@@ -1047,10 +1042,13 @@ protected function updateStatus($blocking)
10471042 }
10481043
10491044 $ this ->processInformation = proc_get_status ($ this ->process );
1050- $ this ->captureExitCode ();
10511045
10521046 $ this ->readPipes ($ blocking , '\\' === DIRECTORY_SEPARATOR ? !$ this ->processInformation ['running ' ] : true );
10531047
1048+ if ($ this ->fallbackStatus && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1049+ $ this ->processInformation = $ this ->fallbackStatus + $ this ->processInformation ;
1050+ }
1051+
10541052 if (!$ this ->processInformation ['running ' ]) {
10551053 $ this ->close ();
10561054 }
@@ -1067,7 +1065,7 @@ protected function isSigchildEnabled()
10671065 return self ::$ sigchild ;
10681066 }
10691067
1070- if (!function_exists ('phpinfo ' )) {
1068+ if (!function_exists ('phpinfo ' ) || defined ( ' HHVM_VERSION ' ) ) {
10711069 return self ::$ sigchild = false ;
10721070 }
10731071
@@ -1093,24 +1091,24 @@ private function readPipes($blocking, $close)
10931091
10941092 $ callback = $ this ->callback ;
10951093 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+ }
10981106 } else {
10991107 $ callback ($ type === self ::STDOUT ? self ::OUT : self ::ERR , $ data );
11001108 }
11011109 }
11021110 }
11031111
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-
11141112 /**
11151113 * Closes process resource, closes file handles, sets the exitcode.
11161114 *
@@ -1120,19 +1118,19 @@ private function close()
11201118 {
11211119 $ this ->processPipes ->close ();
11221120 if (is_resource ($ this ->process )) {
1123- $ exitcode = proc_close ($ this ->process );
1124- } else {
1125- $ exitcode = -1 ;
1121+ proc_close ($ this ->process );
11261122 }
1127-
1128- $ this ->exitcode = -1 !== $ exitcode ? $ exitcode : (null !== $ this ->exitcode ? $ this ->exitcode : -1 );
1123+ $ this ->exitcode = $ this ->processInformation ['exitcode ' ];
11291124 $ this ->status = self ::STATUS_TERMINATED ;
11301125
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+ }
11361134 }
11371135
11381136 // Free memory from self-reference callback created by buildCallback
@@ -1151,7 +1149,7 @@ private function resetProcessData()
11511149 $ this ->starttime = null ;
11521150 $ this ->callback = null ;
11531151 $ this ->exitcode = null ;
1154- $ this ->fallbackExitcode = null ;
1152+ $ this ->fallbackStatus = array () ;
11551153 $ this ->processInformation = null ;
11561154 $ this ->stdout = null ;
11571155 $ this ->stderr = null ;
@@ -1171,7 +1169,7 @@ private function resetProcessData()
11711169 * @return bool True if the signal was sent successfully, false otherwise
11721170 *
11731171 * @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
11751173 * @throws RuntimeException In case of failure
11761174 */
11771175 private function doSignal ($ signal , $ throwException )
@@ -1184,9 +1182,9 @@ private function doSignal($signal, $throwException)
11841182 return false ;
11851183 }
11861184
1187- if ($ this ->isSigchildEnabled ()) {
1185+ if ($ this ->enhanceSigchildCompatibility && $ this -> isSigchildEnabled () && ! isset ( self :: $ posixSignals [ $ signal ]) && !( function_exists ( ' posix_kill ' ) && @ posix_kill ( $ this -> getPid (), $ signal ) )) {
11881186 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 . ' );
11901188 }
11911189
11921190 return false ;
@@ -1211,7 +1209,10 @@ private function doSignal($signal, $throwException)
12111209 return false ;
12121210 }
12131211
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 ;
12151216
12161217 return true ;
12171218 }
0 commit comments