Issues (6)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Image.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace DominionEnterprises\Util;
4
5
final class Image
6
{
7
    /**
8
     * Calls @see resizeMulti() with $boxWidth and $boxHeight as a single element in $boxSizes
9
     */
10
    public static function resize(\Imagick $source, $boxWidth, $boxHeight, array $options = [])
11
    {
12
        $results = self::resizeMulti($source, [['width' => $boxWidth, 'height' => $boxHeight]], $options);
13
        return $results[0];
14
    }
15
16
    /**
17
     * resizes images into a bounding box. Maintains aspect ratio, extra space filled with given color.
18
     *
19
     * @param \Imagick $source source image to resize. Will not modify
20
     * @param array $boxSizes resulting bounding boxes. Each value should be an array with width and height, both integers
21
     * @param array $options options
22
     *     string color (default white) background color. Any supported from
23
     *         http://www.imagemagick.org/script/color.php#color_names
24
     *     bool upsize (default false) true to upsize the original image or false to upsize just the bounding box
25
     *     int maxWidth (default 10000) max width allowed for $boxWidth
26
     *     int maxHeight (default 10000) max height allowed for $boxHeight
27
     *
28
     * @return array array of \Imagick objects resized. Keys maintained from $boxSizes
29
     *
30
     * @throws \InvalidArgumentException if $options["color"] was not a string
31
     * @throws \InvalidArgumentException if $options["upsize"] was not a bool
32
     * @throws \InvalidArgumentException if $options["maxWidth"] was not an int
33
     * @throws \InvalidArgumentException if $options["maxHeight"] was not an int
34
     * @throws \InvalidArgumentException if a width in a $boxSizes value was not an int
35
     * @throws \InvalidArgumentException if a height in a $boxSizes value was not an int
36
     * @throws \InvalidArgumentException if a $boxSizes width was not between 0 and $options["maxWidth"]
37
     * @throws \InvalidArgumentException if a $boxSizes height was not between 0 and $options["maxHeight"]
38
     * @throws \Exception
39
     */
40
    public static function resizeMulti(\Imagick $source, array $boxSizes, array $options = [])
41
    {
42
        //algorithm inspiration from http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
43
        //use of 2x2 binning is arguably the best quality one will get downsizing and is what lots of hardware does in the photography field,
44
        //while being reasonably fast. Upsizing is more subjective but you can't get much better than bicubic which is what is used here.
45
46
        $color = 'white';
47
        if (isset($options['color'])) {
48
            $color = $options['color'];
49
            if (!is_string($color)) {
50
                throw new \InvalidArgumentException('$options["color"] was not a string');
51
            }
52
        }
53
54
        $upsize = false;
55
        if (isset($options['upsize'])) {
56
            $upsize = $options['upsize'];
57
            if ($upsize !== true && $upsize !== false) {
58
                throw new \InvalidArgumentException('$options["upsize"] was not a bool');
59
            }
60
        }
61
62
        $maxWidth = 10000;
63
        if (isset($options['maxWidth'])) {
64
            $maxWidth = $options['maxWidth'];
65
            if (!is_int($maxWidth)) {
66
                throw new \InvalidArgumentException('$options["maxWidth"] was not an int');
67
            }
68
        }
69
70
        $maxHeight = 10000;
71
        if (isset($options['maxHeight'])) {
72
            $maxHeight = $options['maxHeight'];
73
            if (!is_int($maxHeight)) {
74
                throw new \InvalidArgumentException('$options["maxHeight"] was not an int');
75
            }
76
        }
77
78
        foreach ($boxSizes as $boxSizeKey => $boxSize) {
79 View Code Duplication
            if (!isset($boxSize['width']) || !is_int($boxSize['width'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
80
                throw new \InvalidArgumentException('a width in a $boxSizes value was not an int');
81
            }
82
83 View Code Duplication
            if (!isset($boxSize['height']) || !is_int($boxSize['height'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
84
                throw new \InvalidArgumentException('a height in a $boxSizes value was not an int');
85
            }
86
87
            if ($boxSize['width'] > $maxWidth || $boxSize['width'] <= 0) {
88
                throw new \InvalidArgumentException('a $boxSizes width was not between 0 and $options["maxWidth"]');
89
            }
90
91
            if ($boxSize['height'] > $maxHeight || $boxSize['height'] <= 0) {
92
                throw new \InvalidArgumentException('a $boxSizes height was not between 0 and $options["maxHeight"]');
93
            }
94
        }
95
96
        $results = [];
97
        $cloneCache = [];
98
        foreach ($boxSizes as $boxSizeKey => $boxSize) {
99
            $boxWidth = $boxSize['width'];
100
            $boxHeight = $boxSize['height'];
101
102
            $clone = clone $source;
103
104
            $orientation = $clone->getImageOrientation();
105
            switch ($orientation)
106
            {
107
                case \Imagick::ORIENTATION_BOTTOMRIGHT:
108
                    $clone->rotateimage('#fff', 180);
109
                    $clone->stripImage();
110
                    break;
111
                case \Imagick::ORIENTATION_RIGHTTOP:
112
                    $clone->rotateimage('#fff', 90);
113
                    $clone->stripImage();
114
                    break;
115
                case \Imagick::ORIENTATION_LEFTBOTTOM:
116
                    $clone->rotateimage('#fff', -90);
117
                    $clone->stripImage();
118
                    break;
119
            }
120
121
            $width = $clone->getImageWidth();
122
            $height = $clone->getImageHeight();
123
124
            //ratio over 1 is horizontal, under 1 is vertical
125
            $boxRatio = $boxWidth / $boxHeight;
126
            $originalRatio = $width / $height;//height should be positive since I didnt find a way you could get zero into imagick
127
128
            $targetWidth = null;
0 ignored issues
show
$targetWidth is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
129
            $targetHeight = null;
0 ignored issues
show
$targetHeight is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
130
            $targetX = null;
0 ignored issues
show
$targetX is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
131
            $targetY = null;
0 ignored issues
show
$targetY is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
132
            if ($width < $boxWidth && $height < $boxHeight && !$upsize) {
133
                $targetWidth = $width;
134
                $targetHeight = $height;
135
                $targetX = ($boxWidth - $width) / 2;
136
                $targetY = ($boxHeight - $height) / 2;
137
            } else {
138
                //if box is more vertical than original
139
                if ($boxRatio < $originalRatio) {
140
                    $targetWidth = $boxWidth;
141
                    $targetHeight = (int)((double)$boxWidth / $originalRatio);
142
                    $targetX = 0;
143
                    $targetY = ($boxHeight - $targetHeight) / 2;
144
                } else {
145
                    $targetWidth = (int)((double)$boxHeight * $originalRatio);
146
                    $targetHeight = $boxHeight;
147
                    $targetX = ($boxWidth - $targetWidth) / 2;
148
                    $targetY = 0;
149
                }
150
            }
151
152
            //do iterative downsize by halfs (2x2 binning is a common name) on dimensions that are bigger than target width and height
153
            while (true) {
154
                $widthReduced = false;
155
                $widthIsHalf = false;
156
                if ($width > $targetWidth) {
157
                    $width = (int)($width / 2);
158
                    $widthReduced = true;
159
                    $widthIsHalf = true;
160
                    if ($width < $targetWidth) {
161
                        $width = $targetWidth;
162
                        $widthIsHalf = false;
163
                    }
164
                }
165
166
                $heightReduced = false;
167
                $heightIsHalf = false;
168
                if ($height > $targetHeight) {
169
                    $height = (int)($height / 2);
170
                    $heightReduced = true;
171
                    $heightIsHalf = true;
172
                    if ($height < $targetHeight) {
173
                        $height = $targetHeight;
174
                        $heightIsHalf = false;
175
                    }
176
                }
177
178
                if (!$widthReduced && !$heightReduced) {
179
                    break;
180
                }
181
182
                $cacheKey = "{$width}x{$height}";
183
                if (isset($cloneCache[$cacheKey])) {
184
                    $clone = clone $cloneCache[$cacheKey];
185
                    continue;
186
                }
187
188
                if ($clone->resizeImage($width, $height, \Imagick::FILTER_BOX, 1.0) !== true) {
189
                    //cumbersome to test
190
                    throw new \Exception('Imagick::resizeImage() did not return true');//@codeCoverageIgnore
191
                }
192
193
                if ($widthIsHalf && $heightIsHalf) {
194
                    $cloneCache[$cacheKey] = clone $clone;
195
                }
196
            }
197
198
            if ($upsize && ($width < $targetWidth || $height < $targetHeight)) {
199
                if ($clone->resizeImage($targetWidth, $targetHeight, \Imagick::FILTER_CUBIC, 1.0) !== true) {
200
                    //cumbersome to test
201
                    throw new \Exception('Imagick::resizeImage() did not return true');//@codeCoverageIgnore
202
                }
203
            }
204
205
            //put image in box
206
            $canvas = new \Imagick();
207
            if ($canvas->newImage($boxWidth, $boxHeight, $color) !== true) {
208
                //cumbersome to test
209
                throw new \Exception('Imagick::newImage() did not return true');//@codeCoverageIgnore
210
            }
211
212
            if ($canvas->compositeImage($clone, \Imagick::COMPOSITE_ATOP, $targetX, $targetY) !== true) {
213
                //cumbersome to test
214
                throw new \Exception('Imagick::compositeImage() did not return true');//@codeCoverageIgnore
215
            }
216
217
            //reason we are not supporting the options in self::write() here is because format, and strip headers are only relevant once written
218
            //Imagick::stripImage() doesnt even have an effect until written
219
            //also the user can just call that function with the resultant $canvas
220
            $results[$boxSizeKey] = $canvas;
221
        }
222
223
        return $results;
224
    }
225
226
    /**
227
     * write $source to $destPath with $options applied
228
     *
229
     * @param \Imagick $source source image. Will not modify
230
     * @param string $destPath destination image path
231
     * @param array $options options
232
     *     string format (default jpeg) format. Any supported from http://www.imagemagick.org/script/formats.php#supported
233
     *     int directoryMode (default 0777) chmod mode for any parent directories created
234
     *     int fileMode (default 0777) chmod mode for the resized image file
235
     *     bool stripHeaders (default true) whether to strip headers (exif, etc). Is only reflected in $destPath, not returned clone
236
     *
237
     * @return void
238
     *
239
     * @throws \InvalidArgumentException if $destPath was not a string
240
     * @throws \InvalidArgumentException if $options["format"] was not a string
241
     * @throws \InvalidArgumentException if $options["directoryMode"] was not an int
242
     * @throws \InvalidArgumentException if $options["fileMode"] was not an int
243
     * @throws \InvalidArgumentException if $options["stripHeaders"] was not a bool
244
     * @throws \Exception
245
     */
246
    public static function write(\Imagick $source, $destPath, array $options = [])
247
    {
248
        if (!is_string($destPath)) {
249
            throw new \InvalidArgumentException('$destPath was not a string');
250
        }
251
252
        $format = 'jpeg';
253
        if (array_key_exists('format', $options)) {
254
            $format = $options['format'];
255
            if (!is_string($format)) {
256
                throw new \InvalidArgumentException('$options["format"] was not a string');
257
            }
258
        }
259
260
        $directoryMode = 0777;
261
        if (array_key_exists('directoryMode', $options)) {
262
            $directoryMode = $options['directoryMode'];
263
            if (!is_int($directoryMode)) {
264
                throw new \InvalidArgumentException('$options["directoryMode"] was not an int');
265
            }
266
        }
267
268
        $fileMode = 0777;
269
        if (array_key_exists('fileMode', $options)) {
270
            $fileMode = $options['fileMode'];
271
            if (!is_int($fileMode)) {
272
                throw new \InvalidArgumentException('$options["fileMode"] was not an int');
273
            }
274
        }
275
276
        $stripHeaders = true;
277
        if (array_key_exists('stripHeaders', $options)) {
278
            $stripHeaders = $options['stripHeaders'];
279
            if ($stripHeaders !== false && $stripHeaders !== true) {
280
                throw new \InvalidArgumentException('$options["stripHeaders"] was not a bool');
281
            }
282
        }
283
284
        $destDir = dirname($destPath);
285
        if (!is_dir($destDir)) {
286
            $oldUmask = umask(0);
287
            if (!mkdir($destDir, $directoryMode, true)) {
288
                //cumbersome to test
289
                throw new \Exception('mkdir() returned false');//@codeCoverageIgnore
290
            }
291
292
            umask($oldUmask);
293
        }
294
295
        $clone = clone $source;
296
297
        if ($clone->setImageFormat($format) !== true) {
298
            //cumbersome to test
299
            throw new \Exception('Imagick::setImageFormat() did not return true');//@codeCoverageIgnore
300
        }
301
302
        if ($stripHeaders && $clone->stripImage() !== true) {
303
            //cumbersome to test
304
            throw new \Exception('Imagick::stripImage() did not return true');//@codeCoverageIgnore
305
        }
306
307
        if ($clone->writeImage($destPath) !== true) {
308
            //cumbersome to test
309
            throw new \Exception('Imagick::writeImage() did not return true');//@codeCoverageIgnore
310
        }
311
312
        if (!chmod($destPath, $fileMode)) {
313
            //cumbersome to test
314
            throw new \Exception('chmod() returned false');//@codeCoverageIgnore
315
        }
316
    }
317
318
    /**
319
     * Strips the headers (exif, etc) from an image at the given path.
320
     *
321
     * @param string $path The image path.
322
     * @return void
323
     * @throws \InvalidArgumentException if $path is not a string
324
     * @throws \Exception if there is a failure stripping the headers
325
     * @throws \Exception if there is a failure writing the image back to path
326
     */
327
    public static function stripHeaders($path)
328
    {
329
        if (!is_string($path)) {
330
            throw new \InvalidArgumentException('$path was not a string');
331
        }
332
333
        $imagick = new \Imagick($path);
334
        if ($imagick->stripImage() !== true) {
335
            //cumbersome to test
336
            throw new \Exception('Imagick::stripImage() did not return true');//@codeCoverageIgnore
337
        }
338
339
        if ($imagick->writeImage($path) !== true) {
340
            //cumbersome to test
341
            throw new \Exception('Imagick::writeImage() did not return true');//@codeCoverageIgnore
342
        }
343
    }
344
}
345