@@ -54,7 +54,7 @@ class Process
54
54
private $ idleTimeout ;
55
55
private $ options ;
56
56
private $ exitcode ;
57
- private $ fallbackExitcode ;
57
+ private $ fallbackStatus = array () ;
58
58
private $ processInformation ;
59
59
private $ outputDisabled = false ;
60
60
private $ stdout ;
@@ -75,6 +75,14 @@ class Process
75
75
private $ latestSignal ;
76
76
77
77
private static $ sigchild ;
78
+ private static $ posixSignals = array (
79
+ 1 => 1 , // SIGHUP
80
+ 2 => 2 , // SIGINT
81
+ 3 => 3 , // SIGQUIT
82
+ 6 => 6 , // SIGABRT
83
+ 14 => 14 , // SIGALRM
84
+ 15 => 15 , // SIGTERM
85
+ );
78
86
79
87
/**
80
88
* Exit codes translation table.
@@ -169,16 +177,7 @@ public function __construct($commandline, $cwd = null, array $env = null, $input
169
177
170
178
public function __destruct ()
171
179
{
172
- if ($ this ->isRunning ()) {
173
- $ this ->doSignal (15 , false );
174
- usleep (10000 );
175
- }
176
- if ($ this ->isRunning ()) {
177
- usleep (100000 );
178
- $ this ->doSignal (9 , false );
179
- }
180
-
181
- // Don't call ->stop() nor ->close() since we don't want to wait for the subprocess here
180
+ $ this ->stop (0 );
182
181
}
183
182
184
183
public function __clone ()
@@ -227,7 +226,7 @@ public function run($callback = null)
227
226
*/
228
227
public function mustRun ($ callback = null )
229
228
{
230
- if ($ this ->isSigchildEnabled () && ! $ this ->enhanceSigchildCompatibility ) {
229
+ if (! $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled () ) {
231
230
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. ' );
232
231
}
233
232
@@ -388,17 +387,9 @@ public function wait($callback = null)
388
387
* Returns the Pid (process identifier), if applicable.
389
388
*
390
389
* @return int|null The process id if running, null otherwise
391
- *
392
- * @throws RuntimeException In case --enable-sigchild is activated
393
390
*/
394
391
public function getPid ()
395
392
{
396
- if ($ this ->isSigchildEnabled ()) {
397
- throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. ' );
398
- }
399
-
400
- $ this ->updateStatus (false );
401
-
402
393
return $ this ->isRunning () ? $ this ->processInformation ['pid ' ] : null ;
403
394
}
404
395
@@ -410,7 +401,7 @@ public function getPid()
410
401
* @return Process
411
402
*
412
403
* @throws LogicException In case the process is not running
413
- * @throws RuntimeException In case --enable-sigchild is activated
404
+ * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
414
405
* @throws RuntimeException In case of failure
415
406
*/
416
407
public function signal ($ signal )
@@ -604,7 +595,9 @@ public function clearErrorOutput()
604
595
*/
605
596
public function getExitCode ()
606
597
{
607
- if ($ this ->isSigchildEnabled () && !$ this ->enhanceSigchildCompatibility ) {
598
+ if (!$ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
599
+ $ this ->stop (0 );
600
+
608
601
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. ' );
609
602
}
610
603
@@ -621,8 +614,6 @@ public function getExitCode()
621
614
*
622
615
* @return null|string A string representation for the exit status code, null if the Process is not terminated.
623
616
*
624
- * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
625
- *
626
617
* @see http://tldp.org/LDP/abs/html/exitcodes.html
627
618
* @see http://en.wikipedia.org/wiki/Unix_signal
628
619
*/
@@ -659,12 +650,12 @@ public function hasBeenSignaled()
659
650
{
660
651
$ this ->requireProcessIsTerminated (__FUNCTION__ );
661
652
662
- if ($ this ->isSigchildEnabled ()) {
653
+ if (!$ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
654
+ $ this ->stop (0 );
655
+
663
656
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
664
657
}
665
658
666
- $ this ->updateStatus (false );
667
-
668
659
return $ this ->processInformation ['signaled ' ];
669
660
}
670
661
@@ -682,12 +673,12 @@ public function getTermSignal()
682
673
{
683
674
$ this ->requireProcessIsTerminated (__FUNCTION__ );
684
675
685
- if ($ this ->isSigchildEnabled ()) {
676
+ if ($ this ->isSigchildEnabled () && (!$ this ->enhanceSigchildCompatibility || -1 === $ this ->processInformation ['termsig ' ])) {
677
+ $ this ->stop (0 );
678
+
686
679
throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. ' );
687
680
}
688
681
689
- $ this ->updateStatus (false );
690
-
691
682
return $ this ->processInformation ['termsig ' ];
692
683
}
693
684
@@ -704,8 +695,6 @@ public function hasBeenStopped()
704
695
{
705
696
$ this ->requireProcessIsTerminated (__FUNCTION__ );
706
697
707
- $ this ->updateStatus (false );
708
-
709
698
return $ this ->processInformation ['stopped ' ];
710
699
}
711
700
@@ -722,8 +711,6 @@ public function getStopSignal()
722
711
{
723
712
$ this ->requireProcessIsTerminated (__FUNCTION__ );
724
713
725
- $ this ->updateStatus (false );
726
-
727
714
return $ this ->processInformation ['stopsig ' ];
728
715
}
729
716
@@ -797,7 +784,7 @@ public function stop($timeout = 10, $signal = null)
797
784
usleep (1000 );
798
785
} while ($ this ->isRunning () && microtime (true ) < $ timeoutMicro );
799
786
800
- if ($ this ->isRunning () && ! $ this -> isSigchildEnabled () ) {
787
+ if ($ this ->isRunning ()) {
801
788
// Avoid exception here: process is supposed to be running, but it might have stopped just
802
789
// after this line. In any case, let's silently discard the error, we cannot do anything.
803
790
$ this ->doSignal ($ signal ?: 9 , false );
@@ -1262,9 +1249,15 @@ private function getDescriptors()
1262
1249
1263
1250
if (!$ this ->useFileHandles && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1264
1251
// last exit code is output on the fourth pipe and caught to work around --enable-sigchild
1265
- $ descriptors = array_merge ($ descriptors , array (array ('pipe ' , 'w ' )));
1252
+ $ descriptors [3 ] = array ('pipe ' , 'w ' );
1253
+
1254
+ $ trap = '' ;
1255
+ foreach (self ::$ posixSignals as $ s ) {
1256
+ $ trap .= "trap 'echo s $ s >&3' $ s; " ;
1257
+ }
1266
1258
1267
- $ this ->commandline = '( ' .$ this ->commandline .') 3>/dev/null; code=$?; echo $code >&3; exit $code ' ;
1259
+ $ this ->commandline = $ trap .'{ ( ' .$ this ->commandline .') <&3 3<&- 3>/dev/null & } 3<&0; ' ;
1260
+ $ this ->commandline .= 'pid=$!; echo p$pid >&3; wait $pid; code=$?; echo x$code >&3; exit $code ' ;
1268
1261
}
1269
1262
1270
1263
return $ descriptors ;
@@ -1311,10 +1304,13 @@ protected function updateStatus($blocking)
1311
1304
}
1312
1305
1313
1306
$ this ->processInformation = proc_get_status ($ this ->process );
1314
- $ this ->captureExitCode ();
1315
1307
1316
1308
$ this ->readPipes ($ blocking , '\\' === DIRECTORY_SEPARATOR ? !$ this ->processInformation ['running ' ] : true );
1317
1309
1310
+ if ($ this ->fallbackStatus && $ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1311
+ $ this ->processInformation = $ this ->fallbackStatus + $ this ->processInformation ;
1312
+ }
1313
+
1318
1314
if (!$ this ->processInformation ['running ' ]) {
1319
1315
$ this ->close ();
1320
1316
}
@@ -1331,7 +1327,7 @@ protected function isSigchildEnabled()
1331
1327
return self ::$ sigchild ;
1332
1328
}
1333
1329
1334
- if (!function_exists ('phpinfo ' )) {
1330
+ if (!function_exists ('phpinfo ' ) || defined ( ' HHVM_VERSION ' ) ) {
1335
1331
return self ::$ sigchild = false ;
1336
1332
}
1337
1333
@@ -1375,24 +1371,24 @@ private function readPipes($blocking, $close)
1375
1371
1376
1372
$ callback = $ this ->callback ;
1377
1373
foreach ($ result as $ type => $ data ) {
1378
- if (3 == $ type ) {
1379
- $ this ->fallbackExitcode = (int ) $ data ;
1374
+ if (3 === $ type ) {
1375
+ foreach (explode ("\n" , substr ($ data , 0 , -1 )) as $ data ) {
1376
+ if ('p ' === $ data [0 ]) {
1377
+ $ this ->fallbackStatus ['pid ' ] = (int ) substr ($ data , 1 );
1378
+ } elseif ('s ' === $ data [0 ]) {
1379
+ $ this ->fallbackStatus ['signaled ' ] = true ;
1380
+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1381
+ $ this ->fallbackStatus ['termsig ' ] = (int ) substr ($ data , 1 );
1382
+ } elseif ('x ' === $ data [0 ] && !isset ($ this ->fallbackStatus ['signaled ' ])) {
1383
+ $ this ->fallbackStatus ['exitcode ' ] = (int ) substr ($ data , 1 );
1384
+ }
1385
+ }
1380
1386
} else {
1381
1387
$ callback ($ type === self ::STDOUT ? self ::OUT : self ::ERR , $ data );
1382
1388
}
1383
1389
}
1384
1390
}
1385
1391
1386
- /**
1387
- * Captures the exitcode if mentioned in the process information.
1388
- */
1389
- private function captureExitCode ()
1390
- {
1391
- if (isset ($ this ->processInformation ['exitcode ' ]) && -1 != $ this ->processInformation ['exitcode ' ]) {
1392
- $ this ->exitcode = $ this ->processInformation ['exitcode ' ];
1393
- }
1394
- }
1395
-
1396
1392
/**
1397
1393
* Closes process resource, closes file handles, sets the exitcode.
1398
1394
*
@@ -1402,19 +1398,19 @@ private function close()
1402
1398
{
1403
1399
$ this ->processPipes ->close ();
1404
1400
if (is_resource ($ this ->process )) {
1405
- $ exitcode = proc_close ($ this ->process );
1406
- } else {
1407
- $ exitcode = -1 ;
1401
+ proc_close ($ this ->process );
1408
1402
}
1409
-
1410
- $ this ->exitcode = -1 !== $ exitcode ? $ exitcode : (null !== $ this ->exitcode ? $ this ->exitcode : -1 );
1403
+ $ this ->exitcode = $ this ->processInformation ['exitcode ' ];
1411
1404
$ this ->status = self ::STATUS_TERMINATED ;
1412
1405
1413
- if (-1 === $ this ->exitcode && null !== $ this ->fallbackExitcode ) {
1414
- $ this ->exitcode = $ this ->fallbackExitcode ;
1415
- } elseif (-1 === $ this ->exitcode && $ this ->processInformation ['signaled ' ] && 0 < $ this ->processInformation ['termsig ' ]) {
1416
- // if process has been signaled, no exitcode but a valid termsig, apply Unix convention
1417
- $ this ->exitcode = 128 + $ this ->processInformation ['termsig ' ];
1406
+ if (-1 === $ this ->exitcode ) {
1407
+ if ($ this ->processInformation ['signaled ' ] && 0 < $ this ->processInformation ['termsig ' ]) {
1408
+ // if process has been signaled, no exitcode but a valid termsig, apply Unix convention
1409
+ $ this ->exitcode = 128 + $ this ->processInformation ['termsig ' ];
1410
+ } elseif ($ this ->enhanceSigchildCompatibility && $ this ->isSigchildEnabled ()) {
1411
+ $ this ->processInformation ['signaled ' ] = true ;
1412
+ $ this ->processInformation ['termsig ' ] = -1 ;
1413
+ }
1418
1414
}
1419
1415
1420
1416
// Free memory from self-reference callback created by buildCallback
@@ -1433,7 +1429,7 @@ private function resetProcessData()
1433
1429
$ this ->starttime = null ;
1434
1430
$ this ->callback = null ;
1435
1431
$ this ->exitcode = null ;
1436
- $ this ->fallbackExitcode = null ;
1432
+ $ this ->fallbackStatus = array () ;
1437
1433
$ this ->processInformation = null ;
1438
1434
$ this ->stdout = null ;
1439
1435
$ this ->stderr = null ;
@@ -1453,7 +1449,7 @@ private function resetProcessData()
1453
1449
* @return bool True if the signal was sent successfully, false otherwise
1454
1450
*
1455
1451
* @throws LogicException In case the process is not running
1456
- * @throws RuntimeException In case --enable-sigchild is activated
1452
+ * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
1457
1453
* @throws RuntimeException In case of failure
1458
1454
*/
1459
1455
private function doSignal ($ signal , $ throwException )
@@ -1466,9 +1462,9 @@ private function doSignal($signal, $throwException)
1466
1462
return false ;
1467
1463
}
1468
1464
1469
- if ($ this ->isSigchildEnabled ()) {
1465
+ if ($ this ->enhanceSigchildCompatibility && $ this -> isSigchildEnabled () && ! isset ( self :: $ posixSignals [ $ signal ]) && !( function_exists ( ' posix_kill ' ) && @ posix_kill ( $ this -> getPid (), $ signal ) )) {
1470
1466
if ($ throwException ) {
1471
- throw new RuntimeException ('This PHP has been compiled with --enable-sigchild. The process can not be signaled . ' );
1467
+ throw new RuntimeException ('This PHP has been compiled with --enable-sigchild and posix_kill() is not available . ' );
1472
1468
}
1473
1469
1474
1470
return false ;
@@ -1493,7 +1489,10 @@ private function doSignal($signal, $throwException)
1493
1489
return false ;
1494
1490
}
1495
1491
1496
- $ this ->latestSignal = $ signal ;
1492
+ $ this ->latestSignal = (int ) $ signal ;
1493
+ $ this ->fallbackStatus ['signaled ' ] = true ;
1494
+ $ this ->fallbackStatus ['exitcode ' ] = -1 ;
1495
+ $ this ->fallbackStatus ['termsig ' ] = $ this ->latestSignal ;
1497
1496
1498
1497
return true ;
1499
1498
}
0 commit comments