Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b2d9c18

Browse files
[Process] Enhance compatiblity with --enable-sigchild
1 parent ed22696 commit b2d9c18

8 files changed

+122
-706
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ addons:
1010
cache:
1111
directories:
1212
- .phpunit
13+
- php-5.3.9
1314

1415
matrix:
1516
include:
@@ -32,6 +33,7 @@ env:
3233

3334
before_install:
3435
- if [[ "$deps" = "no" ]] && [[ "$TRAVIS_PHP_VERSION" =~ 5.[45] ]] && [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then export deps=skip; fi;
36+
- if [[ $deps = no && $TRAVIS_PHP_VERSION = 5.3 && ! -d php-5.3.9/sapi ]]; then wget http://museum.php.net/php5/php-5.3.9.tar.bz2; tar -xjf php-5.3.9.tar.bz2; (cd php-5.3.9; ./configure --enable-sigchild --enable-pcntl; make -j2); fi;
3537
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi;
3638
- echo "memory_limit = -1" >> $INI_FILE
3739
- echo "session.gc_probability = 0" >> $INI_FILE
@@ -54,6 +56,7 @@ install:
5456
script:
5557
- if [ "$deps" = "no" ]; then echo "$COMPONENTS" | parallel --gnu '$PHPUNIT --exclude-group tty,benchmark,intl-data {}'; fi;
5658
- if [ "$deps" = "no" ]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi;
59+
- if [[ $deps = no && $TRAVIS_PHP_VERSION = 5.3 ]]; then php-5.3.9/sapi/cli/php .phpunit/phpunit-4.8/phpunit src/Symfony/Component/Process/; fi;
5760
- if [ "$deps" = "high" ]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer --prefer-source update; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi;
5861
- if [ "$deps" = "low" ]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi;
5962
- if [ "$deps" = "skip" ]; then echo 'This matrix line is skipped for pull requests.'; fi;

src/Symfony/Component/Process/Process.php

Lines changed: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

src/Symfony/Component/Process/Tests/PhpProcessTest.php

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,20 @@ public function testNonBlockingWorks()
3030

3131
public function testCommandLine()
3232
{
33-
if ('phpdbg' === PHP_SAPI) {
34-
$this->markTestSkipped('phpdbg SAPI is not supported by this test.');
35-
}
36-
3733
$process = new PhpProcess(<<<PHP
3834
<?php echo 'foobar';
3935
PHP
4036
);
4137

42-
$f = new PhpExecutableFinder();
43-
$commandLine = $f->find();
38+
$commandLine = $process->getCommandLine();
4439

45-
$this->assertSame($commandLine, $process->getCommandLine(), '::getCommandLine() returns the command line of PHP before start');
40+
$f = new PhpExecutableFinder();
41+
$this->assertContains($f->find(), $commandLine, '::getCommandLine() returns the command line of PHP before start');
4642

4743
$process->start();
48-
$this->assertSame($commandLine, $process->getCommandLine(), '::getCommandLine() returns the command line of PHP after start');
44+
$this->assertContains($commandLine, $process->getCommandLine(), '::getCommandLine() returns the command line of PHP after start');
4945

5046
$process->wait();
51-
$this->assertSame($commandLine, $process->getCommandLine(), '::getCommandLine() returns the command line of PHP after wait');
47+
$this->assertContains($commandLine, $process->getCommandLine(), '::getCommandLine() returns the command line of PHP after wait');
5248
}
5349
}

src/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)