Passed
Pull Request — master (#407)
by Kirill
06:33
created

Storage   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 456
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 132
c 1
b 0
f 0
dl 0
loc 456
rs 8.4
wmc 50

24 Methods

Rating   Name   Duplication   Size   Complexity  
A writeStream() 0 13 2
A getFileSystem() 0 7 2
A write() 0 13 2
A mountFilesystem() 0 7 2
A mimeType() 0 9 2
A copyInSameFilesystem() 0 7 1
A delete() 0 9 2
A getFileSystemsNames() 0 3 1
A setVisibility() 0 9 2
A move() 0 21 4
A readStream() 0 9 2
A copyAcrossFilesystem() 0 16 2
A moveAcrossFilesystems() 0 8 1
A copy() 0 28 4
A fileSize() 0 9 2
A fileExists() 0 9 2
A tempFilename() 0 21 4
A lastModified() 0 9 2
A isFileSystemExists() 0 3 1
A determineFilesystemAndPath() 0 7 1
A read() 0 9 2
A __construct() 0 8 2
A visibility() 0 9 2
A moveInTheSameFilesystem() 0 11 3

How to fix   Complexity   

Complex Class

Complex classes like Storage 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 Storage, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file is part of Spiral Framework package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Storage;
13
14
use League\Flysystem\Filesystem;
15
use League\Flysystem\FilesystemException;
16
use League\Flysystem\FilesystemOperator;
17
use Spiral\Storage\Builder\AdapterFactory;
18
use Spiral\Storage\Config\ConfigInterface;
19
use Spiral\Storage\Exception\FileOperationException;
20
use Spiral\Storage\Exception\MountException;
21
use Spiral\Storage\Exception\StorageException;
22
use Spiral\Storage\Exception\UriException;
23
use Spiral\Storage\Parser\Uri;
24
use Spiral\Storage\Parser\UriParserInterface;
25
26
class Storage implements StorageInterface
27
{
28
    /**
29
     * @var ConfigInterface
30
     */
31
    protected $config;
32
33
    /**
34
     * @var UriParserInterface
35
     */
36
    protected $uriParser;
37
38
    /**
39
     * @var array<string, FilesystemOperator>
40
     */
41
    protected $fileSystems = [];
42
43
    /**
44
     * @param ConfigInterface $config
45
     * @param UriParserInterface $uriParser
46
     * @throws StorageException
47
     */
48
    public function __construct(ConfigInterface $config, UriParserInterface $uriParser)
49
    {
50
        $this->config = $config;
51
        $this->uriParser = $uriParser;
52
53
        foreach ($config->getBucketsKeys() as $fs) {
54
            $this->mountFilesystem($fs, new Filesystem(
55
                AdapterFactory::build($this->config->buildFileSystemInfo($fs))
56
            ));
57
        }
58
    }
59
60
    /**
61
     * @inheritDoc
62
     */
63
    public function getFileSystem(string $key): FilesystemOperator
64
    {
65
        if (!$this->isFileSystemExists($key)) {
66
            throw new MountException(\sprintf('Filesystem `%s` has not been defined', $key));
67
        }
68
69
        return $this->fileSystems[$key];
70
    }
71
72
    /**
73
     * @inheritDoc
74
     */
75
    public function getFileSystemsNames(): array
76
    {
77
        return \array_keys($this->fileSystems);
78
    }
79
80
    /**
81
     * @inheritDoc
82
     */
83
    public function fileExists(string $uri): bool
84
    {
85
        /** @var FilesystemOperator $filesystem */
86
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
87
88
        try {
89
            return $filesystem->fileExists($path);
90
        } catch (FilesystemException $e) {
91
            throw new FileOperationException($e->getMessage(), $e->getCode(), $e);
92
        }
93
    }
94
95
    /**
96
     * @inheritDoc
97
     */
98
    public function read(string $uri): string
99
    {
100
        /** @var FilesystemOperator $filesystem */
101
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
102
103
        try {
104
            return $filesystem->read($path);
105
        } catch (FilesystemException $e) {
106
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
107
        }
108
    }
109
110
    /**
111
     * @inheritDoc
112
     */
113
    public function readStream(string $uri)
114
    {
115
        /** @var FilesystemOperator $filesystem */
116
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
117
118
        try {
119
            return $filesystem->readStream($path);
120
        } catch (FilesystemException $e) {
121
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
122
        }
123
    }
124
125
    /**
126
     * @inheritDoc
127
     */
128
    public function lastModified(string $uri): int
129
    {
130
        /** @var FilesystemOperator $filesystem */
131
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
132
133
        try {
134
            return $filesystem->lastModified($path);
135
        } catch (FilesystemException $e) {
136
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
137
        }
138
    }
139
140
    /**
141
     * @inheritDoc
142
     */
143
    public function fileSize(string $uri): int
144
    {
145
        /** @var FilesystemOperator $filesystem */
146
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
147
148
        try {
149
            return $filesystem->fileSize($path);
150
        } catch (FilesystemException $e) {
151
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
152
        }
153
    }
154
155
    /**
156
     * @inheritDoc
157
     */
158
    public function mimeType(string $uri): string
159
    {
160
        /** @var FilesystemOperator $filesystem */
161
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
162
163
        try {
164
            return $filesystem->mimeType($path);
165
        } catch (FilesystemException $e) {
166
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
167
        }
168
    }
169
170
    /**
171
     * @inheritDoc
172
     */
173
    public function visibility(string $uri): string
174
    {
175
        /** @var FilesystemOperator $filesystem */
176
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
177
178
        try {
179
            return $filesystem->visibility($path);
180
        } catch (FilesystemException $e) {
181
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
182
        }
183
    }
184
185
    /**
186
     * @inheritDoc
187
     */
188
    public function tempFilename(string $uri = null): string
189
    {
190
        try {
191
            $prefix = 'tmpStorageFile_';
192
193
            if ($uri !== null) {
194
                /** @var FilesystemOperator $filesystem */
195
                [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
196
                $content = $filesystem->readStream($path);
197
                $prefix = basename($uri) . '_';
198
            }
199
200
            $filePath = tempnam($this->config->getTmpDir(), $prefix);
201
202
            if (isset($content)) {
203
                file_put_contents($filePath, $content);
204
            }
205
206
            return $filePath;
207
        } catch (\Throwable $e) {
208
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
209
        }
210
    }
211
212
    /**
213
     * @inheritDoc
214
     */
215
    public function write(string $fileSystem, string $filePath, string $content, array $config = []): string
216
    {
217
        $uri = (string)Uri::create($fileSystem, $filePath);
218
219
        /** @var FilesystemOperator $filesystem */
220
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
221
222
        try {
223
            $filesystem->write($path, $content, $config);
224
225
            return $uri;
226
        } catch (FilesystemException $e) {
227
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
228
        }
229
    }
230
231
    /**
232
     * @inheritDoc
233
     */
234
    public function writeStream(string $fileSystem, string $filePath, $content, array $config = []): string
235
    {
236
        $uri = (string)Uri::create($fileSystem, $filePath);
237
238
        /** @var FilesystemOperator $filesystem */
239
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
240
241
        try {
242
            $filesystem->writeStream($path, $content, $config);
243
244
            return $uri;
245
        } catch (FilesystemException $e) {
246
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
247
        }
248
    }
249
250
    /**
251
     * @inheritDoc
252
     */
253
    public function setVisibility(string $uri, string $visibility): void
254
    {
255
        /** @var FilesystemOperator $filesystem */
256
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
257
258
        try {
259
            $filesystem->setVisibility($path, $visibility);
260
        } catch (FilesystemException $e) {
261
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
262
        }
263
    }
264
265
    /**
266
     * @inheritDoc
267
     */
268
    public function move(
269
        string $sourceUri,
270
        string $destinationFileSystem,
271
        ?string $targetFilePath = null,
272
        array $config = []
273
    ): string {
274
        /** @var FilesystemOperator $sourceFilesystem */
275
        [$sourceFilesystem, $sourcePath] = $this->determineFilesystemAndPath($sourceUri);
276
277
        $destinationFilesystem = $this->getFileSystem($destinationFileSystem);
278
279
        try {
280
            $targetFilePath = $targetFilePath ?: $sourcePath;
281
282
            $sourceFilesystem === $destinationFilesystem
283
                ? $this->moveInTheSameFilesystem($sourceFilesystem, $sourcePath, $targetFilePath, $config)
284
                : $this->moveAcrossFilesystems($sourceUri, $destinationFileSystem, $targetFilePath, $config);
285
286
            return (string)Uri::create($destinationFileSystem, $targetFilePath);
287
        } catch (FilesystemException $e) {
288
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
289
        }
290
    }
291
292
    /**
293
     * @inheritDoc
294
     */
295
    public function copy(
296
        string $sourceUri,
297
        string $destinationFileSystem,
298
        ?string $targetFilePath = null,
299
        array $config = []
300
    ): string {
301
        /** @var FilesystemOperator $sourceFilesystem */
302
        [$sourceFilesystem, $sourcePath] = $this->determineFilesystemAndPath($sourceUri);
303
304
        $destinationFilesystem = $this->getFileSystem($destinationFileSystem);
305
306
        try {
307
            $targetFilePath = $targetFilePath ?: $sourcePath;
308
309
            $sourceFilesystem === $destinationFilesystem
310
                ? $this->copyInSameFilesystem($sourceFilesystem, $sourcePath, $targetFilePath, $config)
311
                : $this->copyAcrossFilesystem(
312
                    $config['visibility'] ?? null,
313
                    $sourceFilesystem,
314
                    $sourcePath,
315
                    $destinationFilesystem,
316
                    $targetFilePath,
317
                    $config
318
                );
319
320
            return (string)Uri::create($destinationFileSystem, $targetFilePath);
321
        } catch (FilesystemException $e) {
322
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
323
        }
324
    }
325
326
    /**
327
     * @inheritDoc
328
     */
329
    public function delete(string $uri): void
330
    {
331
        /** @var FilesystemOperator $filesystem */
332
        [$filesystem, $path] = $this->determineFilesystemAndPath($uri);
333
334
        try {
335
            $filesystem->delete($path);
336
        } catch (FilesystemException $e) {
337
            throw new FileOperationException($e->getMessage(), (int)$e->getCode(), $e);
338
        }
339
    }
340
341
    /**
342
     * Mount new filesystem in collection
343
     * Key should be unique
344
     *
345
     * @param string $key
346
     * @param FilesystemOperator $filesystem
347
     *
348
     * @throws MountException
349
     */
350
    protected function mountFilesystem(string $key, FilesystemOperator $filesystem): void
351
    {
352
        if ($this->isFileSystemExists($key)) {
353
            throw new MountException(\sprintf('Filesystem %s is already mounted', $key));
354
        }
355
356
        $this->fileSystems[$key] = $filesystem;
357
    }
358
359
    /**
360
     * Check if filesystem was mounted
361
     *
362
     * @param string $key
363
     *
364
     * @return bool
365
     */
366
    protected function isFileSystemExists(string $key): bool
367
    {
368
        return \array_key_exists($key, $this->fileSystems);
369
    }
370
371
    /**
372
     * Identify used filesystem key and filepath by provided uri
373
     *
374
     * @param string $uri
375
     *
376
     * @return array { 0: FilesystemOperator, 1: string }
377
     *
378
     * @throws MountException
379
     * @throws UriException
380
     */
381
    protected function determineFilesystemAndPath(string $uri): array
382
    {
383
        $uriStructure = $this->uriParser->parse($uri);
0 ignored issues
show
Bug introduced by
$uri of type string is incompatible with the type Spiral\Storage\Parser\UriLikeType expected by parameter $uri of Spiral\Storage\Parser\UriParserInterface::parse(). ( Ignorable by Annotation )

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

383
        $uriStructure = $this->uriParser->parse(/** @scrutinizer ignore-type */ $uri);
Loading history...
384
385
        return [
386
            $this->getFileSystem($uriStructure->getFileSystem()),
387
            $uriStructure->getPath(),
388
        ];
389
    }
390
391
    /**
392
     * Copy file in one filesystem
393
     *
394
     * @param FilesystemOperator $sourceFilesystem
395
     * @param string $sourcePath
396
     * @param string $destinationPath
397
     * @param array $config
398
     *
399
     * @throws FilesystemException
400
     */
401
    protected function copyInSameFilesystem(
402
        FilesystemOperator $sourceFilesystem,
403
        string $sourcePath,
404
        string $destinationPath,
405
        array $config = []
406
    ): void {
407
        $sourceFilesystem->copy($sourcePath, $destinationPath, $config);
408
    }
409
410
    /**
411
     * Copy file across different filesystems
412
     *
413
     * @param string|null $visibility
414
     * @param FilesystemOperator $sourceFilesystem
415
     * @param string $sourcePath
416
     * @param FilesystemOperator $destinationFilesystem
417
     * @param string $destinationPath
418
     * @param array $config
419
     *
420
     * @throws FilesystemException
421
     */
422
    protected function copyAcrossFilesystem(
423
        ?string $visibility,
424
        FilesystemOperator $sourceFilesystem,
425
        string $sourcePath,
426
        FilesystemOperator $destinationFilesystem,
427
        string $destinationPath,
428
        array $config = []
429
    ): void {
430
        $visibility = $visibility ?? $sourceFilesystem->visibility($sourcePath);
431
        $stream = $sourceFilesystem->readStream($sourcePath);
432
        $destinationFilesystem->writeStream(
433
            $destinationPath,
434
            $stream,
435
            !empty($config)
436
                ? array_merge($config, compact('visibility'))
437
                : compact('visibility')
438
        );
439
    }
440
441
    /**
442
     * Move file in one filesystem
443
     *
444
     * @param FilesystemOperator $sourceFilesystem
445
     * @param string $sourcePath
446
     * @param string $destinationPath
447
     * @param array $config
448
     *
449
     * @throws FilesystemException
450
     */
451
    protected function moveInTheSameFilesystem(
452
        FilesystemOperator $sourceFilesystem,
453
        string $sourcePath,
454
        string $destinationPath,
455
        array $config = []
456
    ): void {
457
        if ($sourcePath === $destinationPath && empty($config)) {
458
            return;
459
        }
460
461
        $sourceFilesystem->move($sourcePath, $destinationPath, $config);
462
    }
463
464
    /**
465
     * Move file across different filesystems
466
     *
467
     * @param string $sourceUri
468
     * @param string $destinationFileSystem
469
     * @param string|null $targetFilePath
470
     * @param array $config
471
     *
472
     * @throws StorageException
473
     */
474
    protected function moveAcrossFilesystems(
475
        string $sourceUri,
476
        string $destinationFileSystem,
477
        ?string $targetFilePath = null,
478
        array $config = []
479
    ): void {
480
        $this->copy($sourceUri, $destinationFileSystem, $targetFilePath, $config);
481
        $this->delete($sourceUri);
482
    }
483
}
484