Passed
Push — master ( 5fb311...42b16f )
by Zlatin
02:02
created

Stream   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 250
Duplicated Lines 0 %

Test Coverage

Coverage 28.39%

Importance

Changes 0
Metric Value
dl 0
loc 250
ccs 23
cts 81
cp 0.2839
rs 9
c 0
b 0
f 0
wmc 35

16 Methods

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