Completed
Pull Request — master (#52)
by Joel
02:18
created

BufferedStream::getMetadata()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5.5069

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 22
ccs 8
cts 11
cp 0.7272
rs 8.6737
cc 5
eloc 11
nc 5
nop 1
crap 5.5069
1
<?php
2
3
namespace Http\Message\Stream;
4
5
use Psr\Http\Message\StreamInterface;
6
7
/**
8
 * A buffered stream allow to buffer over an existing.
9
 *
10
 * You should use this decorator when you want to seek over a not seekable stream.
11
 * This stream is however read-only.
12
 */
13
class BufferedStream implements StreamInterface
14
{
15
    /** @var resource The buffered resource used to seek previous data */
16
    private $resource;
17
18
    /** @var integer size of the stream if available */
19
    private $size;
20
21
    /** @var StreamInterface The underlying stream decorated by this class */
22
    private $stream;
23
24
    /** @var int How many bytes were written */
25
    private $writed = 0;
26
27 13
    public function __construct(StreamInterface $stream, $useFileBuffer = true, $memoryBuffer = 2097152)
28
    {
29 13
        $this->stream = $stream;
30 13
        $this->size = $stream->getSize();
31
32 13
        if ($useFileBuffer) {
33 13
            $this->resource = fopen('php://temp/maxmemory:'.$memoryBuffer, 'rw+');
34 13
        } else {
35
            $this->resource = fopen('php://memory', 'rw+');
36
        }
37
38 13
        if (false === $this->resource) {
39
            throw new \RuntimeException('Cannot create a resource over temp or memory implementation');
40
        }
41 13
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46 1
    public function __toString()
47
    {
48
        try {
49 1
            $this->rewind();
50
51 1
            return $this->getContents();
52
        } catch (\Throwable $throwable) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
53
            return '';
54
        } catch (\Exception $exception) {
55
            return '';
56
        }
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function close()
63
    {
64
        if (null === $this->resource) {
65
            throw new \RuntimeException('Cannot close on a detached stream');
66
        }
67
68
        $this->stream->close();
69
        fclose($this->resource);
70
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75 1
    public function detach()
76
    {
77 1
        if (null === $this->resource) {
78 1
            return null;
79
        }
80
81
        // Force reading the remaining data of the stream
82 1
        $this->getContents();
83
84 1
        $resource = $this->resource;
85 1
        $this->stream = null;
86 1
        $this->resource = null;
87
88 1
        return $resource;
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94 1
    public function getSize()
95
    {
96 1
        if (null === $this->size && $this->stream->eof()) {
97 1
            if (null === $this->resource) {
98
                return null;
99
            }
100
101 1
            return $this->writed;
102
        }
103
104 1
        return $this->size;
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 3
    public function tell()
111
    {
112 3
        if (null === $this->resource) {
113
            throw new \RuntimeException('Cannot tell on a detached stream');
114
        }
115
116 3
        return ftell($this->resource);
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122 2
    public function eof()
123
    {
124 2
        if (null === $this->resource) {
125
            throw new \RuntimeException('Cannot call eof on a detached stream');
126
        }
127
128
        // We are at the end only when both our resource and underlying stream are at eof
129 2
        return $this->stream->eof() && (ftell($this->resource) === $this->writed);
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 1
    public function isSeekable()
136
    {
137 1
        if (null === $this->resource) {
138
            return false;
139
        }
140
141 1
        return true;
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147 2
    public function seek($offset, $whence = SEEK_SET)
148
    {
149 2
        if (null === $this->resource) {
150
            throw new \RuntimeException('Cannot seek on a detached stream');
151
        }
152
153 2
        fseek($this->resource, $offset, $whence);
154 2
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159 3
    public function rewind()
160
    {
161 3
        if (null === $this->resource) {
162
            throw new \RuntimeException('Cannot rewind on a detached stream');
163
        }
164
165 3
        rewind($this->resource);
166 3
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171 1
    public function isWritable()
172
    {
173 1
        return false;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 1
    public function write($string)
180
    {
181 1
        throw new \RuntimeException('Cannot write on this stream');
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 1
    public function isReadable()
188
    {
189 1
        return (null !== $this->resource);
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195 8
    public function read($length)
196
    {
197 8
        if (null === $this->resource) {
198
            throw new \RuntimeException('Cannot read on a detached stream');
199
        }
200
201 8
        $read = "";
202
203
        // First read from the resource
204 8
        if (ftell($this->resource) !== $this->writed) {
205 1
            $read = fread($this->resource, $length);
206 1
        }
207
208 8
        $bytesRead = strlen($read);
209
210 8
        if ($bytesRead < $length) {
211 8
            $streamRead = $this->stream->read($length - $bytesRead);
212
213
            // Write on the underlying stream what we read
214 8
            $this->writed += fwrite($this->resource, $streamRead);
215 8
            $read .= $streamRead;
216 8
        }
217
218 8
        return $read;
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224 8
    public function getContents()
225
    {
226 8
        if (null === $this->resource) {
227
            throw new \RuntimeException('Cannot read on a detached stream');
228
        }
229
230 8
        $read = "";
231
232 8
        while (!$this->stream->eof()) {
233 7
            $read .= $this->read(8192);
234 7
        }
235
236 8
        return $read;
237
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242 1
    public function getMetadata($key = null)
243
    {
244 1
        if (null === $this->resource) {
245
            if (null === $key) {
246
                return [];
247
            }
248
249
            return null;
250
        }
251
252 1
        $metadata = stream_get_meta_data($this->resource);
253
254 1
        if (null === $key) {
255 1
            return $metadata;
256
        }
257
258 1
        if (!array_key_exists($key, $metadata)) {
259 1
            return null;
260
        }
261
262 1
        return $metadata[$key];
263
    }
264
}
265