Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 22e0eae

Browse filesBrowse files
committed
feature #18414 [Process] Implement IteratorAggregate to stream output (nicolas-grekas)
This PR was merged into the 3.1-dev branch. Discussion ---------- [Process] Implement IteratorAggregate to stream output | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | symfony/symfony-docs#6424 Sibling to #18386 for iterating of the output streams. Commits ------- 5947f5d [Process] Implement IteratorAggregate to stream output
2 parents 804b924 + 5947f5d commit 22e0eae
Copy full SHA for 22e0eae

File tree

2 files changed

+89
-4
lines changed
Filter options

2 files changed

+89
-4
lines changed

‎src/Symfony/Component/Process/Process.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Process/Process.php
+53-4Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* @author Fabien Potencier <fabien@symfony.com>
2828
* @author Romain Neutron <imprec@gmail.com>
2929
*/
30-
class Process
30+
class Process implements \IteratorAggregate
3131
{
3232
const ERR = 'err';
3333
const OUT = 'out';
@@ -498,6 +498,54 @@ public function getIncrementalOutput()
498498
return $latest;
499499
}
500500

501+
/**
502+
* Returns an iterator to the output of the process, with the output type as keys (Process::OUT/ERR).
503+
*
504+
* @param bool $blocking Whether to use a blocking read call.
505+
* @param bool $clearOutput Whether to clear or keep output in memory.
506+
*
507+
* @throws LogicException in case the output has been disabled
508+
* @throws LogicException In case the process is not started
509+
*
510+
* @return \Generator
511+
*/
512+
public function getIterator($blocking = true, $clearOutput = true)
513+
{
514+
$this->readPipesForOutput(__FUNCTION__, false);
515+
516+
while (null !== $this->callback) {
517+
$out = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
518+
519+
if (isset($out[0])) {
520+
if ($clearOutput) {
521+
$this->clearOutput();
522+
} else {
523+
$this->incrementalOutputOffset = ftell($this->stdout);
524+
}
525+
526+
yield self::OUT => $out;
527+
}
528+
529+
$err = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
530+
531+
if (isset($err[0])) {
532+
if ($clearOutput) {
533+
$this->clearErrorOutput();
534+
} else {
535+
$this->incrementalErrorOutputOffset = ftell($this->stderr);
536+
}
537+
538+
yield self::ERR => $err;
539+
}
540+
541+
if (!$blocking && !isset($out[0]) && !isset($err[0])) {
542+
yield self::OUT => '';
543+
}
544+
545+
$this->readPipesForOutput(__FUNCTION__, $blocking);
546+
}
547+
}
548+
501549
/**
502550
* Clears the process output.
503551
*
@@ -1296,19 +1344,20 @@ protected function isSigchildEnabled()
12961344
/**
12971345
* Reads pipes for the freshest output.
12981346
*
1299-
* @param $caller The name of the method that needs fresh outputs
1347+
* @param string $caller The name of the method that needs fresh outputs
1348+
* @param bool $blocking Whether to use blocking calls or not.
13001349
*
13011350
* @throws LogicException in case output has been disabled or process is not started
13021351
*/
1303-
private function readPipesForOutput($caller)
1352+
private function readPipesForOutput($caller, $blocking = false)
13041353
{
13051354
if ($this->outputDisabled) {
13061355
throw new LogicException('Output has been disabled.');
13071356
}
13081357

13091358
$this->requireProcessIsStarted($caller);
13101359

1311-
$this->updateStatus(false);
1360+
$this->updateStatus($blocking);
13121361
}
13131362

13141363
/**

‎src/Symfony/Component/Process/Tests/ProcessTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Process/Tests/ProcessTest.php
+36Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,42 @@ public function testInputStreamOnEmpty()
12701270
$this->assertSame('123456', $process->getOutput());
12711271
}
12721272

1273+
public function testIteratorOutput()
1274+
{
1275+
$input = new InputStream();
1276+
1277+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('fwrite(STDOUT, 123); fwrite(STDERR, 234); fwrite(STDOUT, fread(STDIN, 3)); fwrite(STDERR, 456);'));
1278+
$process->setInput($input);
1279+
$process->start();
1280+
$output = array();
1281+
1282+
foreach ($process as $type => $data) {
1283+
$output[] = array($type, $data);
1284+
break;
1285+
}
1286+
$expectedOutput = array(
1287+
array($process::OUT, '123'),
1288+
);
1289+
$this->assertSame($expectedOutput, $output);
1290+
1291+
$input->write(345);
1292+
1293+
foreach ($process as $type => $data) {
1294+
$output[] = array($type, $data);
1295+
}
1296+
1297+
$this->assertSame('', $process->getOutput());
1298+
$this->assertFalse($process->isRunning());
1299+
1300+
$expectedOutput = array(
1301+
array($process::OUT, '123'),
1302+
array($process::ERR, '234'),
1303+
array($process::OUT, '345'),
1304+
array($process::ERR, '456'),
1305+
);
1306+
$this->assertSame($expectedOutput, $output);
1307+
}
1308+
12731309
/**
12741310
* @param string $commandline
12751311
* @param null|string $cwd

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.