feature #10425 [Process] Add Process::disableOutput and Process::enableOutput methods (romainneutron)
This PR was merged into the 2.5-dev branch.
Discussion
----------
[Process] Add Process::disableOutput and Process::enableOutput methods
| Q | A
| ------------- | ---
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #9007
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/pull/3664
This is another implementation for #9007 that allows to disable the output/error-output storage in a Process in order to avoid using memory.
This is particularly useful when the process outputs large data and it's not read.
Commits
-------
a891e14
[Process] Add Process::disableOutput and Process::enableOutput methods
This commit is contained in:
commit
5e0bb71b90
@ -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 @@ class Process
|
||||
* @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 @@ class Process
|
||||
*
|
||||
* @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 @@ class Process
|
||||
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 @@ class Process
|
||||
* 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 @@ class Process
|
||||
*
|
||||
* @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 @@ class Process
|
||||
* 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 @@ class Process
|
||||
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
|
||||
|
@ -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 @@ class ProcessBuilder
|
||||
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 @@ class ProcessBuilder
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
@ -101,10 +101,22 @@ class ProcessPipes
|
||||
/**
|
||||
* 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'),
|
||||
|
@ -700,6 +700,84 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
$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(
|
||||
|
@ -193,4 +193,23 @@ class ProcessBuilderTest extends \PHPUnit_Framework_TestCase
|
||||
$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());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user