Passed
Push — master ( 5b1734...3d81f7 )
by Bruno
05:51
created

File::accept()   C

Complexity

Conditions 11
Paths 34

Size

Total Lines 74
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 53
nc 34
nop 2
dl 0
loc 74
rs 6.8787
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
namespace Formularium\Validator;
4
5
use Formularium\Datatype;
6
use Formularium\Exception\ValidatorException;
7
use Formularium\Model;
8
use Formularium\ValidatorArgs;
9
use Formularium\ValidatorInterface;
10
use Formularium\ValidatorMetadata;
11
12
/**
13
 * File validation
14
 */
15
class File implements ValidatorInterface
16
{
17
    const MAX_SIZE = 'maxSize';
18
19
    /**
20
     * Key for extension. Value can be array or string.
21
     */
22
    const ACCEPT = 'accept';
23
    const ACCEPT_AUDIO = 'audio/*';
24
    const ACCEPT_IMAGE = 'image/*';
25
    const ACCEPT_VIDEO = 'video/*';
26
27
    const DIMENSION_WIDTH = 'DIMENSION_WIDTH';
28
    const DIMENSION_HEIGHT = 'DIMENSION_HEIGHT';
29
    const DIMENSION_MIN_WIDTH = 'DIMENSION_MIN_WIDTH';
30
    const DIMENSION_MAX_WIDTH = 'DIMENSION_MAX_WIDTH';
31
    const DIMENSION_MIN_HEIGHT = 'DIMENSION_MIN_HEIGHT';
32
    const DIMENSION_MAX_HEIGHT = 'DIMENSION_MAX_HEIGHT';
33
    const DIMENSION_RATIO = 'DIMENSION_RATIO';
34
35
    protected static function size(string $value, array $options = []): void
36
    {
37
        $max_size = $options[self::MAX_SIZE] ?? 0;
38
        if ($max_size > 0 && filesize($value) > $max_size) {
39
            throw new ValidatorException(
40
                'File too big. Maximum size: ' . $max_size
41
            );
42
        }
43
    }
44
45
    public static function accept(string $value, array $options = []): void
46
    {
47
        $accept = $options[self::ACCEPT];
48
        if (!is_array($accept)) {
49
            $accept = [$accept];
50
        }
51
52
        /**
53
         * @var array $accept
54
         */
55
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
56
        if ($finfo === false) {
57
            throw new ValidatorException(
58
                'Cannot load fileinfo'
59
            );
60
        }
61
        $mime = finfo_file($finfo, $value);
62
63
        $valid = false;
64
        foreach ($accept as $a) {
65
            switch ($a) {
66
                case self::ACCEPT_AUDIO:
67
                    $validMimes = [
68
                        'audio/aac',
69
                        'audio/mpeg',
70
                        'audio/ogg',
71
                        'audio/wav',
72
                        'audio/webm',
73
                    ];
74
                    if (in_array($mime, $validMimes)) {
75
                        $valid = true;
76
                        break;
77
                    }
78
                break;
79
                case self::ACCEPT_IMAGE:
80
                    $validMimes = [
81
                        'image/jpg',
82
                        'image/jpeg',
83
                        'image/gif',
84
                        'image/png',
85
                        'image/webp'
86
                    ];
87
                    if (in_array($mime, $validMimes)) {
88
                        $valid = true;
89
                        break;
90
                    }
91
                break;
92
                case self::ACCEPT_VIDEO:
93
                    $validMimes = [
94
                        'video/x-flv',
95
                        'video/mp4',
96
                        'video/mpeg',
97
                        'application/x-mpegURL',
98
                        'video/MP2T',
99
                        'video/3gpp',
100
                        'video/ogg',
101
                        'video/quicktime',
102
                        'video/x-msvideo',
103
                        'video/x-ms-wmv',
104
                        'video/webm',
105
                    ];
106
                    if (in_array($mime, $validMimes)) {
107
                        $valid = true;
108
                        break;
109
                    }
110
                break;
111
            }
112
        }
113
114
        // TODO: 'accept' extensions
115
116
        if (!$valid) {
117
            throw new ValidatorException(
118
                'Not an accepted file'
119
            );
120
        }
121
    }
122
123
    public static function validate($value, array $options = [], Datatype $datatype, ?Model $model = null)
124
    {
125
        if ($datatype->getBasetype() !== 'file') {
126
            throw new ValidatorException(
127
                'Not a file'
128
            );
129
        }
130
131
        self::size($value, $options);
132
133
        if ($options[self::ACCEPT] ?? false) {
134
            self::accept($value, $options);
135
        }
136
137
        if (($options[self::DIMENSION_HEIGHT] ?? false) ||
138
            ($options[self::DIMENSION_WIDTH] ?? false) ||
139
            ($options[self::DIMENSION_MIN_HEIGHT] ?? false) ||
140
            ($options[self::DIMENSION_MIN_WIDTH] ?? false) ||
141
            ($options[self::DIMENSION_MAX_HEIGHT] ?? false) ||
142
            ($options[self::DIMENSION_MAX_WIDTH] ?? false) ||
143
            ($options[self::DIMENSION_RATIO] ?? false)
144
        ) {
145
            $imageData = getimagesize($value);
146
            if ($imageData === false) {
147
                throw new ValidatorException(
148
                    'Not an image'
149
                );
150
            }
151
            $width = $imageData[0];
152
            $height = $imageData[1];
153
154
            $expectedHeight = $options[self::DIMENSION_HEIGHT] ?? false;
155
            if ($expectedHeight !== false) {
156
                if ($expectedHeight !== $height) {
157
                    throw new ValidatorException(
158
                        'Image height should be exactly ' . $options[self::DIMENSION_HEIGHT] ?? false
0 ignored issues
show
Bug introduced by
It seems like 'Image height should be ...ENSION_HEIGHT] ?? false can also be of type false; however, parameter $message of Formularium\Exception\Va...xception::__construct() 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

158
                        /** @scrutinizer ignore-type */ 'Image height should be exactly ' . $options[self::DIMENSION_HEIGHT] ?? false
Loading history...
159
                    );
160
                }
161
            }
162
163
            $expectedWidth = $options[self::DIMENSION_WIDTH] ?? false;
164
            if ($expectedWidth !== false) {
165
                if ($expectedWidth !== $width) {
166
                    throw new ValidatorException(
167
                        'Image width should be exactly ' . $expectedWidth
168
                    );
169
                }
170
            }
171
172
            $minHeight = $options[self::DIMENSION_MIN_HEIGHT] ?? false;
173
            if ($minHeight !== false) {
174
                if ($height < $minHeight) {
175
                    throw new ValidatorException(
176
                        'Image height should be at least ' . $minHeight
177
                    );
178
                }
179
            }
180
181
            $minWidth = $options[self::DIMENSION_MIN_WIDTH] ?? false;
182
            if ($minWidth !== false) {
183
                if ($width < $minWidth) {
184
                    throw new ValidatorException(
185
                        'Image width should be at least ' . $minWidth
186
                    );
187
                }
188
            }
189
190
            $maxHeight = $options[self::DIMENSION_MAX_HEIGHT] ?? false;
191
            if ($maxHeight !== false) {
192
                if ($height > $maxHeight) {
193
                    throw new ValidatorException(
194
                        'Image height should be at most ' . $maxHeight
195
                    );
196
                }
197
            }
198
199
            $maxWidth = $options[self::DIMENSION_MAX_WIDTH] ?? false;
200
            if ($maxWidth !== false) {
201
                if ($width > $maxWidth) {
202
                    throw new ValidatorException(
203
                        'Image width should be at most ' . $maxWidth
204
                    );
205
                }
206
            }
207
208
            $ratio = $options[self::DIMENSION_RATIO] ?? false;
209
            if ($ratio !== false) {
210
                if (!$width || !$height) {
211
                    throw new ValidatorException(
212
                        'Zero width or height'
213
                    );
214
                }
215
                $ratio = $width/$height;
216
                $expected = $ratio;
217
                if (abs(($ratio-$expected)/$expected) > 0.0001) {
218
                    throw new ValidatorException(
219
                        'Image width/height ratio should be ' . $ratio
220
                    );
221
                }
222
            }
223
        }
224
225
        return $value;
226
    }
227
228
    public static function getMetadata(): ValidatorMetadata
229
    {
230
        return new ValidatorMetadata(
231
            'File',
232
            "File validations.",
233
            [
234
                new ValidatorArgs(
235
                    self::MAX_SIZE,
236
                    'Int',
237
                    'Maximum file size in bytes'
238
                )
239
                // TODO: missing args
240
            ]
241
        );
242
    }
243
}
244