Chris@0: stream = $stream; Chris@0: $this->setLimit($limit); Chris@0: $this->setOffset($offset); Chris@0: } Chris@0: Chris@0: public function eof() Chris@0: { Chris@0: // Always return true if the underlying stream is EOF Chris@0: if ($this->stream->eof()) { Chris@0: return true; Chris@0: } Chris@0: Chris@0: // No limit and the underlying stream is not at EOF Chris@0: if ($this->limit == -1) { Chris@0: return false; Chris@0: } Chris@0: Chris@0: return $this->stream->tell() >= $this->offset + $this->limit; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the size of the limited subset of data Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getSize() Chris@0: { Chris@0: if (null === ($length = $this->stream->getSize())) { Chris@0: return null; Chris@0: } elseif ($this->limit == -1) { Chris@0: return $length - $this->offset; Chris@0: } else { Chris@0: return min($this->limit, $length - $this->offset); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Allow for a bounded seek on the read limited stream Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function seek($offset, $whence = SEEK_SET) Chris@0: { Chris@0: if ($whence !== SEEK_SET || $offset < 0) { Chris@0: throw new \RuntimeException(sprintf( Chris@0: 'Cannot seek to offset % with whence %s', Chris@0: $offset, Chris@0: $whence Chris@0: )); Chris@0: } Chris@0: Chris@0: $offset += $this->offset; Chris@0: Chris@0: if ($this->limit !== -1) { Chris@0: if ($offset > $this->offset + $this->limit) { Chris@0: $offset = $this->offset + $this->limit; Chris@0: } Chris@0: } Chris@0: Chris@0: $this->stream->seek($offset); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Give a relative tell() Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function tell() Chris@0: { Chris@0: return $this->stream->tell() - $this->offset; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set the offset to start limiting from Chris@0: * Chris@0: * @param int $offset Offset to seek to and begin byte limiting from Chris@0: * Chris@0: * @throws \RuntimeException if the stream cannot be seeked. Chris@0: */ Chris@0: public function setOffset($offset) Chris@0: { Chris@0: $current = $this->stream->tell(); Chris@0: Chris@0: if ($current !== $offset) { Chris@0: // If the stream cannot seek to the offset position, then read to it Chris@0: if ($this->stream->isSeekable()) { Chris@0: $this->stream->seek($offset); Chris@0: } elseif ($current > $offset) { Chris@0: throw new \RuntimeException("Could not seek to stream offset $offset"); Chris@0: } else { Chris@0: $this->stream->read($offset - $current); Chris@0: } Chris@0: } Chris@0: Chris@0: $this->offset = $offset; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set the limit of bytes that the decorator allows to be read from the Chris@0: * stream. Chris@0: * Chris@0: * @param int $limit Number of bytes to allow to be read from the stream. Chris@0: * Use -1 for no limit. Chris@0: */ Chris@0: public function setLimit($limit) Chris@0: { Chris@0: $this->limit = $limit; Chris@0: } Chris@0: Chris@0: public function read($length) Chris@0: { Chris@0: if ($this->limit == -1) { Chris@0: return $this->stream->read($length); Chris@0: } Chris@0: Chris@0: // Check if the current position is less than the total allowed Chris@0: // bytes + original offset Chris@0: $remaining = ($this->offset + $this->limit) - $this->stream->tell(); Chris@0: if ($remaining > 0) { Chris@0: // Only return the amount of requested data, ensuring that the byte Chris@0: // limit is not exceeded Chris@0: return $this->stream->read(min($remaining, $length)); Chris@0: } Chris@0: Chris@0: return ''; Chris@0: } Chris@0: }