annotate vendor/guzzlehttp/psr7/src/AppendStream.php @ 0:4c8ae668cc8c

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