1: <?php
2:
3: namespace Guzzle\Http;
4:
5: /**
6: * EntityBody decorator used to return only a subset of an entity body
7: */
8: class ReadLimitEntityBody extends AbstractEntityBodyDecorator
9: {
10: /**
11: * @var int Limit the number of bytes that can be read
12: */
13: protected $limit;
14:
15: /**
16: * @var int Offset to start reading from
17: */
18: protected $offset;
19:
20: /**
21: * @param EntityBodyInterface $body Body to wrap
22: * @param int $limit Total number of bytes to allow to be read from the stream
23: * @param int $offset Position to seek to before reading (only works on seekable streams)
24: */
25: public function __construct(EntityBodyInterface $body, $limit, $offset = 0)
26: {
27: parent::__construct($body);
28: $this->setLimit($limit)->setOffset($offset);
29: $this->body->seek($offset);
30: }
31:
32: /**
33: * Returns only a subset of the decorated entity body when cast as a string
34: * {@inheritdoc}
35: */
36: public function __toString()
37: {
38: return substr((string) $this->body, $this->offset, $this->limit) ?: '';
39: }
40:
41: /**
42: * {@inheritdoc}
43: */
44: public function isConsumed()
45: {
46: return (($this->offset + $this->limit) - $this->body->ftell()) <= 0;
47: }
48:
49: /**
50: * Returns the Content-Length of the limited subset of data
51: * {@inheritdoc}
52: */
53: public function getContentLength()
54: {
55: $length = $this->body->getContentLength();
56:
57: return $length === false
58: ? $this->limit
59: : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset);
60: }
61:
62: /**
63: * Allow for a bounded seek on the read limited entity body
64: * {@inheritdoc}
65: */
66: public function seek($offset, $whence = SEEK_SET)
67: {
68: return $whence === SEEK_SET
69: ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset)))
70: : false;
71: }
72:
73: /**
74: * Set the offset to start limiting from
75: *
76: * @param int $offset Offset to seek to and begin byte limiting from
77: *
78: * @return self
79: */
80: public function setOffset($offset)
81: {
82: $this->body->seek($offset);
83: $this->offset = $offset;
84:
85: return $this;
86: }
87:
88: /**
89: * Set the limit of bytes that the decorator allows to be read from the stream
90: *
91: * @param int $limit Total number of bytes to allow to be read from the stream
92: *
93: * @return self
94: */
95: public function setLimit($limit)
96: {
97: $this->limit = $limit;
98:
99: return $this;
100: }
101:
102: /**
103: * {@inheritdoc}
104: */
105: public function read($length)
106: {
107: // Check if the current position is less than the total allowed bytes + original offset
108: $remaining = ($this->offset + $this->limit) - $this->body->ftell();
109: if ($remaining > 0) {
110: // Only return the amount of requested data, ensuring that the byte limit is not exceeded
111: return $this->body->read(min($remaining, $length));
112: } else {
113: return false;
114: }
115: }
116: }
117: