Completed
Push — master ( 14b6ee...630d51 )
by Pol
02:16
created

PhpVfs   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 267
Duplicated Lines 0 %

Test Coverage

Coverage 83.84%

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 85
c 6
b 0
f 0
dl 0
loc 267
ccs 83
cts 99
cp 0.8384
rs 10
wmc 30

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getCurrentFile() 0 7 1
A stream_close() 0 7 2
A stream_eof() 0 3 1
A stream_read() 0 7 2
A setCurrentFile() 0 9 1
B stream_open() 0 43 7
A stream_write() 0 7 2
A stream_stat() 0 7 2
A stripScheme() 0 3 1
A rename() 0 38 5
A register() 0 11 1
A unlink() 0 9 3
A unregister() 0 3 1
A fs() 0 7 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace drupol\phpvfs;
6
7
use drupol\phpvfs\Command\Cd;
8
use drupol\phpvfs\Command\Exist;
9
use drupol\phpvfs\Command\Get;
10
use drupol\phpvfs\Filesystem\Filesystem;
11
use drupol\phpvfs\Filesystem\FilesystemInterface;
12
use drupol\phpvfs\Node\File;
13
use drupol\phpvfs\Node\FileInterface;
14
use drupol\phpvfs\Utils\Path;
15
16
class PhpVfs
17
{
18
    public const SCHEME = 'phpvfs';
19
20
    /**
21
     * @var array
22
     */
23
    public $context;
24
25
    /**
26
     * @return \drupol\phpvfs\Filesystem\FilesystemInterface
27
     */
28 6
    public static function fs(): FilesystemInterface
29
    {
30 6
        $options = \stream_context_get_options(
31 6
            \stream_context_get_default()
32
        );
33
34 6
        return $options[static::SCHEME]['filesystem'];
35
    }
36
37
    /**
38
     * @param \drupol\phpvfs\Filesystem\Filesystem $filesystem
39
     * @param array $options
40
     */
41 6
    public static function register(Filesystem $filesystem, array $options = [])
42
    {
43
        $options = [
44 6
            static::SCHEME => [
45 6
                'filesystem' => $filesystem,
46
                'currentFile' => null,
47 6
            ] + $options,
48
        ];
49
50 6
        \stream_context_set_default($options);
51 6
        \stream_wrapper_register(self::SCHEME, __CLASS__);
52 6
    }
53
54
    /**
55
     * @param string $from
56
     * @param string $to
57
     *
58
     * @throws \Exception
59
     *
60
     * @return bool
61
     */
62 1
    public function rename(string $from, string $to): bool
63
    {
64 1
        $from = $this->stripScheme($from);
65 1
        $to = $this->stripScheme($to);
66
67 1
        if (!Exist::exec($this::fs(), $from)) {
68 1
            throw new \Exception('Source resource does not exist.');
69
        }
70
71 1
        $from = Get::exec($this::fs(), $from);
72
73 1
        if (Exist::exec($this::fs(), $to)) {
74 1
            throw new \Exception('Destination already exist.');
75
        }
76
77 1
        $toPath = Path::fromString($to);
78
79 1
        $this::fs()
80 1
            ->getCwd()
81 1
            ->mkdir($toPath->dirname());
82
83 1
        if (null !== $parent = $from->getParent()) {
84 1
            $parent->delete($from);
85
        }
86
87 1
        Cd::exec($this::fs(), $toPath->dirname());
88
89 1
        $from->setAttribute('id', $toPath->basename());
90
91 1
        if ($from instanceof FileInterface) {
92 1
            $from->setPosition(0);
93
        }
94
95 1
        $this::fs()
96 1
            ->getCwd()
97 1
            ->add($from);
98
99 1
        return true;
100
    }
101
102
    /**
103
     * @see http://php.net/streamwrapper.stream-close
104
     */
105 6
    public function stream_close() // phpcs:ignore
106
    {
107 6
        if (null !== $file = $this->getCurrentFile()) {
108 6
            $file->setPosition(0);
109
        }
110
111 6
        $this->setCurrentFile(null);
112 6
    }
113
114
    /**
115
     * @return bool
116
     *
117
     * @see http://php.net/streamwrapper.stream-eof
118
     */
119 1
    public function stream_eof(): bool // phpcs:ignore
120
    {
121 1
        return true;
122
    }
123
124
    /**
125
     * @param string $resource
126
     * @param mixed $mode
127
     * @param mixed $options
128
     * @param mixed $openedPath
129
     *
130
     * @throws \Exception
131
     *
132
     * @return bool
133
     */
134 6
    public function stream_open(string $resource, $mode, $options, &$openedPath) // phpcs:ignore
0 ignored issues
show
Unused Code introduced by
The parameter $openedPath 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

134
    public function stream_open(string $resource, $mode, $options, /** @scrutinizer ignore-unused */ &$openedPath) // phpcs:ignore

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...
135
    {
136 6
        $mode = \str_split(\str_replace('b', '', $mode));
137
138 6
        $appendMode = \in_array('a', $mode, true);
0 ignored issues
show
Unused Code introduced by
The assignment to $appendMode is dead and can be removed.
Loading history...
139 6
        $readMode = \in_array('r', $mode, true);
140 6
        $writeMode = \in_array('w', $mode, true);
0 ignored issues
show
Unused Code introduced by
The assignment to $writeMode is dead and can be removed.
Loading history...
141 6
        $extended = \in_array('+', $mode, true);
0 ignored issues
show
Unused Code introduced by
The assignment to $extended is dead and can be removed.
Loading history...
142
143 6
        $resource = $this->stripScheme($resource);
144
145 6
        $resourcePath = Path::fromString($resource);
146
147 6
        if (!Exist::exec($this::fs(), $resource)) {
148
            if ($readMode || !Exist::exec($this::fs(), $resourcePath->dirname())) {
149
                if ($options & STREAM_REPORT_ERRORS) {
150
                    \trigger_error(\sprintf('%s: failed to open stream.', $resourcePath), E_USER_WARNING);
151
                }
152
153
                return false;
154
            }
155
156
            $file = File::create($resource);
157
158
            $this->setCurrentFile($file);
159
            $this::fs()
160
                ->getCwd()
161
                ->add($file->root());
162
        }
163
164 6
        if (null === $file = $this::fs()->get($resource)) {
165
            return false;
166
        }
167
168 6
        if (!($file instanceof FileInterface)) {
169
            return false;
170
        }
171
172 6
        $file->setPosition(0);
173
174 6
        $this->setCurrentFile($file);
175
176 6
        return true;
177
    }
178
179
    /**
180
     * @see http://php.net/streamwrapper.stream-read
181
     *
182
     * @param int $bytes
183
     *
184
     * @return mixed
185
     */
186
    public function stream_read(int $bytes) // phpcs:ignore
187
    {
188
        if (null !== $file = $this->getCurrentFile()) {
189
            return $file->read($bytes);
190
        }
191
192
        return false;
193
    }
194
195
    /**
196
     * @return array
197
     */
198 1
    public function stream_stat(): array // phpcs:ignore
199
    {
200 1
        if (null === $file = $this->getCurrentFile()) {
201 1
            return [];
202
        }
203
204 1
        return (array) $file->getAttributes();
205
    }
206
207
    /**
208
     * @param string $data
209
     *
210
     * @return int
211
     */
212 6
    public function stream_write(string $data) // phpcs:ignore
213
    {
214 6
        if (null !== $file = $this->getCurrentFile()) {
215 6
            return $file->write($data);
216
        }
217
218
        return 0;
219
    }
220
221
    /**
222
     * @param string $path
223
     *
224
     * @throws \Exception
225
     */
226 1
    public function unlink(string $path)
227
    {
228 1
        $path = $this->stripScheme($path);
229
230 1
        if (true === Exist::exec($this::fs(), $path)) {
231 1
            $file = Get::exec($this::fs(), $path);
232
233 1
            if (null !== $parent = $file->getParent()) {
234 1
                $parent->delete($file);
235
            }
236
        }
237 1
    }
238
239
    /**
240
     * @todo
241
     */
242 6
    public static function unregister()
243
    {
244 6
        \stream_wrapper_unregister(self::SCHEME);
245 6
    }
246
247
    /**
248
     * @return null|FileInterface
249
     */
250 6
    protected function getCurrentFile(): ?FileInterface
251
    {
252 6
        $options = \stream_context_get_options(
253 6
            \stream_context_get_default()
254
        );
255
256 6
        return $options[static::SCHEME]['currentFile'];
257
    }
258
259
    /**
260
     * @param null|FileInterface $file
261
     */
262 6
    protected function setCurrentFile(?FileInterface $file)
263
    {
264 6
        $options = \stream_context_get_options(
265 6
            \stream_context_get_default()
266
        );
267
268 6
        $options[static::SCHEME]['currentFile'] = $file;
269
270 6
        \stream_context_set_default($options);
271 6
    }
272
273
    /**
274
     * Returns path stripped of url scheme (http://, ftp://, test:// etc.).
275
     *
276
     * @param string $path
277
     *
278
     * @return string
279
     */
280 6
    protected function stripScheme(string $path): string
281
    {
282 6
        return '/' . \ltrim(\substr($path, 9), '/');
283
    }
284
}
285