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