Passed
Push — master ( d0466a...c02546 )
by Zlatin
01:22
created

Stream   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 253
Duplicated Lines 0 %

Test Coverage

Coverage 28.05%

Importance

Changes 0
Metric Value
dl 0
loc 253
ccs 23
cts 82
cp 0.2805
rs 8.8
c 0
b 0
f 0
wmc 36

16 Methods

Rating   Name   Duplication   Size   Complexity  
A rewind() 0 7 2
A getMetadata() 0 13 3
A close() 0 8 2
A getContents() 0 11 3
A eof() 0 7 2
A getSize() 0 8 2
A isWritable() 0 8 2
A isSeekable() 0 11 3
A seek() 0 7 2
A detach() 0 6 1
A write() 0 7 2
A isReadable() 0 8 2
A tell() 0 7 2
A read() 0 7 2
A __toString() 0 9 3
A __construct() 0 12 3
1
<?php
2
namespace DevOp\Core\Http;
3
4
use Psr\Http\Message\StreamInterface;
5
6
class Stream implements StreamInterface
7
{
8
9
    /**
10
     * @var array
11
     * @link http://php.net/manual/function.fopen.php
12
     */
13
    protected $modes = [
14
        'readable' => [
15
            'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
16
            'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
17
            'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
18
            'x+t' => true, 'c+t' => true, 'a+' => true
19
        ],
20
        'writable' => [
21
            'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
22
            'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
23
            'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
24
            'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true
25
        ],
26
    ];
27
28
    /**
29
     * @var resource
30
     */
31
    private $stream;
32
33
    /**
34
     * @param resource|string $handle
35
     * @param string|null $mode
36
     * @throws \InvalidArgumentException
37
     */
38 16
    public function __construct($handle, $mode = 'r')
39
    {
40
41 16
        if (is_string($handle)) {
42 16
            $handle = fopen($handle, $mode);
43
        }
44
45 16
        if (!is_resource($handle)) {
46
            throw new \InvalidArgumentException('Argument must be a valid stream.');
47
        }
48
49 16
        $this->stream = $handle;
50 16
    }
51
52
    /**
53
     * @return string
54
     */
55
    public function __toString()
56
    {
57
        try {
58
            if ($this->isSeekable()) {
59
                $this->rewind();
60
            }
61
            return $this->getContents();
62
        } catch (\Exception $e) {
63
            return '';
64
        }
65
    }
66
67
    /**
68
     * @return boolean
69
     */
70
    public function close()
71
    {
72
        if (!$this->stream) {
73
            return;
74
        }
75
76
        $handle = $this->detach();
77
        fclose($handle);
78
    }
79
80
    /**
81
     * @return resource
82
     */
83
    public function detach()
84
    {
85
        $handle = $this->stream;
86
        $this->stream = null;
87
88
        return $handle;
89
    }
90
91
    /**
92
     * @return boolean
93
     */
94
    public function eof()
95
    {
96
        if (!$this->stream) {
97
            return true;
98
        }
99
100
        return feof($this->stream);
101
    }
102
103
    /**
104
     * @return string
105
     * @throws \RuntimeException
106
     */
107
    public function getContents()
108
    {
109
        if (!$this->stream) {
110
            throw new \RuntimeException('Empty stream');
111
        }
112
113
        if ($this->isReadable()) {
114
            throw new \RuntimeException('Unable to read stream');
115
        }
116
117
        return (string) stream_get_contents($this->stream);
118
    }
119
120
    /**
121
     * @param string|null $key
122
     * @return mixed
123
     */
124 6
    public function getMetadata($key = null)
125
    {
126 6
        $metadata = stream_get_meta_data($this->stream);
127
128 6
        if (null === $key) {
129
            return $metadata;
130
        }
131
132 6
        if (isset($metadata[$key])) {
133 6
            return $metadata[$key];
134
        }
135
136
        return null;
137
    }
138
139
    /**
140
     * @return int
141
     */
142
    public function getSize()
143
    {
144
        $fstats = fstat($this->stream);
145
        if (isset($fstats['size'])) {
146
            return $fstats['size'];
147
        }
148
149
        return null;
150
    }
151
152
    /**
153
     * @return boolean
154
     */
155 2
    public function isReadable()
156
    {
157 2
        if (!$this->stream) {
158
            return false;
159
        }
160
161 2
        $mode = $this->getMetadata('mode');
162 2
        return isset($this->modes['readable'][$mode]);
163
    }
164
165
    /**
166
     * @return boolean
167
     */
168 2
    public function isSeekable()
169
    {
170 2
        if (!$this->stream) {
171
            return false;
172
        }
173
174 2
        if ($this->getMetadata('seekable')) {
175 2
            return true;
176
        }
177
178
        return false;
179
    }
180
181
    /**
182
     * @return boolean
183
     */
184 2
    public function isWritable()
185
    {
186 2
        if (!$this->stream) {
187
            return false;
188
        }
189
190 2
        $mode = $this->getMetadata('mode');
191 2
        return isset($this->modes['writable'][$mode]);
192
    }
193
194
    /**
195
     * @param int $length
196
     * @return string
197
     * @throws \RuntimeException
198
     */
199
    public function read($length)
200
    {
201
        if (!$this->isReadable()) {
202
            throw new \RuntimeException('Stream is not readable');
203
        }
204
205
        return fread($this->stream, $length);
206
    }
207
208
    /**
209
     * @throws \RuntimeException
210
     */
211
    public function rewind()
212
    {
213
        if ($this->isSeekable()) {
214
            throw new \RuntimeException('Stream is not seekable');
215
        }
216
217
        $this->seek(0);
218
    }
219
220
    /**
221
     * @param int $offset
222
     * @param int $whence
223
     * @throws \RuntimeException
224
     */
225
    public function seek($offset, $whence = SEEK_SET)
226
    {
227
        if (!$this->isSeekable()) {
228
            throw new \RuntimeException('Stream is not seekable');
229
        }
230
231
        return fseek($this->stream, $offset, $whence);
232
    }
233
234
    /**
235
     * @return int
236
     * @throws \RuntimeException
237
     */
238
    public function tell()
239
    {
240
        if (!$this->stream) {
241
            throw new \RuntimeException('Invalid stream handle');
242
        }
243
244
        return ftell($this->stream);
245
    }
246
247
    /**
248
     * @param string $string
249
     * @return int
250
     * @throws \RuntimeException
251
     */
252
    public function write($string)
253
    {
254
        if (!$this->isWritable()) {
255
            throw new \RuntimeException('Stream is not writable');
256
        }
257
258
        return fwrite($this->stream, $string);
259
    }
260
}
261