Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cdd3627

Browse files
[Process] Implement IteratorAggregate to stream output
1 parent dc189f0 commit cdd3627

File tree

2 files changed

+89
-10
lines changed

2 files changed

+89
-10
lines changed

src/Symfony/Component/Process/Process.php

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* @author Fabien Potencier <[email protected]>
2828
* @author Romain Neutron <[email protected]>
2929
*/
30-
class Process
30+
class Process implements \IteratorAggregate
3131
{
3232
const ERR = 'err';
3333
const OUT = 'out';
@@ -362,7 +362,7 @@ public function wait(callable $callback = null)
362362
do {
363363
$this->checkTimeout();
364364
$running = '\\' === DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();
365-
$this->readPipes($running, '\\' !== DIRECTORY_SEPARATOR || !$running);
365+
$this->readPipes($running, '\\' !== DIRECTORY_SEPARATOR || !$running, $this->callback);
366366
} while ($running);
367367

368368
while ($this->isRunning()) {
@@ -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
*
@@ -1261,7 +1309,7 @@ protected function updateStatus($blocking)
12611309
$this->processInformation = proc_get_status($this->process);
12621310
$running = $this->processInformation['running'];
12631311

1264-
$this->readPipes($running && $blocking, '\\' !== DIRECTORY_SEPARATOR || !$running);
1312+
$this->readPipes($running && $blocking, '\\' !== DIRECTORY_SEPARATOR || !$running, $this->callback);
12651313

12661314
if ($this->fallbackStatus && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
12671315
$this->processInformation = $this->fallbackStatus + $this->processInformation;
@@ -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
/**
@@ -1336,14 +1385,14 @@ private function validateTimeout($timeout)
13361385
/**
13371386
* Reads pipes, executes callback.
13381387
*
1339-
* @param bool $blocking Whether to use blocking calls or not.
1340-
* @param bool $close Whether to close file handles or not.
1388+
* @param bool $blocking Whether to use blocking calls or not.
1389+
* @param bool $close Whether to close file handles or not.
1390+
* @param \Closure $callback The internal callback used to handle the data.
13411391
*/
1342-
private function readPipes($blocking, $close)
1392+
private function readPipes($blocking, $close, $callback)
13431393
{
13441394
$result = $this->processPipes->readAndWrite($blocking, $close);
13451395

1346-
$callback = $this->callback;
13471396
foreach ($result as $type => $data) {
13481397
if (3 !== $type) {
13491398
$callback($type === self::STDOUT ? self::OUT : self::ERR, $data);

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,36 @@ public function testInputStreamOnEmpty()
12701270
$this->assertSame('123456', $process->getOutput());
12711271
}
12721272

1273+
public function testIteratorOutput()
1274+
{
1275+
$process = new Process(self::$phpBin.' -r '.escapeshellarg('fwrite(STDOUT, 123); fwrite(STDERR, 234); flush(); usleep(100000); fwrite(STDOUT, 345); fwrite(STDERR, 456);'));
1276+
$process->start();
1277+
$output = array();
1278+
1279+
foreach ($process as $type => $data) {
1280+
$output[] = array($type, $data);
1281+
break;
1282+
}
1283+
$expectedOutput = array(
1284+
array($process::OUT, '123'),
1285+
);
1286+
$this->assertSame($expectedOutput, $output);
1287+
1288+
foreach ($process as $type => $data) {
1289+
$output[] = array($type, $data);
1290+
}
1291+
1292+
$this->assertFalse($process->isRunning());
1293+
1294+
$expectedOutput = array(
1295+
array($process::OUT, '123'),
1296+
array($process::ERR, '234'),
1297+
array($process::OUT, '345'),
1298+
array($process::ERR, '456'),
1299+
);
1300+
$this->assertSame($expectedOutput, $output);
1301+
}
1302+
12731303
/**
12741304
* @param string $commandline
12751305
* @param null|string $cwd

0 commit comments

Comments
 (0)