ImageValidator::checkDimension()   C
last analyzed

Complexity

Conditions 11
Paths 11

Size

Total Lines 49
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 49
rs 5.2653
cc 11
eloc 40
nc 11
nop 2

How to fix   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 namespace Cviebrock\ImageValidator;
2
3
use Illuminate\Contracts\Translation\Translator;
4
use Illuminate\Validation\Validator;
5
use RuntimeException;
6
use Symfony\Component\HttpFoundation\File\UploadedFile;
7
8
9
class ImageValidator extends Validator
10
{
11
12
    /**
13
     * Creates a new instance of ImageValidator
14
     *
15
     * @param \Illuminate\Contracts\Translation\Translator $translator
16
     * @param array $data
17
     * @param array $rules
18
     * @param array $messages
19
     * @param array $customAttributes
20
     */
21
    public function __construct(
22
        Translator $translator,
23
        array $data,
24
        array $rules,
25
        array $messages = [],
26
        array $customAttributes = []
27
    ) {
28
        parent::__construct($translator, $data, $rules, $messages, $customAttributes);
29
    }
30
31
    /**
32
     * Usage: image_size:width[,height]
33
     *
34
     * @param  $attribute  string
35
     * @param  $value      string|array
36
     * @param  $parameters array
37
     * @return boolean
38
     */
39
    public function validateImageSize($attribute, $value, $parameters)
0 ignored issues
show
Unused Code introduced by
The parameter $attribute is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
40
    {
41
        $image = $this->getImagePath($value);
42
43
        // Get the image dimension info, or fail.
44
45
        $image_size = @getimagesize($image);
46
        if ($image_size === false) {
47
            return false;
48
        }
49
50
        // If only one dimension rule is passed, assume it applies to both height and width.
51
52
        if (!isset($parameters[1])) {
53
            $parameters[1] = $parameters[0];
54
        }
55
56
        // Parse the parameters.  Options are:
57
        //
58
        // 	"300" or "=300"   - dimension must be exactly 300 pixels
59
        // 	"<300"            - dimension must be less than 300 pixels
60
        // 	"<=300"           - dimension must be less than or equal to 300 pixels
61
        // 	">300"            - dimension must be greater than 300 pixels
62
        // 	">=300"           - dimension must be greater than or equal to 300 pixels
63
64
        $width_check = $this->checkDimension($parameters[0], $image_size[0]);
65
        $height_check = $this->checkDimension($parameters[1], $image_size[1]);
66
67
        return $width_check['pass'] && $height_check['pass'];
68
    }
69
70
    /**
71
     * Build the error message for validation failures.
72
     *
73
     * @param  string $message
74
     * @param  string $attribute
75
     * @param  string $rule
76
     * @param  array $parameters
77
     * @return string
78
     */
79
    public function replaceImageSize($message, $attribute, $rule, $parameters)
0 ignored issues
show
Unused Code introduced by
The parameter $attribute is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $rule is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
80
    {
81
        $width = $height = $this->checkDimension($parameters[0]);
82
        if (isset($parameters[1])) {
83
            $height = $this->checkDimension($parameters[1]);
84
        }
85
86
        return str_replace(
87
            [':width', ':height'],
88
            [$width['message'], $height['message']],
89
            $message
90
        );
91
    }
92
93
    /**
94
     * Usage: image_aspect:ratio
95
     *
96
     * @param  $attribute  string
97
     * @param  $value      string|array
98
     * @param  $parameters array
99
     * @return boolean
100
     * @throws \RuntimeException
101
     */
102
    public function validateImageAspect($attribute, $value, $parameters)
0 ignored issues
show
Unused Code introduced by
The parameter $attribute is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
103
    {
104
        $image = $this->getImagePath($value);
105
106
        // Get the image dimension info, or fail.
107
108
        $image_size = @getimagesize($image);
109
        if ($image_size === false) {
110
            return false;
111
        }
112
113
        $image_width = $image_size[0];
114
        $image_height = $image_size[1];
115
116
        // Parse the parameter(s).  Options are:
117
        //
118
        // 	"0.75"   - one param: a decimal ratio (width/height)
119
        // 	"3,4"    - two params: width, height
120
        //
121
        // If the first value is prefixed with "~", the orientation doesn't matter, i.e.:
122
        //
123
        // 	"~3,4"   - would accept either "3:4" or "4:3" images
124
125
        $both_orientations = false;
126
127
        if (substr($parameters[0], 0, 1) === '~') {
128
            $parameters[0] = substr($parameters[0], 1);
129
            $both_orientations = true;
130
        }
131
132
        if (count($parameters) === 1) {
133
            $aspect_width = $parameters[0];
134
            $aspect_height = 1;
135
        } else {
136
            $aspect_width = (int) $parameters[0];
137
            $aspect_height = (int) $parameters[1];
138
        }
139
140
        if ($aspect_width === 0 || $aspect_height === 0) {
141
            throw new RuntimeException('Aspect is zero or infinite: ' . $parameters[0]);
142
        }
143
144
        $check = ($image_width * $aspect_height) / $aspect_width;
145
146
        if ((int) round($check) === $image_height) {
147
            return true;
148
        }
149
150
        if ($both_orientations) {
151
            $check = ($image_width * $aspect_width) / $aspect_height;
152
            if ((int) round($check) === $image_height) {
153
                return true;
154
            }
155
        }
156
157
        return false;
158
    }
159
160
    /**
161
     * Build the error message for validation failures.
162
     *
163
     * @param  string $message
164
     * @param  string $attribute
165
     * @param  string $rule
166
     * @param  array $parameters
167
     * @return string
168
     */
169
    public function replaceImageAspect($message, $attribute, $rule, $parameters)
0 ignored issues
show
Unused Code introduced by
The parameter $attribute is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $rule is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
170
    {
171
        return str_replace(':aspect', implode(':', $parameters), $message);
172
    }
173
174
    /**
175
     * Parse the dimension rule and check if the dimension passes the rule.
176
     *
177
     * @param  string $rule
178
     * @param  integer $dimension
179
     * @return array
180
     * @throws \RuntimeException
181
     */
182
    protected function checkDimension($rule, $dimension = 0)
183
    {
184
185
        $dimension = (int) $dimension;
186
187
        if ($rule === '*') {
188
            $message = $this->translator->trans('image-validator::validation.anysize');
189
            $pass = true;
190
        } else if (preg_match('/^(\d+)\-(\d+)$/', $rule, $matches)) {
191
            $size1 = (int) $matches[1];
192
            $size2 = (int) $matches[2];
193
            $message = $this->translator->trans('image-validator::validation.between', compact('size1', 'size2'));
194
            $pass = ($dimension >= $size1) && ($dimension <= $size2);
195
        } else if (preg_match('/^([<=>]*)(\d+)$/', $rule, $matches)) {
196
197
            $size = (int) $matches[2];
198
199
            switch ($matches[1]) {
200
                case '>':
201
                    $message = $this->translator->trans('image-validator::validation.greaterthan', compact('size'));
202
                    $pass = $dimension > $size;
203
                    break;
204
                case '>=':
205
                    $message = $this->translator->trans('image-validator::validation.greaterthanorequal',
206
                        compact('size'));
207
                    $pass = $dimension >= $size;
208
                    break;
209
                case '<':
210
                    $message = $this->translator->trans('image-validator::validation.lessthan', compact('size'));
211
                    $pass = $dimension < $size;
212
                    break;
213
                case '<=':
214
                    $message = $this->translator->trans('image-validator::validation.lessthanorequal', compact('size'));
215
                    $pass = $dimension <= $size;
216
                    break;
217
                case '=':
218
                case '':
219
                    $message = $this->translator->trans('image-validator::validation.equal', compact('size'));
220
                    $pass = $dimension == $size;
221
                    break;
222
                default:
223
                    throw new RuntimeException('Unknown image size validation rule: ' . $rule);
224
            }
225
        } else {
226
            throw new RuntimeException('Unknown image size validation rule: ' . $rule);
227
        }
228
229
        return compact('message', 'pass');
230
    }
231
232
    /**
233
     * @param mixed $value
234
     * @return string
235
     */
236
    protected function getImagePath($value)
237
    {
238
        // if were passed an instance of UploadedFile, return the path
239
        if ($value instanceof UploadedFile) {
240
            return $value->getPathname();
241
        }
242
243
        // if we're passed a PHP file upload array, return the "tmp_name"
244
        if (is_array($value) && array_get($value, 'tmp_name') !== null) {
245
            return $value['tmp_name'];
246
        }
247
248
        // fallback: we were likely passed a path already
249
        return $value;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $value; (object|integer|double|string|null|boolean|array) is incompatible with the return type documented by Cviebrock\ImageValidator...Validator::getImagePath of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
250
    }
251
}
252