From a891e14d11cc2d2d759f8d9e3e87c8b44f05cb99 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Wed, 11 Dec 2013 19:23:45 +0100 Subject: [PATCH] [Process] Add Process::disableOutput and Process::enableOutput methods --- src/Symfony/Component/Process/Process.php | 70 ++++++++++++++++- .../Component/Process/ProcessBuilder.php | 33 +++++++- .../Component/Process/ProcessPipes.php | 14 +++- .../Process/Tests/AbstractProcessTest.php | 78 +++++++++++++++++++ .../Process/Tests/ProcessBuilderTest.php | 19 +++++ 5 files changed, 211 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index f5104164bff6f..c7a1cf2a445b3 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -54,6 +54,7 @@ class Process private $exitcode; private $fallbackExitcode; private $processInformation; + private $outputDisabled = false; private $stdout; private $stderr; private $enhanceWindowsCompatibility; @@ -193,6 +194,7 @@ public function __clone() * @return integer The exit status code * * @throws RuntimeException When process can't be launch or is stopped + * @throws LogicException In case a callback is provided and output has been disabled * * @api */ @@ -244,12 +246,16 @@ public function mustRun($callback = null) * * @throws RuntimeException When process can't be launch or is stopped * @throws RuntimeException When process is already running + * @throws LogicException In case a callback is provided and output has been disabled */ public function start($callback = null) { if ($this->isRunning()) { throw new RuntimeException('Process is already running'); } + if ($this->outputDisabled && null !== $callback) { + throw new LogicException('Output has been disabled, enable it to allow the use of a callback.'); + } $this->resetProcessData(); $this->starttime = $this->lastOutputTime = microtime(true); @@ -400,15 +406,67 @@ public function signal($signal) return $this; } + /** + * Disables fetching output and error output from the underlying process. + * + * @return Process + * + * @throws RuntimeException In case the process is already running + */ + public function disableOutput() + { + if ($this->isRunning()) { + throw new RuntimeException('Disabling output while the process is running is not possible.'); + } + + $this->outputDisabled = true; + + return $this; + } + + /** + * Enables fetching output and error output from the underlying process. + * + * @return Process + * + * @throws RuntimeException In case the process is already running + */ + public function enableOutput() + { + if ($this->isRunning()) { + throw new RuntimeException('Enabling output while the process is running is not possible.'); + } + + $this->outputDisabled = false; + + return $this; + } + + /** + * Returns true in case the output is disabled, false otherwise. + * + * @return Boolean + */ + public function isOutputDisabled() + { + return $this->outputDisabled; + } + /** * Returns the current output of the process (STDOUT). * * @return string The process output * + * @throws LogicException in case the output has been disabled. + * * @api */ public function getOutput() { + if ($this->outputDisabled) { + throw new LogicException('Output has been disabled.'); + } + $this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true); return $this->stdout; @@ -420,6 +478,8 @@ public function getOutput() * In comparison with the getOutput method which always return the whole * output, this one returns the new output since the last call. * + * @throws LogicException in case the output has been disabled. + * * @return string The process output since the last call */ public function getIncrementalOutput() @@ -450,10 +510,16 @@ public function clearOutput() * * @return string The process error output * + * @throws LogicException in case the output has been disabled. + * * @api */ public function getErrorOutput() { + if ($this->outputDisabled) { + throw new LogicException('Output has been disabled.'); + } + $this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true); return $this->stderr; @@ -466,6 +532,8 @@ public function getErrorOutput() * whole error output, this one returns the new error output since the last * call. * + * @throws LogicException in case the output has been disabled. + * * @return string The process error output since the last call */ public function getIncrementalErrorOutput() @@ -1083,7 +1151,7 @@ public static function isPtySupported() private function getDescriptors() { $this->processPipes = new ProcessPipes($this->useFileHandles, $this->tty, $this->pty); - $descriptors = $this->processPipes->getDescriptors(); + $descriptors = $this->processPipes->getDescriptors($this->outputDisabled); if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { // last exit code is output on the fourth pipe and caught to work around --enable-sigchild diff --git a/src/Symfony/Component/Process/ProcessBuilder.php b/src/Symfony/Component/Process/ProcessBuilder.php index b6168feb62d0e..26a0a9b32d513 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 $outputDisabled = false; public function __construct(array $arguments = array()) { @@ -154,6 +155,30 @@ public function setOption($name, $value) return $this; } + /** + * Disables fetching output and error output from the underlying process. + * + * @return Process + */ + public function disableOutput() + { + $this->outputDisabled = true; + + return $this; + } + + /** + * Enables fetching output and error output from the underlying process. + * + * @return Process + */ + public function enableOutput() + { + $this->outputDisabled = false; + + return $this; + } + public function getProcess() { if (0 === count($this->prefix) && 0 === count($this->arguments)) { @@ -172,6 +197,12 @@ 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->outputDisabled) { + $process->disableOutput(); + } + + return $process; } } diff --git a/src/Symfony/Component/Process/ProcessPipes.php b/src/Symfony/Component/Process/ProcessPipes.php index fa107d8c03419..b3040314c1df8 100644 --- a/src/Symfony/Component/Process/ProcessPipes.php +++ b/src/Symfony/Component/Process/ProcessPipes.php @@ -101,10 +101,22 @@ public function closeUnixPipes() /** * Returns an array of descriptors for the use of proc_open. * + * @param Boolean $disableOutput Whether to redirect STDOUT and STDERR to /dev/null or not. + * * @return array */ - public function getDescriptors() + public function getDescriptors($disableOutput) { + if ($disableOutput) { + $nullstream = fopen(defined('PHP_WINDOWS_VERSION_BUILD') ? 'NUL' : '/dev/null', 'c'); + + return array( + array('pipe', 'r'), + $nullstream, + $nullstream, + ); + } + if ($this->useFiles) { return array( array('pipe', 'r'), diff --git a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php index f5291d0e15bb4..75981c97d346f 100644 --- a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php +++ b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php @@ -700,6 +700,84 @@ public function testSignalWithWrongNonIntSignal() $process->signal('Céphalopodes'); } + public function testDisableOutputDisablesTheOutput() + { + $p = $this->getProcess('php -r "usleep(500000);"'); + $this->assertFalse($p->isOutputDisabled()); + $p->disableOutput(); + $this->assertTrue($p->isOutputDisabled()); + $p->enableOutput(); + $this->assertFalse($p->isOutputDisabled()); + } + + public function testDisableOutputWhileRunningThrowsException() + { + $p = $this->getProcess('php -r "usleep(500000);"'); + $p->start(); + $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Disabling output while the process is running is not possible.'); + $p->disableOutput(); + } + + public function testEnableOutputWhileRunningThrowsException() + { + $p = $this->getProcess('php -r "usleep(500000);"'); + $p->disableOutput(); + $p->start(); + $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Enabling output while the process is running is not possible.'); + $p->enableOutput(); + } + + public function testEnableOrDisableOutputAfterRunDoesNotThrowException() + { + $p = $this->getProcess('php -r "usleep(500000);"'); + $p->disableOutput(); + $p->start(); + $p->wait(); + $p->enableOutput(); + $p->disableOutput(); + } + + /** + * @dataProvider provideStartMethods + */ + public function testStartWithACallbackAndDisabledOutput($startMethod) + { + $p = $this->getProcess('php -r "usleep(500000);"'); + $p->disableOutput(); + $this->setExpectedException('Symfony\Component\Process\Exception\LogicException', 'Output has been disabled, enable it to allow the use of a callback.'); + call_user_func(array($p, $startMethod), function () {}); + } + + public function provideStartMethods() + { + return array( + array('start'), + array('run'), + array('mustRun'), + ); + } + + /** + * @dataProvider provideOutputFetchingMethods + */ + public function testGetOutputWhileDisabled($fetchMethod) + { + $p = $this->getProcess('php -r "usleep(500000);"'); + $p->disableOutput(); + $this->setExpectedException('Symfony\Component\Process\Exception\LogicException', 'Output has been disabled.'); + call_user_func(array($p, $fetchMethod)); + } + + public function provideOutputFetchingMethods() + { + return array( + array('getOutput'), + array('getIncrementalOutput'), + array('getErrorOutput'), + array('getIncrementalErrorOutput'), + ); + } + public function responsesCodeProvider() { return array( diff --git a/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php b/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php index 2982aff9f9886..6262fc3cd7f52 100644 --- a/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php @@ -193,4 +193,23 @@ public function testShouldNotThrowALogicExceptionIfNoPrefix() $this->assertEquals("'/usr/bin/php'", $process->getCommandLine()); } } + + public function testShouldReturnProcessWithDisabledOutput() + { + $process = ProcessBuilder::create(array('/usr/bin/php')) + ->disableOutput() + ->getProcess(); + + $this->assertTrue($process->isOutputDisabled()); + } + + public function testShouldReturnProcessWithEnabledOutput() + { + $process = ProcessBuilder::create(array('/usr/bin/php')) + ->disableOutput() + ->enableOutput() + ->getProcess(); + + $this->assertFalse($process->isOutputDisabled()); + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy