Passed
Push — master ( de351b...604f37 )
by Pol
02:29
created

PhpVfs   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 377
Duplicated Lines 0 %

Test Coverage

Coverage 74.47%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 116
c 2
b 0
f 1
dl 0
loc 377
ccs 105
cts 141
cp 0.7447
rs 8.5599
wmc 48

27 Methods

Rating   Name   Duplication   Size   Complexity  
A stream_eof() 0 7 2
A fs() 0 7 1
A stream_close() 0 3 1
A dir_readdir() 0 3 1
A rmdir() 0 10 1
A dir_opendir() 0 3 1
A mkdir() 0 3 1
A stream_cast() 0 3 1
A stream_lock() 0 3 1
A dir_rewinddir() 0 3 1
A register() 0 11 1
A dir_closedir() 0 3 1
A stream_flush() 0 5 1
A unlink() 0 11 3
A setCurrentFile() 0 9 1
A stream_read() 0 7 3
A stream_write() 0 7 3
A stream_set_option() 0 3 1
A rename() 0 30 4
A stream_truncate() 0 8 2
A url_stat() 0 3 1
A stream_tell() 0 4 2
A stream_stat() 0 7 2
A unregister() 0 3 1
A getCurrentFile() 0 7 1
A stream_seek() 0 7 2
B stream_open() 0 59 8

How to fix   Complexity   

Complex Class

Complex classes like PhpVfs often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PhpVfs, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types = 1);
4
5
namespace drupol\phpvfs\StreamWrapper;
6
7
use drupol\phpvfs\Filesystem\Filesystem;
8
use drupol\phpvfs\Filesystem\FilesystemInterface;
9
use drupol\phpvfs\Node\File;
10
use drupol\phpvfs\Node\FileInterface;
11
use drupol\phpvfs\Utils\Path;
12
13
/**
14
 * Class PhpVfs.
15
 */
16
class PhpVfs implements StreamWrapperInterface
17
{
18
    /**
19
     * The scheme.
20
     */
21
    protected const SCHEME = 'phpvfs';
22
23
    /**
24
     * The stream context.
25
     *
26
     * @var array
27
     */
28
    public $context;
29
30
    /**
31
     * {@inheritdoc}
32
     */
33
    public function dir_closedir(): bool // phpcs:ignore
34
    {
35
        throw new \Exception('Not implemented yet.');
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function dir_opendir(string $path, int $options): bool // phpcs:ignore
42
    {
43
        throw new \Exception('Not implemented yet.');
44
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49
    public function dir_readdir(): string // phpcs:ignore
50
    {
51
        throw new \Exception('Not implemented yet.');
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function dir_rewinddir(): bool // phpcs:ignore
58
    {
59
        throw new \Exception('Not implemented yet.');
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65 10
    public static function fs(): FilesystemInterface
66
    {
67 10
        $options = \stream_context_get_options(
68 10
            \stream_context_get_default()
69
        );
70
71 10
        return $options[static::SCHEME]['filesystem'];
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function mkdir(string $path, int $mode, int $options): bool
78
    {
79
        throw new \Exception('Not implemented yet.');
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 11
    public static function register(Filesystem $filesystem, array $options = [])
86
    {
87
        $options = [
88 11
            static::SCHEME => [
89 11
                'filesystem' => $filesystem,
90
                'currentFile' => null,
91 11
            ] + $options,
92
        ];
93
94 11
        \stream_context_set_default($options);
95 11
        \stream_wrapper_register(self::SCHEME, __CLASS__);
96 11
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101 1
    public function rename(string $from, string $to): bool
102
    {
103 1
        if (!$this::fs()->getCwd()->exist($from)) {
104 1
            throw new \Exception('Source resource does not exist.');
105
        }
106
107 1
        $from = $this::fs()->getCwd()->get($from);
108
109 1
        if ($this::fs()->getCwd()->exist($to)) {
110 1
            throw new \Exception('Destination already exist.');
111
        }
112
113 1
        $toPath = Path::fromString($to);
114
115 1
        $this::fs()
116 1
            ->getCwd()
117 1
            ->mkdir($toPath->dirname());
118
119 1
        if (null !== $parent = $from->getParent()) {
120 1
            $parent->delete($from);
121
        }
122
123 1
        $directory = $this::fs()->getCwd()->cd($toPath->dirname());
124
125 1
        $from->setAttribute('id', $toPath->basename());
126
127
        $directory
128 1
            ->add($from);
129
130 1
        return true;
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136 1
    public function rmdir(string $path, int $options): bool
137
    {
138 1
        $cwd = $this::fs()
139 1
            ->getCwd()
140 1
            ->rmdir($path);
141
142 1
        $this::fs()
143 1
            ->setCwd($cwd);
144
145 1
        return true;
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    public function stream_cast(int $cast_as) // phpcs:ignore
152
    {
153
        throw new \Exception('Not implemented yet.');
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159 8
    public function stream_close(): void // phpcs:ignore
160
    {
161 8
        $this->setCurrentFile(null);
162 8
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167 3
    public function stream_eof(): bool // phpcs:ignore
168
    {
169 3
        if (!(($file = $this->getCurrentFile()) instanceof Handler\FileInterface)) {
170
            throw new \Exception('The current file does not implement FileInterface.');
171
        }
172
173 3
        return $file->getPosition() === $file->size();
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 6
    public function stream_flush(): bool // phpcs:ignore
180
    {
181 6
        \clearstatcache();
182
183 6
        return true;
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    public function stream_lock($operation): bool // phpcs:ignore
190
    {
191
        throw new \Exception('Not implemented yet.');
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197 8
    public function stream_open(string $resource, string $mode, int $options, ?string &$openedPath): bool // phpcs:ignore
198
    {
199 8
        $modeSplit = \str_split(\str_replace('b', '', $mode));
200
201 8
        $appendMode = \in_array('a', $modeSplit, true);
202 8
        $readMode = \in_array('r', $modeSplit, true);
203 8
        $writeMode = \in_array('w', $modeSplit, true);
204 8
        $extended = \in_array('+', $modeSplit, true);
0 ignored issues
show
Unused Code introduced by
The assignment to $extended is dead and can be removed.
Loading history...
205
206 8
        $resourcePath = Path::fromString($resource)
207 8
            ->withScheme(null);
208
209 8
        $resourceExist = $this::fs()->getCwd()->exist($resource);
210 8
        $resourceDirnameExist = $this::fs()->getCwd()->exist($resourcePath->dirname());
0 ignored issues
show
Unused Code introduced by
The assignment to $resourceDirnameExist is dead and can be removed.
Loading history...
211
212 8
        if (false === $resourceExist) {
213
            if (true === $readMode) {
214
                if ($options & STREAM_REPORT_ERRORS) {
215
                    \trigger_error(
216
                        \sprintf(
217
                            '%s: failed to open stream: Unknown resource.',
218
                            $resourcePath
219
                        ),
220
                        E_USER_WARNING
221
                    );
222
                }
223
224
                return false;
225
            }
226
227
            $this::fs()
228
                ->getCwd()
229
                ->add(File::create($resource)->root());
230
        }
231
232 8
        $file = $this::fs()->getCwd()->get($resource);
233
234 8
        if (!($file instanceof FileInterface)) {
235
            if ($options & STREAM_REPORT_ERRORS) {
236
                \trigger_error(\sprintf('fopen(%s): failed to open stream: Not a file.', $resource), E_USER_WARNING);
237
            }
238
239
            return false;
240
        }
241
242 8
        $fileHandler = new Handler\File($file, $mode);
243
244 8
        if (true === $appendMode) {
245
            $fileHandler->seekToEnd();
246 8
        } elseif (true === $writeMode) {
247 5
            $fileHandler->truncate();
248 5
            \clearstatcache();
249
        }
250
251 8
        $this->setCurrentFile($fileHandler);
252
253 8
        $openedPath = (string) $file->getPath();
254
255 8
        return true;
256
    }
257
258
    /**
259
     * {@inheritdoc}
260
     */
261 3
    public function stream_read(int $bytes) // phpcs:ignore
262
    {
263 3
        if ((null === $file = $this->getCurrentFile()) || (false === $file->isReadable())) {
264
            return false;
265
        }
266
267 3
        return $file->read($bytes);
268
    }
269
270
    /**
271
     * {@inheritdoc}
272
     */
273 2
    public function stream_seek(int $offset, int $whence = SEEK_SET): bool // phpcs:ignore
274
    {
275 2
        if (($file = $this->getCurrentFile()) instanceof Handler\File) {
276 2
            $file->setPosition($offset);
277
        }
278
279 2
        return true;
280
    }
281
282
    /**
283
     * {@inheritdoc}
284
     */
285
    public function stream_set_option(int $option, int $arg1, int $arg2): bool // phpcs:ignore
286
    {
287
        throw new \Exception('Not implemented yet.');
288
    }
289
290
    /**
291
     * {@inheritdoc}
292
     */
293 1
    public function stream_stat(): array // phpcs:ignore
294
    {
295 1
        if (null === $file = $this->getCurrentFile()) {
296 1
            return [];
297
        }
298
299 1
        return (array) $file->getFile()->getAttributes();
300
    }
301
302
    /**
303
     * {@inheritdoc}
304
     */
305 1
    public function stream_tell(): int // phpcs:ignore
306
    {
307 1
        if (($file = $this->getCurrentFile()) instanceof Handler\File) {
308 1
            return $file->getPosition();
309
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 307 is false. This is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
310
    }
311
312
    /**
313
     * {@inheritdoc}
314
     */
315 1
    public function stream_truncate(int $bytes): bool // phpcs:ignore
316
    {
317 1
        if (($file = $this->getCurrentFile()) instanceof Handler\File) {
318 1
            $file->truncate($bytes);
319 1
            \clearstatcache();
320
        }
321
322 1
        return true;
323
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328 5
    public function stream_write(string $data): int // phpcs:ignore
329
    {
330 5
        if ((null === $file = $this->getCurrentFile()) || (false === $file->isWritable())) {
331 1
            return 0;
332
        }
333
334 5
        return $file->write($data);
335
    }
336
337
    /**
338
     * {@inheritdoc}
339
     */
340 1
    public function unlink(string $path): bool
341
    {
342 1
        if (true === $this::fs()->getCwd()->exist($path)) {
343 1
            $file = $this::fs()->getCwd()->get($path);
344
345 1
            if (null !== $parent = $file->getParent()) {
346 1
                $parent->delete($file);
347
            }
348
        }
349
350 1
        return true;
351
    }
352
353
    /**
354
     * {@inheritdoc}
355
     */
356 11
    public static function unregister()
357
    {
358 11
        \stream_wrapper_unregister(self::SCHEME);
359 11
    }
360
361
    /**
362
     * {@inheritdoc}
363
     */
364
    public function url_stat(string $path, int $flags): array // phpcs:ignore
365
    {
366
        throw new \Exception('Not implemented yet.');
367
    }
368
369
    /**
370
     * @return null|\drupol\phpvfs\StreamWrapper\Handler\FileInterface
371
     */
372 8
    private function getCurrentFile(): ?Handler\FileInterface
373
    {
374 8
        $options = \stream_context_get_options(
375 8
            \stream_context_get_default()
376
        );
377
378 8
        return $options[static::SCHEME]['currentFile'];
379
    }
380
381
    /**
382
     * @param null|\drupol\phpvfs\StreamWrapper\Handler\FileInterface $file
383
     */
384 8
    private function setCurrentFile(?Handler\FileInterface $file)
385
    {
386 8
        $options = \stream_context_get_options(
387 8
            \stream_context_get_default()
388
        );
389
390 8
        $options[static::SCHEME]['currentFile'] = $file;
391
392 8
        \stream_context_set_default($options);
393 8
    }
394
}
395