setLimit($limit)->setOffset($offset); } /** * Returns only a subset of the decorated entity body when cast as a string * {@inheritdoc} */ public function __toString() { if (!$this->body->isReadable() || (!$this->body->isSeekable() && $this->body->isConsumed()) ) { return ''; } $originalPos = $this->body->ftell(); $this->body->seek($this->offset); $data = ''; while (!$this->feof()) { $data .= $this->read(1048576); } $this->body->seek($originalPos); return (string) $data ?: ''; } public function isConsumed() { return $this->body->isConsumed() || ($this->body->ftell() >= $this->offset + $this->limit); } /** * Returns the Content-Length of the limited subset of data * {@inheritdoc} */ public function getContentLength() { $length = $this->body->getContentLength(); return $length === false ? $this->limit : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset); } /** * Allow for a bounded seek on the read limited entity body * {@inheritdoc} */ public function seek($offset, $whence = SEEK_SET) { return $whence === SEEK_SET ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset))) : false; } /** * Set the offset to start limiting from * * @param int $offset Offset to seek to and begin byte limiting from * * @return self */ public function setOffset($offset) { $this->body->seek($offset); $this->offset = $offset; return $this; } /** * Set the limit of bytes that the decorator allows to be read from the stream * * @param int $limit Total number of bytes to allow to be read from the stream * * @return self */ public function setLimit($limit) { $this->limit = $limit; return $this; } public function read($length) { // Check if the current position is less than the total allowed bytes + original offset $remaining = ($this->offset + $this->limit) - $this->body->ftell(); if ($remaining > 0) { // Only return the amount of requested data, ensuring that the byte limit is not exceeded return $this->body->read(min($remaining, $length)); } else { return false; } } }