8000 [Process] Implement IteratorAggregate to stream output by nicolas-grekas · Pull Request #18414 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Process] Implement IteratorAggregate to stream output #18414

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
Apr 3, 2016
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
57 changes: 53 additions & 4 deletions src/Symfony/Component/Process/Process.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* @author Fabien Potencier <fabien@symfony.com>
* @author Romain Neutron <imprec@gmail.com>
*/
class Process
class Process implements \IteratorAggregate
{
const ERR = 'err';
const OUT = 'out';
Expand Down Expand Up @@ -498,6 +498,54 @@ public function getIncrementalOutput()
return $latest;
}

/**
* Returns an iterator to the output of the process, with the output type as keys (Process::OUT/ERR).
*
* @param bool $blocking Whether to use a blocking read call.
* @param bool $clearOutput Whether to clear or keep output in memory.
*
* @throws LogicException in case the output has been disabled
* @throws LogicException In case the process is not started
*
* @return \Generator
*/
public function getIterator($blocking = true, $clearOutput = true)
Copy link
Member

Choose a reason for hiding this comment

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

This signature does not match the \IteratorAggregate, which looks weird to me.

< 8000 /form>

{
$this->readPipesForOutput(__FUNCTION__, false);

while (null !== $this->callback) {
$out = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);

if (isset($out[0])) {
if ($clearOutput) {
$this->clearOutput();
} else {
$this->incrementalOutputOffset = ftell($this->stdout);
}

yield self::OUT => $out;
}

$err = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);

if (isset($err[0])) {
if ($clearOutput) {
$this->clearErrorOutput();
} else {
$this->incrementalErrorOutputOffset = ftell($this->stderr);
}

yield self::ERR => $err;
}

if (!$blocking && !isset($out[0]) && !isset($err[0])) {
yield self::OUT => '';
}

$this->readPipesForOutput(__FUNCTION__, $blocking);
}
}

/**
* Clears the process output.
*
Expand Down Expand Up @@ -1296,19 +1344,20 @@ protected function isSigchildEnabled()
/**
* Reads pipes for the freshest output.
*
* @param $caller The name of the method that needs fresh outputs
* @param string $caller The name of the method that needs fresh outputs
* @param bool $blocking Whether to use blocking calls or not.
*
* @throws LogicException in case output has been disabled or process is not started
*/
private function readPipesForOutput($caller)
private function readPipesForOutput($caller, $blocking = false)
{
if ($this->outputDisabled) {
throw new LogicException('Output has been disabled.');
}

$this->requireProcessIsStarted($caller);

$this->updateStatus(false);
$this->updateStatus($blocking);
}

/**
Expand Down
36 changes: 36 additions & 0 deletions src/Symfony/Component/Process/Tests/ProcessTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,42 @@ public function testInputStreamOnEmpty()
$this->assertSame('123456', $process->getOutput());
}

public function testIteratorOutput()
{
$input = new InputStream();

$process = new Process(self::$phpBin.' -r '.escapeshellarg('fwrite(STDOUT, 123); fwrite(STDERR, 234); fwrite(STDOUT, fread(STDIN, 3)); fwrite(STDERR, 456);'));
$process->setInput($input);
$process->start();
$output = array();

foreach ($process as $type => $data) {
$output[] = array($type, $data);
break;
}
$expectedOutput = array(
array($process::OUT, '123'),
);
$this->assertSame($expectedOutput, $output);

$input->write(345);

foreach ($process as $type => $data) {
$output[] = array($type, $data);
}

$this->assertSame('', $process->getOutput());
$this->assertFalse($process->isRunning());

$expectedOutput = array(
array($process::OUT, '123'),
array($process::ERR, '234'),
array($process::OUT, '345'),
array($process::ERR, '456'),
);
$this->assertSame($expectedOutput, $output);
}

/**
* @param string $commandline
* @param null|string $cwd
Expand Down
0