diff --git a/src/Symfony/Component/Process/Exception/LogicException.php b/src/Symfony/Component/Process/Exception/LogicException.php new file mode 100644 index 0000000000000..be3d490dde8cd --- /dev/null +++ b/src/Symfony/Component/Process/Exception/LogicException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +/** + * LogicException for the Process Component. + * + * @author Romain Neutron + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index cd1e8b42155a2..b418175d4e969 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Process; +use Symfony\Component\Process\Exception\LogicException; +use Symfony\Component\Process\Exception\RuntimeException; + /** * Process is a thin wrapper around proc_* functions to ease * start independent PHP processes. @@ -361,7 +364,7 @@ public function wait($callback = null) } $this->updateStatus(); if ($this->processInformation['signaled']) { - throw new \RuntimeException(sprintf('The process stopped because of a "%s" signal.', $this->processInformation['stopsig'])); + throw new RuntimeException(sprintf('The process stopped because of a "%s" signal.', $this->processInformation['stopsig'])); } $time = 0; @@ -539,6 +542,41 @@ public function isRunning() return $this->processInformation['running']; } + /** + * Return the Pid (process id), if applicable + * + * @return integer|null The process id if running, null otherwise + */ + public function getPid() + { + $this->updateStatus(); + + return $this->isRunning() ? $this->processInformation['pid'] : null; + } + + /** + * Send a posix signal to the process + * + * @param integer $signal A valid posix signal (see http://www.php.net/manual/en/pcntl.constants.php) + * @return Process + * + * @throws LogicException In case the process is not running + * @throws RuntimeException In case the environment does not support posix signals + * @throws RuntimeException In case of failure + */ + public function signal($signal) + { + if (!$this->isRunning()) { + throw new LogicException('Can not send signal on a non running process'); + } + + if (true !== proc_terminate($this->process, $signal)) { + throw new RuntimeException('Error while sending signal'); + } + + return $this; + } + /** * Stops the process. * diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index 64f929cc20d61..92374fbdc8443 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Process\Tests; use Symfony\Component\Process\Process; +use Symfony\Component\Process\Exception\RuntimeException; /** * @author Robert Schönthal @@ -141,6 +142,53 @@ public function testIsRunning() $this->assertFalse($process->isRunning()); } + public function testGetPid() + { + $process = new Process('php -r "sleep(1);"'); + $this->assertNull($process->getPid()); + $process->start(); + $this->assertGreaterThan(0, $process->getPid()); + $process->wait(); + $this->assertNull($process->getPid()); + } + + public function testSendSignal() + { + $process = new Process('php -r "sleep(1);'); + $process->start(); + $process->sendSignal(SIGCONT); + $process->stop(); + } + + /** + * @expectedException Symfony\Component\Process\Exception\RuntimeException + */ + public function testSendKillSignal() + { + $process = new Process('php -r "sleep(1);'); + $process->start(); + $process->sendSignal(SIGKILL); + $process->wait(); + } + + public function testSendKillSignalAndCatch() + { + $process = new Process('php -r "sleep(1);'); + $process->start(); + $process->sendSignal(SIGKILL); + + try { + $process->wait(); + $this->fail('Should throw an exception as the signal stops the process'); + } catch (RuntimeException $e) { + + } + + $this->assertFalse($process->isRunning()); + $this->assertTrue($process->hasBeenSignaled()); + $this->assertEquals(SIGKILL, $process->getTermSignal()); + } + public function testStop() { $process = new Process('php -r "while (true) {}"');