Completed
Push — master ( 6b8416...c520c0 )
by Filipe
02:12
created

AbstractStream   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 303
Duplicated Lines 17.82 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 44
lcom 1
cbo 1
dl 54
loc 303
rs 8.3396
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __toString() 0 13 3
A close() 0 5 2
A detach() 0 6 1
A getSize() 0 7 2
A tell() 15 15 3
A eof() 0 8 2
A isSeekable() 0 9 2
A seek() 0 14 4
A rewind() 0 4 1
B isWritable() 0 15 6
A write() 14 14 4
A isReadable() 0 9 3
A read() 14 14 4
A getContents() 11 11 3
A getMetadata() 0 11 3
A __destruct() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AbstractStream often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractStream, and based on these observations, apply Extract Interface, too.

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
     * Separates any underlying resources from the stream.
68
     *
69
     * After the stream has been detached, the stream is in an unusable state.
70
     *
71
     * @return resource|null Underlying PHP stream, if any
72
     */
73
    public function detach()
74
    {
75
        $resource = $this->stream;
76
        $this->stream = null;
77
        return $resource;
78
    }
79
80
    /**
81
     * Get the size of the stream if known.
82
     *
83
     * @return int|null Returns the size in bytes if known, or null if unknown.
84
     */
85
    public function getSize()
86
    {
87
        if (is_null($this->stream)) return null;
88
89
        $stats = fstat($this->stream);
90
        return $stats['size'];
91
    }
92
93
    /**
94
     * Returns the current position of the file read/write pointer
95
     *
96
     * @return int Position of the file pointer
97
     * @throws RuntimeException on error.
98
     */
99 View Code Duplication
    public function tell()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
100
    {
101
        if (!$this->stream) {
102
            throw new RuntimeException(
103
                'No resource available; cannot tell position'
104
            );
105
        }
106
        $result = ftell($this->stream);
107
        if (!is_int($result)) {
108
            throw new RuntimeException(
109
                'Error occurred during tell operation'
110
            );
111
        }
112
        return $result;
113
    }
114
115
    /**
116
     * Returns true if the stream is at the end of the stream.
117
     *
118
     * @return bool
119
     */
120
    public function eof()
121
    {
122
        $return = true;
123
        if (is_resource($this->stream)) {
124
            $return = feof($this->stream);
125
        }
126
        return $return;
127
    }
128
129
    /**
130
     * Returns whether or not the stream is seekable.
131
     *
132
     * @return bool
133
     */
134
    public function isSeekable()
135
    {
136
        $seekable = false;
137
        if ($this->stream) {
138
            $meta = stream_get_meta_data($this->stream);
139
            $seekable = $meta['seekable'];
140
        }
141
        return $seekable;
142
    }
143
144
    /**
145
     * Seek to a position in the stream.
146
     *
147
     * @link http://www.php.net/manual/en/function.fseek.php
148
     * @param int $offset Stream offset
149
     * @param int $whence Specifies how the cursor position will be calculated
150
     *     based on the seek offset. Valid values are identical to the built-in
151
     *     PHP $whence values for `fseek()`.  SEEK_SET: Set position equal to
152
     *     offset bytes SEEK_CUR: Set position to current location plus offset
153
     *     SEEK_END: Set position to end-of-stream plus offset.
154
     * @throws RuntimeException on failure.
155
     *
156
     * @return bool
157
     */
158
    public function seek($offset, $whence = SEEK_SET)
159
    {
160
        if (! $this->stream) {
161
            throw new RuntimeException('No resource available; cannot seek position');
162
        }
163
        if (! $this->isSeekable()) {
164
            throw new RuntimeException('Stream is not seekable');
165
        }
166
        $result = fseek($this->stream, $offset, $whence);
167
        if (0 !== $result) {
168
            throw new RuntimeException('Error seeking within stream');
169
        }
170
        return true;
171
    }
172
173
    /**
174
     * Seek to the beginning of the stream.
175
     *
176
     * If the stream is not seekable, this method will raise an exception;
177
     * otherwise, it will perform a seek(0).
178
     *
179
     * @see seek()
180
     * @link http://www.php.net/manual/en/function.fseek.php
181
     * @throws \RuntimeException on failure.
182
     */
183
    public function rewind()
184
    {
185
        return $this->seek(0);
186
    }
187
188
    /**
189
     * Returns whether or not the stream is writable.
190
     *
191
     * @return bool
192
     */
193
    public function isWritable()
194
    {
195
        if (! $this->stream) {
196
            return false;
197
        }
198
        $meta = stream_get_meta_data($this->stream);
199
        $mode = $meta['mode'];
200
        return (
201
            strstr($mode, 'x')
202
            || strstr($mode, 'w')
203
            || strstr($mode, 'c')
204
            || strstr($mode, 'a')
205
            || strstr($mode, '+')
206
        );
207
    }
208
209
    /**
210
     * Write data to the stream.
211
     *
212
     * @param string $string The string that is to be written.
213
     * @return int Returns the number of bytes written to the stream.
214
     * @throws RuntimeException on failure.
215
     */
216 View Code Duplication
    public function write($string)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
217
    {
218
        if (! $this->stream) {
219
            throw new RuntimeException('No resource available; cannot write');
220
        }
221
        if (! $this->isWritable()) {
222
            throw new RuntimeException('Stream is not writable');
223
        }
224
        $result = fwrite($this->stream, $string);
225
        if (false === $result) {
226
            throw new RuntimeException('Error writing to stream');
227
        }
228
        return $result;
229
    }
230
231
    /**
232
     * Returns whether or not the stream is readable.
233
     *
234
     * @return bool
235
     */
236
    public function isReadable()
237
    {
238
        if (! $this->stream) {
239
            return false;
240
        }
241
        $meta = stream_get_meta_data($this->stream);
242
        $mode = $meta['mode'];
243
        return (strstr($mode, 'r') || strstr($mode, '+'));
244
    }
245
246
    /**
247
     * Read data from the stream.
248
     *
249
     * @param int $length Read up to $length bytes from the object and return
250
     *     them. Fewer than $length bytes may be returned if underlying stream
251
     *     call returns fewer bytes.
252
     * @return string Returns the data read from the stream, or an empty string
253
     *     if no bytes are available.
254
     * @throws RuntimeException if an error occurs.
255
     */
256 View Code Duplication
    public function read($length)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
257
    {
258
        if (! $this->stream) {
259
            throw new RuntimeException('No resource available; cannot read');
260
        }
261
        if (! $this->isReadable()) {
262
            throw new RuntimeException('Stream is not readable');
263
        }
264
        $result = fread($this->stream, $length);
265
        if (false === $result) {
266
            throw new RuntimeException('Error reading stream');
267
        }
268
        return $result;
269
    }
270
271
    /**
272
     * Returns the remaining contents in a string
273
     *
274
     * @return string
275
     * @throws RuntimeException if unable to read or an error occurs while
276
     *     reading.
277
     */
278 View Code Duplication
    public function getContents()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
279
    {
280
        if (! $this->isReadable()) {
281
            throw new RuntimeException('Stream is not readable');
282
        }
283
        $result = stream_get_contents($this->stream);
284
        if (false === $result) {
285
            throw new RuntimeException('Error reading from stream');
286
        }
287
        return $result;
288
    }
289
290
    /**
291
     * Get stream metadata as an associative array or retrieve a specific key.
292
     *
293
     * The keys returned are identical to the keys returned from PHP's
294
     * stream_get_meta_data() function.
295
     *
296
     * @link http://php.net/manual/en/function.stream-get-meta-data.php
297
     * @param string $key Specific metadata to retrieve.
298
     * @return array|mixed|null Returns an associative array if no key is
299
     *     provided. Returns a specific key value if a key is provided and the
300
     *     value is found, or null if the key is not found.
301
     */
302
    public function getMetadata($key = null)
303
    {
304
        if (null === $key) {
305
            return stream_get_meta_data($this->stream);
306
        }
307
        $metadata = stream_get_meta_data($this->stream);
308
        if (! array_key_exists($key, $metadata)) {
309
            return null;
310
        }
311
        return $metadata[$key];
312
    }
313
314
    /**
315
     * Closes the stream on destroy
316
     */
317
    public function __destruct()
318
    {
319
        $this->close();
320
    }
321
}