Blame | Last modification | View Log | RSS feed
<?phpnamespace GuzzleHttp\Psr7;use Psr\Http\Message\StreamInterface;/*** Reads from multiple streams, one after the other.** This is a read-only stream decorator.*/class AppendStream implements StreamInterface{/** @var StreamInterface[] Streams being decorated */private $streams = [];private $seekable = true;private $current = 0;private $pos = 0;private $detached = false;/*** @param StreamInterface[] $streams Streams to decorate. Each stream must* be readable.*/public function __construct(array $streams = []){foreach ($streams as $stream) {$this->addStream($stream);}}public function __toString(){try {$this->rewind();return $this->getContents();} catch (\Exception $e) {return '';}}/*** Add a stream to the AppendStream** @param StreamInterface $stream Stream to append. Must be readable.** @throws \InvalidArgumentException if the stream is not readable*/public function addStream(StreamInterface $stream){if (!$stream->isReadable()) {throw new \InvalidArgumentException('Each stream must be readable');}// The stream is only seekable if all streams are seekableif (!$stream->isSeekable()) {$this->seekable = false;}$this->streams[] = $stream;}public function getContents(){return copy_to_string($this);}/*** Closes each attached stream.** {@inheritdoc}*/public function close(){$this->pos = $this->current = 0;foreach ($this->streams as $stream) {$stream->close();}$this->streams = [];}/*** Detaches each attached stream** {@inheritdoc}*/public function detach(){$this->close();$this->detached = true;}public function tell(){return $this->pos;}/*** Tries to calculate the size by adding the size of each stream.** If any of the streams do not return a valid number, then the size of the* append stream cannot be determined and null is returned.** {@inheritdoc}*/public function getSize(){$size = 0;foreach ($this->streams as $stream) {$s = $stream->getSize();if ($s === null) {return null;}$size += $s;}return $size;}public function eof(){return !$this->streams ||($this->current >= count($this->streams) - 1 &&$this->streams[$this->current]->eof());}public function rewind(){$this->seek(0);}/*** Attempts to seek to the given position. Only supports SEEK_SET.** {@inheritdoc}*/public function seek($offset, $whence = SEEK_SET){if (!$this->seekable) {throw new \RuntimeException('This AppendStream is not seekable');} elseif ($whence !== SEEK_SET) {throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');}$this->pos = $this->current = 0;// Rewind each streamforeach ($this->streams as $i => $stream) {try {$stream->rewind();} catch (\Exception $e) {throw new \RuntimeException('Unable to seek stream '. $i . ' of the AppendStream', 0, $e);}}// Seek to the actual position by reading from each streamwhile ($this->pos < $offset && !$this->eof()) {$result = $this->read(min(8096, $offset - $this->pos));if ($result === '') {break;}}}/*** Reads from all of the appended streams until the length is met or EOF.** {@inheritdoc}*/public function read($length){$buffer = '';$total = count($this->streams) - 1;$remaining = $length;$progressToNext = false;while ($remaining > 0) {// Progress to the next stream if needed.if ($progressToNext || $this->streams[$this->current]->eof()) {$progressToNext = false;if ($this->current === $total) {break;}$this->current++;}$result = $this->streams[$this->current]->read($remaining);// Using a loose comparison here to match on '', false, and nullif ($result == null) {$progressToNext = true;continue;}$buffer .= $result;$remaining = $length - strlen($buffer);}$this->pos += strlen($buffer);return $buffer;}public function isReadable(){return true;}public function isWritable(){return false;}public function isSeekable(){return $this->seekable;}public function write($string){throw new \RuntimeException('Cannot write to an AppendStream');}public function getMetadata($key = null){return $key ? null : [];}}