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

Skip to content

Commit 7b11f1f

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

7 files changed

+116
-706
lines changed

src/Symfony/Component/Process/Process.php

Lines changed: 58 additions & 54 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
*/
@@ -515,17 +512,12 @@ public function isSuccessful()
515512
*
516513
* @return bool
517514
*
518-
* @throws RuntimeException In case --enable-sigchild is activated
519-
* @throws LogicException In case the process is not terminated
515+
* @throws LogicException In case the process is not terminated
520516
*/
521517
public function hasBeenSignaled()
522518
{
523519
$this->requireProcessIsTerminated(__FUNCTION__);
524520

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

531523
return $this->processInformation['signaled'];
@@ -545,12 +537,12 @@ public function getTermSignal()
545537
{
546538
$this->requireProcessIsTerminated(__FUNCTION__);
547539

548-
if ($this->isSigchildEnabled()) {
540+
$this->updateStatus(false);
541+
542+
if ($this->isSigchildEnabled() && -1 === $this->processInformation['termsig']) {
549543
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
550544
}
551545

552-
$this->updateStatus(false);
553-
554546
return $this->processInformation['termsig'];
555547
}
556548

@@ -660,7 +652,7 @@ public function stop($timeout = 10, $signal = null)
660652
usleep(1000);
661653
} while ($this->isRunning() && microtime(true) < $timeoutMicro);
662654

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

999991
if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
1000992
// 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')));
993+
$descriptors[3] = array('pipe', 'w');
1002994

1003-
$this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code';
995+
$trap = '';
996+
foreach (self::$posixSignals as $s) {
997+
$trap .= "trap 'echo s$s >&3' $s;";
998+
}
999+
1000+
$this->commandline = $trap.'{ ('.$this->commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
1001+
$this->commandline .= 'pid=$!; echo p$pid >&3; wait $pid; code=$?; echo x$code >&3; exit $code';
10041002
}
10051003

10061004
return $descriptors;
@@ -1047,10 +1045,13 @@ protected function updateStatus($blocking)
10471045
}
10481046

10491047
$this->processInformation = proc_get_status($this->process);
1050-
$this->captureExitCode();
10511048

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

1051+
if ($this->fallbackStatus && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
1052+
$this->processInformation = $this->fallbackStatus + $this->processInformation;
1053+
}
1054+
10541055
if (!$this->processInformation['running']) {
10551056
$this->close();
10561057
}
@@ -1067,7 +1068,7 @@ protected function isSigchildEnabled()
10671068
return self::$sigchild;
10681069
}
10691070

1070-
if (!function_exists('phpinfo')) {
1071+
if (!function_exists('phpinfo') || defined('HHVM_VERSION')) {
10711072
return self::$sigchild = false;
10721073
}
10731074

@@ -1093,24 +1094,24 @@ private function readPipes($blocking, $close)
10931094

10941095
$callback = $this->callback;
10951096
foreach ($result as $type => $data) {
1096-
if (3 == $type) {
1097-
$this->fallbackExitcode = (int) $data;
1097+
if (3 === $type) {
1098+
foreach (explode("\n", substr($data, 0, -1)) as $data) {
1099+
if ('p' === $data[0]) {
1100+
$this->fallbackStatus['pid'] = (int) substr($data, 1);
1101+
} elseif ('s' === $data[0]) {
1102+
$this->fallbackStatus['signaled'] = true;
1103+
$this->fallbackStatus['exitcode'] = -1;
1104+
$this->fallbackStatus['termsig'] = (int) substr($data, 1);
1105+
} elseif ('x' === $data[0] && !isset($this->fallbackStatus['signaled'])) {
1106+
$this->fallbackStatus['exitcode'] = (int) substr($data, 1);
1107+
}
1108+
}
10981109
} else {
10991110
$callback($type === self::STDOUT ? self::OUT : self::ERR, $data);
11001111
}
11011112
}
11021113
}
11031114

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-
11141115
/**
11151116
* Closes process resource, closes file handles, sets the exitcode.
11161117
*
@@ -1120,19 +1121,19 @@ private function close()
11201121
{
11211122
$this->processPipes->close();
11221123
if (is_resource($this->process)) {
1123-
$exitcode = proc_close($this->process);
1124-
} else {
1125-
$exitcode = -1;
1124+
proc_close($this->process);
11261125
}
1127-
1128-
$this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1);
1126+
$this->exitcode = $this->processInformation['exitcode'];
11291127
$this->status = self::STATUS_TERMINATED;
11301128

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'];
1129+
if (-1 === $this->exitcode) {
1130+
if ($this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) {
1131+
// if process has been signaled, no exitcode but a valid termsig, apply Unix convention
1132+
$this->exitcode = 128 + $this->processInformation['termsig'];
1133+
} elseif ($this->isSigchildEnabled()) {
1134+
$this->processInformation['signaled'] = true;
1135+
$this->processInformation['termsig'] = -1;
1136+
}
11361137
}
11371138

11381139
// Free memory from self-reference callback created by buildCallback
@@ -1151,7 +1152,7 @@ private function resetProcessData()
11511152
$this->starttime = null;
11521153
$this->callback = null;
11531154
$this->exitcode = null;
1154-
$this->fallbackExitcode = null;
1155+
$this->fallbackStatus = array();
11551156
$this->processInformation = null;
11561157
$this->stdout = null;
11571158
$this->stderr = null;
@@ -1171,7 +1172,7 @@ private function resetProcessData()
11711172
* @return bool True if the signal was sent successfully, false otherwise
11721173
*
11731174
* @throws LogicException In case the process is not running
1174-
* @throws RuntimeException In case --enable-sigchild is activated
1175+
* @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
11751176
* @throws RuntimeException In case of failure
11761177
*/
11771178
private function doSignal($signal, $throwException)
@@ -1184,9 +1185,9 @@ private function doSignal($signal, $throwException)
11841185
return false;
11851186
}
11861187

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

11921193
return false;
@@ -1211,7 +1212,10 @@ private function doSignal($signal, $throwException)
12111212
return false;
12121213
}
12131214

1214-
$this->latestSignal = $signal;
1215+
$this->latestSignal = (int) $signal;
1216+
$this->fallbackStatus['signaled'] = true;
1217+
$this->fallbackStatus['exitcode'] = -1;
1218+
$this->fallbackStatus['termsig'] = $this->latestSignal;
12151219

12161220
return true;
12171221
}

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)