PumpStream.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <?php
  2. namespace GuzzleHttp\Psr7;
  3. use Psr\Http\Message\StreamInterface;
  4. /**
  5. * Provides a read only stream that pumps data from a PHP callable.
  6. *
  7. * When invoking the provided callable, the PumpStream will pass the amount of
  8. * data requested to read to the callable. The callable can choose to ignore
  9. * this value and return fewer or more bytes than requested. Any extra data
  10. * returned by the provided callable is buffered internally until drained using
  11. * the read() function of the PumpStream. The provided callable MUST return
  12. * false when there is no more data to read.
  13. */
  14. class PumpStream implements StreamInterface
  15. {
  16. /** @var callable */
  17. private $source;
  18. /** @var int */
  19. private $size;
  20. /** @var int */
  21. private $tellPos = 0;
  22. /** @var array */
  23. private $metadata;
  24. /** @var BufferStream */
  25. private $buffer;
  26. /**
  27. * @param callable $source Source of the stream data. The callable MAY
  28. * accept an integer argument used to control the
  29. * amount of data to return. The callable MUST
  30. * return a string when called, or false on error
  31. * or EOF.
  32. * @param array $options Stream options:
  33. * - metadata: Hash of metadata to use with stream.
  34. * - size: Size of the stream, if known.
  35. */
  36. public function __construct(callable $source, array $options = [])
  37. {
  38. $this->source = $source;
  39. $this->size = isset($options['size']) ? $options['size'] : null;
  40. $this->metadata = isset($options['metadata']) ? $options['metadata'] : [];
  41. $this->buffer = new BufferStream();
  42. }
  43. public function __toString()
  44. {
  45. try {
  46. return Utils::copyToString($this);
  47. } catch (\Exception $e) {
  48. return '';
  49. }
  50. }
  51. public function close()
  52. {
  53. $this->detach();
  54. }
  55. public function detach()
  56. {
  57. $this->tellPos = false;
  58. $this->source = null;
  59. return null;
  60. }
  61. public function getSize()
  62. {
  63. return $this->size;
  64. }
  65. public function tell()
  66. {
  67. return $this->tellPos;
  68. }
  69. public function eof()
  70. {
  71. return !$this->source;
  72. }
  73. public function isSeekable()
  74. {
  75. return false;
  76. }
  77. public function rewind()
  78. {
  79. $this->seek(0);
  80. }
  81. public function seek($offset, $whence = SEEK_SET)
  82. {
  83. throw new \RuntimeException('Cannot seek a PumpStream');
  84. }
  85. public function isWritable()
  86. {
  87. return false;
  88. }
  89. public function write($string)
  90. {
  91. throw new \RuntimeException('Cannot write to a PumpStream');
  92. }
  93. public function isReadable()
  94. {
  95. return true;
  96. }
  97. public function read($length)
  98. {
  99. $data = $this->buffer->read($length);
  100. $readLen = strlen($data);
  101. $this->tellPos += $readLen;
  102. $remaining = $length - $readLen;
  103. if ($remaining) {
  104. $this->pump($remaining);
  105. $data .= $this->buffer->read($remaining);
  106. $this->tellPos += strlen($data) - $readLen;
  107. }
  108. return $data;
  109. }
  110. public function getContents()
  111. {
  112. $result = '';
  113. while (!$this->eof()) {
  114. $result .= $this->read(1000000);
  115. }
  116. return $result;
  117. }
  118. public function getMetadata($key = null)
  119. {
  120. if (!$key) {
  121. return $this->metadata;
  122. }
  123. return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
  124. }
  125. private function pump($length)
  126. {
  127. if ($this->source) {
  128. do {
  129. $data = call_user_func($this->source, $length);
  130. if ($data === false || $data === null) {
  131. $this->source = null;
  132. return;
  133. }
  134. $this->buffer->write($data);
  135. $length -= strlen($data);
  136. } while ($length > 0);
  137. }
  138. }
  139. }