AbstractStream::eof()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 7
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
/**
4
 * This file is part of slick/http
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Http\Message\Stream;
11
12
use Psr\Http\Message\StreamInterface;
13
use Slick\Http\Message\Exception\RuntimeException;
14
15
/**
16
 * Class AbstractStream
17
 * @package Slick\Http\Message\Stream
18
 */
19
abstract class AbstractStream implements StreamInterface
20
{
21
22
    /**
23
     * @var resource
24
     */
25
    protected $stream;
26
27
    /**
28
     * Reads all data from the stream into a string, from the beginning to end.
29
     *
30
     * This method MUST attempt to seek to the beginning of the stream before
31
     * reading data and read the stream until the end is reached.
32
     *
33
     * Warning: This could attempt to load a large amount of data into memory.
34
     *
35
     * This method MUST NOT raise an exception in order to conform with PHP's
36
     * string casting operations.
37
     *
38
     * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
39
     * @return string
40
     */
41
    public function __toString()
42
    {
43
        if (!$this->isReadable()) {
44
            return '';
45
        }
46
        $value = '';
47
        try {
48
            $this->rewind();
49
            $value = $this->getContents();
50
        } catch (\RuntimeException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
51
        }
52
        return $value;
53
    }
54
55
    /**
56
     * Closes the stream and any underlying resources.
57
     *
58
     * @return void
59
     */
60
    public function close()
61
    {
62
        if (is_resource($this->stream)) {
63
            fclose($this->stream);
64
        }
65
    }
66
67
    /**
68
     * Separates any underlying resources from the stream.
69
     *
70
     * After the stream has been detached, the stream is in an unusable state.
71
     *
72
     * @return resource|null Underlying PHP stream, if any
73
     */
74
    public function detach()
75
    {
76
        $resource = $this->stream;
77
        $this->stream = null;
78
        return $resource;
79
    }
80
81
    /**
82
     * Get the size of the stream if known.
83
     *
84
     * @return int|null Returns the size in bytes if known, or null if unknown.
85
     */
86
    public function getSize()
87
    {
88
        if (is_null($this->stream)) {
0 ignored issues
show
introduced by
The condition is_null($this->stream) is always false.
Loading history...
89
            return null;
90
        }
91
92
        $stats = fstat($this->stream);
93
        return $stats['size'];
94
    }
95
96
    /**
97
     * Returns the current position of the file read/write pointer
98
     *
99
     * @return int Position of the file pointer
100
     * @throws RuntimeException on error.
101
     */
102
    public function tell()
103
    {
104
        if (!$this->stream) {
105
            throw new RuntimeException(
106
                'No resource available; cannot tell position'
107
            );
108
        }
109
        $result = ftell($this->stream);
110
        if (!is_int($result)) {
0 ignored issues
show
introduced by
The condition is_int($result) is always true.
Loading history...
111
            throw new RuntimeException(
112
                'Error occurred during tell operation'
113
            );
114
        }
115
        return $result;
116
    }
117
118
    /**
119
     * Returns true if the stream is at the end of the stream.
120
     *
121
     * @return bool
122
     */
123
    public function eof()
124
    {
125
        $return = true;
126
        if (is_resource($this->stream)) {
127
            $return = feof($this->stream);
128
        }
129
        return $return;
130
    }
131
132
    /**
133
     * Returns whether or not the stream is seekable.
134
     *
135
     * @return bool
136
     */
137
    public function isSeekable()
138
    {
139
        $seekable = false;
140
        if ($this->stream) {
141
            $meta = stream_get_meta_data($this->stream);
142
            $seekable = $meta['seekable'];
143
        }
144
        return $seekable;
145
    }
146
147
    /**
148
     * Seek to a position in the stream.
149
     *
150
     * @link http://www.php.net/manual/en/function.fseek.php
151
     * @param int $offset Stream offset
152
     * @param int $whence Specifies how the cursor position will be calculated
153
     *     based on the seek offset. Valid values are identical to the built-in
154
     *     PHP $whence values for `fseek()`.  SEEK_SET: Set position equal to
155
     *     offset bytes SEEK_CUR: Set position to current location plus offset
156
     *     SEEK_END: Set position to end-of-stream plus offset.
157
     * @throws RuntimeException on failure.
158
     *
159
     * @return bool
160
     */
161
    public function seek($offset, $whence = SEEK_SET)
162
    {
163
        if (! $this->stream) {
164
            throw new RuntimeException('No resource available; cannot seek position');
165
        }
166
        if (! $this->isSeekable()) {
167
            throw new RuntimeException('Stream is not seekable');
168
        }
169
        $result = fseek($this->stream, $offset, $whence);
170
        if (0 !== $result) {
171
            throw new RuntimeException('Error seeking within stream');
172
        }
173
        return true;
174
    }
175
176
    /**
177
     * Seek to the beginning of the stream.
178
     *
179
     * If the stream is not seekable, this method will raise an exception;
180
     * otherwise, it will perform a seek(0).
181
     *
182
     * @see seek()
183
     * @link http://www.php.net/manual/en/function.fseek.php
184
     * @throws \RuntimeException on failure.
185
     */
186
    public function rewind()
187
    {
188
        return $this->seek(0);
189
    }
190
191
    /**
192
     * Returns whether or not the stream is writable.
193
     *
194
     * @return bool
195
     */
196
    public function isWritable()
197
    {
198
        if (! $this->stream) {
199
            return false;
200
        }
201
        $meta = stream_get_meta_data($this->stream);
202
        $mode = $meta['mode'];
203
        return (
204
            strstr($mode, 'x')
205
            || strstr($mode, 'w')
206
            || strstr($mode, 'c')
207
            || strstr($mode, 'a')
208
            || strstr($mode, '+')
209
        );
210
    }
211
212
    /**
213
     * Write data to the stream.
214
     *
215
     * @param string $string The string that is to be written.
216
     * @return int Returns the number of bytes written to the stream.
217
     * @throws RuntimeException on failure.
218
     */
219
    public function write($string)
220
    {
221
        if (! $this->stream) {
222
            throw new RuntimeException('No resource available; cannot write');
223
        }
224
        if (! $this->isWritable()) {
225
            throw new RuntimeException('Stream is not writable');
226
        }
227
        $result = fwrite($this->stream, $string);
228
        if (false === $result) {
229
            throw new RuntimeException('Error writing to stream');
230
        }
231
        return $result;
232
    }
233
234
    /**
235
     * Returns whether or not the stream is readable.
236
     *
237
     * @return bool
238
     */
239
    public function isReadable()
240
    {
241
        if (! $this->stream) {
242
            return false;
243
        }
244
        $meta = stream_get_meta_data($this->stream);
245
        $mode = $meta['mode'];
246
        return (strstr($mode, 'r') || strstr($mode, '+'));
247
    }
248
249
    /**
250
     * Read data from the stream.
251
     *
252
     * @param int $length Read up to $length bytes from the object and return
253
     *     them. Fewer than $length bytes may be returned if underlying stream
254
     *     call returns fewer bytes.
255
     * @return string Returns the data read from the stream, or an empty string
256
     *     if no bytes are available.
257
     * @throws RuntimeException if an error occurs.
258
     */
259
    public function read($length)
260
    {
261
        if (! $this->stream) {
262
            throw new RuntimeException('No resource available; cannot read');
263
        }
264
        if (! $this->isReadable()) {
265
            throw new RuntimeException('Stream is not readable');
266
        }
267
        $result = fread($this->stream, $length);
268
        if (false === $result) {
269
            throw new RuntimeException('Error reading stream');
270
        }
271
        return $result;
272
    }
273
274
    /**
275
     * Returns the remaining contents in a string
276
     *
277
     * @return string
278
     * @throws RuntimeException if unable to read or an error occurs while
279
     *     reading.
280
     */
281
    public function getContents()
282
    {
283
        if (! $this->isReadable()) {
284
            throw new RuntimeException('Stream is not readable');
285
        }
286
        $result = stream_get_contents($this->stream);
287
        if (false === $result) {
288
            throw new RuntimeException('Error reading from stream');
289
        }
290
        return $result;
291
    }
292
293
    /**
294
     * Get stream metadata as an associative array or retrieve a specific key.
295
     *
296
     * The keys returned are identical to the keys returned from PHP's
297
     * stream_get_meta_data() function.
298
     *
299
     * @link http://php.net/manual/en/function.stream-get-meta-data.php
300
     * @param string $key Specific metadata to retrieve.
301
     * @return array|mixed|null Returns an associative array if no key is
302
     *     provided. Returns a specific key value if a key is provided and the
303
     *     value is found, or null if the key is not found.
304
     */
305
    public function getMetadata($key = null)
306
    {
307
        if (null === $key) {
308
            return stream_get_meta_data($this->stream);
309
        }
310
        $metadata = stream_get_meta_data($this->stream);
311
        if (! array_key_exists($key, $metadata)) {
312
            return null;
313
        }
314
        return $metadata[$key];
315
    }
316
317
    /**
318
     * Closes the stream on destroy
319
     */
320
    public function __destruct()
321
    {
322
        $this->close();
323
    }
324
}
325