Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit deab96b

Browse filesBrowse files
[Process] Enhance compatiblity with --enable-sigchild
1 parent ed22696 commit deab96b
Copy full SHA for deab96b

7 files changed

+116
-706
lines changed

‎src/Symfony/Component/Process/Process.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Process/Process.php
+58-54Lines 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,
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)
@@ -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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Process/Tests/PhpProcessTest.php
+5-9Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php
-22Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.