Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
namespace Guzzle\Http;
4
 
5
use Guzzle\Common\Exception\RuntimeException;
6
 
7
/**
8
 * EntityBody decorator that can cache previously read bytes from a sequentially read tstream
9
 */
10
class CachingEntityBody extends AbstractEntityBodyDecorator
11
{
12
    /** @var EntityBody Remote stream used to actually pull data onto the buffer */
13
    protected $remoteStream;
14
 
15
    /** @var int The number of bytes to skip reading due to a write on the temporary buffer */
16
    protected $skipReadBytes = 0;
17
 
18
    /**
19
     * We will treat the buffer object as the body of the entity body
20
     * {@inheritdoc}
21
     */
22
    public function __construct(EntityBodyInterface $body)
23
    {
24
        $this->remoteStream = $body;
25
        $this->body = new EntityBody(fopen('php://temp', 'r+'));
26
    }
27
 
28
    /**
29
     * Will give the contents of the buffer followed by the exhausted remote stream.
30
     *
31
     * Warning: Loads the entire stream into memory
32
     *
33
     * @return string
34
     */
35
    public function __toString()
36
    {
37
        $pos = $this->ftell();
38
        $this->rewind();
39
 
40
        $str = '';
41
        while (!$this->isConsumed()) {
42
            $str .= $this->read(16384);
43
        }
44
 
45
        $this->seek($pos);
46
 
47
        return $str;
48
    }
49
 
50
    public function getSize()
51
    {
52
        return max($this->body->getSize(), $this->remoteStream->getSize());
53
    }
54
 
55
    /**
56
     * {@inheritdoc}
57
     * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream
58
     */
59
    public function seek($offset, $whence = SEEK_SET)
60
    {
61
        if ($whence == SEEK_SET) {
62
            $byte = $offset;
63
        } elseif ($whence == SEEK_CUR) {
64
            $byte = $offset + $this->ftell();
65
        } else {
66
            throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations');
67
        }
68
 
69
        // You cannot skip ahead past where you've read from the remote stream
70
        if ($byte > $this->body->getSize()) {
71
            throw new RuntimeException(
72
                "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes"
73
            );
74
        }
75
 
76
        return $this->body->seek($byte);
77
    }
78
 
79
    public function rewind()
80
    {
81
        return $this->seek(0);
82
    }
83
 
84
    /**
85
     * Does not support custom rewind functions
86
     *
87
     * @throws RuntimeException
88
     */
89
    public function setRewindFunction($callable)
90
    {
91
        throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions');
92
    }
93
 
94
    public function read($length)
95
    {
96
        // Perform a regular read on any previously read data from the buffer
97
        $data = $this->body->read($length);
98
        $remaining = $length - strlen($data);
99
 
100
        // More data was requested so read from the remote stream
101
        if ($remaining) {
102
            // If data was written to the buffer in a position that would have been filled from the remote stream,
103
            // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This
104
            // mimics the behavior of other PHP stream wrappers.
105
            $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes);
106
 
107
            if ($this->skipReadBytes) {
108
                $len = strlen($remoteData);
109
                $remoteData = substr($remoteData, $this->skipReadBytes);
110
                $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
111
            }
112
 
113
            $data .= $remoteData;
114
            $this->body->write($remoteData);
115
        }
116
 
117
        return $data;
118
    }
119
 
120
    public function write($string)
121
    {
122
        // When appending to the end of the currently read stream, you'll want to skip bytes from being read from
123
        // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length.
124
        $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell();
125
        if ($overflow > 0) {
126
            $this->skipReadBytes += $overflow;
127
        }
128
 
129
        return $this->body->write($string);
130
    }
131
 
132
    /**
133
     * {@inheritdoc}
134
     * @link http://php.net/manual/en/function.fgets.php
135
     */
136
    public function readLine($maxLength = null)
137
    {
138
        $buffer = '';
139
        $size = 0;
140
        while (!$this->isConsumed()) {
141
            $byte = $this->read(1);
142
            $buffer .= $byte;
143
            // Break when a new line is found or the max length - 1 is reached
144
            if ($byte == PHP_EOL || ++$size == $maxLength - 1) {
145
                break;
146
            }
147
        }
148
 
149
        return $buffer;
150
    }
151
 
152
    public function isConsumed()
153
    {
154
        return $this->body->isConsumed() && $this->remoteStream->isConsumed();
155
    }
156
 
157
    /**
158
     * Close both the remote stream and buffer stream
159
     */
160
    public function close()
161
    {
162
        return $this->remoteStream->close() && $this->body->close();
163
    }
164
 
165
    public function setStream($stream, $size = 0)
166
    {
167
        $this->remoteStream->setStream($stream, $size);
168
    }
169
 
170
    public function getContentType()
171
    {
172
        return $this->remoteStream->getContentType();
173
    }
174
 
175
    public function getContentEncoding()
176
    {
177
        return $this->remoteStream->getContentEncoding();
178
    }
179
 
180
    public function getMetaData($key = null)
181
    {
182
        return $this->remoteStream->getMetaData($key);
183
    }
184
 
185
    public function getStream()
186
    {
187
        return $this->remoteStream->getStream();
188
    }
189
 
190
    public function getWrapper()
191
    {
192
        return $this->remoteStream->getWrapper();
193
    }
194
 
195
    public function getWrapperData()
196
    {
197
        return $this->remoteStream->getWrapperData();
198
    }
199
 
200
    public function getStreamType()
201
    {
202
        return $this->remoteStream->getStreamType();
203
    }
204
 
205
    public function getUri()
206
    {
207
        return $this->remoteStream->getUri();
208
    }
209
 
210
    /**
211
     * Always retrieve custom data from the remote stream
212
     * {@inheritdoc}
213
     */
214
    public function getCustomData($key)
215
    {
216
        return $this->remoteStream->getCustomData($key);
217
    }
218
 
219
    /**
220
     * Always set custom data on the remote stream
221
     * {@inheritdoc}
222
     */
223
    public function setCustomData($key, $value)
224
    {
225
        $this->remoteStream->setCustomData($key, $value);
226
 
227
        return $this;
228
    }
229
}