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

Skip to content

Commit b9395dc

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

7 files changed

+113
-714
lines changed

src/Symfony/Component/Process/Process.php

Lines changed: 55 additions & 62 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,
70+
2 => 2,
71+
3 => 3,
72+
6 => 6,
73+
14 => 14,
74+
15 => 15,
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)
@@ -462,15 +461,9 @@ public function getIncrementalErrorOutput()
462461
* Returns the exit code returned by the process.
463462
*
464463
* @return null|int The exit status code, null if the Process is not terminated
465-
*
466-
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
467464
*/
468465
public function getExitCode()
469466
{
470-
if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
471-
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
472-
}
473-
474467
$this->updateStatus(false);
475468

476469
return $this->exitcode;
@@ -484,8 +477,6 @@ public function getExitCode()
484477
*
485478
* @return null|string A string representation for the exit status code, null if the Process is not terminated.
486479
*
487-
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
488-
*
489480
* @see http://tldp.org/LDP/abs/html/exitcodes.html
490481
* @see http://en.wikipedia.org/wiki/Unix_signal
491482
*/
@@ -515,17 +506,12 @@ public function isSuccessful()
515506
*
516507
* @return bool
517508
*
518-
* @throws RuntimeException In case --enable-sigchild is activated
519-
* @throws LogicException In case the process is not terminated
509+
* @throws LogicException In case the process is not terminated
520510
*/
521511
public function hasBeenSignaled()
522512
{
523513
$this->requireProcessIsTerminated(__FUNCTION__);
524514

525-
if ($this->isSigchildEnabled()) {
526-
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
527-
}
528-
529515
$this->updateStatus(false);
530516

531517
return $this->processInformation['signaled'];
@@ -538,17 +524,12 @@ public function hasBeenSignaled()
538524
*
539525
* @return int
540526
*
541-
* @throws RuntimeException In case --enable-sigchild is activated
542-
* @throws LogicException In case the process is not terminated
527+
* @throws LogicException In case the process is not terminated
543528
*/
544529
public function getTermSignal()
545530
{
546531
$this->requireProcessIsTerminated(__FUNCTION__);
547532

548-
if ($this->isSigchildEnabled()) {
549-
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
550-
}
551-
552533
$this->updateStatus(false);
553534

554535
return $this->processInformation['termsig'];
@@ -660,7 +641,7 @@ public function stop($timeout = 10, $signal = null)
660641
usleep(1000);
661642
} while ($this->isRunning() && microtime(true) < $timeoutMicro);
662643

663-
if ($this->isRunning() && !$this->isSigchildEnabled()) {
644+
if ($this->isRunning()) {
664645
// Avoid exception here: process is supposed to be running, but it might have stopped just
665646
// after this line. In any case, let's silently discard the error, we cannot do anything.
666647
$this->doSignal($signal ?: 9, false);
@@ -998,9 +979,15 @@ private function getDescriptors()
998979

999980
if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
1000981
// 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')));
982+
$descriptors[3] = array('pipe', 'w');
983+
984+
$trap = '';
985+
foreach (self::$posixSignals as $s) {
986+
$trap .= "trap 'echo s$s >&3' $s;";
987+
}
1002988

1003-
$this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code';
989+
$this->commandline = $trap.'{ ('.$this->commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
990+
$this->commandline .= 'pid=$!; echo p$pid >&3; wait $pid; code=$?; echo x$code >&3; exit $code';
1004991
}
1005992

1006993
return $descriptors;
@@ -1047,10 +1034,13 @@ protected function updateStatus($blocking)
10471034
}
10481035

10491036
$this->processInformation = proc_get_status($this->process);
1050-
$this->captureExitCode();
10511037

10521038
$this->readPipes($blocking, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true);
10531039

1040+
if ($this->fallbackStatus && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
1041+
$this->processInformation = $this->fallbackStatus + $this->processInformation;
1042+
}
1043+
10541044
if (!$this->processInformation['running']) {
10551045
$this->close();
10561046
}
@@ -1067,7 +1057,7 @@ protected function isSigchildEnabled()
10671057
return self::$sigchild;
10681058
}
10691059

1070-
if (!function_exists('phpinfo')) {
1060+
if (!function_exists('phpinfo') || defined('HHVM_VERSION')) {
10711061
return self::$sigchild = false;
10721062
}
10731063

@@ -1093,24 +1083,24 @@ private function readPipes($blocking, $close)
10931083

10941084
$callback = $this->callback;
10951085
foreach ($result as $type => $data) {
1096-
if (3 == $type) {
1097-
$this->fallbackExitcode = (int) $data;
1086+
if (3 === $type) {
1087+
foreach (explode("\n", substr($data, 0, -1)) as $data) {
1088+
if ('p' === $data[0]) {
1089+
$this->fallbackStatus['pid'] = (int) substr($data, 1);
1090+
} elseif ('s' === $data[0]) {
1091+
$this->fallbackStatus['signaled'] = true;
1092+
$this->fallbackStatus['exitcode'] = -1;
1093+
$this->fallbackStatus['termsig'] = (int) substr($data, 1);
1094+
} elseif ('x' === $data[0] && !isset($this->fallbackStatus['signaled'])) {
1095+
$this->fallbackStatus['exitcode'] = (int) substr($data, 1);
1096+
}
1097+
}
10981098
} else {
10991099
$callback($type === self::STDOUT ? self::OUT : self::ERR, $data);
11001100
}
11011101
}
11021102
}
11031103

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-
11141104
/**
11151105
* Closes process resource, closes file handles, sets the exitcode.
11161106
*
@@ -1120,19 +1110,19 @@ private function close()
11201110
{
11211111
$this->processPipes->close();
11221112
if (is_resource($this->process)) {
1123-
$exitcode = proc_close($this->process);
1124-
} else {
1125-
$exitcode = -1;
1113+
proc_close($this->process);
11261114
}
1127-
1128-
$this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1);
1115+
$this->exitcode = $this->processInformation['exitcode'];
11291116
$this->status = self::STATUS_TERMINATED;
11301117

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'];
1118+
if (-1 === $this->exitcode) {
1119+
if ($this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) {
1120+
// if process has been signaled, no exitcode but a valid termsig, apply Unix convention
1121+
$this->exitcode = 128 + $this->processInformation['termsig'];
1122+
} elseif ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
1123+
$this->processInformation['signaled'] = true;
1124+
$this->processInformation['termsig'] = -1;
1125+
}
11361126
}
11371127

11381128
// Free memory from self-reference callback created by buildCallback
@@ -1151,7 +1141,7 @@ private function resetProcessData()
11511141
$this->starttime = null;
11521142
$this->callback = null;
11531143
$this->exitcode = null;
1154-
$this->fallbackExitcode = null;
1144+
$this->fallbackStatus = array();
11551145
$this->processInformation = null;
11561146
$this->stdout = null;
11571147
$this->stderr = null;
@@ -1171,7 +1161,7 @@ private function resetProcessData()
11711161
* @return bool True if the signal was sent successfully, false otherwise
11721162
*
11731163
* @throws LogicException In case the process is not running
1174-
* @throws RuntimeException In case --enable-sigchild is activated
1164+
* @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
11751165
* @throws RuntimeException In case of failure
11761166
*/
11771167
private function doSignal($signal, $throwException)
@@ -1184,9 +1174,9 @@ private function doSignal($signal, $throwException)
11841174
return false;
11851175
}
11861176

1187-
if ($this->isSigchildEnabled()) {
1177+
if ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled() && !isset(self::$posixSignals[$signal]) && !(function_exists('posix_kill') && @posix_kill($this->getPid(), $signal))) {
11881178
if ($throwException) {
1189-
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
1179+
throw new RuntimeException(sprintf('This PHP has been compiled with --enable-sigchild and posix_kill() is not available.', $signal));
11901180
}
11911181

11921182
return false;
@@ -1211,7 +1201,10 @@ private function doSignal($signal, $throwException)
12111201
return false;
12121202
}
12131203

1214-
$this->latestSignal = $signal;
1204+
$this->latestSignal = (int) $signal;
1205+
$this->fallbackStatus['signaled'] = true;
1206+
$this->fallbackStatus['exitcode'] = -1;
1207+
$this->fallbackStatus['termsig'] = $this->latestSignal;
12151208

12161209
return true;
12171210
}

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)