Completed
Push — master ( b8b57a...0791f9 )
by Josh
10s
created

Stream::getMetadata()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 3
eloc 7
nc 3
nop 1
1
<?php
2
3
namespace CodeJet\Http;
4
5
use InvalidArgumentException;
6
use Psr\Http\Message\StreamInterface;
7
8
class Stream implements StreamInterface
9
{
10
11
    protected $handle;
12
13
    /**
14
     * Stream constructor.
15
     * @param $stream
16
     */
17
    public function __construct($stream)
18
    {
19
        if (!$this->isStream($stream)) {
20
            throw new InvalidArgumentException('Must be a stream.');
21
        }
22
23
        $this->handle = $stream;
24
    }
25
26
    /**
27
     * @param $stream
28
     * @return bool
29
     */
30
    protected function isStream($stream)
31
    {
32
        if (is_resource($stream) && get_resource_type($stream) === 'stream') {
33
            return true;
34
        }
35
36
        return false;
37
    }
38
39
    /**
40
     * @inheritdoc
41
     */
42
    public function __toString()
43
    {
44
        if (!$this->isReadable()) {
45
            return '';
46
        }
47
48
        if ($this->isSeekable()) {
49
            $this->rewind();
50
        }
51
52
        return stream_get_contents($this->handle);
53
    }
54
55
    /**
56
     * @inheritdoc
57
     */
58
    public function close()
59
    {
60
        if (!$this->handle) {
61
            return;
62
        }
63
64
        $handle = $this->detach();
65
        fclose($handle);
66
    }
67
68
    /**
69
     * @inheritdoc
70
     */
71
    public function detach()
72
    {
73
        $handle = $this->handle;
74
        $this->handle = null;
75
76
        return $handle;
77
    }
78
79
    /**
80
     * @inheritdoc
81
     */
82
    public function getSize()
83
    {
84
        $fstatData = fstat($this->handle);
85
86
        if (isset($fstatData['size'])) {
87
            return $fstatData['size'];
88
        }
89
90
        return null;
91
    }
92
93
    /**
94
     * @inheritdoc
95
     */
96
    public function tell()
97
    {
98
        if (!$this->handle) {
99
            throw new \RuntimeException('Cannot tell position, stream is closed.');
100
        }
101
102
        return ftell($this->handle);
103
    }
104
105
    /**
106
     * @inheritdoc
107
     */
108
    public function eof()
109
    {
110
        if (!$this->handle) {
111
            return true;
112
        }
113
114
        return feof($this->handle);
115
    }
116
117
    /**
118
     * @inheritdoc
119
     */
120
    public function isSeekable()
121
    {
122
        if (!$this->handle) {
123
            return false;
124
        }
125
126
        if ($this->getMetadata('seekable')) {
127
            return true;
128
        }
129
130
        return false;
131
    }
132
133
    /**
134
     * @inheritdoc
135
     */
136
    public function seek($offset, $whence = SEEK_SET)
137
    {
138
        if (!$this->isSeekable()) {
139
            throw new \RuntimeException('Stream is not seekable.');
140
        }
141
142
        fseek($this->handle, $offset, $whence);
143
    }
144
145
    /**
146
     * @inheritdoc
147
     */
148
    public function rewind()
149
    {
150
        if (!$this->isSeekable()) {
151
            throw new \RuntimeException('Cannot rewind, stream is not seekable.');
152
        }
153
154
        $this->seek(0);
155
    }
156
157
    /**
158
     * @inheritdoc
159
     */
160
    public function isWritable()
161
    {
162
        if (!$this->handle) {
163
            return false;
164
        }
165
166
        $mode = $this->getMetadata('mode');
167
168
        if (!$mode) {
169
            return false;
170
        }
171
172
        // Nix 'b'inary and 't'ext only identifiers from the mode.
173
        $mode = rtrim(rtrim($mode, 'b'), 't');
174
175
        if ($mode === 'r') {
176
            // 'r' is the only read-only mode.
177
            // The rest are read-write or write-only.
178
            return false;
179
        }
180
181
        return true;
182
    }
183
184
    /**
185
     * @inheritdoc
186
     */
187
    public function write($string)
188
    {
189
        if (!$this->isWritable($this->handle)) {
0 ignored issues
show
Unused Code introduced by
The call to Stream::isWritable() has too many arguments starting with $this->handle.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
190
            throw new \RuntimeException('Stream is not writable.');
191
        }
192
193
        if (!is_string($string)) {
194
            throw new \RuntimeException('Stream write value must be a string.');
195
        }
196
197
        return fwrite($this->handle, $string);
198
    }
199
200
    /**
201
     * @inheritdoc
202
     */
203
    public function isReadable()
204
    {
205
        if (!$this->handle) {
206
            return false;
207
        }
208
209
        $mode = $this->getMetadata('mode');
210
211
        if (!$mode) {
212
            return false;
213
        }
214
215
        // Nix 'b'inary and 't'ext only identifiers from the mode.
216
        $mode = rtrim(rtrim($mode, 'b'), 't');
217
        // Grab the last character of the mode.
218
        $readIdentifier = substr($mode, -1, 1);
219
220
        if ($readIdentifier === 'r' || $readIdentifier === '+') {
221
            return true;
222
        }
223
224
        return false;
225
    }
226
227
    /**
228
     * @inheritdoc
229
     */
230
    public function read($length)
231
    {
232
        if (!$this->isReadable()) {
233
            throw new \RuntimeException('Stream is not readable.');
234
        }
235
236
        $data = fread($this->handle, $length);
237
238
        return $data;
239
    }
240
241
    /**
242
     * @inheritdoc
243
     */
244
    public function getContents()
245
    {
246
        if (!$this->isReadable()) {
247
            throw new \RuntimeException('Stream is not readable.');
248
        }
249
250
        return stream_get_contents($this->handle);
251
    }
252
253
    /**
254
     * @inheritdoc
255
     */
256
    public function getMetadata($key = null)
257
    {
258
        $metaData = stream_get_meta_data($this->handle);
259
260
        if (is_null($key)) {
261
            return $metaData;
262
        }
263
264
        if (isset($metaData[$key])) {
265
            return $metaData[$key];
266
        }
267
268
        return null;
269
    }
270
}
271