InputStream.php 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Process;
  11. use Symfony\Component\Process\Exception\RuntimeException;
  12. /**
  13. * Provides a way to continuously write to the input of a Process until the InputStream is closed.
  14. *
  15. * @author Nicolas Grekas <p@tchwork.com>
  16. *
  17. * @implements \IteratorAggregate<int, string>
  18. */
  19. class InputStream implements \IteratorAggregate
  20. {
  21. private ?\Closure $onEmpty = null;
  22. private array $input = [];
  23. private bool $open = true;
  24. /**
  25. * Sets a callback that is called when the write buffer becomes empty.
  26. */
  27. public function onEmpty(?callable $onEmpty = null): void
  28. {
  29. $this->onEmpty = null !== $onEmpty ? $onEmpty(...) : null;
  30. }
  31. /**
  32. * Appends an input to the write buffer.
  33. *
  34. * @param resource|string|int|float|bool|\Traversable|null $input The input to append as scalar,
  35. * stream resource or \Traversable
  36. */
  37. public function write(mixed $input): void
  38. {
  39. if (null === $input) {
  40. return;
  41. }
  42. if ($this->isClosed()) {
  43. throw new RuntimeException(sprintf('"%s" is closed.', static::class));
  44. }
  45. $this->input[] = ProcessUtils::validateInput(__METHOD__, $input);
  46. }
  47. /**
  48. * Closes the write buffer.
  49. */
  50. public function close(): void
  51. {
  52. $this->open = false;
  53. }
  54. /**
  55. * Tells whether the write buffer is closed or not.
  56. */
  57. public function isClosed(): bool
  58. {
  59. return !$this->open;
  60. }
  61. public function getIterator(): \Traversable
  62. {
  63. $this->open = true;
  64. while ($this->open || $this->input) {
  65. if (!$this->input) {
  66. yield '';
  67. continue;
  68. }
  69. $current = array_shift($this->input);
  70. if ($current instanceof \Iterator) {
  71. yield from $current;
  72. } else {
  73. yield $current;
  74. }
  75. if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) {
  76. $this->write($onEmpty($this));
  77. }
  78. }
  79. }
  80. }