diff --git a/src/Symfony/Component/Process/NullProcessPipes.php b/src/Symfony/Component/Process/NullProcessPipes.php new file mode 100644 index 0000000000000..438c1c0581474 --- /dev/null +++ b/src/Symfony/Component/Process/NullProcessPipes.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\RuntimeException; + +/** + * NullProcessPipes allow redirecting output to dev/null without a subshell. Useful for processes that communicate + * over other means. + */ +class NullProcessPipes extends ProcessPipes +{ + /** + * Returns an array of descriptors for the use of proc_open. + * + * @return array + */ + public function getDescriptors() + { + $nullfile = defined('PHP_WINDOWS_VERSION_BUILD') ? 'NUL' : '/dev/null'; + return array( + + array('pipe', 'r'), // stdin + array('file', $nullfile, 'a+'), // stdout + array('file', $nullfile, 'a+'), //stderr + ); + } +} + diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 39caa3308f7e3..0eb26476a12c9 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -991,6 +991,20 @@ public function checkTimeout() } } + /** + * Sets the process pipes to use. + * + * @param ProcessPipes $pipes + * @throws LogicException If called after start() + */ + public function setProcessPipes(ProcessPipes $pipes) + { + if ($this->status !== self::STATUS_READY) { + throw new LogicException('ProcessPipes cannot be changed after the process has started'); + } + $this->processPipes = $pipes; + } + /** * Creates the descriptors needed by the proc_open. * @@ -998,7 +1012,9 @@ public function checkTimeout() */ private function getDescriptors() { - $this->processPipes = new ProcessPipes($this->useFileHandles); + if (!$this->processPipes) { + $this->processPipes = new ProcessPipes($this->useFileHandles); + } $descriptors = $this->processPipes->getDescriptors(); if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { diff --git a/src/Symfony/Component/Process/ProcessBuilder.php b/src/Symfony/Component/Process/ProcessBuilder.php index b6168feb62d0e..dcb1b18c445c8 100644 --- a/src/Symfony/Component/Process/ProcessBuilder.php +++ b/src/Symfony/Component/Process/ProcessBuilder.php @@ -29,6 +29,7 @@ class ProcessBuilder private $options = array(); private $inheritEnv = true; private $prefix = array(); + private $processPipes; public function __construct(array $arguments = array()) { @@ -117,6 +118,20 @@ public function setInput($stdin) return $this; } + /** + * Sets the process pipes to use for the new process + * + * @param ProcessPipes $pipes + * + * @return ProcessBuilder + */ + public function setProcessPipes(ProcessPipes $pipes) + { + $this->processPipes = $pipes; + + return $this; + } + /** * Sets the process timeout. * @@ -172,6 +187,11 @@ public function getProcess() $env = $this->env; } - return new Process($script, $this->cwd, $env, $this->stdin, $this->timeout, $options); + $process = new Process($script, $this->cwd, $env, $this->stdin, $this->timeout, $options); + if ($this->processPipes) { + $process->setProcessPipes($this->processPipes); + } + + return $process; } } diff --git a/src/Symfony/Component/Process/Tests/HeavyOutputtingProcess.php b/src/Symfony/Component/Process/Tests/HeavyOutputtingProcess.php new file mode 100644 index 0000000000000..c350fbd995a5b --- /dev/null +++ b/src/Symfony/Component/Process/Tests/HeavyOutputtingProcess.php @@ -0,0 +1,9 @@ +setProcessPipes(new NullProcessPipes()); + $process->start(); + + while ($process->isRunning()) { + usleep(100e3); + } + + // No output! + $this->assertEquals('', $process->getOutput()); + } + + public function testReassigningPipesAfterStartIsNotAllowed() + { + $process = new Process('php -r "sleep(1);'); + + $process->setProcessPipes(new NullProcessPipes()); + $process->start(); + + $this->setExpectedException('Symfony\Component\Process\Exception\LogicException'); + $process->setProcessPipes(new ProcessPipes()); + } + + public function testRestartedProcesses() { + $process = new Process('echo asdf'); + + $process->run(); + $this->assertEquals("asdf\n", $process->getOutput()); + + $process->run(); + $this->assertEquals("asdf\n", $process->getOutput()); + } +}