StreamWrapper   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 280
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 94.37%

Importance

Changes 0
Metric Value
dl 0
loc 280
ccs 67
cts 71
cp 0.9437
rs 10
c 0
b 0
f 0
wmc 26
lcom 1
cbo 2

14 Methods

Rating   Name   Duplication   Size   Complexity  
A stream_eof() 0 4 1
A stream_open() 0 13 2
A stream_read() 0 4 1
A stream_seek() 0 8 1
A stream_stat() 0 4 1
A stream_tell() 0 6 1
A stream_write() 0 4 1
A url_stat() 0 8 2
A getStreamStats() 0 29 5
A register() 0 9 2
A localFilename() 0 9 1
A isWrapped() 0 4 1
A getResource() 0 17 5
A releaseUri() 0 8 2
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Spiral\Files\Streams;
10
11
use Psr\Http\Message\StreamInterface;
12
use Spiral\Files\Exceptions\WrapperException;
13
14
/**
15
 * Spiral converter of PSR-7 streams to virtual filenames. Static as hell.
16
 */
17
class StreamWrapper
18
{
19
    /**
20
     * @var bool
21
     */
22
    private static $registered = false;
23
24
    /**
25
     * Uris associated with StreamInterfaces.
26
     *
27
     * @var array
28
     */
29
    private static $uris = [];
30
31
    /**
32
     * @var array
33
     */
34
    private static $modes = [
35
        'r'   => 33060,
36
        'rb'  => 33060,
37
        'r+'  => 33206,
38
        'rb+' => 33206,
39
        'w'   => 33188,
40
        'wb'  => 33188
41
    ];
42
43
    /**
44
     * @var StreamInterface
45
     */
46
    private $stream = null;
47
48
    /**
49
     * @var int
50
     */
51
    private $mode = 0;
52
53
    /**
54
     * Stream context.
55
     *
56
     * @var resource
57
     */
58
    public $context = null;
59
60
    /**
61
     * Check if StreamInterface ended.
62
     *
63
     * @return bool
64
     */
65 3
    public function stream_eof()
66
    {
67 3
        return $this->stream->eof();
68
    }
69
70
    /**
71
     * Open pre-mocked StreamInterface by it's unique uri.
72
     *
73
     * @param string $path
74
     * @param int    $mode
75
     * @param int    $options
76
     * @param string &$opened_path
77
     *
78
     * @return bool
79
     */
80 3
    public function stream_open($path, $mode, $options, &$opened_path)
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $opened_path is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
81
    {
82 3
        if (!isset(self::$uris[$path])) {
83
            return false;
84
        }
85
86 3
        $this->stream = self::$uris[$path];
87 3
        $this->mode = $mode;
88
89 3
        $this->stream->rewind();
90
91 3
        return true;
92
    }
93
94
    /**
95
     * Read data from StreamInterface.
96
     *
97
     * @param int $count
98
     *
99
     * @return string
100
     */
101 3
    public function stream_read($count)
102
    {
103 3
        return $this->stream->read($count);
104
    }
105
106
    /**
107
     * Seek into StreamInterface.
108
     *
109
     * @param int $offset
110
     * @param int $whence = SEEK_SET
111
     *
112
     * @return bool
113
     */
114 1
    public function stream_seek($offset, $whence = SEEK_SET)
115
    {
116
        //Note, MongoDB native streams DO NOT support seeking at the moment
117
        //@see https://jira.mongodb.org/browse/PHPLIB-213
118 1
        $this->stream->seek($offset, $whence);
119
120 1
        return true;
121
    }
122
123
    /**
124
     * Get StreamInterface stats.
125
     *
126
     * @see stat()
127
     * @return array|null
128
     */
129 3
    public function stream_stat()
130
    {
131 3
        return $this->getStreamStats($this->stream);
132
    }
133
134
    /**
135
     * Get StreamInterface position.
136
     *
137
     * @return int
138
     */
139 1
    public function stream_tell()
140
    {
141
        //Note, MongoDB native streams DO NOT support seeking at the moment
142
        //@see https://jira.mongodb.org/browse/PHPLIB-213
143 1
        return $this->stream->tell();
144
    }
145
146
    /**
147
     * Write content into StreamInterface.
148
     *
149
     * @param string $data
150
     *
151
     * @return int
152
     */
153 1
    public function stream_write($data)
154
    {
155 1
        return $this->stream->write($data);
156
    }
157
158
    /**
159
     * Get stats based on wrapped StreamInterface by it's mocked uri.
160
     *
161
     * @see stat()
162
     *
163
     * @param string $path
164
     * @param int    $flags
165
     *
166
     * @return array|null
167
     */
168 1
    public function url_stat($path, $flags)
0 ignored issues
show
Unused Code introduced by
The parameter $flags is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
169
    {
170 1
        if (!isset(self::$uris[$path])) {
171
            return null;
172
        }
173
174 1
        return $this->getStreamStats(self::$uris[$path]);
175
    }
176
177
    /**
178
     * Helper method used to correctly resolve StreamInterface stats.
179
     *
180
     * @param StreamInterface $stream
181
     *
182
     * @return array
183
     */
184 3
    private function getStreamStats(StreamInterface $stream)
185
    {
186 3
        $mode = $this->mode;
187 3
        if (empty($mode)) {
188 1
            if ($stream->isReadable()) {
189 1
                $mode = 'r';
190
            }
191
192 1
            if ($stream->isWritable()) {
193 1
                $mode = !empty($mode) ? 'r+' : 'w';
194
            }
195
        }
196
197
        return [
198 3
            'dev'     => 0,
199 3
            'ino'     => 0,
200 3
            'mode'    => self::$modes[$mode],
201 3
            'nlink'   => 0,
202 3
            'uid'     => 0,
203 3
            'gid'     => 0,
204 3
            'rdev'    => 0,
205 3
            'size'    => (string)$stream->getSize(),
206 3
            'atime'   => 0,
207 3
            'mtime'   => 0,
208 3
            'ctime'   => 0,
209 3
            'blksize' => 0,
210 3
            'blocks'  => 0
211
        ];
212
    }
213
214
    /**
215
     * Register stream wrapper.
216
     */
217 3
    public static function register()
218
    {
219 3
        if (self::$registered) {
220 2
            return;
221
        }
222
223 1
        stream_wrapper_register('spiral', __CLASS__);
224 1
        self::$registered = true;
225 1
    }
226
227
    /**
228
     * Register StreamInterface and get unique url for it.
229
     *
230
     * @param StreamInterface $stream
231
     *
232
     * @return string
233
     */
234 3
    public static function localFilename(StreamInterface $stream)
235
    {
236 3
        self::register();
237
238 3
        $uri = 'spiral://' . spl_object_hash($stream);
239 3
        self::$uris[$uri] = $stream;
240
241 3
        return $uri;
242
    }
243
244
    /**
245
     * Check if given uri points to one of wrapped streams.
246
     *
247
     * @param string $uri
248
     *
249
     * @return bool
250
     */
251 1
    public static function isWrapped($uri)
252
    {
253 1
        return isset(self::$uris[$uri]);
254
    }
255
256
    /**
257
     * Create StreamInterface associated resource.
258
     *
259
     * @param StreamInterface $stream
260
     *
261
     * @return resource
262
     * @throws WrapperException
263
     */
264 1
    public static function getResource(StreamInterface $stream)
265
    {
266 1
        $mode = null;
267 1
        if ($stream->isReadable()) {
268 1
            $mode = 'r';
269
        }
270
271 1
        if ($stream->isWritable()) {
272 1
            $mode = !empty($mode) ? 'r+' : 'w';
273
        }
274
275 1
        if (empty($mode)) {
276
            throw new WrapperException("Stream is not available in read or write modes");
277
        }
278
279 1
        return fopen(self::localFilename($stream), $mode);
280
    }
281
282
    /**
283
     * Free uri dedicated to specified StreamInterface. Method is useful for long living
284
     * applications.
285
     *
286
     * @param string|StreamInterface $uri String uri or StreamInterface.
287
     */
288 1
    public static function releaseUri($uri)
289
    {
290 1
        if ($uri instanceof StreamInterface) {
291
            $uri = 'spiral://' . spl_object_hash($uri);
292
        }
293
294 1
        unset(self::$uris[$uri]);
295 1
    }
296
}
297