| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 | <?phpdeclare(strict_types=1);namespace GuzzleHttp\Psr7;use Psr\Http\Message\StreamInterface;/** * Decorator used to return only a subset of a stream. */final class LimitStream implements StreamInterface{    use StreamDecoratorTrait;    /** @var int Offset to start reading from */    private $offset;    /** @var int Limit the number of bytes that can be read */    private $limit;    /** @var StreamInterface */    private $stream;    /**     * @param StreamInterface $stream Stream to wrap     * @param int             $limit  Total number of bytes to allow to be read     *                                from the stream. Pass -1 for no limit.     * @param int             $offset Position to seek to before reading (only     *                                works on seekable streams).     */    public function __construct(        StreamInterface $stream,        int $limit = -1,        int $offset = 0    ) {        $this->stream = $stream;        $this->setLimit($limit);        $this->setOffset($offset);    }    public function eof(): bool    {        // Always return true if the underlying stream is EOF        if ($this->stream->eof()) {            return true;        }        // No limit and the underlying stream is not at EOF        if ($this->limit === -1) {            return false;        }        return $this->stream->tell() >= $this->offset + $this->limit;    }    /**     * Returns the size of the limited subset of data     */    public function getSize(): ?int    {        if (null === ($length = $this->stream->getSize())) {            return null;        } elseif ($this->limit === -1) {            return $length - $this->offset;        } else {            return min($this->limit, $length - $this->offset);        }    }    /**     * Allow for a bounded seek on the read limited stream     */    public function seek($offset, $whence = SEEK_SET): void    {        if ($whence !== SEEK_SET || $offset < 0) {            throw new \RuntimeException(sprintf(                'Cannot seek to offset %s with whence %s',                $offset,                $whence            ));        }        $offset += $this->offset;        if ($this->limit !== -1) {            if ($offset > $this->offset + $this->limit) {                $offset = $this->offset + $this->limit;            }        }        $this->stream->seek($offset);    }    /**     * Give a relative tell()     */    public function tell(): int    {        return $this->stream->tell() - $this->offset;    }    /**     * Set the offset to start limiting from     *     * @param int $offset Offset to seek to and begin byte limiting from     *     * @throws \RuntimeException if the stream cannot be seeked.     */    public function setOffset(int $offset): void    {        $current = $this->stream->tell();        if ($current !== $offset) {            // If the stream cannot seek to the offset position, then read to it            if ($this->stream->isSeekable()) {                $this->stream->seek($offset);            } elseif ($current > $offset) {                throw new \RuntimeException("Could not seek to stream offset $offset");            } else {                $this->stream->read($offset - $current);            }        }        $this->offset = $offset;    }    /**     * Set the limit of bytes that the decorator allows to be read from the     * stream.     *     * @param int $limit Number of bytes to allow to be read from the stream.     *                   Use -1 for no limit.     */    public function setLimit(int $limit): void    {        $this->limit = $limit;    }    public function read($length): string    {        if ($this->limit === -1) {            return $this->stream->read($length);        }        // Check if the current position is less than the total allowed        // bytes + original offset        $remaining = ($this->offset + $this->limit) - $this->stream->tell();        if ($remaining > 0) {            // Only return the amount of requested data, ensuring that the byte            // limit is not exceeded            return $this->stream->read(min($remaining, $length));        }        return '';    }}
 |