diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 6f345bc57f..c8772d9ae7 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -332,8 +332,6 @@ class Process usleep(1000); } - $exitcode = proc_close($this->process); - if ($this->processInformation['signaled']) { if ($this->isSigchildEnabled()) { throw new RuntimeException('The process has been signaled.'); @@ -342,12 +340,6 @@ class Process throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig'])); } - $this->exitcode = $this->processInformation['running'] ? $exitcode : $this->processInformation['exitcode']; - - if (-1 == $this->exitcode && null !== $this->fallbackExitcode) { - $this->exitcode = $this->fallbackExitcode; - } - return $this->exitcode; } @@ -664,20 +656,7 @@ class Process } } - foreach ($this->pipes as $pipe) { - fclose($pipe); - } - $this->pipes = array(); - - $exitcode = proc_close($this->process); - $this->exitcode = -1 === $this->processInformation['exitcode'] ? $exitcode : $this->processInformation['exitcode']; - - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - foreach ($this->fileHandles as $fileHandle) { - fclose($fileHandle); - } - $this->fileHandles = array(); - } + $this->updateStatus(false); } $this->status = self::STATUS_TERMINATED; @@ -1070,11 +1049,10 @@ class Process $this->readPipes($blocking); $this->processInformation = proc_get_status($this->process); + $this->captureExitCode(); if (!$this->processInformation['running']) { + $this->close(); $this->status = self::STATUS_TERMINATED; - if (-1 !== $this->processInformation['exitcode']) { - $this->exitcode = $this->processInformation['exitcode']; - } } } @@ -1237,4 +1215,53 @@ class Process } } } + + /** + * Captures the exitcode if mentioned in the process informations. + */ + private function captureExitCode() + { + if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) { + $this->exitcode = $this->processInformation['exitcode']; + } + } + + + /** + * Closes process resource, closes file handles, sets the exitcode. + * + * @return Integer The exitcode + */ + private function close() + { + foreach ($this->pipes as $pipe) { + fclose($pipe); + } + + $this->pipes = null; + $exitcode = -1; + + if (is_resource($this->process)) { + $exitcode = proc_close($this->process); + } + + $this->exitcode = $this->exitcode !== null ? $this->exitcode : -1; + $this->exitcode = -1 != $exitcode ? $exitcode : $this->exitcode; + + if (-1 == $this->exitcode && null !== $this->fallbackExitcode) { + $this->exitcode = $this->fallbackExitcode; + } elseif (-1 === $this->exitcode && $this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) { + // if process has been signaled, no exitcode but a valid termsig, apply unix convention + $this->exitcode = 128 + $this->processInformation['termsig']; + } + + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + foreach ($this->fileHandles as $fileHandle) { + fclose($fileHandle); + } + $this->fileHandles = array(); + } + + return $this->exitcode; + } } diff --git a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php index 53f79c5b55..a3b8c2a937 100644 --- a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php +++ b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php @@ -483,6 +483,24 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase $this->assertEquals('Caught SIGUSR1', $process->getOutput()); } + public function testExitCodeIsAvailableAfterSignal() + { + $this->verifyPosixIsEnabled(); + + $process = $this->getProcess('sleep 4'); + $process->start(); + $process->signal(SIGKILL); + + while ($process->isRunning()) { + usleep(10000); + } + + $this->assertFalse($process->isRunning()); + $this->assertTrue($process->hasBeenSignaled()); + $this->assertFalse($process->isSuccessful()); + $this->assertEquals(137, $process->getExitCode()); + } + /** * @expectedException Symfony\Component\Process\Exception\LogicException */ diff --git a/src/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php b/src/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php index 2e364d6392..8779263736 100644 --- a/src/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php +++ b/src/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php @@ -138,6 +138,11 @@ class SigchildDisabledProcessTest extends AbstractProcessTest $this->markTestSkipped('Retrieving Pid is not supported in sigchild environment'); } + public function testExitCodeIsAvailableAfterSignal() + { + $this->markTestSkipped('Signal is not supported in sigchild environment'); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php b/src/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php index 40ffb772b7..296b00dfcb 100644 --- a/src/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php +++ b/src/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php @@ -98,6 +98,11 @@ class SigchildEnabledProcessTest extends AbstractProcessTest $this->markTestSkipped('Retrieving Pid is not supported in sigchild environment'); } + public function testExitCodeIsAvailableAfterSignal() + { + $this->markTestSkipped('Signal is not supported in sigchild environment'); + } + /** * {@inheritdoc} */