@@ -46,7 +46,7 @@ class Process
4646private $ timeout ;
4747private $ options ;
4848private $ exitcode ;
49- private $ fallbackExitcode ;
49+ private $ fallbackStatus = array () ;
5050private $ processInformation ;
5151private $ stdout ;
5252private $ stderr ;
@@ -65,6 +65,14 @@ class Process
6565private $ latestSignal ;
6666
6767private 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 */
345351public 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-
353353return $ 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 */
367367public function signal ($ signal )
@@ -467,7 +467,7 @@ public function getIncrementalErrorOutput()
467467 */
468468public function getExitCode ()
469469 {
470- if ($ this ->isSigchildEnabled () &&! $ this ->enhanceSigchildCompatibility ) {
470+ if (! $ this ->enhanceSigchildCompatibility &&$ this ->isSigchildEnabled () ) {
471471throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. ' );
472472 }
473473
@@ -484,8 +484,6 @@ public function getExitCode()
484484 *
485485 * @return null|string A string representation for the exit status code, null if the Process is not terminated.
486486 *
487- * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
488- *
489487 * @see http://tldp.org/LDP/abs/html/exitcodes.html
490488 * @see http://en.wikipedia.org/wiki/Unix_signal
491489 */
@@ -522,12 +520,10 @@ public function hasBeenSignaled()
522520 {
523521$ this ->requireProcessIsTerminated (__FUNCTION__ );
524522
525- if ($ this ->isSigchildEnabled ()) {
523+ if (! $ this -> enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
526524throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
527525 }
528526
529- $ this ->updateStatus (false );
530-
531527return $ this ->processInformation ['signaled ' ];
532528 }
533529
@@ -545,12 +541,10 @@ public function getTermSignal()
545541 {
546542$ this ->requireProcessIsTerminated (__FUNCTION__ );
547543
548- if ($ this ->isSigchildEnabled ()) {
544+ if ($ this ->isSigchildEnabled () && (! $ this -> enhanceSigchildCompatibility || - 1 === $ this -> processInformation [ ' termsig ' ]) ) {
549545throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
550546 }
551547
552- $ this ->updateStatus (false );
553-
554548return $ this ->processInformation ['termsig ' ];
555549 }
556550
@@ -567,8 +561,6 @@ public function hasBeenStopped()
567561 {
568562$ this ->requireProcessIsTerminated (__FUNCTION__ );
569563
570- $ this ->updateStatus (false );
571-
572564return $ this ->processInformation ['stopped ' ];
573565 }
574566
@@ -585,8 +577,6 @@ public function getStopSignal()
585577 {
586578$ this ->requireProcessIsTerminated (__FUNCTION__ );
587579
588- $ this ->updateStatus (false );
589-
590580return $ this ->processInformation ['stopsig ' ];
591581 }
592582
@@ -660,7 +650,7 @@ public function stop($timeout = 10, $signal = null)
660650usleep (1000 );
661651 }while ($ this ->isRunning () &µtime (true ) <$ timeoutMicro );
662652
663- if ($ this ->isRunning () && ! $ this -> isSigchildEnabled () ) {
653+ if ($ this ->isRunning ()) {
664654// Avoid exception here: process is supposed to be running, but it might have stopped just
665655// after this line. In any case, let's silently discard the error, we cannot do anything.
666656$ this ->doSignal ($ signal ?:9 ,false );
@@ -998,9 +988,15 @@ private function getDescriptors()
998988
999989if (!$ this ->useFileHandles &&$ this ->enhanceSigchildCompatibility &&$ this ->isSigchildEnabled ()) {
1000990// 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 ' )));
991+ $ descriptors [3 ] =array ('pipe ' ,'w ' );
992+
993+ $ trap ='' ;
994+ foreach (self ::$ posixSignalsas $ s ) {
995+ $ trap .="trap 'echo s $ s >&3' $ s; " ;
996+ }
1002997
1003- $ this ->commandline ='( ' .$ this ->commandline .') 3>/dev/null; code=$?; echo $code >&3; exit $code ' ;
998+ $ this ->commandline =$ trap .'{ ( ' .$ this ->commandline .') <&3 3<&- 3>/dev/null & } 3<&0; ' ;
999+ $ this ->commandline .='pid=$!; echo p$pid >&3; wait $pid; code=$?; echo x$code >&3; exit $code ' ;
10041000 }
10051001
10061002return $ descriptors ;
@@ -1047,10 +1043,13 @@ protected function updateStatus($blocking)
10471043 }
10481044
10491045$ this ->processInformation =proc_get_status ($ this ->process );
1050- $ this ->captureExitCode ();
10511046
10521047$ this ->readPipes ($ blocking ,'\\' ===DIRECTORY_SEPARATOR ? !$ this ->processInformation ['running ' ] :true );
10531048
1049+ if ($ this ->fallbackStatus &&$ this ->enhanceSigchildCompatibility &&$ this ->isSigchildEnabled ()) {
1050+ $ this ->processInformation =$ this ->fallbackStatus +$ this ->processInformation ;
1051+ }
1052+
10541053if (!$ this ->processInformation ['running ' ]) {
10551054$ this ->close ();
10561055 }
@@ -1067,7 +1066,7 @@ protected function isSigchildEnabled()
10671066return self ::$ sigchild ;
10681067 }
10691068
1070- if (!function_exists ('phpinfo ' )) {
1069+ if (!function_exists ('phpinfo ' ) || defined ( ' HHVM_VERSION ' ) ) {
10711070return self ::$ sigchild =false ;
10721071 }
10731072
@@ -1093,24 +1092,24 @@ private function readPipes($blocking, $close)
10931092
10941093$ callback =$ this ->callback ;
10951094foreach ($ resultas $ type =>$ data ) {
1096- if (3 ==$ type ) {
1097- $ this ->fallbackExitcode = (int )$ data ;
1095+ if (3 ===$ type ) {
1096+ foreach (explode ("\n" ,substr ($ data ,0 , -1 ))as $ data ) {
1097+ if ('p ' ===$ data [0 ]) {
1098+ $ this ->fallbackStatus ['pid ' ] = (int )substr ($ data ,1 );
1099+ }elseif ('s ' ===$ data [0 ]) {
1100+ $ this ->fallbackStatus ['signaled ' ] =true ;
1101+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1102+ $ this ->fallbackStatus ['termsig ' ] = (int )substr ($ data ,1 );
1103+ }elseif ('x ' ===$ data [0 ] && !isset ($ this ->fallbackStatus ['signaled ' ])) {
1104+ $ this ->fallbackStatus ['exitcode ' ] = (int )substr ($ data ,1 );
1105+ }
1106+ }
10981107 }else {
10991108$ callback ($ type ===self ::STDOUT ?self ::OUT :self ::ERR ,$ data );
11001109 }
11011110 }
11021111 }
11031112
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-
11141113/**
11151114 * Closes process resource, closes file handles, sets the exitcode.
11161115 *
@@ -1120,19 +1119,19 @@ private function close()
11201119 {
11211120$ this ->processPipes ->close ();
11221121if (is_resource ($ this ->process )) {
1123- $ exitcode =proc_close ($ this ->process );
1124- }else {
1125- $ exitcode = -1 ;
1122+ proc_close ($ this ->process );
11261123 }
1127-
1128- $ this ->exitcode = -1 !==$ exitcode ?$ exitcode : (null !==$ this ->exitcode ?$ this ->exitcode : -1 );
1124+ $ this ->exitcode =$ this ->processInformation ['exitcode ' ];
11291125$ this ->status =self ::STATUS_TERMINATED ;
11301126
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 ' ];
1127+ if (-1 ===$ this ->exitcode ) {
1128+ if ($ this ->processInformation ['signaled ' ] &&0 <$ this ->processInformation ['termsig ' ]) {
1129+ // if process has been signaled, no exitcode but a valid termsig, apply Unix convention
1130+ $ this ->exitcode =128 +$ this ->processInformation ['termsig ' ];
1131+ }elseif ($ this ->enhanceSigchildCompatibility &&$ this ->isSigchildEnabled ()) {
1132+ $ this ->processInformation ['signaled ' ] =true ;
1133+ $ this ->processInformation ['termsig ' ] = -1 ;
1134+ }
11361135 }
11371136
11381137// Free memory from self-reference callback created by buildCallback
@@ -1151,7 +1150,7 @@ private function resetProcessData()
11511150$ this ->starttime =null ;
11521151$ this ->callback =null ;
11531152$ this ->exitcode =null ;
1154- $ this ->fallbackExitcode =null ;
1153+ $ this ->fallbackStatus =array () ;
11551154$ this ->processInformation =null ;
11561155$ this ->stdout =null ;
11571156$ this ->stderr =null ;
@@ -1171,7 +1170,7 @@ private function resetProcessData()
11711170 * @return bool True if the signal was sent successfully, false otherwise
11721171 *
11731172 * @throws LogicException In case the process is not running
1174- * @throws RuntimeException In case --enable-sigchild is activated
1173+ * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
11751174 * @throws RuntimeException In case of failure
11761175 */
11771176private function doSignal ($ signal ,$ throwException )
@@ -1184,9 +1183,9 @@ private function doSignal($signal, $throwException)
11841183return false ;
11851184 }
11861185
1187- if ($ this ->isSigchildEnabled ()) {
1186+ if ($ this ->enhanceSigchildCompatibility && $ this -> isSigchildEnabled () && ! isset ( self :: $ posixSignals [ $ signal ]) && !( function_exists ( ' posix_kill ' ) && @ posix_kill ( $ this -> getPid (), $ signal ) )) {
11881187if ($ throwException ) {
1189- throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. The process can notbe signaled . ' );
1188+ throw new RuntimeException ('This PHP has been compiled with --enable-sigchild and posix_kill() is notavailable . ' );
11901189 }
11911190
11921191return false ;
@@ -1211,7 +1210,10 @@ private function doSignal($signal, $throwException)
12111210return false ;
12121211 }
12131212
1214- $ this ->latestSignal =$ signal ;
1213+ $ this ->latestSignal = (int )$ signal ;
1214+ $ this ->fallbackStatus ['signaled ' ] =true ;
1215+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1216+ $ this ->fallbackStatus ['termsig ' ] =$ this ->latestSignal ;
12151217
12161218return true ;
12171219 }