Completed
Push — master ( 204760...d7809c )
by Sebastian
02:32
created

Stream::getSize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Linna Psr7.
5
 *
6
 * @author Sebastian Rapetti <[email protected]>
7
 * @copyright (c) 2018, Sebastian Rapetti
8
 * @license http://opensource.org/licenses/MIT MIT License
9
 */
10
declare(strict_types=1);
11
12
namespace Linna\Psr7;
13
14
use Psr\Http\Message\StreamInterface;
15
use InvalidArgumentException;
16
use RuntimeException;
17
18
/**
19
 * Psr7 Stream implementation.
20
 */
21
class Stream implements StreamInterface
22
{
23
    /**
24
     * @var resource The streem resource.
25
     */
26
    protected $resource;
27
    
28
    /**
29
     * @var bool Is stream a proces file pointer?
30
     */
31
    protected $isPipe;
32
    
33
    /**
34
     * Constructor.
35
     *
36
     * @param Resource $resource
37
     */
38
    public function __construct($resource)
39
    {
40
        if (!is_resource($resource)) {
41
            throw new InvalidArgumentException(__CLASS__.': Invalid resource provided');
42
        }
43
44
        if ('stream' !== get_resource_type($resource)) {
45
            throw new InvalidArgumentException(__CLASS__.': Resource provided is not a stream');
46
        }
47
48
        $this->resource = $resource;
49
        $this->isPipe = $this->checkFileMode($resource);
0 ignored issues
show
Bug introduced by
$resource of type resource is incompatible with the type Linna\Psr7\type expected by parameter $resource of Linna\Psr7\Stream::checkFileMode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

49
        $this->isPipe = $this->checkFileMode(/** @scrutinizer ignore-type */ $resource);
Loading history...
50
    }
51
    
52
    /**
53
     * Check if file is a pipe.
54
     * http://man7.org/linux/man-pages/man7/inode.7.html
55
     *
56
     * @param type $resource
0 ignored issues
show
Bug introduced by
The type Linna\Psr7\type was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
57
     * @return bool
58
     */
59
    protected function checkFileMode($resource) : bool
60
    {
61
        //file modes
62
        //check if resource is a process file pointer.
63
        //0140000   socket
64
        //0120000   symbolic link
65
        //0100000   regular file
66
        //0060000   block device
67
        //0040000   directory
68
        //0020000   character device
69
        //0010000   FIFO
70
        return ((fstat($resource)['mode'] & 0010000) !== 0) ? true: false;
0 ignored issues
show
Bug introduced by
$resource of type Linna\Psr7\type is incompatible with the type resource expected by parameter $handle of fstat(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

70
        return ((fstat(/** @scrutinizer ignore-type */ $resource)['mode'] & 0010000) !== 0) ? true: false;
Loading history...
71
    }
72
    
73
    /**
74
     * {@inheritdoc}
75
     */
76
    public function __toString() : string
77
    {
78
        try {
79
            $this->rewind();
80
            return $this->getContents();
81
        } catch (RuntimeException $e) {
82
            return '';
83
        }
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function close()
90
    {
91
        if (!$this->resource) {
92
            return;
93
        }
94
95
        if ($this->isPipe) {
96
            pclose($this->resource);
97
            $this->resource = null;
98
            return;
99
        }
100
101
        fclose($this->resource);
102
        $this->resource = null;
103
    }
104
    
105
    /**
106
     * {@inheritdoc}
107
     */
108
    public function detach()
109
    {
110
        if (!$this->resource) {
111
            return;
112
        }
113
        
114
        $tmpResource = $this->resource;
115
        $this->resource = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type resource of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
116
        
117
        return $tmpResource;
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    public function getSize() : int
124
    {
125
        return (!$this->resource) ? 0 : fstat($this->resource)['size'];
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131
    public function tell() : int
132
    {
133
        if (!$this->resource) {
134
            throw new RuntimeException(__CLASS__.': No resource available; cannot tell position');
135
        }
136
        
137
        if (($position = ftell($this->resource)) === false) {
138
            throw new RuntimeException(__CLASS__.': Error occurred during tell operation');
139
        }
140
        
141
        return $position;
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147
    public function eof() : bool
148
    {
149
        return (!$this->resource) ? feof($this->resource) : true;
150
    }
151
152
    /**
153
     * {@inheritdoc}
154
     */
155
    public function isSeekable() : bool
156
    {
157
        return (!$this->resource) ? false : stream_get_meta_data($this->resource)['seekable'];
158
    }
159
160
    /**
161
     * {@inheritdoc}
162
     */
163
    public function seek(int $offset, int $whence = SEEK_SET)
164
    {
165
        if (!$this->isSeekable()) {
166
            throw new RuntimeException(__CLASS__.': Can not seek the stream');
167
        }
168
        
169
        if (fseek($this->resource, $offset, $whence) !== 0) {
170
            throw new RuntimeException(__CLASS__.': Error seeking within stream');
171
        }
172
        
173
        return true;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    public function rewind()
180
    {
181
        if (!$this->isSeekable() || rewind($this->stream) === false) {
0 ignored issues
show
Bug Best Practice introduced by
The property stream does not exist on Linna\Psr7\Stream. Did you maybe forget to declare it?
Loading history...
182
            throw new RuntimeException(__CLASS__.': Can not rewind the stream');
183
        }
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    protected function can(array $modes) : bool
190
    {
191
        $metaMode = stream_get_meta_data($this->resource)['mode'];
192
        
193
        foreach ($modes as $mode) {
194
            if (strpos($metaMode, $mode) !== false) {
195
                return true;
196
            }
197
        }
198
        
199
        return false;
200
    }
201
    
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function isWritable() : bool
206
    {
207
        return (!$this->resource) ? false : $this->can(['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+']);
208
    }
209
    
210
    /**
211
     * {@inheritdoc}
212
     */
213
    public function write(string $string) : int
214
    {
215
        //if (!$this->resource) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
216
        //    throw new RuntimeException(__CLASS__.': Resource not available; '.__METHOD__);
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
217
        //}
218
        
219
        if (!$this->isWritable()) {
220
            throw new RuntimeException(__CLASS__.': Stream is not writable; '.__METHOD__);
221
        }
222
        
223
        if (($bytes = fwrite($this->resource, $string)) === false) {
224
            throw new RuntimeException(__CLASS__.': Error writing stream; '.__METHOD__);
225
        }
226
        
227
        return $bytes;
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233
    public function isReadable() : bool
234
    {
235
        return (!$this->resource) ? false : $this->can(['r', 'r+', 'w+', 'a+', 'x+', 'c+']);
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241
    public function read(int $length) : string
242
    {
243
        //if (!$this->resource) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
244
        //    throw new RuntimeException(__CLASS__.': Resource not available; '.__METHOD__);
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
245
        //}
246
        
247
        if (!$this->isReadable()) {
248
            throw new RuntimeException(__CLASS__.': Stream is not readable; '.__METHOD__);
249
        }
250
        
251
        if (($data = fread($this->resource, $length)) === false) {
252
            throw new RuntimeException(__CLASS__.': Error reading stream; '.__METHOD__);
253
        }
254
        
255
        return $data;
256
    }
257
258
    /**
259
     * {@inheritdoc}
260
     */
261
    public function getContents() : string
262
    {
263
        if (!$this->isReadable()) {
264
            throw new RuntimeException(__CLASS__.': Stream is not readable; '.__METHOD__);
265
        }
266
        
267
        if (($content = stream_get_contents($this->resource)) === false) {
268
            throw new RuntimeException(__CLASS__.': Error reading stream; '.__METHOD__);
269
        }
270
        
271
        return $content;
272
    }
273
274
    /**
275
     * {@inheritdoc}
276
     */
277
    public function getMetadata(string $key = '') : array
278
    {
279
        $metadata = stream_get_meta_data($this->resource);
280
        
281
        //if key is empty strung
282
        return ($key === '') ?
283
            //return metadata
284
            $metadata :
285
            //else check if key exist and if key exist return as array else return void array
286
            (isset($metadata[$key]) ? [$key => $metadata[$key]] : []);
287
    }
288
}
289