annotate vendor/guzzlehttp/psr7/src/AppendStream.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2 namespace GuzzleHttp\Psr7;
Chris@0 3
Chris@0 4 use Psr\Http\Message\StreamInterface;
Chris@0 5
Chris@0 6 /**
Chris@0 7 * Reads from multiple streams, one after the other.
Chris@0 8 *
Chris@0 9 * This is a read-only stream decorator.
Chris@0 10 */
Chris@0 11 class AppendStream implements StreamInterface
Chris@0 12 {
Chris@0 13 /** @var StreamInterface[] Streams being decorated */
Chris@0 14 private $streams = [];
Chris@0 15
Chris@0 16 private $seekable = true;
Chris@0 17 private $current = 0;
Chris@0 18 private $pos = 0;
Chris@0 19
Chris@0 20 /**
Chris@0 21 * @param StreamInterface[] $streams Streams to decorate. Each stream must
Chris@0 22 * be readable.
Chris@0 23 */
Chris@0 24 public function __construct(array $streams = [])
Chris@0 25 {
Chris@0 26 foreach ($streams as $stream) {
Chris@0 27 $this->addStream($stream);
Chris@0 28 }
Chris@0 29 }
Chris@0 30
Chris@0 31 public function __toString()
Chris@0 32 {
Chris@0 33 try {
Chris@0 34 $this->rewind();
Chris@0 35 return $this->getContents();
Chris@0 36 } catch (\Exception $e) {
Chris@0 37 return '';
Chris@0 38 }
Chris@0 39 }
Chris@0 40
Chris@0 41 /**
Chris@0 42 * Add a stream to the AppendStream
Chris@0 43 *
Chris@0 44 * @param StreamInterface $stream Stream to append. Must be readable.
Chris@0 45 *
Chris@0 46 * @throws \InvalidArgumentException if the stream is not readable
Chris@0 47 */
Chris@0 48 public function addStream(StreamInterface $stream)
Chris@0 49 {
Chris@0 50 if (!$stream->isReadable()) {
Chris@0 51 throw new \InvalidArgumentException('Each stream must be readable');
Chris@0 52 }
Chris@0 53
Chris@0 54 // The stream is only seekable if all streams are seekable
Chris@0 55 if (!$stream->isSeekable()) {
Chris@0 56 $this->seekable = false;
Chris@0 57 }
Chris@0 58
Chris@0 59 $this->streams[] = $stream;
Chris@0 60 }
Chris@0 61
Chris@0 62 public function getContents()
Chris@0 63 {
Chris@0 64 return copy_to_string($this);
Chris@0 65 }
Chris@0 66
Chris@0 67 /**
Chris@0 68 * Closes each attached stream.
Chris@0 69 *
Chris@0 70 * {@inheritdoc}
Chris@0 71 */
Chris@0 72 public function close()
Chris@0 73 {
Chris@0 74 $this->pos = $this->current = 0;
Chris@17 75 $this->seekable = true;
Chris@0 76
Chris@0 77 foreach ($this->streams as $stream) {
Chris@0 78 $stream->close();
Chris@0 79 }
Chris@0 80
Chris@0 81 $this->streams = [];
Chris@0 82 }
Chris@0 83
Chris@0 84 /**
Chris@17 85 * Detaches each attached stream.
Chris@17 86 *
Chris@17 87 * Returns null as it's not clear which underlying stream resource to return.
Chris@0 88 *
Chris@0 89 * {@inheritdoc}
Chris@0 90 */
Chris@0 91 public function detach()
Chris@0 92 {
Chris@17 93 $this->pos = $this->current = 0;
Chris@17 94 $this->seekable = true;
Chris@17 95
Chris@17 96 foreach ($this->streams as $stream) {
Chris@17 97 $stream->detach();
Chris@17 98 }
Chris@17 99
Chris@17 100 $this->streams = [];
Chris@0 101 }
Chris@0 102
Chris@0 103 public function tell()
Chris@0 104 {
Chris@0 105 return $this->pos;
Chris@0 106 }
Chris@0 107
Chris@0 108 /**
Chris@0 109 * Tries to calculate the size by adding the size of each stream.
Chris@0 110 *
Chris@0 111 * If any of the streams do not return a valid number, then the size of the
Chris@0 112 * append stream cannot be determined and null is returned.
Chris@0 113 *
Chris@0 114 * {@inheritdoc}
Chris@0 115 */
Chris@0 116 public function getSize()
Chris@0 117 {
Chris@0 118 $size = 0;
Chris@0 119
Chris@0 120 foreach ($this->streams as $stream) {
Chris@0 121 $s = $stream->getSize();
Chris@0 122 if ($s === null) {
Chris@0 123 return null;
Chris@0 124 }
Chris@0 125 $size += $s;
Chris@0 126 }
Chris@0 127
Chris@0 128 return $size;
Chris@0 129 }
Chris@0 130
Chris@0 131 public function eof()
Chris@0 132 {
Chris@0 133 return !$this->streams ||
Chris@0 134 ($this->current >= count($this->streams) - 1 &&
Chris@0 135 $this->streams[$this->current]->eof());
Chris@0 136 }
Chris@0 137
Chris@0 138 public function rewind()
Chris@0 139 {
Chris@0 140 $this->seek(0);
Chris@0 141 }
Chris@0 142
Chris@0 143 /**
Chris@0 144 * Attempts to seek to the given position. Only supports SEEK_SET.
Chris@0 145 *
Chris@0 146 * {@inheritdoc}
Chris@0 147 */
Chris@0 148 public function seek($offset, $whence = SEEK_SET)
Chris@0 149 {
Chris@0 150 if (!$this->seekable) {
Chris@0 151 throw new \RuntimeException('This AppendStream is not seekable');
Chris@0 152 } elseif ($whence !== SEEK_SET) {
Chris@0 153 throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
Chris@0 154 }
Chris@0 155
Chris@0 156 $this->pos = $this->current = 0;
Chris@0 157
Chris@0 158 // Rewind each stream
Chris@0 159 foreach ($this->streams as $i => $stream) {
Chris@0 160 try {
Chris@0 161 $stream->rewind();
Chris@0 162 } catch (\Exception $e) {
Chris@0 163 throw new \RuntimeException('Unable to seek stream '
Chris@0 164 . $i . ' of the AppendStream', 0, $e);
Chris@0 165 }
Chris@0 166 }
Chris@0 167
Chris@0 168 // Seek to the actual position by reading from each stream
Chris@0 169 while ($this->pos < $offset && !$this->eof()) {
Chris@0 170 $result = $this->read(min(8096, $offset - $this->pos));
Chris@0 171 if ($result === '') {
Chris@0 172 break;
Chris@0 173 }
Chris@0 174 }
Chris@0 175 }
Chris@0 176
Chris@0 177 /**
Chris@0 178 * Reads from all of the appended streams until the length is met or EOF.
Chris@0 179 *
Chris@0 180 * {@inheritdoc}
Chris@0 181 */
Chris@0 182 public function read($length)
Chris@0 183 {
Chris@0 184 $buffer = '';
Chris@0 185 $total = count($this->streams) - 1;
Chris@0 186 $remaining = $length;
Chris@0 187 $progressToNext = false;
Chris@0 188
Chris@0 189 while ($remaining > 0) {
Chris@0 190
Chris@0 191 // Progress to the next stream if needed.
Chris@0 192 if ($progressToNext || $this->streams[$this->current]->eof()) {
Chris@0 193 $progressToNext = false;
Chris@0 194 if ($this->current === $total) {
Chris@0 195 break;
Chris@0 196 }
Chris@0 197 $this->current++;
Chris@0 198 }
Chris@0 199
Chris@0 200 $result = $this->streams[$this->current]->read($remaining);
Chris@0 201
Chris@0 202 // Using a loose comparison here to match on '', false, and null
Chris@0 203 if ($result == null) {
Chris@0 204 $progressToNext = true;
Chris@0 205 continue;
Chris@0 206 }
Chris@0 207
Chris@0 208 $buffer .= $result;
Chris@0 209 $remaining = $length - strlen($buffer);
Chris@0 210 }
Chris@0 211
Chris@0 212 $this->pos += strlen($buffer);
Chris@0 213
Chris@0 214 return $buffer;
Chris@0 215 }
Chris@0 216
Chris@0 217 public function isReadable()
Chris@0 218 {
Chris@0 219 return true;
Chris@0 220 }
Chris@0 221
Chris@0 222 public function isWritable()
Chris@0 223 {
Chris@0 224 return false;
Chris@0 225 }
Chris@0 226
Chris@0 227 public function isSeekable()
Chris@0 228 {
Chris@0 229 return $this->seekable;
Chris@0 230 }
Chris@0 231
Chris@0 232 public function write($string)
Chris@0 233 {
Chris@0 234 throw new \RuntimeException('Cannot write to an AppendStream');
Chris@0 235 }
Chris@0 236
Chris@0 237 public function getMetadata($key = null)
Chris@0 238 {
Chris@0 239 return $key ? null : [];
Chris@0 240 }
Chris@0 241 }