Passed
Push — master ( ecb839...55faee )
by Sam
07:41
created

AbstractFile::setFilename()   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
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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
     *
38
     * @API\Exclude
39
     *
40
     * @ORM\Column(type="string", length=190, options={"default" = ""})
41
     */
42
    private $filename = '';
43
44
    /**
45
     * @var string
46
     * @ORM\Column(type="string", length=255, options={"default" = ""})
47
     */
48
    private $mime = '';
49
50
    /**
51
     * Set the file
52
     *
53
     * @param UploadedFileInterface $file
54
     *
55
     * @throws \Exception
56
     */
57 2
    public function setFile(UploadedFileInterface $file): void
58
    {
59 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

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