Completed
Push — master ( 73dfb2...722094 )
by Daniel
05:05
created

Stream::isWritable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * This file is part of the stream package
4
 *
5
 * @author Daniel Schröder <[email protected]>
6
 */
7
8
namespace GravityMedia\Stream;
9
10
use GravityMedia\Stream\Exception;
11
use GravityMedia\Stream\Reader\StringReader;
12
13
/**
14
 * Stream
15
 *
16
 * @package GravityMedia\Stream
17
 */
18
class Stream implements StreamInterface
19
{
20
    /**
21
     * @var string[]
22
     */
23
    private static $readModes = ['r', 'w+', 'r+', 'x+', 'c+', 'rb', 'w+b', 'r+b', 'x+b', 'c+b', 'rt', 'w+t',
24
        'r+t', 'x+t', 'c+t', 'a+'];
25
26
    /**
27
     * @var string[]
28
     */
29
    private static $writeModes = ['w', 'w+', 'rw', 'r+', 'x+', 'c+', 'wb', 'w+b', 'r+b', 'x+b', 'c+b', 'w+t',
30
        'r+t', 'x+t', 'c+t', 'a', 'a+'];
31
32
    /**
33
     * @var resource
34
     */
35
    protected $resource;
36
37
    /**
38
     * @var bool
39
     */
40
    protected $local;
41
42
    /**
43
     * @var bool
44
     */
45
    protected $readable;
46
47
    /**
48
     * @var bool
49
     */
50
    protected $writable;
51
52
    /**
53
     * @var bool
54
     */
55
    protected $seekable;
56
57
    /**
58
     * @var string
59
     */
60
    protected $uri;
61
62
    /**
63
     * @var StringReader
64
     */
65
    protected $stringReader;
66
67
    /**
68
     * Create stream object from resource
69
     *
70
     * @param resource $resource
71
     *
72
     * @return StreamInterface
73
     */
74 78
    public static function fromResource($resource)
75
    {
76 78
        $instance = new static();
77
78 78
        return $instance->bindResource($resource);
79
    }
80
81
    /**
82
     * Get meta data from the stream
83
     *
84
     * @throws Exception\IOException An exception will be thrown for invalid stream resources
85
     *
86
     * @return array
87
     */
88 75
    protected function getMetaData()
89
    {
90 75
        return stream_get_meta_data($this->getResource());
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96 78
    public function bindResource($resource)
97
    {
98 78
        $this->resource = $resource;
99
100 78
        $this->local = stream_is_local($this->getResource());
101
102 75
        $metaData = $this->getMetaData();
103 75
        $this->readable = in_array($metaData['mode'], self::$readModes);
104 75
        $this->writable = in_array($metaData['mode'], self::$writeModes);
105 75
        $this->seekable = $metaData['seekable'];
106 75
        $this->uri = $metaData['uri'];
107
108 75
        return $this;
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114 78
    public function getResource()
115
    {
116 78
        if (!is_resource($this->resource)) {
117 27
            throw new Exception\IOException('Invalid resource');
118
        }
119
120 75
        return $this->resource;
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 9
    public function isLocal()
127
    {
128 9
        return $this->local;
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 12
    public function isReadable()
135
    {
136 12
        return $this->readable;
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142 12
    public function isWritable()
143
    {
144 12
        return $this->writable;
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150 12
    public function isSeekable()
151
    {
152 12
        return $this->seekable;
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158 6
    public function getUri()
159
    {
160 6
        return $this->uri;
161
    }
162
163
    /**
164
     * Get string reader
165
     *
166
     * @return StringReader
167
     */
168 9
    public function getStringReader()
169
    {
170 9
        if (null === $this->stringReader) {
171 6
            $this->stringReader = new StringReader();
172 6
        }
173
174 9
        return $this->stringReader;
175
    }
176
177
    /**
178
     * Set string reader
179
     *
180
     * @param StringReader $stringReader
181
     *
182
     * @return $this
183
     */
184 3
    public function setStringReader(StringReader $stringReader)
185
    {
186 3
        $this->stringReader = $stringReader;
187
188 3
        return $this;
189
    }
190
191
    /**
192
     * Get information about the stream
193
     *
194
     * @param string $info The information to retrieve
195
     *
196
     * @throws Exception\IOException An exception will be thrown for invalid stream resources
197
     *
198
     * @return int
199
     */
200 6
    protected function getStat($info)
201
    {
202 6
        $resource = $this->getResource();
203
204 3
        $uri = $this->getUri();
205 3
        if (is_string($uri)) {
206 3
            clearstatcache(true, $uri);
207 3
        }
208
209 3
        $stat = fstat($resource);
210
211 3
        return $stat[$info];
212
    }
213
214
    /**
215
     * {@inheritdoc}
216
     */
217 9
    public function getSize()
218
    {
219 9
        if (!$this->isLocal()) {
220 3
            throw new Exception\BadMethodCallException('Stream not local');
221
        }
222
223 6
        return $this->getStat('size');
224
    }
225
226
    /**
227
     * {@inheritdoc}
228
     */
229 6
    public function eof()
230
    {
231 6
        return feof($this->getResource());
232
    }
233
234
    /**
235
     * {@inheritdoc}
236
     */
237 12
    public function tell()
238
    {
239 12
        return ftell($this->getResource());
240
    }
241
242
    /**
243
     * {@inheritdoc}
244
     */
245 15
    public function seek($offset, $whence = SEEK_SET)
246
    {
247 15
        $resource = $this->getResource();
248
249 12
        if (!$this->isSeekable()) {
250 3
            throw new Exception\BadMethodCallException('Stream not seekable');
251
        }
252
253 9
        if (fseek($resource, $offset, $whence) < 0) {
254 3
            throw new Exception\IOException('Unexpected result of operation');
255
        }
256
257 6
        return $this->tell();
258
    }
259
260
    /**
261
     * {@inheritdoc}
262
     */
263 3
    public function rewind()
264
    {
265 3
        return $this->seek(0);
266
    }
267
268
    /**
269
     * {@inheritdoc}
270
     */
271 15 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...
272
    {
273 15
        $resource = $this->getResource();
274
275 12
        if (!$this->isReadable()) {
276 3
            throw new Exception\BadMethodCallException('Stream not readable');
277
        }
278
279 9
        $data = @fread($resource, $length);
280 9
        if (false === $data) {
281 3
            throw new Exception\IOException('Unexpected result of operation');
282
        }
283
284 6
        return $data;
285
    }
286
287
    /**
288
     * {@inheritdoc}
289
     */
290 3
    public function readString($length = 1)
291
    {
292 3
        return $this->getStringReader()
293 3
            ->setStream($this)
294 3
            ->setLength($length)
295 3
            ->read();
296
    }
297
298
    /**
299
     * {@inheritdoc}
300
     */
301 12 View Code Duplication
    public function write($data)
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...
302
    {
303 12
        $resource = $this->getResource();
304
305 9
        if (!$this->isWritable()) {
306 3
            throw new Exception\BadMethodCallException('Stream not writable');
307
        }
308
309 6
        $length = @fwrite($resource, $data);
310 6
        if (false === $length) {
311 3
            throw new Exception\IOException('Unexpected result of operation');
312
        }
313
314 3
        return $length;
315
    }
316
317
    /**
318
     * {@inheritdoc}
319
     */
320 9
    public function truncate($size)
321
    {
322 9
        $resource = $this->getResource();
323
324 6
        if (!$this->isWritable()) {
325 3
            throw new Exception\BadMethodCallException('Stream not writable');
326
        }
327
328 3
        return @ftruncate($resource, $size);
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     */
334 3
    public function close()
335
    {
336 3
        return @fclose($this->resource);
337
    }
338
}
339