Failed Conditions
Push — master ( f78e48...913fd9 )
by Adrien
15:58
created

AbstractFile::getMime()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
cc 1
rs 10
ccs 0
cts 2
cp 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ecodev\Felix\Model\Traits;
6
7
use Doctrine\ORM\Mapping as ORM;
8
use Exception;
9
use GraphQL\Doctrine\Annotation as API;
10
use Psr\Http\Message\UploadedFileInterface;
11
12
/**
13
 * Wrapping class for an uploaded file.
14
 */
15
trait AbstractFile
16
{
17
    /**
18
     * Get base path where the files are stored in the server.
19
     */
20
    abstract protected function getBasePath(): string;
21
22
    /**
23
     * Get list of accepted MIME types.
24
     *
25
     * @return string[]
26
     */
27
    abstract protected function getAcceptedMimeTypes(): array;
28
29
    /**
30
     * @var string
31
     *
32
     * @API\Exclude
33
     *
34
     * @ORM\Column(type="string", length=190, options={"default" = ""})
35
     */
36
    private $filename = '';
37
38
    /**
39
     * @var string
40
     * @ORM\Column(type="string", length=255, options={"default" = ""})
41
     */
42
    private $mime = '';
43
44
    /**
45
     * Set the file.
46
     */
47
    public function setFile(UploadedFileInterface $file): void
48
    {
49
        $this->generateUniqueFilename($file->getClientFilename() ?? '');
50
51
        $path = $this->getPath();
52
        if (file_exists($path)) {
53
            throw new Exception('A file already exist with the same name: ' . $this->getFilename());
54
        }
55
        $file->moveTo($path);
56
57
        $this->validateMimeType();
58
    }
59
60
    /**
61
     * Set filename (without path).
62
     *
63
     * @API\Exclude
64
     */
65 2
    public function setFilename(string $filename): void
66
    {
67 2
        $this->filename = $filename;
68
    }
69
70
    /**
71
     * Get filename (without path).
72
     *
73
     * @API\Exclude
74
     */
75 2
    public function getFilename(): string
76
    {
77 2
        return $this->filename;
78
    }
79
80
    /**
81
     * Get mime.
82
     */
83
    public function getMime(): string
84
    {
85
        return $this->mime;
86
    }
87
88
    /**
89
     * Get absolute path to file on disk.
90
     *
91
     * @API\Exclude
92
     */
93 2
    public function getPath(): string
94
    {
95 2
        return realpath('.') . '/' . $this->getBasePath() . $this->getFilename();
96
    }
97
98
    /**
99
     * Automatically called by Doctrine when the object is deleted
100
     * Is called after database update because we can have issues on remove operation (like integrity test)
101
     * and it's preferable to keep a related file on drive before removing it definitely.
102
     *
103
     * @ORM\PostRemove
104
     */
105
    public function deleteFile(): void
106
    {
107
        $path = $this->getPath();
108
        if (file_exists($path) && is_file($path) && !str_contains($this->getFilename(), 'dw4jV3zYSPsqE2CB8BcP8ABD0.')) {
109
            unlink($path);
110
        }
111
    }
112
113
    /**
114
     * Generate unique filename while trying to preserve original extension.
115
     */
116
    private function generateUniqueFilename(string $originalFilename): void
117
    {
118
        $extension = pathinfo($originalFilename, PATHINFO_EXTENSION);
119
        $filename = uniqid() . ($extension ? '.' . $extension : '');
0 ignored issues
show
Bug introduced by
Are you sure $extension of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

119
        $filename = uniqid() . ($extension ? '.' . /** @scrutinizer ignore-type */ $extension : '');
Loading history...
120
        $this->setFilename($filename);
121
    }
122
123
    /**
124
     * Delete file and throw exception if MIME type is invalid.
125
     */
126
    private function validateMimeType(): void
127
    {
128
        $path = $this->getPath();
129
        $mime = mime_content_type($path);
130
        if ($mime === false) {
131
            throw new Exception('Could not get mimetype for path: ' . $path);
132
        }
133
134
        if ($mime === 'image/svg') {
135
            $mime = 'image/svg+xml';
136
        }
137
138
        // Validate mimetype
139
        if (!in_array($mime, $this->getAcceptedMimeTypes(), true)) {
140
            unlink($path);
141
142
            throw new Exception('Invalid file type of: ' . $mime);
143
        }
144
145
        $this->mime = $mime;
146
    }
147
}
148