|
27 | 27 | * @author Fabien Potencier <fabien@symfony.com>
|
28 | 28 | * @author Romain Neutron <imprec@gmail.com>
|
29 | 29 | */
|
30 |
| -class Process |
| 30 | +class Process implements \IteratorAggregate |
31 | 31 | {
|
32 | 32 | const ERR = 'err';
|
33 | 33 | const OUT = 'out';
|
@@ -498,6 +498,54 @@ public function getIncrementalOutput()
|
498 | 498 | return $latest;
|
499 | 499 | }
|
500 | 500 |
|
| 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 | + |
501 | 549 | /**
|
502 | 550 | * Clears the process output.
|
503 | 551 | *
|
@@ -1296,19 +1344,20 @@ protected function isSigchildEnabled()
|
1296 | 1344 | /**
|
1297 | 1345 | * Reads pipes for the freshest output.
|
1298 | 1346 | *
|
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. |
1300 | 1349 | *
|
1301 | 1350 | * @throws LogicException in case output has been disabled or process is not started
|
1302 | 1351 | */
|
1303 |
| - private function readPipesForOutput($caller) |
| 1352 | + private function readPipesForOutput($caller, $blocking = false) |
1304 | 1353 | {
|
1305 | 1354 | if ($this->outputDisabled) {
|
1306 | 1355 | throw new LogicException('Output has been disabled.');
|
1307 | 1356 | }
|
1308 | 1357 |
|
1309 | 1358 | $this->requireProcessIsStarted($caller);
|
1310 | 1359 |
|
1311 |
| - $this->updateStatus(false); |
| 1360 | + $this->updateStatus($blocking); |
1312 | 1361 | }
|
1313 | 1362 |
|
1314 | 1363 | /**
|
|
0 commit comments