Failed Conditions
Push — master ( 5e6761...398dce )
by Adrien
04:14 queued 01:57
created

AbstractFile::validateMimeType()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
nc 5
nop 0
dl 0
loc 20
cc 4
rs 9.9332
ccs 0
cts 11
cp 0
crap 20
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
     * @return string
21
     */
22
    abstract protected function getBasePath(): string;
23
24
    /**
25
     * Get list of accepted MIME types
26
     *
27
     * @return string[]
28
     */
29
    abstract protected function getAcceptedMimeTypes(): array;
30
31
    /**
32
     * @var string
33
     *
34
     * @API\Exclude
35
     *
36
     * @ORM\Column(type="string", length=190, options={"default" = ""})
37
     */
38
    private $filename = '';
39
40
    /**
41
     * @var string
42
     * @ORM\Column(type="string", length=255, options={"default" = ""})
43
     */
44
    private $mime = '';
45
46
    /**
47
     * Set the file
48
     *
49
     * @param UploadedFileInterface $file
50
     */
51
    public function setFile(UploadedFileInterface $file): void
52
    {
53
        $this->generateUniqueFilename($file->getClientFilename() ?? '');
54
55
        $path = $this->getPath();
56
        if (file_exists($path)) {
57
            throw new Exception('A file already exist with the same name: ' . $this->getFilename());
58
        }
59
        $file->moveTo($path);
60
61
        $this->validateMimeType();
62
    }
63
64
    /**
65
     * Set filename (without path)
66
     *
67
     * @API\Exclude
68
     *
69
     * @param string $filename
70
     */
71 2
    public function setFilename(string $filename): void
72
    {
73 2
        $this->filename = $filename;
74 2
    }
75
76
    /**
77
     * Get filename (without path)
78
     *
79
     * @API\Exclude
80
     *
81
     * @return string
82
     */
83 2
    public function getFilename(): string
84
    {
85 2
        return $this->filename;
86
    }
87
88
    /**
89
     * Get mime
90
     *
91
     * @return string
92
     */
93
    public function getMime(): string
94
    {
95
        return $this->mime;
96
    }
97
98
    /**
99
     * Get absolute path to file on disk
100
     *
101
     * @API\Exclude
102
     *
103
     * @return string
104
     */
105 2
    public function getPath(): string
106
    {
107 2
        return realpath('.') . '/' . $this->getBasePath() . $this->getFilename();
108
    }
109
110
    /**
111
     * Automatically called by Doctrine when the object is deleted
112
     * Is called after database update because we can have issues on remove operation (like integrity test)
113
     * and it's preferable to keep a related file on drive before removing it definitely.
114
     *
115
     * @ORM\PostRemove
116
     */
117
    public function deleteFile(): void
118
    {
119
        $path = $this->getPath();
120
        if (file_exists($path) && is_file($path) && mb_strpos($this->getFilename(), 'dw4jV3zYSPsqE2CB8BcP8ABD0.') === false) {
121
            unlink($path);
122
        }
123
    }
124
125
    /**
126
     * Generate unique filename while trying to preserve original extension
127
     *
128
     * @param string $originalFilename
129
     */
130
    private function generateUniqueFilename(string $originalFilename): void
131
    {
132
        $extension = pathinfo($originalFilename, PATHINFO_EXTENSION);
133
        $filename = uniqid() . ($extension ? '.' . $extension : '');
134
        $this->setFilename($filename);
135
    }
136
137
    /**
138
     * Delete file and throw exception if MIME type is invalid
139
     */
140
    private function validateMimeType(): void
141
    {
142
        $path = $this->getPath();
143
        $mime = mime_content_type($path);
144
        if ($mime === false) {
145
            throw new Exception('Could not get mimetype for path: ' . $path);
146
        }
147
148
        if ($mime === 'image/svg') {
149
            $mime = 'image/svg+xml';
150
        }
151
152
        // Validate mimetype
153
        if (!in_array($mime, $this->getAcceptedMimeTypes(), true)) {
154
            unlink($path);
155
156
            throw new Exception('Invalid file type of: ' . $mime);
157
        }
158
159
        $this->mime = $mime;
160
    }
161
}
162