Blame | Last modification | View Log | RSS feed
<?phpnamespace Guzzle\Http;use Guzzle\Common\Exception\RuntimeException;/*** EntityBody decorator that can cache previously read bytes from a sequentially read tstream*/class CachingEntityBody extends AbstractEntityBodyDecorator{/** @var EntityBody Remote stream used to actually pull data onto the buffer */protected $remoteStream;/** @var int The number of bytes to skip reading due to a write on the temporary buffer */protected $skipReadBytes = 0;/*** We will treat the buffer object as the body of the entity body* {@inheritdoc}*/public function __construct(EntityBodyInterface $body){$this->remoteStream = $body;$this->body = new EntityBody(fopen('php://temp', 'r+'));}/*** Will give the contents of the buffer followed by the exhausted remote stream.** Warning: Loads the entire stream into memory** @return string*/public function __toString(){$pos = $this->ftell();$this->rewind();$str = '';while (!$this->isConsumed()) {$str .= $this->read(16384);}$this->seek($pos);return $str;}public function getSize(){return max($this->body->getSize(), $this->remoteStream->getSize());}/*** {@inheritdoc}* @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream*/public function seek($offset, $whence = SEEK_SET){if ($whence == SEEK_SET) {$byte = $offset;} elseif ($whence == SEEK_CUR) {$byte = $offset + $this->ftell();} else {throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations');}// You cannot skip ahead past where you've read from the remote streamif ($byte > $this->body->getSize()) {throw new RuntimeException("Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes");}return $this->body->seek($byte);}public function rewind(){return $this->seek(0);}/*** Does not support custom rewind functions** @throws RuntimeException*/public function setRewindFunction($callable){throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions');}public function read($length){// Perform a regular read on any previously read data from the buffer$data = $this->body->read($length);$remaining = $length - strlen($data);// More data was requested so read from the remote streamif ($remaining) {// If data was written to the buffer in a position that would have been filled from the remote stream,// then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This// mimics the behavior of other PHP stream wrappers.$remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes);if ($this->skipReadBytes) {$len = strlen($remoteData);$remoteData = substr($remoteData, $this->skipReadBytes);$this->skipReadBytes = max(0, $this->skipReadBytes - $len);}$data .= $remoteData;$this->body->write($remoteData);}return $data;}public function write($string){// When appending to the end of the currently read stream, you'll want to skip bytes from being read from// the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length.$overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell();if ($overflow > 0) {$this->skipReadBytes += $overflow;}return $this->body->write($string);}/*** {@inheritdoc}* @link http://php.net/manual/en/function.fgets.php*/public function readLine($maxLength = null){$buffer = '';$size = 0;while (!$this->isConsumed()) {$byte = $this->read(1);$buffer .= $byte;// Break when a new line is found or the max length - 1 is reachedif ($byte == PHP_EOL || ++$size == $maxLength - 1) {break;}}return $buffer;}public function isConsumed(){return $this->body->isConsumed() && $this->remoteStream->isConsumed();}/*** Close both the remote stream and buffer stream*/public function close(){return $this->remoteStream->close() && $this->body->close();}public function setStream($stream, $size = 0){$this->remoteStream->setStream($stream, $size);}public function getContentType(){return $this->remoteStream->getContentType();}public function getContentEncoding(){return $this->remoteStream->getContentEncoding();}public function getMetaData($key = null){return $this->remoteStream->getMetaData($key);}public function getStream(){return $this->remoteStream->getStream();}public function getWrapper(){return $this->remoteStream->getWrapper();}public function getWrapperData(){return $this->remoteStream->getWrapperData();}public function getStreamType(){return $this->remoteStream->getStreamType();}public function getUri(){return $this->remoteStream->getUri();}/*** Always retrieve custom data from the remote stream* {@inheritdoc}*/public function getCustomData($key){return $this->remoteStream->getCustomData($key);}/*** Always set custom data on the remote stream* {@inheritdoc}*/public function setCustomData($key, $value){$this->remoteStream->setCustomData($key, $value);return $this;}}