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 }