PhpVfs   B
last analyzed

Complexity

Total Complexity 48

Size/Duplication

Total Lines 379
Duplicated Lines 0 %

Test Coverage

Coverage 74.47%

Importance

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

27 Methods

Rating   Name   Duplication   Size   Complexity  
A fs() 0 7 1
A dir_readdir() 0 3 1
A rmdir() 0 10 1
A dir_opendir() 0 3 1
A mkdir() 0 3 1
A dir_rewinddir() 0 3 1
A register() 0 11 1
A dir_closedir() 0 3 1
A rename() 0 30 4
A unlink() 0 11 3
A stream_eof() 0 7 2
A setCurrentFile() 0 9 1
A stream_read() 0 7 3
A stream_close() 0 3 1
A stream_write() 0 7 3
A stream_set_option() 0 3 1
A stream_cast() 0 5 1
A stream_lock() 0 3 1
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
A stream_flush() 0 5 1

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 = []): void
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
        return false;
0 ignored issues
show
Unused Code introduced by
return false is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161 8
    public function stream_close(): void // phpcs:ignore
162
    {
163 8
        $this->setCurrentFile(null);
164 8
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169 3
    public function stream_eof(): bool // phpcs:ignore
170
    {
171 3
        if (!(($file = $this->getCurrentFile()) instanceof Handler\FileInterface)) {
172
            throw new \Exception('The current file does not implement FileInterface.');
173
        }
174
175 3
        return $file->getPosition() === $file->size();
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181 6
    public function stream_flush(): bool // phpcs:ignore
182
    {
183 6
        \clearstatcache();
184
185 6
        return true;
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191
    public function stream_lock($operation): bool // phpcs:ignore
192
    {
193
        throw new \Exception('Not implemented yet.');
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199 8
    public function stream_open(string $resource, string $mode, int $options, ?string &$openedPath): bool // phpcs:ignore
200
    {
201 8
        $modeSplit = \str_split(\str_replace('b', '', $mode));
202
203 8
        $appendMode = \in_array('a', $modeSplit, true);
204 8
        $readMode = \in_array('r', $modeSplit, true);
205 8
        $writeMode = \in_array('w', $modeSplit, true);
206 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...
207
208 8
        $resourcePath = Path::fromString($resource)
209 8
            ->withScheme(null);
210
211 8
        $resourceExist = $this::fs()->getCwd()->exist($resource);
212 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...
213
214 8
        if (false === $resourceExist) {
215
            if (true === $readMode) {
216
                if (0 !== ($options & \STREAM_REPORT_ERRORS)) {
217
                    \trigger_error(
218
                        \sprintf(
219
                            '%s: failed to open stream: Unknown resource.',
220
                            $resourcePath
221
                        ),
222
                        \E_USER_WARNING
223
                    );
224
                }
225
226
                return false;
227
            }
228
229
            $this::fs()
230
                ->getCwd()
231
                ->add(File::create($resource)->root());
232
        }
233
234 8
        $file = $this::fs()->getCwd()->get($resource);
235
236 8
        if (!($file instanceof FileInterface)) {
237
            if (0 !== ($options & \STREAM_REPORT_ERRORS)) {
238
                \trigger_error(\sprintf('fopen(%s): failed to open stream: Not a file.', $resource), \E_USER_WARNING);
239
            }
240
241
            return false;
242
        }
243
244 8
        $fileHandler = new Handler\File($file, $mode);
245
246 8
        if (true === $appendMode) {
247
            $fileHandler->seekToEnd();
248 8
        } elseif (true === $writeMode) {
249 5
            $fileHandler->truncate();
250 5
            \clearstatcache();
251
        }
252
253 8
        $this->setCurrentFile($fileHandler);
254
255 8
        $openedPath = (string) $file->getPath();
256
257 8
        return true;
258
    }
259
260
    /**
261
     * {@inheritdoc}
262
     */
263 3
    public function stream_read(int $bytes) // phpcs:ignore
264
    {
265 3
        if ((null === $file = $this->getCurrentFile()) || (false === $file->isReadable())) {
266
            return false;
267
        }
268
269 3
        return $file->read($bytes);
270
    }
271
272
    /**
273
     * {@inheritdoc}
274
     */
275 2
    public function stream_seek(int $offset, int $whence = \SEEK_SET): bool // phpcs:ignore
276
    {
277 2
        if (($file = $this->getCurrentFile()) instanceof Handler\File) {
278 2
            $file->setPosition($offset);
279
        }
280
281 2
        return true;
282
    }
283
284
    /**
285
     * {@inheritdoc}
286
     */
287
    public function stream_set_option(int $option, int $arg1, int $arg2): bool // phpcs:ignore
288
    {
289
        throw new \Exception('Not implemented yet.');
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295 1
    public function stream_stat(): array // phpcs:ignore
296
    {
297 1
        if (null === $file = $this->getCurrentFile()) {
298 1
            return [];
299
        }
300
301 1
        return (array) $file->getFile()->getAttributes();
302
    }
303
304
    /**
305
     * {@inheritdoc}
306
     */
307 1
    public function stream_tell(): int // phpcs:ignore
308
    {
309 1
        if (($file = $this->getCurrentFile()) instanceof Handler\File) {
310 1
            return $file->getPosition();
311
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 309 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...
312
    }
313
314
    /**
315
     * {@inheritdoc}
316
     */
317 1
    public function stream_truncate(int $bytes): bool // phpcs:ignore
318
    {
319 1
        if (($file = $this->getCurrentFile()) instanceof Handler\File) {
320 1
            $file->truncate($bytes);
321 1
            \clearstatcache();
322
        }
323
324 1
        return true;
325
    }
326
327
    /**
328
     * {@inheritdoc}
329
     */
330 5
    public function stream_write(string $data): int // phpcs:ignore
331
    {
332 5
        if ((null === $file = $this->getCurrentFile()) || (false === $file->isWritable())) {
333 1
            return 0;
334
        }
335
336 5
        return $file->write($data);
337
    }
338
339
    /**
340
     * {@inheritdoc}
341
     */
342 1
    public function unlink(string $path): bool
343
    {
344 1
        if (true === $this::fs()->getCwd()->exist($path)) {
345 1
            $file = $this::fs()->getCwd()->get($path);
346
347 1
            if (null !== $parent = $file->getParent()) {
348 1
                $parent->delete($file);
349
            }
350
        }
351
352 1
        return true;
353
    }
354
355
    /**
356
     * {@inheritdoc}
357
     */
358 11
    public static function unregister(): void
359
    {
360 11
        \stream_wrapper_unregister(self::SCHEME);
361 11
    }
362
363
    /**
364
     * {@inheritdoc}
365
     */
366
    public function url_stat(string $path, int $flags): array // phpcs:ignore
367
    {
368
        throw new \Exception('Not implemented yet.');
369
    }
370
371
    /**
372
     * @return null|\drupol\phpvfs\StreamWrapper\Handler\FileInterface
373
     */
374 8
    private function getCurrentFile(): ?Handler\FileInterface
375
    {
376 8
        $options = \stream_context_get_options(
377 8
            \stream_context_get_default()
378
        );
379
380 8
        return $options[static::SCHEME]['currentFile'];
381
    }
382
383
    /**
384
     * @param null|\drupol\phpvfs\StreamWrapper\Handler\FileInterface $file
385
     */
386 8
    private function setCurrentFile(?Handler\FileInterface $file): void
387
    {
388 8
        $options = \stream_context_get_options(
389 8
            \stream_context_get_default()
390
        );
391
392 8
        $options[static::SCHEME]['currentFile'] = $file;
393
394 8
        \stream_context_set_default($options);
395 8
    }
396
}
397