Passed
Push — master ( 0f5bf2...4c34c0 )
by Pol
14:44
created

PhpVfs::stream_lock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
    public static function fs(): FilesystemInterface
66
    {
67
        $options = \stream_context_get_options(
68
            \stream_context_get_default()
69
        );
70
71
        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
    public static function register(Filesystem $filesystem, array $options = [])
86
    {
87
        $options = [
88
            static::SCHEME => [
89
                'filesystem' => $filesystem,
90
                'currentFile' => null,
91
            ] + $options,
92
        ];
93
94
        \stream_context_set_default($options);
95
        \stream_wrapper_register(self::SCHEME, __CLASS__);
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function rename(string $from, string $to): bool
102
    {
103
        if (!$this::fs()->getCwd()->exist($from)) {
104
            throw new \Exception('Source resource does not exist.');
105
        }
106
107
        $from = $this::fs()->getCwd()->get($from);
108
109
        if ($this::fs()->getCwd()->exist($to)) {
110
            throw new \Exception('Destination already exist.');
111
        }
112
113
        $toPath = Path::fromString($to);
114
115
        $this::fs()
116
            ->getCwd()
117
            ->mkdir($toPath->dirname());
118
119
        if (null !== $parent = $from->getParent()) {
120
            $parent->delete($from);
121
        }
122
123
        $directory = $this::fs()->getCwd()->cd($toPath->dirname());
124
125
        $from->setAttribute('id', $toPath->basename());
126
127
        $directory
128
            ->add($from);
129
130
        return true;
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function rmdir(string $path, int $options): bool
137
    {
138
        $cwd = $this::fs()
139
            ->getCwd()
140
            ->rmdir($path);
141
142
        $this::fs()
143
            ->setCwd($cwd);
144
145
        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
    public function stream_close(): void // phpcs:ignore
160
    {
161
        $this->setCurrentFile(null);
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167
    public function stream_eof(): bool // phpcs:ignore
168
    {
169
        if (!(($file = $this->getCurrentFile()) instanceof Handler\FileInterface)) {
170
            throw new \Exception('The current file does not implement FileInterface.');
171
        }
172
173
        return $file->getPosition() === $file->size();
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    public function stream_flush(): bool // phpcs:ignore
180
    {
181
        \clearstatcache();
182
183
        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
    public function stream_open(string $resource, string $mode, int $options, ?string &$openedPath): bool // phpcs:ignore
198
    {
199
        $modeSplit = \str_split(\str_replace('b', '', $mode));
200
201
        $appendMode = \in_array('a', $modeSplit, true);
202
        $readMode = \in_array('r', $modeSplit, true);
203
        $writeMode = \in_array('w', $modeSplit, true);
204
        $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
        $resourcePath = Path::fromString($resource);
207
208
        $resourceExist = $this::fs()->getCwd()->exist($resource);
209
        $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...
210
211
        if (false === $resourceExist) {
212
            if (true === $readMode) {
213
                if ($options & STREAM_REPORT_ERRORS) {
214
                    \trigger_error(
215
                        \sprintf(
216
                            '%s: failed to open stream: Unknown resource.',
217
                            $resourcePath
218
                        ),
219
                        E_USER_WARNING
220
                    );
221
                }
222
223
                return false;
224
            }
225
226
            $this::fs()
227
                ->getCwd()
228
                ->add(File::create($resource)->root());
229
        }
230
231
        $file = $this::fs()->getCwd()->get($resource);
232
233
        if (!($file instanceof FileInterface)) {
234
            if ($options & STREAM_REPORT_ERRORS) {
235
                \trigger_error(\sprintf('fopen(%s): failed to open stream: Not a file.', $resource), E_USER_WARNING);
236
            }
237
238
            return false;
239
        }
240
241
        $fileHandler = new Handler\File($file, $mode);
242
243
        if (true === $appendMode) {
244
            $fileHandler->seekToEnd();
245
        } elseif (true === $writeMode) {
246
            $fileHandler->truncate();
247
            \clearstatcache();
248
        }
249
250
        $this->setCurrentFile($fileHandler);
251
252
        $openedPath = $file->getPath()->__toString();
253
254
        return true;
255
    }
256
257
    /**
258
     * {@inheritdoc}
259
     */
260
    public function stream_read(int $bytes) // phpcs:ignore
261
    {
262
        if ((null === $file = $this->getCurrentFile()) || (false === $file->isReadable())) {
263
            return false;
264
        }
265
266
        return $file->read($bytes);
267
    }
268
269
    /**
270
     * {@inheritdoc}
271
     */
272
    public function stream_seek(int $offset, int $whence = SEEK_SET): bool // phpcs:ignore
273
    {
274
        if (($file = $this->getCurrentFile()) instanceof Handler\File) {
275
            $file->setPosition($offset);
276
        }
277
278
        return true;
279
    }
280
281
    /**
282
     * {@inheritdoc}
283
     */
284
    public function stream_set_option(int $option, int $arg1, int $arg2): bool // phpcs:ignore
285
    {
286
        throw new \Exception('Not implemented yet.');
287
    }
288
289
    /**
290
     * {@inheritdoc}
291
     */
292
    public function stream_stat(): array // phpcs:ignore
293
    {
294
        if (null === $file = $this->getCurrentFile()) {
295
            return [];
296
        }
297
298
        return (array) $file->getFile()->getAttributes();
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304
    public function stream_tell(): int // phpcs:ignore
305
    {
306
        if (($file = $this->getCurrentFile()) instanceof Handler\File) {
307
            return $file->getPosition();
308
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 306 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...
309
    }
310
311
    /**
312
     * {@inheritdoc}
313
     */
314
    public function stream_truncate(int $bytes): bool // phpcs:ignore
315
    {
316
        if (($file = $this->getCurrentFile()) instanceof Handler\File) {
317
            $file->truncate($bytes);
318
            \clearstatcache();
319
        }
320
321
        return true;
322
    }
323
324
    /**
325
     * {@inheritdoc}
326
     */
327
    public function stream_write(string $data): int // phpcs:ignore
328
    {
329
        if ((null === $file = $this->getCurrentFile()) || (false === $file->isWritable())) {
330
            return 0;
331
        }
332
333
        return $file->write($data);
334
    }
335
336
    /**
337
     * {@inheritdoc}
338
     */
339
    public function unlink(string $path): bool
340
    {
341
        if (true === $this::fs()->getCwd()->exist($path)) {
342
            $file = $this::fs()->getCwd()->get($path);
343
344
            if (null !== $parent = $file->getParent()) {
345
                $parent->delete($file);
346
            }
347
        }
348
349
        return true;
350
    }
351
352
    /**
353
     * {@inheritdoc}
354
     */
355
    public static function unregister()
356
    {
357
        \stream_wrapper_unregister(self::SCHEME);
358
    }
359
360
    /**
361
     * {@inheritdoc}
362
     */
363
    public function url_stat(string $path, int $flags): array // phpcs:ignore
364
    {
365
        throw new \Exception('Not implemented yet.');
366
    }
367
368
    /**
369
     * @return null|\drupol\phpvfs\Handler\FileInterface
0 ignored issues
show
Bug introduced by
The type drupol\phpvfs\Handler\FileInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
370
     */
371
    private function getCurrentFile(): ?Handler\FileInterface
372
    {
373
        $options = \stream_context_get_options(
374
            \stream_context_get_default()
375
        );
376
377
        return $options[static::SCHEME]['currentFile'];
378
    }
379
380
    /**
381
     * @param null|\drupol\phpvfs\Handler\FileInterface $file
382
     */
383
    private function setCurrentFile(?Handler\FileInterface $file)
384
    {
385
        $options = \stream_context_get_options(
386
            \stream_context_get_default()
387
        );
388
389
        $options[static::SCHEME]['currentFile'] = $file;
390
391
        \stream_context_set_default($options);
392
    }
393
}
394