File indexing completed on 2024-12-29 05:27:23

0001 <?php
0002 namespace GuzzleHttp\Psr7;
0003 
0004 use Psr\Http\Message\StreamInterface;
0005 
0006 
0007 /**
0008  * Decorator used to return only a subset of a stream
0009  */
0010 class LimitStream implements StreamInterface
0011 {
0012     use StreamDecoratorTrait;
0013 
0014     /** @var int Offset to start reading from */
0015     private $offset;
0016 
0017     /** @var int Limit the number of bytes that can be read */
0018     private $limit;
0019 
0020     /**
0021      * @param StreamInterface $stream Stream to wrap
0022      * @param int             $limit  Total number of bytes to allow to be read
0023      *                                from the stream. Pass -1 for no limit.
0024      * @param int             $offset Position to seek to before reading (only
0025      *                                works on seekable streams).
0026      */
0027     public function __construct(
0028         StreamInterface $stream,
0029         $limit = -1,
0030         $offset = 0
0031     ) {
0032         $this->stream = $stream;
0033         $this->setLimit($limit);
0034         $this->setOffset($offset);
0035     }
0036 
0037     public function eof()
0038     {
0039         // Always return true if the underlying stream is EOF
0040         if ($this->stream->eof()) {
0041             return true;
0042         }
0043 
0044         // No limit and the underlying stream is not at EOF
0045         if ($this->limit == -1) {
0046             return false;
0047         }
0048 
0049         return $this->stream->tell() >= $this->offset + $this->limit;
0050     }
0051 
0052     /**
0053      * Returns the size of the limited subset of data
0054      * {@inheritdoc}
0055      */
0056     public function getSize()
0057     {
0058         if (null === ($length = $this->stream->getSize())) {
0059             return null;
0060         } elseif ($this->limit == -1) {
0061             return $length - $this->offset;
0062         } else {
0063             return min($this->limit, $length - $this->offset);
0064         }
0065     }
0066 
0067     /**
0068      * Allow for a bounded seek on the read limited stream
0069      * {@inheritdoc}
0070      */
0071     public function seek($offset, $whence = SEEK_SET)
0072     {
0073         if ($whence !== SEEK_SET || $offset < 0) {
0074             throw new \RuntimeException(sprintf(
0075                 'Cannot seek to offset % with whence %s',
0076                 $offset,
0077                 $whence
0078             ));
0079         }
0080 
0081         $offset += $this->offset;
0082 
0083         if ($this->limit !== -1) {
0084             if ($offset > $this->offset + $this->limit) {
0085                 $offset = $this->offset + $this->limit;
0086             }
0087         }
0088 
0089         $this->stream->seek($offset);
0090     }
0091 
0092     /**
0093      * Give a relative tell()
0094      * {@inheritdoc}
0095      */
0096     public function tell()
0097     {
0098         return $this->stream->tell() - $this->offset;
0099     }
0100 
0101     /**
0102      * Set the offset to start limiting from
0103      *
0104      * @param int $offset Offset to seek to and begin byte limiting from
0105      *
0106      * @throws \RuntimeException if the stream cannot be seeked.
0107      */
0108     public function setOffset($offset)
0109     {
0110         $current = $this->stream->tell();
0111 
0112         if ($current !== $offset) {
0113             // If the stream cannot seek to the offset position, then read to it
0114             if ($this->stream->isSeekable()) {
0115                 $this->stream->seek($offset);
0116             } elseif ($current > $offset) {
0117                 throw new \RuntimeException("Could not seek to stream offset $offset");
0118             } else {
0119                 $this->stream->read($offset - $current);
0120             }
0121         }
0122 
0123         $this->offset = $offset;
0124     }
0125 
0126     /**
0127      * Set the limit of bytes that the decorator allows to be read from the
0128      * stream.
0129      *
0130      * @param int $limit Number of bytes to allow to be read from the stream.
0131      *                   Use -1 for no limit.
0132      */
0133     public function setLimit($limit)
0134     {
0135         $this->limit = $limit;
0136     }
0137 
0138     public function read($length)
0139     {
0140         if ($this->limit == -1) {
0141             return $this->stream->read($length);
0142         }
0143 
0144         // Check if the current position is less than the total allowed
0145         // bytes + original offset
0146         $remaining = ($this->offset + $this->limit) - $this->stream->tell();
0147         if ($remaining > 0) {
0148             // Only return the amount of requested data, ensuring that the byte
0149             // limit is not exceeded
0150             return $this->stream->read(min($remaining, $length));
0151         }
0152 
0153         return '';
0154     }
0155 }