Passed
Push — master ( 9a106c...3096a8 )
by Adrien
10:22
created

AbstractFile::getMime()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Model;
6
7
use Doctrine\ORM\Mapping as ORM;
8
use GraphQL\Doctrine\Annotation as API;
9
use Psr\Http\Message\UploadedFileInterface;
10
11
/**
12
 * Wrapping class for an uploaded file
13
 *
14
 * @ORM\MappedSuperclass
15
 * @ORM\Table(uniqueConstraints={
16
 *     @ORM\UniqueConstraint(name="unique_name", columns={"filename"})
17
 * })
18
 */
19
abstract class AbstractFile extends AbstractModel
20
{
21
    /**
22
     * Get base path where the files are stored in the server
23
     *
24
     * @return string
25
     */
26
    abstract protected function getBasePath(): string;
27
28
    /**
29
     * Get list of accepted MIME types
30
     *
31
     * @return string[]
32
     */
33
    abstract protected function getAcceptedMimeTypes(): array;
34
35
    /**
36
     * @var string
37
     * @ORM\Column(type="string", length=190)
38
     */
39
    private $filename = '';
40
41
    /**
42
     * @var string
43
     * @ORM\Column(type="string", length=255)
44
     */
45
    private $mime = '';
46
47
    /**
48
     * Set the file
49
     *
50
     * @param UploadedFileInterface $file
51
     *
52
     * @throws \Exception
53
     */
54 2
    public function setFile(UploadedFileInterface $file): void
55
    {
56 2
        $this->generateUniqueFilename($file->getClientFilename());
0 ignored issues
show
Bug introduced by
It seems like $file->getClientFilename() can also be of type null; however, parameter $originalFilename of Application\Model\Abstra...enerateUniqueFilename() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

56
        $this->generateUniqueFilename(/** @scrutinizer ignore-type */ $file->getClientFilename());
Loading history...
57
58 2
        $path = $this->getPath();
59 2
        if (file_exists($path)) {
60
            throw new \Exception('A file already exist with the same name: ' . $this->getFilename());
61
        }
62 2
        $file->moveTo($path);
63
64 2
        $this->validateMimeType();
65 2
    }
66
67
    /**
68
     * Set filename (without path)
69
     *
70
     * @API\Exclude
71
     *
72
     * @param string $filename
73
     */
74 6
    public function setFilename(string $filename): void
75
    {
76 6
        $this->filename = $filename;
77 6
    }
78
79
    /**
80
     * Get filename (without path)
81
     *
82
     * @API\Exclude
83
     *
84
     * @return string
85
     */
86 7
    public function getFilename(): string
87
    {
88 7
        return $this->filename;
89
    }
90
91
    /**
92
     * Get mime
93
     *
94
     * @return string
95
     */
96 1
    public function getMime(): string
97
    {
98 1
        return $this->mime;
99
    }
100
101
    /**
102
     * Get absolute path to file on disk
103
     *
104
     * @API\Exclude
105
     *
106
     * @return string
107
     */
108 7
    public function getPath(): string
109
    {
110 7
        return realpath('.') . '/' . $this->getBasePath() . $this->getFilename();
111
    }
112
113
    /**
114
     * Automatically called by Doctrine when the object is deleted
115
     * Is called after database update because we can have issues on remove operation (like integrity test)
116
     * and it's preferable to keep a related file on drive before removing it definitely.
117
     *
118
     * @ORM\PostRemove
119
     */
120 3
    public function deleteFile(): void
121
    {
122 3
        $path = $this->getPath();
123 3
        if (file_exists($path) && is_file($path) && mb_strpos($this->getFilename(), 'dw4jV3zYSPsqE2CB8BcP8ABD0.') === false) {
124 3
            unlink($path);
125
        }
126 3
    }
127
128
    /**
129
     * Generate unique filename while trying to preserve original extension
130
     *
131
     * @param string $originalFilename
132
     */
133 2
    private function generateUniqueFilename(string $originalFilename): void
134
    {
135 2
        $extension = pathinfo($originalFilename, PATHINFO_EXTENSION);
136 2
        $filename = uniqid() . ($extension ? '.' . $extension : '');
137 2
        $this->setFilename($filename);
138 2
    }
139
140
    /**
141
     * Delete file and throw exception if MIME type is invalid
142
     *
143
     * @throws \Exception
144
     */
145 2
    private function validateMimeType(): void
146
    {
147 2
        $path = $this->getPath();
148 2
        $mime = mime_content_type($path);
149
150 2
        if ($mime === 'image/svg') {
151
            $mime = 'image/svg+xml';
152
        }
153
154
        // Validate mimetype
155 2
        if (!in_array($mime, $this->getAcceptedMimeTypes(), true)) {
156
            unlink($path);
157
158
            throw new \Exception('Invalid file type of: ' . $mime);
159
        }
160
161 2
        $this->mime = $mime;
162 2
    }
163
}
164