Skip to content

[Process] Add Process::disableOutput and Process::enableOutput methods #10425

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 12, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion src/Symfony/Component/Process/Process.php
Original file line number Diff line number Diff line change
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()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be marked as part of the api with @api along enableOutput ?

{
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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());
}
}