File   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 610
Duplicated Lines 0 %

Test Coverage

Coverage 84.11%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 140
c 1
b 0
f 0
dl 0
loc 610
ccs 127
cts 151
cp 0.8411
rs 7.44
wmc 52

41 Methods

Rating   Name   Duplication   Size   Complexity  
A filename() 0 3 1
A uuid() 0 3 1
A mimeType() 0 3 1
A extension() 0 3 1
A buildPath() 0 6 1
A path() 0 9 2
A withFilename() 0 9 2
A withResource() 0 8 1
A modelId() 0 3 1
A addToCollection() 0 5 1
A belongsToModel() 0 6 1
A collection() 0 3 1
A assertStreamResource() 0 7 3
A model() 0 3 1
A readableSize() 0 6 1
A filesize() 0 3 1
A withPath() 0 6 1
A create() 0 32 3
A resource() 0 3 1
A withFile() 0 5 1
A withUuid() 0 6 1
A storage() 0 3 1
A withMetadata() 0 10 2
A variant() 0 7 2
A jsonSerialize() 0 3 1
A withVariants() 0 9 2
A variantPaths() 0 10 3
A variants() 0 3 1
A withMetadataKey() 0 6 1
A metadata() 0 3 1
A hasVariant() 0 3 1
A withUrl() 0 6 1
A withVariant() 0 6 1
A url() 0 3 1
A buildUrl() 0 5 1
A toArray() 0 16 1
A withoutMetadataByKey() 0 6 1
A withoutMetadata() 0 6 1
A hasVariants() 0 3 1
A withMetadataByKey() 0 6 1
A withoutMetadataKey() 0 6 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/**
4
 * Copyright (c) Florian Krämer (https://florian-kraemer.net)
5
 * Licensed under The MIT License
6
 * For full copyright and license information, please see the LICENSE.txt
7
 * Redistributions of files must retain the above copyright notice.
8
 *
9
 * @copyright Copyright (c) Florian Krämer (https://florian-kraemer.net)
10
 * @author    Florian Krämer
11
 * @link      https://github.com/Phauthentic
12
 * @license   https://opensource.org/licenses/MIT MIT License
13
 */
14
15
declare(strict_types=1);
16
17
namespace Phauthentic\Infrastructure\Storage;
18
19
use Phauthentic\Infrastructure\Storage\Exception\InvalidStreamResourceException;
20
use Phauthentic\Infrastructure\Storage\PathBuilder\PathBuilderInterface;
21
use Phauthentic\Infrastructure\Storage\Processor\Exception\VariantDoesNotExistException;
22
use Phauthentic\Infrastructure\Storage\UrlBuilder\UrlBuilderInterface;
23
use RuntimeException;
24
25
/**
26
 * File
27
 */
28
class File implements FileInterface
29
{
30
    /**
31
     * @var int
32
     */
33
    protected int $id;
34
35
    /**
36
     * @var string
37
     */
38
    protected string $uuid;
39
40
    /**
41
     * @var string
42
     */
43
    protected string $filename;
44
45
    /**
46
     * @var int
47
     */
48
    protected int $filesize;
49
50
    /**
51
     * @var string
52
     */
53
    protected string $mimeType = '';
54
55
    /**
56
     * @var string|null
57
     */
58
    protected ?string $extension = null;
59
60
    /**
61
     * @var string|null
62
     */
63
    protected ?string $path = null;
64
65
    /**
66
     * @var string|null
67
     */
68
    protected ?string $collection = null;
69
70
    /**
71
     * @var string
72
     */
73
    protected string $storage = 'local';
74
75
    /**
76
     * @var array
77
     */
78
    protected array $metadata = [];
79
80
    /**
81
     * @var string|null
82
     */
83
    protected ?string $model = null;
84
85
    /**
86
     * @var string|null
87
     */
88
    protected ?string $modelId = null;
89
90
    /**
91
     * Source file to be stored in our system
92
     *
93
     * @var mixed
94
     */
95
    protected $sourceFile;
96
97
    /**
98
     * @var resource
99
     */
100
    protected $resource;
101
102
    /**
103
     * @var string
104
     */
105
    protected string $url = '';
106
107
    /**
108
     * @var array
109
     */
110
    protected array $variants = [];
111
112
    /**
113
     * Creates a new instance
114
     *
115
     * @param string $filename Filename
116
     * @param int $filesize Filesize
117
     * @param string $mimeType Mime Type
118
     * @param string $storage Storage config name
119
     * @param string|null $collection Collection name
120
     * @param string|null $model Model name
121
     * @param string|null $modelId Model id
122
     * @param array $variants Variants
123
     * @param array $metadata Meta data
124
     * @param resource|null $resource
125
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
126
     */
127 10
    public static function create(
128
        string $filename,
129
        int $filesize,
130
        string $mimeType,
131
        string $storage,
132
        ?string $collection = null,
133
        ?string $model = null,
134
        ?string $modelId = null,
135
        array $metadata = [],
136
        array $variants = [],
137
        $resource = null
138
    ): FileInterface {
139 10
        $that = new self();
140
141 10
        $that->filename = $filename;
142 10
        $that->filesize = $filesize;
143 10
        $that->mimeType = $mimeType;
144 10
        $that->storage = $storage;
145 10
        $that->model = $model;
146 10
        $that->modelId = $modelId;
147 10
        $that->collection = $collection;
148 10
        $that->variants = $variants;
149 10
        $that->metadata = $metadata;
150
151 10
        $extension = pathinfo($filename, PATHINFO_EXTENSION);
152 10
        $that->extension = empty($extension) ? null : (string)$extension;
153
154 10
        if ($resource !== null) {
155
            $that = $that->withResource($resource);
156
        }
157
158 10
        return $that;
159
    }
160
161
    /**
162
     * Storage name
163
     *
164
     * @return string
165
     */
166 1
    public function storage(): string
167
    {
168 1
        return $this->storage;
169
    }
170
171
    /**
172
     * UUID of the file
173
     *
174
     * @param string $uuid UUID string
175
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
176
     */
177 8
    public function withUuid(string $uuid): FileInterface
178
    {
179 8
        $that = clone $this;
180 8
        $that->uuid = $uuid;
181
182 8
        return $that;
183
    }
184
185
    /**
186
     * Stream resource that should be stored
187
     *
188
     * @return resource|null
189
     */
190 2
    public function resource()
191
    {
192 2
        return $this->resource;
193
    }
194
195
    /**
196
     * Same as withResource() but takes a file path
197
     *
198
     * @param string $file File
199
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
200
     */
201
    public function withFile(string $file): FileInterface
202
    {
203
        $resource = fopen($file, 'rb');
204
205
        return $this->withResource($resource);
206
    }
207
208
    /**
209
     * @param mixed $resource
210
     * @return void
211
     */
212 9
    protected function assertStreamResource($resource): void
213
    {
214
        if (
215 9
            !is_resource($resource)
216 9
            || get_resource_type($resource) !== 'stream'
217
        ) {
218
            throw InvalidStreamResourceException::create();
219
        }
220 9
    }
221
222
    /**
223
     * Stream resource of the file to be stored
224
     *
225
     * @param resource $resource Stream Resource
226
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
227
     */
228 9
    public function withResource($resource): FileInterface
229
    {
230 9
        $this->assertStreamResource($resource);
231
232 9
        $that = clone $this;
233 9
        $that->resource = $resource;
234
235 9
        return $that;
236
    }
237
238
    /**
239
     * Assign a model and model id to a file
240
     *
241
     * @param string $model Model
242
     * @param string|int $modelId Model ID, UUID string or integer
243
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
244
     */
245 6
    public function belongsToModel(string $model, $modelId): FileInterface
246
    {
247 6
        $this->model = $model;
248 6
        $this->modelId = (string)$modelId;
249
250 6
        return $this;
251
    }
252
253
    /**
254
     * Adds the file to a collection
255
     *
256
     * @param string $collection Collection
257
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
258
     */
259 3
    public function addToCollection(string $collection): FileInterface
260
    {
261 3
        $this->collection = $collection;
262
263 3
        return $this;
264
    }
265
266
    /**
267
     * Sets the path, immutable
268
     *
269
     * @param string $path Path to the file
270
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
271
     */
272 1
    public function withPath(string $path): FileInterface
273
    {
274 1
        $that = clone $this;
275 1
        $that->path = $path;
276
277 1
        return $that;
278
    }
279
280
    /**
281
     * Filename
282
     *
283
     * @param string $filename Filename
284
     * @return self
285
     */
286 2
    public function withFilename(string $filename): self
287
    {
288 2
        $that = clone $this;
289 2
        $that->filename = $filename;
290
291 2
        $extension = pathinfo($filename, PATHINFO_EXTENSION);
292 2
        $that->extension = empty($extension) ? null : (string)$extension;
293
294 2
        return $that;
295
    }
296
297
    /**
298
     * The collections name this file belongs into
299
     *
300
     * @return string|null
301
     */
302 7
    public function collection(): ?string
303
    {
304 7
        return $this->collection;
305
    }
306
307
    /**
308
     * Model name
309
     *
310
     * @return string|null
311
     */
312 8
    public function model(): ?string
313
    {
314 8
        return $this->model;
315
    }
316
317
    /**
318
     * Model ID
319
     *
320
     * @return string|null
321
     */
322 7
    public function modelId(): ?string
323
    {
324 7
        return $this->modelId;
325
    }
326
327
    /**
328
     * Size of the file in bytes
329
     *
330
     * @return int
331
     */
332 1
    public function filesize(): int
333
    {
334 1
        return $this->filesize;
335
    }
336
337
    /**
338
     * Returns a human readable file size
339
     *
340
     * @return string
341
     */
342 1
    public function readableSize(): string
343
    {
344 1
        $i = floor(log($this->filesize, 1024));
345 1
        $round = (string)round($this->filesize / (1024 ** $i), [0, 0, 2, 2, 3][$i]);
346
347 1
        return $round . ['B','kB','MB','GB','TB'][$i];
348
    }
349
350
    /**
351
     * @return string|null
352
     */
353 7
    public function extension(): ?string
354
    {
355 7
        return $this->extension;
356
    }
357
358
    /**
359
     * @return string
360
     */
361 7
    public function mimeType(): string
362
    {
363 7
        return $this->mimeType;
364
    }
365
366
    /**
367
     * @return string
368
     */
369 7
    public function filename(): string
370
    {
371 7
        return $this->filename;
372
    }
373
374
    /**
375
     * @return string
376
     */
377 7
    public function uuid(): string
378
    {
379 7
        return $this->uuid;
380
    }
381
382
    /**
383
     * @return string
384
     */
385 4
    public function path(): string
386
    {
387 4
        if ($this->path === null) {
388 1
            throw new RuntimeException(
389 1
                'Path has not been set'
390
            );
391
        }
392
393 3
        return $this->path;
394
    }
395
396
    /**
397
     * Builds the path for this file
398
     *
399
     * @param \Phauthentic\Infrastructure\Storage\PathBuilder\PathBuilderInterface $pathBuilder Path Builder
400
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
401
     */
402 3
    public function buildPath(PathBuilderInterface $pathBuilder): FileInterface
403
    {
404 3
        $that = clone $this;
405 3
        $that->path = $pathBuilder->path($this);
406
407 3
        return $that;
408
    }
409
410
    /**
411
     * @param array $metadata Meta data
412
     * @param bool $overwrite Overwrite whole metadata instead of assoc merging.
413
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
414 1
     */
415
    public function withMetadata(array $metadata, bool $overwrite = false): FileInterface
416 1
    {
417 1
        $that = clone $this;
418
        if ($overwrite) {
419 1
            $that->metadata = $metadata;
420
        } else {
421
            $that->metadata = $metadata + $that->metadata;
422
        }
423
424
        return $that;
425
    }
426
427 2
    /**
428
     * @inheritDoc
429 2
     */
430 2
    public function withMetadataByKey(string $name, $data): FileInterface
431
    {
432 2
        $that = clone $this;
433
        $that->metadata[$name] = $data;
434
435
        return $that;
436
    }
437
438
    /**
439 1
     * @inheritDoc
440
     */
441 1
    public function withoutMetadataByKey(string $name): FileInterface
442 1
    {
443
        $that = clone $this;
444 1
        unset($that->metadata[$name]);
445
446
        return $that;
447
    }
448
449
    /**
450 1
     * @inheritDoc
451
     */
452 1
    public function withoutMetadata(): self
453 1
    {
454
        $that = clone $this;
455 1
        $that->metadata = [];
456
457
        return $that;
458
    }
459
460
    /**
461 1
     * @inheritDoc
462
     */
463 1
    public function metadata(): array
464
    {
465
        return $this->metadata;
466
    }
467
468
    /**
469 1
     * @return bool
470
     */
471 1
    public function hasVariants(): bool
472
    {
473
        return !empty($this->variants);
474
    }
475
476
    /**
477
     * @param string $name Name
478 1
     * @return bool
479
     */
480 1
    public function hasVariant(string $name): bool
481
    {
482
        return isset($this->variants[$name]);
483
    }
484
485
    /**
486 3
     * @return array
487
     */
488 3
    public function variants(): array
489
    {
490
        return $this->variants;
491
    }
492
493
    /**
494
     * Returns a variant by name
495
     *
496
     * @param string $name Name
497
     * @return array
498
     */
499
    public function variant(string $name): array
500
    {
501
        if (!isset($this->variants[$name])) {
502
            throw VariantDoesNotExistException::withName($name);
503
        }
504
505
        return $this->variants[$name];
506
    }
507
508
    /**
509
     * Adds a variant
510
     *
511
     * @param string $name Name
512
     * @param array $data Data
513 1
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
514
     */
515 1
    public function withVariant(string $name, array $data): FileInterface
516 1
    {
517
        $that = clone $this;
518 1
        $that->variants[$name] = $data;
519
520
        return $that;
521
    }
522
523
    /**
524
     * Gets the paths for all variants
525
     *
526
     * @return array
527
     */
528
    public function variantPaths(): array
529
    {
530
        $paths = [];
531
        foreach ($this->variants as $variant => $data) {
532
            if (isset($data['path'])) {
533
                $paths[$variant] = $data['path'];
534
            }
535
        }
536
537
        return $paths;
538
    }
539
540
    /**
541
     * @param string $key
542
     * @param mixed $data;
543
     */
544
    public function withMetadataKey(string $key, $data): FileInterface
545 2
    {
546
        $that = clone $this;
547 2
        $that->metadata[$key] = $data;
548 2
549 2
        return $that;
550
    }
551
552
    /**
553 2
     * @inheritDoc
554
     */
555
    public function withoutMetadataKey(string $key): FileInterface
556
    {
557
        $that = clone $this;
558
        unset($that->metadata[$key]);
559
560
        return $that;
561
    }
562
563
    /**
564
     * Sets many variants at once
565
     *
566
     * @param array $variants Variants
567
     * @param bool $merge Merge Variants, default is true
568
     * @return \Phauthentic\Infrastructure\Storage\FileInterface
569
     */
570
    public function withVariants(array $variants, bool $merge = true): FileInterface
571
    {
572
        $that = clone $this;
573
        $that->variants = array_merge_recursive(
574
            $merge ? $that->variants : [],
575
            $variants
576
        );
577
578
        return $that;
579
    }
580
581
    /**
582
     * @inheritDoc
583
     */
584
    public function buildUrl(UrlBuilderInterface $urlBuilder): FileInterface
585
    {
586
        $this->url = $urlBuilder->url($this);
587
588 1
        return $this;
589
    }
590
591 1
    /**
592 1
     * @inheritDoc
593 1
     */
594 1
    public function url(): string
595 1
    {
596 1
        return $this->url;
597 1
    }
598 1
599 1
    /**
600 1
     * @inheritDoc
601 1
     */
602 1
    public function withUrl(string $url): FileInterface
603 1
    {
604
        $that = clone $this;
605
        $that->url = $url;
606
607
        return $that;
608
    }
609
610 1
    /**
611
     * @return array
612 1
     */
613
    public function toArray(): array
614
    {
615
        return [
616
            'uuid' => $this->uuid,
617
            'filename' => $this->filename,
618
            'filesize' => $this->filesize,
619
            'mimeType' => $this->mimeType,
620
            'extension' => $this->extension,
621
            'path' => $this->path,
622
            'model' => $this->model,
623
            'modelId' => $this->modelId,
624
            'collection' => $this->collection,
625
            'readableSize' => $this->readableSize(),
626
            'variants' => $this->variants,
627
            'metadata' => $this->metadata,
628
            'url' => $this->url
629
        ];
630
    }
631
632
    /**
633
     * @inheritDoc
634
     */
635
    public function jsonSerialize(): array
636
    {
637
        return $this->toArray();
638
    }
639
}
640