diff --git a/src/Symfony/Component/Process/ProcessBuilder.php b/src/Symfony/Component/Process/ProcessBuilder.php index 71633cd7fac3c..07f8be42eab1d 100644 --- a/src/Symfony/Component/Process/ProcessBuilder.php +++ b/src/Symfony/Component/Process/ProcessBuilder.php @@ -13,6 +13,7 @@ use Symfony\Component\Process\Exception\InvalidArgumentException; use Symfony\Component\Process\Exception\LogicException; +use Symfony\Component\Process\Exception\RuntimeException; /** * Process builder. @@ -30,6 +31,12 @@ class ProcessBuilder private $inheritEnv = true; private $prefix = array(); private $outputDisabled = false; + private $shellWrapperEnabled = false; + + /** + * @var bool + */ + private static $sigchild; /** * Constructor @@ -251,6 +258,40 @@ public function enableOutput() return $this; } + /** + * Do not enable the shell wrapper (i.e. do not prepend the executed command with "exec"). + * + * @return ProcessBuilder + */ + public function disableShellWrapper() + { + $this->shellWrapperEnabled = false; + + return $this; + } + + /** + * Enable the shell wrapper (i.e. prepend the executed command with "exec"). + * + * @return ProcessBuilder + * + * @throws RuntimeException if the OS is Windows or if PHP was built with --enable-sigchild + */ + public function enableShellWrapper() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + throw new RuntimeException('The shell wrapper is not supported on Windows platforms.'); + } + + if ($this->isSigchildEnabled()) { + throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The shell wrapper cannot be enabled.'); + } + + $this->shellWrapperEnabled = true; + + return $this; + } + /** * Creates a Process instance and returns it. * @@ -269,6 +310,10 @@ public function getProcess() $arguments = array_merge($this->prefix, $this->arguments); $script = implode(' ', array_map(array(__NAMESPACE__.'\\ProcessUtils', 'escapeArgument'), $arguments)); + if ($this->shellWrapperEnabled) { + $script = 'exec '.$script; + } + if ($this->inheritEnv) { // include $_ENV for BC purposes $env = array_replace($_ENV, $_SERVER, $this->env); @@ -284,4 +329,25 @@ public function getProcess() return $process; } + + /** + * Returns whether PHP has been compiled with the '--enable-sigchild' option or not. + * + * @return bool + */ + private function isSigchildEnabled() + { + if (null !== self::$sigchild) { + return self::$sigchild; + } + + if (!function_exists('phpinfo')) { + return self::$sigchild = false; + } + + ob_start(); + phpinfo(INFO_GENERAL); + + return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); + } } diff --git a/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php b/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php index e864f6623456b..877e3e88c2fd2 100644 --- a/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php @@ -11,10 +11,13 @@ namespace Symfony\Component\Process\Tests; +use Symfony\Component\Process\Exception\RuntimeException; use Symfony\Component\Process\ProcessBuilder; class ProcessBuilderTest extends \PHPUnit_Framework_TestCase { + private static $sigchild; + public function testInheritEnvironmentVars() { $_ENV['MY_VAR_1'] = 'foo'; @@ -222,4 +225,84 @@ public function testInvalidInput() $builder = ProcessBuilder::create(); $builder->setInput(array()); } + + public function testShouldNotPrependCommandLineWithExecByDefault() + { + $process = ProcessBuilder::create() + ->add('foo') + ->getProcess(); + + $this->assertNotRegExp('/^exec /', $process->getCommandLine()); + } + + public function testShouldPrependCommandLineWithExec() + { + if ($this->isSigchildEnabled()) { + $this->markTestSkipped('->enableShellWrapper() is not supported in sigchild environment'); + } + + $process = ProcessBuilder::create() + ->add('foo') + ->enableShellWrapper() + ->getProcess(); + + $this->assertRegExp('/^exec /', $process->getCommandLine()); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage The shell wrapper is not supported on Windows platforms. + */ + public function testEnableShellWrapperOnWindowsThrowsException() + { + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('->enableShellWrapper() is not supported on Windows'); + } + + ProcessBuilder::create()->enableShellWrapper(); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The shell wrapper cannot be enabled. + */ + public function testEnableShellWrapperInSigchildEnvironmentThrowsException() + { + if (!$this->isSigchildEnabled()) { + $this->markTestSkipped('->enableShellWrapper() is not supported in sigchild environment'); + } + + ProcessBuilder::create()->enableShellWrapper(); + } + + public function testShouldNotPrependCommandLineWithExec() + { + $process = ProcessBuilder::create() + ->add('foo') + ->disableShellWrapper() + ->getProcess(); + + $this->assertNotRegExp('/^exec /', $process->getCommandLine()); + } + + /** + * Returns whether PHP has been compiled with the '--enable-sigchild' option or not. + * + * @return bool + */ + private function isSigchildEnabled() + { + if (null !== self::$sigchild) { + return self::$sigchild; + } + + if (!function_exists('phpinfo')) { + return self::$sigchild = false; + } + + ob_start(); + phpinfo(INFO_GENERAL); + + return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); + } }