Skip to content

Commit

Permalink
[Process] Add Process::disableOutput and Process::enableOutput methods
Browse files Browse the repository at this point in the history
  • Loading branch information
romainneutron committed Mar 12, 2014
1 parent c2d4be1 commit a891e14
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 3 deletions.
70 changes: 69 additions & 1 deletion src/Symfony/Component/Process/Process.php
Expand Up @@ -54,6 +54,7 @@ class Process
private $exitcode;
private $fallbackExitcode;
private $processInformation;
private $outputDisabled = false;
private $stdout;
private $stderr;
private $enhanceWindowsCompatibility;
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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()
Expand Down Expand Up @@ -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;
Expand All @@ -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()
Expand Down Expand Up @@ -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
Expand Down
33 changes: 32 additions & 1 deletion src/Symfony/Component/Process/ProcessBuilder.php
Expand Up @@ -29,6 +29,7 @@ class ProcessBuilder
private $options = array();
private $inheritEnv = true;
private $prefix = array();
private $outputDisabled = false;

public function __construct(array $arguments = array())
{
Expand Down Expand Up @@ -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)) {
Expand All @@ -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;
}
}
14 changes: 13 additions & 1 deletion src/Symfony/Component/Process/ProcessPipes.php
Expand Up @@ -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'),
Expand Down
78 changes: 78 additions & 0 deletions src/Symfony/Component/Process/Tests/AbstractProcessTest.php
Expand Up @@ -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(
Expand Down
19 changes: 19 additions & 0 deletions src/Symfony/Component/Process/Tests/ProcessBuilderTest.php
Expand Up @@ -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());
}
}

0 comments on commit a891e14

Please sign in to comment.