Passed
Push — master ( 5584fc...9013bb )
by Kirill
04:20
created

StreamWrapper::stream_seek()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 7
rs 10
cc 1
nc 1
nop 2
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
declare(strict_types=1);
9
10
namespace Spiral\Streams;
11
12
use Psr\Http\Message\StreamInterface;
13
use Spiral\Streams\Exception\WrapperException;
14
15
/**
16
 * Spiral converter of PSR-7 streams to virtual filenames. Static as hell.
17
 */
18
final class StreamWrapper
19
{
20
    /** @var bool */
21
    private static $registered = false;
22
23
    /**
24
     * Uris associated with StreamInterfaces.
25
     *
26
     * @var array
27
     */
28
    private static $uris = [];
29
30
    /** @var array */
31
    private static $modes = [
32
        'r'   => 33060,
33
        'rb'  => 33060,
34
        'r+'  => 33206,
35
        'rb+' => 33206,
36
        'w'   => 33188,
37
        'wb'  => 33188
38
    ];
39
40
    /** @var StreamInterface */
41
    private $stream = null;
42
43
    /** @var int */
44
    private $mode = 0;
45
46
    /**
47
     * Stream context.
48
     *
49
     * @var resource
50
     */
51
    public $context = null;
52
53
    /**
54
     * Check if StreamInterface ended.
55
     *
56
     * @return bool
57
     */
58
    public function stream_eof()
59
    {
60
        return $this->stream->eof();
61
    }
62
63
    /**
64
     * Open pre-mocked StreamInterface by it's unique uri.
65
     *
66
     * @param string  $path
67
     * @param int     $mode
68
     * @param int     $options
69
     * @param string &$opened_path
70
     * @return bool
71
     */
72
    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. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

72
    public function stream_open($path, $mode, /** @scrutinizer ignore-unused */ $options, &$opened_path)

This check looks for 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. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

72
    public function stream_open($path, $mode, $options, /** @scrutinizer ignore-unused */ &$opened_path)

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

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

156
    public function url_stat($path, /** @scrutinizer ignore-unused */ $flags)

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

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