Issues (807)

Security Analysis    not enabled

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

  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.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  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.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  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.
  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.
  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.
  Header Injection
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.

library/Zebra_Image.php (22 issues)

1
<?php
2
3
/**
4
 *  Methods used with the {@link resize()} method.
5
 */
6
define('ZEBRA_IMAGE_BOXED', 0);
7
define('ZEBRA_IMAGE_NOT_BOXED', 1);
8
define('ZEBRA_IMAGE_CROP_TOPLEFT', 2);
9
define('ZEBRA_IMAGE_CROP_TOPCENTER', 3);
10
define('ZEBRA_IMAGE_CROP_TOPRIGHT', 4);
11
define('ZEBRA_IMAGE_CROP_MIDDLELEFT', 5);
12
define('ZEBRA_IMAGE_CROP_CENTER', 6);
13
define('ZEBRA_IMAGE_CROP_MIDDLERIGHT', 7);
14
define('ZEBRA_IMAGE_CROP_BOTTOMLEFT', 8);
15
define('ZEBRA_IMAGE_CROP_BOTTOMCENTER', 9);
16
define('ZEBRA_IMAGE_CROP_BOTTOMRIGHT', 10);
17
18
// this enables handling of partially broken JPEG files without warnings/errors
19
ini_set('gd.jpeg_ignore_warning', true);
0 ignored issues
show
true of type true is incompatible with the type string expected by parameter $value of ini_set(). ( Ignorable by Annotation )

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

19
ini_set('gd.jpeg_ignore_warning', /** @scrutinizer ignore-type */ true);
Loading history...
20
21
/**
22
 *  A compact (one-file only) and lightweight PHP library for image manipulation providing methods for performing several
23
 *  types of image manipulation operations and applying filters to images.
24
 *
25
 *  Read more {@link https://github.com/stefangabos/Zebra_Image/ here}
26
 *
27
 * @author         Stefan Gabos <[email protected]>
28
 * @version        2.3.0 (last revision: June 06, 2019)
29
 * @copyright  (c) 2006 - 2019 Stefan Gabos
30
 * @license        https://www.gnu.org/licenses/lgpl-3.0.txt GNU LESSER GENERAL PUBLIC LICENSE
31
 * @package        Zebra_Image
32
 */
33
class Zebra_Image
34
{
35
    /**
36
     *  Indicates the file system permissions to be set for newly created images.
37
     *
38
     *  Better is to leave this setting as it is.
39
     *
40
     *  If you know what you are doing, here is how you can calculate the permission levels:
41
     *
42
     *  - 400 Owner Read
43
     *  - 200 Owner Write
44
     *  - 100 Owner Execute
45
     *  - 40 Group Read
46
     *  - 20 Group Write
47
     *  - 10 Group Execute
48
     *  - 4 Global Read
49
     *  - 2 Global Write
50
     *  - 1 Global Execute
51
     *
52
     *  Default is 0755
53
     *
54
     * @var integer
55
     */
56
    public $chmod_value;
57
    /**
58
     *  If set to FALSE, images having both width and height smaller than the required width and height, will be left
59
     *  untouched ({@link jpeg_quality} and {@link png_compression} will still apply).
60
     *
61
     *  Available only for the {@link resize()} method
62
     *
63
     *  Default is TRUE
64
     *
65
     * @var boolean
66
     */
67
    public $enlarge_smaller_images;
68
    /**
69
     *  In case of an error read this property's value to see the error's code.
70
     *
71
     *  Possible error codes are:
72
     *
73
     *  - 1:  source file could not be found
74
     *  - 2:  source file is not readable
75
     *  - 3:  could not write target file
76
     *  - 4:  unsupported source file format
77
     *  - 5:  unsupported target file format
78
     *  - 6:  GD library version does not support target file format
79
     *  - 7:  GD library is not installed!
80
     *  - 8:  "chmod" command is disabled via configuration
81
     *  - 9:  "exif_read_data" function is not available
82
     *
83
     *  Default is 0 (no error).
84
     *
85
     * @var integer
86
     */
87
    public $error;
88
    /**
89
     *  If set to TRUE, JPEG images will be auto-rotated according to the {@link http://keyj.emphy.de/exif-orientation-rant/ Exif Orientation Tag}
90
     *  so that they are always shown correctly.
91
     *
92
     *  <samp>If you set this to TRUE you must also enable exif-support with --enable-exif. Windows users must enable both
93
     *  the php_mbstring.dll and php_exif.dll DLL's in php.ini. The php_mbstring.dll DLL must be loaded before the
94
     *  php_exif.dll DLL so adjust your php.ini accordingly. See {@link http://php.net/manual/en/exif.installation.php the PHP manual}</samp>
95
     *
96
     *  Default is FALSE
97
     *
98
     * @since 2.2.4
99
     *
100
     * @var boolean
101
     */
102
    public $auto_handle_exif_orientation;
103
    /**
104
     *  Indicates the quality of the output image (better quality means bigger file size).
105
     *
106
     *  Used only if the file at {@link target_path} is a JPG/JPEG image.
107
     *
108
     *  Range is 0 - 100
109
     *
110
     *  Default is 85
111
     *
112
     * @var integer
113
     */
114
    public $jpeg_quality;
115
    /**
116
     *  Indicates the compression level of the output image (lower compression means bigger file size).
117
     *
118
     *  Available only if PHP version is 5.1.2+, and only if the file at {@link target_path} is a PNG image. It will be
119
     *  ignored otherwise.
120
     *
121
     *  Range is 0 - 9
122
     *
123
     *  Default is 9
124
     *
125
     * @since 2.2
126
     *
127
     * @var integer
128
     */
129
    public $png_compression;
130
    /**
131
     *  Specifies whether, upon resizing, images should preserve their aspect ratio.
132
     *
133
     *  Available only for the {@link resize()} method
134
     *
135
     *  Default is TRUE
136
     *
137
     * @var boolean
138
     */
139
    public $preserve_aspect_ratio;
140
    /**
141
     *  Indicates whether a target files should preserve the source file's date/time.
142
     *
143
     *  Default is TRUE
144
     *
145
     * @since 1.0.4
146
     *
147
     * @var boolean
148
     */
149
    public $preserve_time;
150
    /**
151
     *  Indicates whether the target image should have a "sharpen" filter applied to it.
152
     *
153
     *  Can be very useful when creating thumbnails and should be used only when creating thumbnails.
154
     *
155
     *  <i>The sharpen filter relies on the "imageconvolution" PHP function which is available only for PHP version
156
     *  5.1.0+, and will leave the images unaltered for older versions!</i>
157
     *
158
     *  Default is FALSE
159
     *
160
     * @since 2.2
161
     *
162
     * @var boolean
163
     */
164
    public $sharpen_images;
165
    /**
166
     *  Path to an image file to apply the transformations to.
167
     *
168
     *  Supported file types are <b>GIF</b>, <b>PNG</b> and <b>JPEG</b>.
169
     *
170
     * @var    string
171
     */
172
    public $source_path;
173
    /**
174
     *  Path (including file name) to where to save the transformed image.
175
     *
176
     *  <i>Can be a different than {@link source_path} - the type of the transformed image will be as indicated by the
177
     *  file's extension (supported file types are GIF, PNG and JPEG)</i>.
178
     *
179
     * @var    string
180
     */
181
    public $target_path;
182
183
    /**
184
     *  Constructor of the class.
185
     *
186
     *  Initializes the class and the default properties
187
     *
188
     * @return void
189
     */
190
    public function __construct()
191
    {
192
        // set default values for properties
193
        $this->chmod_value = 0755;
194
195
        $this->error = 0;
196
197
        $this->jpeg_quality = 85;
198
199
        $this->png_compression = 9;
200
201
        $this->preserve_aspect_ratio = $this->preserve_time = $this->enlarge_smaller_images = true;
202
203
        $this->sharpen_images = $this->auto_handle_exif_orientation = false;
204
205
        $this->source_path = $this->target_path = '';
206
    }
207
208
    /**
209
     *  Applies one or more filters to the image given as {@link source_path} and outputs it as the file specified as
210
     *  {@link target_path}.
211
     *
212
     *  <samp>This method is available only if the {@link http://php.net/manual/en/function.imagefilter.php imagefilter}
213
     *  function is available (available from PHP 5+), and will leave images unaltered otherwise.</samp>
214
     *
215
     *  <code>
216
     *  // include the Zebra_Image library
217
     *  require 'path/to/Zebra_Image.php';
218
     *
219
     *  // instantiate the class
220
     *  $img = new Zebra_Image();
221
     *
222
     *  // a source image
223
     *  $img->source_path = 'path/to/source.ext';
224
     *
225
     *  // path to where should the resulting image be saved
226
     *  // note that by simply setting a different extension to the file will
227
     *  // instruct the script to create an image of that particular type
228
     *  $img->target_path = 'path/to/target.ext';
229
     *
230
     *  // apply the "grayscale" filter
231
     *  $img->apply_filter('grayscale');
232
     *
233
     *  // apply the "contrast" filter
234
     *  $img->apply_filter('contrast', -20);
235
     *  </code>
236
     *
237
     *  You can also apply multiple filters at once. In this case, the method requires a single argument, an array of
238
     *  arrays, containing the filters and associated arguments, where applicable:
239
     *
240
     *  <code>
241
     *  // create a sepia effect
242
     *  // note how we're applying multiple filters at once
243
     *  // each filter is in its own array
244
     *  $img->apply_filter(array(
245
     *
246
     *      // first we apply the "grayscale" filter
247
     *      array('grayscale'),
248
     *
249
     *      // then we apply the "colorize" filter with 90, 60, 40 as
250
     *      // the values for red, green and blue
251
     *      array('colorize', 90, 60, 40),
252
     *
253
     *  ));
254
     *  </code>
255
     *
256
     * @param string $filter                                        The (case-insensitive) name of the filter to apply. Can be one of the following:
257
     *
258
     *                              -   <b>brightness</b>       -   changes the brightness of the image; use <b>arg1</b>
259
     *                                                              to set the level of brightness; the range of brightness
260
     *                                                              is -255 to 255;
261
     *                              -   <b>colorize</b>         -   adds (subtracts) specified RGB values to each pixel;
262
     *                                                              use <b>arg1</b>, <b>arg2</b> and <b>arg3</b> in the
263
     *                                                              form of red, green, blue and <b>arg4</b> for the alpha
264
     *                                                              channel. the range for each color is -255 to 255 and
265
     *                                                              0 to 127 for alpha; <i>alpha support is available only
266
     *                                                              for PHP 5.2.5+</i>;
267
     *                              -   <b>contrast</b>         -   changes the contrast of the image; use <b>arg1</b>
268
     *                                                              to set the level of contrast; the range of contrast
269
     *                                                              is -100 to 100;
270
     *                              -   <b>gausian_blur</b>     -   blurs the image using the Gaussian method;
271
     *                              -   <b>grayscale</b>        -   converts the image into grayscale;
272
     *                              -   <b>edgedetect</b>       -   uses edge detection to highlight the edges in the image;
273
     *                              -   <b>emboss</b>           -   embosses the image;
274
     *                              -   <b>mean_removal</b>     -   uses mean removal to achieve a "sketchy" effect;
275
     *                              -   <b>negate</b>           -   reverses all the colors of the image;
276
     *                              -   <b>pixelate</b>         -   applies pixelation effect to the image, use <b>arg1</b>
277
     *                                                              to set the block size and <b>arg2</b> to set the
278
     *                                                              pixelation effect mode; <i>this filter is available
279
     *                                                              only for PHP 5.3.0+</i>;
280
     *                              -   <b>selective_blur</b>   -   blurs the image;
281
     *                              -   <b>smooth</b>           -   makes the image smoother. Use <b>arg1</b> to set the
282
     *                                                              level of smoothness. applies a 9-cell convolution matrix
283
     *                                                              where center pixel has the weight of <b>arg1</b> and
284
     *                                                              others weight of 1.0. the result is normalized by dividing
285
     *                                                              the sum with <b>arg1</b> + 8.0 (sum of the matrix).
286
     *                                                              any float is accepted;
287
     *
288
     * @param mixed  $arg1                                          Used by the following filters:
289
     *                                                              -   <b>brightness</b>       -   sets the brightness level (-255 to 255)
290
     *                                                              -   <b>contrast</b>         -   sets the contrast level (-100 to 100)
291
     *                                                              -   <b>colorize</b>         -   sets the value of the red component (-255 to 255)
292
     *                                                              -   <b>smooth</b>           -   sets the smoothness level
293
     *                                                              -   <b>pixelate</b>         -   sets the block size, in pixels
294
     *
295
     * @param mixed  $arg2                                          Used by the following filters:
296
     *                                                              -   <b>colorize</b>         -   sets the value of the green component (-255 to 255)
297
     *                                                              -   <b>pixelate</b>         -   whether to use advanced pixelation effect or not (defaults to FALSE).
298
     *
299
     * @param mixed  $arg3                                          Used by the following filters:
300
     *                                                              -   <b>colorize</b>         -   sets the value of the blue component (-255 to 255)
301
     *
302
     * @param mixed  $arg4                                          Used by the following filters:
303
     *                                                              -   <b>colorize</b>         -   alpha channel; a value between 0 and 127. 0 indicates
304
     *                                                              completely opaque while 127 indicates completely
305
     *                                                              transparent.
306
     *
307
     * @return boolean             Returns TRUE on success or FALSE on error.
308
     *
309
     *                              If {@link http://php.net/manual/en/function.imagefilter.php imagefilter} is not
310
     *                              available the method will return FALSE without setting an {@link error} code.
311
     *
312
     *                              If the requested filter doesn't exist, or invalid arguments are passed, the method
313
     *                              will trigger a warning.
314
     *
315
     *                              If FALSE is returned and you are sure that
316
     *                              {@link http://php.net/manual/en/function.imagefilter.php imagefilter} exists and that
317
     *                              the requested filter is valid, check the {@link error} property to see the error code.
318
     * @since 2.2.2
319
     *
320
     */
321
    public function apply_filter($filter, $arg1 = '', $arg2 = '', $arg3 = '', $arg4 = '')
322
    {
323
        // if "imagefilter" function exists and the requested filter exists
324
        if (function_exists('imagefilter')) // if image resource was successfully created
325
        {
326
            if ($this->_create_from_source()) {
327
                // prepare the target image
328
                $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, -1);
329
330
                // copy the original image
331
                imagecopyresampled(
332
333
                    $target_identifier,
334
                    $this->source_identifier,
335
                    0,
336
                    0,
337
                    0,
338
                    0,
339
                    $this->source_width,
340
                    $this->source_height,
341
                    $this->source_width,
342
                    $this->source_height
343
344
                );
345
346
                // if multiple filters are to be applied at once
347
                if (is_array($filter)) {
348
                    // iterate through the filters
349
                    foreach ($filter as $arguments) // if filter exists
350
                    {
351
                        if (defined('IMG_FILTER_' . strtoupper($arguments[0]))) {
352
                            // try to apply the filter...
353
                            if (!@call_user_func_array('imagefilter', array_merge([$target_identifier, constant('IMG_FILTER_' . strtoupper($arguments[0]))], array_slice($arguments, 1)))) // ...and trigger an error if the filter could not be applied
354
                            {
355
                                trigger_error('Invalid arguments used for "' . strtoupper($arguments[0]) . '" filter', E_USER_WARNING);
356
                            }
357
                            // if filter doesn't exists, trigger an error
358
                        } else {
359
                            trigger_error('Filter "' . strtoupper($arguments[0]) . '" is not available', E_USER_WARNING);
360
                        }
361
                    }
362
                    // if a single filter is to be applied and it is available
363
                } elseif (defined('IMG_FILTER_' . strtoupper($filter))) {
364
                    // get all the arguments passed to the method
365
                    $arguments = func_get_args();
366
367
                    // try to apply the filter...
368
                    if (!@call_user_func_array('imagefilter', array_merge([$target_identifier, constant('IMG_FILTER_' . strtoupper($filter))], array_slice($arguments, 1)))) // ...and trigger an error if the filter could not be applied
369
                    {
370
                        trigger_error('Invalid arguments used for "' . strtoupper($arguments[0]) . '" filter', E_USER_WARNING);
371
                    }
372
                    // if filter doesn't exists, trigger an error
373
                } else {
374
                    trigger_error('Filter "' . strtoupper($arguments[0]) . '" is not available', E_USER_WARNING);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $arguments seems to be never defined.
Loading history...
375
                }
376
377
                // write image
378
                return $this->_write_image($target_identifier);
379
            }
380
        }
381
382
        // if script gets this far, return false
383
        // note that we do not set the error level as it has been already set
384
        // by the _create_from_source() method earlier, if the case
385
        return false;
386
    }
387
388
    /**
389
     *  Crops a portion of the image given as {@link source_path} and outputs it as the file specified as {@link target_path}.
390
     *
391
     *  <code>
392
     *  // include the Zebra_Image library
393
     *  require 'path/to/Zebra_Image.php';
394
     *
395
     *  // instantiate the class
396
     *  $img = new Zebra_Image();
397
     *
398
     *  // a source image
399
     *  $img->source_path = 'path/to/source.ext';
400
     *
401
     *  // path to where should the resulting image be saved
402
     *  // note that by simply setting a different extension to the file will
403
     *  // instruct the script to create an image of that particular type
404
     *  $img->target_path = 'path/to/target.ext';
405
     *
406
     *  // crop a rectangle of 100x100 pixels, starting from the top-left corner
407
     *  $img->crop(0, 0, 100, 100);
408
     *  </code>
409
     *
410
     * @param integer $start_x x coordinate to start cropping from
411
     *
412
     * @param integer $start_y y coordinate to start cropping from
413
     *
414
     * @param integer $end_x   x coordinate where to end the cropping
415
     *
416
     * @param integer $end_y   y coordinate where to end the cropping
417
     *
418
     * @return boolean     Returns TRUE on success or FALSE on error.
419
     *
420
     *                      If FALSE is returned, check the {@link error} property to see the error code.
421
     * @since  1.0.4
422
     *
423
     */
424
    public function crop($start_x, $start_y, $end_x, $end_y)
425
    {
426
        // this method might be also called internally
427
        // in this case, there's a fifth argument that points to an already existing image identifier
428
        $args = func_get_args();
429
430
        // if fifth argument exists
431
        if (isset($args[4]) && is_resource($args[4])) {
432
            // that it is the image identifier that we'll be using further on
433
            $this->source_identifier = $args[4];
0 ignored issues
show
Bug Best Practice introduced by
The property source_identifier does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
434
435
            // set this to true so that the script will continue to execute at the next IF
436
            $result = true;
437
438
            // if method is called as usually
439
            // try to create an image resource from source path
440
        } else {
441
            $result = $this->_create_from_source();
442
        }
443
444
        // if image resource was successfully created
445
        if ($result !== false) {
446
            // compute width and height
447
            $width  = $end_x - $start_x;
448
            $height = $end_y - $start_y;
449
450
            // prepare the target image
451
            $target_identifier = $this->_prepare_image($width, $height, -1);
452
453
            // crop the image
454
            imagecopyresampled(
455
456
                $target_identifier,
457
                $this->source_identifier,
458
                0,
459
                0,
460
                $start_x,
461
                $start_y,
462
                $width,
463
                $height,
464
                $width,
465
                $height
466
467
            );
468
469
            // write image
470
            return $this->_write_image($target_identifier);
471
        }
472
473
        // if script gets this far, return false
474
        // note that we do not set the error level as it has been already set
475
        // by the _create_from_source() method earlier
476
        return false;
477
    }
478
479
    /**
480
     *  Flips both horizontally and vertically the image given as {@link source_path} and outputs the resulted image as
481
     *  {@link target_path}
482
     *
483
     *  <code>
484
     *  // include the Zebra_Image library
485
     *  require 'path/to/Zebra_Image.php';
486
     *
487
     *  // instantiate the class
488
     *  $img = new Zebra_Image();
489
     *
490
     *  // a source image
491
     *  $img->source_path = 'path/to/source.ext';
492
     *
493
     *  // path to where should the resulting image be saved
494
     *  // note that by simply setting a different extension to the file will
495
     *  // instruct the script to create an image of that particular type
496
     *  $img->target_path = 'path/to/target.ext';
497
     *
498
     *  // flip the image both horizontally and vertically
499
     *  $img->flip_both();
500
     *  </code>
501
     *
502
     * @return boolean     Returns TRUE on success or FALSE on error.
503
     *
504
     *                      If FALSE is returned, check the {@link error} property to see the error code.
505
     * @since 2.1
506
     *
507
     */
508
    public function flip_both()
509
    {
510
        return $this->_flip('both');
511
    }
512
513
    /**
514
     *  Flips horizontally the image given as {@link source_path} and outputs the resulted image as {@link target_path}
515
     *
516
     *  <code>
517
     *  // include the Zebra_Image library
518
     *  require 'path/to/Zebra_Image.php';
519
     *
520
     *  // instantiate the class
521
     *  $img = new Zebra_Image();
522
     *
523
     *  // a source image
524
     *  $img->source_path = 'path/to/source.ext';
525
     *
526
     *  // path to where should the resulting image be saved
527
     *  // note that by simply setting a different extension to the file will
528
     *  // instruct the script to create an image of that particular type
529
     *  $img->target_path = 'path/to/target.ext';
530
     *
531
     *  // flip the image horizontally
532
     *  $img->flip_horizontal();
533
     *  </code>
534
     *
535
     * @return boolean     Returns TRUE on success or FALSE on error.
536
     *
537
     *                      If FALSE is returned, check the {@link error} property to see the error code.
538
     */
539
    public function flip_horizontal()
540
    {
541
        return $this->_flip('horizontal');
542
    }
543
544
    /**
545
     *  Flips vertically the image given as {@link source_path} and outputs the resulted image as {@link target_path}
546
     *
547
     *  <code>
548
     *  // include the Zebra_Image library
549
     *  require 'path/to/Zebra_Image.php';
550
     *
551
     *  // instantiate the class
552
     *  $img = new Zebra_Image();
553
     *
554
     *  // a source image
555
     *  $img->source_path = 'path/to/source.ext';
556
     *
557
     *  // path to where should the resulting image be saved
558
     *  // note that by simply setting a different extension to the file will
559
     *  // instruct the script to create an image of that particular type
560
     *  $img->target_path = 'path/to/target.ext';
561
     *
562
     *  // flip the image vertically
563
     *  $img->flip_vertical();
564
     *  </code>
565
     *
566
     * @return boolean     Returns TRUE on success or FALSE on error.
567
     *
568
     *                      If FALSE is returned, check the {@link error} property to see the error code.
569
     */
570
    public function flip_vertical()
571
    {
572
        return $this->_flip('vertical');
573
    }
574
575
    /**
576
     *  Resizes the image given as {@link source_path} and outputs the resulted image as {@link target_path}.
577
     *
578
     *  <code>
579
     *  // include the Zebra_Image library
580
     *  require 'path/to/Zebra_Image.php';
581
     *
582
     *  // instantiate the class
583
     *  $img = new Zebra_Image();
584
     *
585
     *  // a source image
586
     *  $img->source_path = 'path/to/source.ext';
587
     *
588
     *  // path to where should the resulting image be saved
589
     *  // note that by simply setting a different extension to the file will
590
     *  // instruct the script to create an image of that particular type
591
     *  $img->target_path = 'path/to/target.ext';
592
     *
593
     *  // apply a "sharpen" filter to the resulting images
594
     *  $img->sharpen_images = true;
595
     *
596
     *  // resize the image to exactly 150x150 pixels, without altering
597
     *  // aspect ratio, by using the CROP_CENTER method
598
     *  $img->resize(150, 150, ZEBRA_IMAGE_CROP_CENTER);
599
     *  </code>
600
     *
601
     * @param integer     $width                The width to resize the image to.
602
     *
603
     *                                          If set to <b>0</b>, the width will be automatically adjusted, depending
604
     *                                          on the value of the <b>height</b> argument so that the image preserves
605
     *                                          its aspect ratio.
606
     *
607
     *                                          If {@link preserve_aspect_ratio} is set to TRUE and both this and the
608
     *                                          <b>height</b> arguments are values greater than <b>0</b>, the image will
609
     *                                          be resized to the exact required width and height and the aspect ratio
610
     *                                          will be preserved - (also see the description for the <b>method</b>
611
     *                                          argument below on how can this be done).
612
     *
613
     *                                          If {@link preserve_aspect_ratio} is set to FALSE, the image will be
614
     *                                          resized to the required width and the aspect ratio will be ignored.
615
     *
616
     *                                          If both <b>width</b> and <b>height</b> are set to <b>0</b>, a copy of
617
     *                                          the source image will be created ({@link jpeg_quality} and
618
     *                                          {@link png_compression} will still apply).
619
     *
620
     *                                          If either <b>width</b> or <b>height</b> are set to <b>0</b>, the script
621
     *                                          will consider the value of the {@link preserve_aspect_ratio} to bet set
622
     *                                          to TRUE regardless of its actual value!
623
     *
624
     * @param integer     $height               The height to resize the image to.
625
     *
626
     *                                          If set to <b>0</b>, the height will be automatically adjusted, depending
627
     *                                          on the value of the <b>width</b> argument so that the image preserves
628
     *                                          its aspect ratio.
629
     *
630
     *                                          If {@link preserve_aspect_ratio} is set to TRUE and both this and the
631
     *                                          <b>width</b> arguments are values greater than <b>0</b>, the image will
632
     *                                          be resized to the exact required width and height and the aspect ratio
633
     *                                          will be preserved - (also see the description for the <b>method</b>
634
     *                                          argument below on how can this be done).
635
     *
636
     *                                          If {@link preserve_aspect_ratio} is set to FALSE, the image will be
637
     *                                          resized to the required height and the aspect ratio will be ignored.
638
     *
639
     *                                          If both <b>width</b> and <b>height</b> are set to <b>0</b>, a copy of
640
     *                                          the source image will be created ({@link jpeg_quality} and
641
     *                                          {@link png_compression} will still apply).
642
     *
643
     *                                          If either <b>height</b> or <b>width</b> are set to <b>0</b>, the script
644
     *                                          will consider the value of the {@link preserve_aspect_ratio} to bet set
645
     *                                          to TRUE regardless of its actual value!
646
     *
647
     * @param int         $method               (Optional) Method to use when resizing images to exact width and height
648
     *                                          while preserving aspect ratio.
649
     *
650
     *                                          If the {@link preserve_aspect_ratio} property is set to TRUE and both the
651
     *                                          <b>width</b> and <b>height</b> arguments are values greater than <b>0</b>,
652
     *                                          the image will be resized to the exact given width and height and the
653
     *                                          aspect ratio will be preserved by using on of the following methods:
654
     *
655
     *                                          -   <b>ZEBRA_IMAGE_BOXED</b> - the image will be scaled so that it will
656
     *                                              fit in a box with the given width and height (both width/height will
657
     *                                              be smaller or equal to the required width/height) and then it will
658
     *                                              be centered both horizontally and vertically. The blank area will be
659
     *                                              filled with the color specified by the <b>bgcolor</b> argument. (the
660
     *                                              blank area will be filled only if the image is not transparent!)
661
     *
662
     *                                          -   <b>ZEBRA_IMAGE_NOT_BOXED</b> - the image will be scaled so that it
663
     *                                              <i>could</i> fit in a box with the given width and height but will
664
     *                                              not be enclosed in a box with given width and height. The new width/
665
     *                                              height will be both smaller or equal to the required width/height
666
     *
667
     *                                          -   <b>ZEBRA_IMAGE_CROP_TOPLEFT</b>
668
     *                                          -   <b>ZEBRA_IMAGE_CROP_TOPCENTER</b>
669
     *                                          -   <b>ZEBRA_IMAGE_CROP_TOPRIGHT</b>
670
     *                                          -   <b>ZEBRA_IMAGE_CROP_MIDDLELEFT</b>
671
     *                                          -   <b>ZEBRA_IMAGE_CROP_CENTER</b>
672
     *                                          -   <b>ZEBRA_IMAGE_CROP_MIDDLERIGHT</b>
673
     *                                          -   <b>ZEBRA_IMAGE_CROP_BOTTOMLEFT</b>
674
     *                                          -   <b>ZEBRA_IMAGE_CROP_BOTTOMCENTER</b>
675
     *                                          -   <b>ZEBRA_IMAGE_CROP_BOTTOMRIGHT</b>
676
     *
677
     *                                          For the methods involving crop, first the image is scaled so that both
678
     *                                          its sides are equal or greater than the respective sizes of the bounding
679
     *                                          box; next, a region of required width and height will be cropped from
680
     *                                          indicated region of the resulted image.
681
     *
682
     *                                          Default is ZEBRA_IMAGE_CROP_CENTER
683
     *
684
     * @param hexadecimal $background_color     (Optional) The hexadecimal color (like "#FFFFFF" or "#FFF") of the
0 ignored issues
show
The type hexadecimal was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
685
     *                                          blank area. See the <b>method</b> argument.
686
     *
687
     *                                          When set to -1 the script will preserve transparency for transparent GIF
688
     *                                          and PNG images. For non-transparent images the background will be white
689
     *                                          (#FFFFFF) in this case.
690
     *
691
     *                                          Default is -1
692
     *
693
     * @return boolean                         Returns TRUE on success or FALSE on error.
694
     *
695
     *                                          If FALSE is returned, check the {@link error} property to see what went
696
     *                                          wrong
697
     */
698
    public function resize($width = 0, $height = 0, $method = ZEBRA_IMAGE_CROP_CENTER, $background_color = -1)
699
    {
700
        // if image resource was successfully created
701
        if ($this->_create_from_source()) {
702
            // if either width or height is to be adjusted automatically
703
            // set a flag telling the script that, even if $preserve_aspect_ratio is set to false
704
            // treat everything as if it was set to true
705
            if ($width == 0 || $height == 0) {
706
                $auto_preserve_aspect_ratio = true;
707
            }
708
709
            // if aspect ratio needs to be preserved
710
            if ($this->preserve_aspect_ratio || isset($auto_preserve_aspect_ratio)) {
711
                // if height is given and width is to be computed accordingly
712
                if ($width == 0 && $height > 0) {
713
                    // get the original image's aspect ratio
714
                    $aspect_ratio = $this->source_width / $this->source_height;
715
716
                    // the target image's height is as given as argument to the method
717
                    $target_height = $height;
718
719
                    // compute the target image's width, preserving the aspect ratio
720
                    $target_width = round($height * $aspect_ratio);
721
                    // if width is given and height is to be computed accordingly
722
                } elseif ($width > 0 && $height == 0) {
723
                    // get the original image's aspect ratio
724
                    $aspect_ratio = $this->source_height / $this->source_width;
725
726
                    // the target image's width is as given as argument to the method
727
                    $target_width = $width;
728
729
                    // compute the target image's height, preserving the aspect ratio
730
                    $target_height = round($width * $aspect_ratio);
731
                    // if both width and height are given and ZEBRA_IMAGE_BOXED or ZEBRA_IMAGE_NOT_BOXED methods are to be used
732
                } elseif ($width > 0 && $height > 0 && ($method == 0 || $method == 1)) {
733
                    // compute the horizontal and vertical aspect ratios
734
                    $vertical_aspect_ratio   = $height / $this->source_height;
735
                    $horizontal_aspect_ratio = $width / $this->source_width;
736
737
                    // if the image's newly computed height would be inside the bounding box
738
                    if (round($horizontal_aspect_ratio * $this->source_height < $height)) {
0 ignored issues
show
$horizontal_aspect_ratio...source_height < $height of type boolean is incompatible with the type double|integer expected by parameter $num of round(). ( Ignorable by Annotation )

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

738
                    if (round(/** @scrutinizer ignore-type */ $horizontal_aspect_ratio * $this->source_height < $height)) {
Loading history...
739
                        // the target image's width is as given as argument to the method
740
                        $target_width = $width;
741
742
                        // compute the target image's height so that the image will stay inside the bounding box
743
                        $target_height = round($horizontal_aspect_ratio * $this->source_height);
744
                        // otherwise
745
                    } else {
746
                        // the target image's height is as given as argument to the method
747
                        $target_height = $height;
748
749
                        // compute the target image's width so that the image will stay inside the bounding box
750
                        $target_width = round($vertical_aspect_ratio * $this->source_width);
751
                    }
752
                    // if both width and height are given and image is to be cropped in order to get to the required size
753
                } elseif ($width > 0 && $height > 0 && $method > 1 && $method < 11) {
754
                    // compute the horizontal and vertical aspect ratios
755
                    $vertical_aspect_ratio   = $this->source_height / $height;
756
                    $horizontal_aspect_ratio = $this->source_width / $width;
757
758
                    // we'll use one of the two
759
                    $aspect_ratio =
760
761
                        $vertical_aspect_ratio < $horizontal_aspect_ratio ?
762
763
                            $vertical_aspect_ratio :
764
765
                            $horizontal_aspect_ratio;
766
767
                    // compute the target image's width, preserving the aspect ratio
768
                    $target_width = round($this->source_width / $aspect_ratio);
769
770
                    // compute the target image's height, preserving the aspect ratio
771
                    $target_height = round($this->source_height / $aspect_ratio);
772
                    // for any other case
773
                } else {
774
                    // we will create a copy of the source image
775
                    $target_width  = $this->source_width;
776
                    $target_height = $this->source_height;
777
                }
778
                // if aspect ratio does not need to be preserved
779
            } else {
780
                // compute the target image's width
781
                $target_width = ($width > 0 ? $width : $this->source_width);
782
783
                // compute the target image's height
784
                $target_height = ($height > 0 ? $height : $this->source_height);
785
            }
786
787
            // if
788
            if (
789
790
                // all images are to be resized - including images that are smaller than the given width/height
791
                $this->enlarge_smaller_images
792
                || // smaller images than the given width/height are to be left untouched
793
                // but current image has at leas one side that is larger than the required width/height
794
                ($width > 0 && $height > 0 ?
795
796
                    ($this->source_width > $width || $this->source_height > $height) :
797
798
                    ($this->source_width > $target_width || $this->source_height > $target_height)
799
800
                )
801
802
            ) {
803
                // if
804
                if (
805
806
                    // aspect ratio needs to be preserved AND
807
                    ($this->preserve_aspect_ratio || isset($auto_preserve_aspect_ratio))
808
                    && // both width and height are given
809
                    ($width > 0 && $height > 0)
810
                    && // images are to be cropped
811
                    ($method > 1 && $method < 11)
812
813
                ) {
814
                    // prepare the target image
815
                    $target_identifier = $this->_prepare_image($target_width, $target_height, $background_color);
816
817
                    imagecopyresampled(
818
819
                        $target_identifier,
820
                        $this->source_identifier,
821
                        0,
822
                        0,
823
                        0,
824
                        0,
825
                        $target_width,
826
                        $target_height,
827
                        $this->source_width,
828
                        $this->source_height
829
830
                    );
831
832
                    // do the crop according to the required method
833
                    switch ($method) {
834
                        // if image needs to be cropped from the top-left corner
835
                        case ZEBRA_IMAGE_CROP_TOPLEFT:
836
837
                            // crop accordingly
838
                            return $this->crop(
839
                                0,
840
                                0,
841
                                $width,
842
                                $height,
843
                                $target_identifier // crop this resource instead
844
                            );
845
846
                            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
847
848
                        // if image needs to be cropped from the top-center
849
                        case ZEBRA_IMAGE_CROP_TOPCENTER:
850
851
                            // crop accordingly
852
                            return $this->crop(
853
                                floor(($target_width - $width) / 2),
854
                                0,
855
                                floor(($target_width - $width) / 2) + $width,
856
                                $height,
857
                                $target_identifier // crop this resource instead
858
                            );
859
860
                            break;
861
862
                        // if image needs to be cropped from the top-right corner
863
                        case ZEBRA_IMAGE_CROP_TOPRIGHT:
864
865
                            // crop accordingly
866
                            return $this->crop(
867
                                $target_width - $width,
868
                                0,
869
                                $target_width,
870
                                $height,
871
                                $target_identifier // crop this resource instead
872
                            );
873
874
                            break;
875
876
                        // if image needs to be cropped from the middle-left
877
                        case ZEBRA_IMAGE_CROP_MIDDLELEFT:
878
879
                            // crop accordingly
880
                            return $this->crop(
881
882
                                0,
883
                                floor(($target_height - $height) / 2),
884
                                $width,
885
                                floor(($target_height - $height) / 2) + $height,
886
                                $target_identifier // crop this resource instead
887
888
                            );
889
890
                            break;
891
892
                        // if image needs to be cropped from the center of the image
893
                        case ZEBRA_IMAGE_CROP_CENTER:
894
895
                            // crop accordingly
896
                            return $this->crop(
897
898
                                floor(($target_width - $width) / 2),
899
                                floor(($target_height - $height) / 2),
900
                                floor(($target_width - $width) / 2) + $width,
901
                                floor(($target_height - $height) / 2) + $height,
902
                                $target_identifier // crop this resource instead
903
904
                            );
905
906
                            break;
907
908
                        // if image needs to be cropped from the middle-right
909
                        case ZEBRA_IMAGE_CROP_MIDDLERIGHT:
910
911
                            // crop accordingly
912
                            return $this->crop(
913
914
                                $target_width - $width,
915
                                floor(($target_height - $height) / 2),
916
                                $target_width,
917
                                floor(($target_height - $height) / 2) + $height,
918
                                $target_identifier // crop this resource instead
919
920
                            );
921
922
                            break;
923
924
                        // if image needs to be cropped from the bottom-left corner
925
                        case ZEBRA_IMAGE_CROP_BOTTOMLEFT:
926
927
                            // crop accordingly
928
                            return $this->crop(
929
930
                                0,
931
                                $target_height - $height,
932
                                $width,
933
                                $target_height,
934
                                $target_identifier // crop this resource instead
935
936
                            );
937
938
                            break;
939
940
                        // if image needs to be cropped from the bottom-center
941
                        case ZEBRA_IMAGE_CROP_BOTTOMCENTER:
942
943
                            // crop accordingly
944
                            return $this->crop(
945
946
                                floor(($target_width - $width) / 2),
947
                                $target_height - $height,
948
                                floor(($target_width - $width) / 2) + $width,
949
                                $target_height,
950
                                $target_identifier // crop this resource instead
951
952
                            );
953
954
                            break;
955
956
                        // if image needs to be cropped from the bottom-right corner
957
                        case ZEBRA_IMAGE_CROP_BOTTOMRIGHT:
958
959
                            // crop accordingly
960
                            return $this->crop(
961
962
                                $target_width - $width,
963
                                $target_height - $height,
964
                                $target_width,
965
                                $target_height,
966
                                $target_identifier // crop this resource instead
967
968
                            );
969
970
                            break;
971
                    }
972
973
                    // if aspect ratio doesn't need to be preserved or
974
                    // it needs to be preserved and method is ZEBRA_IMAGE_BOXED or ZEBRA_IMAGE_NOT_BOXED
975
                } else {
976
                    // prepare the target image
977
                    $target_identifier = $this->_prepare_image(
978
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? $width : $target_width),
979
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? $height : $target_height),
980
                        $background_color
981
                    );
982
983
                    imagecopyresampled(
984
985
                        $target_identifier,
986
                        $this->source_identifier,
987
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? ($width - $target_width) / 2 : 0),
988
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? ($height - $target_height) / 2 : 0),
989
                        0,
990
                        0,
991
                        $target_width,
992
                        $target_height,
993
                        $this->source_width,
994
                        $this->source_height
995
996
                    );
997
998
                    // if script gets this far, write the image to disk
999
                    return $this->_write_image($target_identifier);
1000
                }
1001
1002
                // if we get here it means that
1003
                // smaller images than the given width/height are to be left untouched
1004
                // therefore, we save the image as it is
1005
            } else {
1006
                // prepare the target image
1007
                $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, $background_color);
1008
1009
                imagecopyresampled(
1010
1011
                    $target_identifier,
1012
                    $this->source_identifier,
1013
                    0,
1014
                    0,
1015
                    0,
1016
                    0,
1017
                    $this->source_width,
1018
                    $this->source_height,
1019
                    $this->source_width,
1020
                    $this->source_height
1021
1022
                );
1023
1024
                // previously to 2.2.7 I was simply calling the _write_images() method without the code from above this
1025
                // comment and therefore, when resizing transparent images to a format which doesn't support transparency
1026
                // and the "enlarge_smaller_images" property being set to FALSE, the "background_color" argument was not
1027
                // applied and lead to unexpected background colors for the resulting images
1028
                return $this->_write_image($target_identifier);
1029
            }
1030
        }
1031
1032
        // if script gets this far return false
1033
        // note that we do not set the error level as it has been already set
1034
        // by the _create_from_source() method earlier
1035
        return false;
1036
    }
1037
1038
    /**
1039
     *  Rotates the image given as {@link source_path} and outputs the resulted image as {@link target_path}.
1040
     *
1041
     *  <code>
1042
     *  // include the Zebra_Image library
1043
     *  require 'path/to/Zebra_Image.php';
1044
     *
1045
     *  // instantiate the class
1046
     *  $img = new Zebra_Image();
1047
     *
1048
     *  // a source image
1049
     *  $img->source_path = 'path/to/source.ext';
1050
     *
1051
     *  // path to where should the resulting image be saved
1052
     *  // note that by simply setting a different extension to the file will
1053
     *  // instruct the script to create an image of that particular type
1054
     *  $img->target_path = 'path/to/target.ext';
1055
     *
1056
     *  // rotate the image 45 degrees, clockwise
1057
     *  $img->rotate(45);
1058
     *  </code>
1059
     *
1060
     * @param double $angle                     Angle by which to rotate the image clockwise.
1061
     *
1062
     *                                          Between 0 and 360.
1063
     *
1064
     * @param mixed  $background_color          (Optional) The hexadecimal color (like "#FFFFFF" or "#FFF") of the
1065
     *                                          uncovered zone after the rotation.
1066
     *
1067
     *                                          When set to -1 the script will preserve transparency for transparent GIF
1068
     *                                          and PNG images. For non-transparent images the background will be white
1069
     *                                          (#FFFFFF) in this case.
1070
     *
1071
     *                                          Default is -1.
1072
     *
1073
     * @return boolean                         Returns TRUE on success or FALSE on error.
1074
     *
1075
     *                                          If FALSE is returned, check the {@link error} property to see the error
1076
     *                                          code.
1077
     */
1078
    public function rotate($angle, $background_color = -1)
1079
    {
1080
        // get function arguments
1081
        $arguments = func_get_args();
1082
1083
        // if a third argument exists
1084
        $use_existing_source = (isset($arguments[2]) && $arguments[2] === false);
1085
1086
        // if we came here just to fix orientation or if image resource was successfully created
1087
        if ($use_existing_source || $this->_create_from_source()) {
1088
            // angles are given clockwise but imagerotate works counterclockwise so we need to negate our value
1089
            $angle = -$angle;
1090
1091
            // if the uncovered zone after the rotation is to be transparent
1092
            if ($background_color == -1) {
1093
                // if target image is a PNG
1094
                if ($this->target_type == 'png') {
1095
                    // allocate a transparent color
1096
                    $background_color = imagecolorallocatealpha($this->source_identifier, 0, 0, 0, 127);
1097
                    // if target image is a GIF
1098
                } elseif ($this->target_type == 'gif') {
1099
                    // if source image was a GIF and a transparent color existed
1100
                    if ($this->source_type == IMAGETYPE_GIF && $this->source_transparent_color_index >= 0) {
1101
                        // use that color
1102
                        $background_color = imagecolorallocate(
1103
                            $this->source_identifier,
1104
                            $this->source_transparent_color['red'],
1105
                            $this->source_transparent_color['green'],
1106
                            $this->source_transparent_color['blue']
1107
                        );
1108
                        // if image had no transparent color
1109
                    } else {
1110
                        // allocate a transparent color
1111
                        $background_color = imagecolorallocate($this->source_identifier, 255, 255, 255);
1112
1113
                        // make color transparent
1114
                        imagecolortransparent($this->source_identifier, $background_color);
1115
                    }
1116
                    // for other image types
1117
                } else {
1118
                    // use white as the color of uncovered zone after the rotation
1119
                    $background_color = imagecolorallocate($this->source_identifier, 255, 255, 255);
1120
                }
1121
                // if a background color is given
1122
            } else {
1123
                // convert the color to RGB values
1124
                $background_color = $this->_hex2rgb($background_color);
1125
1126
                // allocate the color to the image identifier
1127
                $background_color = imagecolorallocate(
1128
1129
                    $this->source_identifier,
1130
                    $background_color['r'],
1131
                    $background_color['g'],
1132
                    $background_color['b']
1133
1134
                );
1135
            }
1136
1137
            // rotate the image
1138
            $target_identifier = imagerotate($this->source_identifier, $angle, $background_color);
1139
1140
            // if we called this method from the _create_from_source() method
1141
            // because we are fixing orientation
1142
            if ($use_existing_source) {
1143
                // make any further method work on the rotated image
1144
                $this->source_identifier = $target_identifier;
0 ignored issues
show
Bug Best Practice introduced by
The property source_identifier does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1145
1146
                // update the width and height of the image to the values
1147
                // of the rotated image
1148
                $this->source_width  = imagesx($target_identifier);
0 ignored issues
show
Bug Best Practice introduced by
The property source_width does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1149
                $this->source_height = imagesy($target_identifier);
0 ignored issues
show
Bug Best Practice introduced by
The property source_height does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1150
1151
                return true;
1152
                // write image otherwise
1153
            } else {
1154
                return $this->_write_image($target_identifier);
1155
            }
1156
        }
1157
1158
        // if script gets this far return false
1159
        // note that we do not set the error level as it has been already set
1160
        // by the _create_from_source() method earlier
1161
        return false;
1162
    }
1163
1164
    /**
1165
     *  Returns an array containing the image identifier representing the image obtained from {@link $source_path}, the
1166
     *  image's width and height and the image's type
1167
     *
1168
     * @access private
1169
     */
1170
    private function _create_from_source()
1171
    {
1172
        // perform some error checking first
1173
        // if the GD library is not installed
1174
        if (!function_exists('gd_info')) {
1175
            // save the error level and stop the execution of the script
1176
            $this->error = 7;
1177
1178
            return false;
1179
            // if source file does not exist
1180
        } elseif (!is_file($this->source_path)) {
1181
            // save the error level and stop the execution of the script
1182
            $this->error = 1;
1183
1184
            return false;
1185
            // if source file is not readable
1186
        } elseif (!is_readable($this->source_path)) {
1187
            // save the error level and stop the execution of the script
1188
            $this->error = 2;
1189
1190
            return false;
1191
            // if target file is same as source file and source file is not writable
1192
        } elseif ($this->target_path == $this->source_path && !is_writable($this->source_path)) {
1193
            // save the error level and stop the execution of the script
1194
            $this->error = 3;
1195
1196
            return false;
1197
1198
            // try to get source file width, height and type
1199
            // and if it founds an unsupported file type
1200
        } elseif (!list($this->source_width, $this->source_height, $this->source_type) = @getimagesize($this->source_path)) {
0 ignored issues
show
Bug Best Practice introduced by
The property source_height does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Bug Best Practice introduced by
The property source_width does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Bug Best Practice introduced by
The property source_type does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1201
            // save the error level and stop the execution of the script
1202
            $this->error = 4;
1203
1204
            return false;
1205
            // if no errors so far
1206
        } else {
1207
            // get target file's type based on the file extension
1208
            $this->target_type = strtolower(substr($this->target_path, strrpos($this->target_path, '.') + 1));
0 ignored issues
show
Bug Best Practice introduced by
The property target_type does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1209
1210
            // create an image from file using extension dependant function
1211
            // checks for file extension
1212
            switch ($this->source_type) {
1213
                // if GIF
1214
                case IMAGETYPE_GIF:
1215
1216
                    // create an image from file
1217
                    $identifier = imagecreatefromgif($this->source_path);
1218
1219
                    // get the index of the transparent color (if any)
1220
                    if (($this->source_transparent_color_index = imagecolortransparent($identifier)) >= 0)
0 ignored issues
show
Bug Best Practice introduced by
The property source_transparent_color_index does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1221
1222
                        // get the transparent color's RGB values
1223
                        // we have to mute errors because there are GIF images which *are* transparent and everything
1224
                        // works as expected, but imagecolortransparent() returns a color that is outside the range of
1225
                        // colors in the image's pallette...
1226
                    {
1227
                        $this->source_transparent_color = @imagecolorsforindex($identifier, $this->source_transparent_color_index);
0 ignored issues
show
Bug Best Practice introduced by
The property source_transparent_color does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1228
                    }
1229
1230
                    break;
1231
1232
                // if JPEG
1233
                case IMAGETYPE_JPEG:
1234
1235
                    // create an image from file
1236
                    $identifier = imagecreatefromjpeg($this->source_path);
1237
1238
                    break;
1239
1240
                // if PNG
1241
                case IMAGETYPE_PNG:
1242
1243
                    // create an image from file
1244
                    $identifier = imagecreatefrompng($this->source_path);
1245
1246
                    // disable blending
1247
                    imagealphablending($identifier, false);
1248
1249
                    // save full alpha channel information
1250
                    imagesavealpha($identifier, true);
1251
1252
                    break;
1253
1254
                default:
1255
1256
                    // if unsupported file type
1257
                    // note that we call this if the file is not GIF, JPG or PNG even though the getimagesize function
1258
                    // handles more image types
1259
                    $this->error = 4;
1260
1261
                    return false;
1262
            }
1263
        }
1264
1265
        // if target file has to have the same timestamp as the source image
1266
        // save it as a global property of the class
1267
        if ($this->preserve_time) {
1268
            $this->source_image_time = filemtime($this->source_path);
0 ignored issues
show
Bug Best Practice introduced by
The property source_image_time does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1269
        }
1270
1271
        // make available the source image's identifier
1272
        $this->source_identifier = $identifier;
0 ignored issues
show
Bug Best Practice introduced by
The property source_identifier does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1273
1274
        // for JPEG files, if we need to handle exif orientation automatically
1275
        if ($this->auto_handle_exif_orientation && $this->source_type === IMAGETYPE_JPEG) // if "exif_read_data" function is not available, return false
1276
        {
1277
            if (!function_exists('exif_read_data')) {
1278
                // save the error level and stop the execution of the script
1279
                $this->error = 9;
1280
1281
                return false;
1282
                // if "exif_read_data" function is available, EXIF information is available, orientation information is available and orientation needs fixing
1283
            } elseif (($exif = exif_read_data($this->source_path)) && isset($exif['Orientation']) && in_array($exif['Orientation'], [3, 6, 8])) {
1284
                // fix the orientation
1285
                switch ($exif['Orientation']) {
1286
                    case 3:
1287
1288
                        // 180 rotate left
1289
                        $this->rotate(180, -1, false);
1290
                        break;
1291
1292
                    case 6:
1293
1294
                        // 90 rotate right
1295
                        $this->rotate(90, -1, false);
1296
                        break;
1297
1298
                    case 8:
1299
1300
                        // 90 rotate left
1301
                        $this->rotate(-90, -1, false);
1302
                        break;
1303
                }
1304
            }
1305
        }
1306
1307
        return true;
1308
    }
1309
1310
    /**
1311
     *  Flips horizontally or vertically or both ways the image given as {@link source_path}.
1312
     *
1313
     * @return boolean     Returns TRUE on success or FALSE on error.
1314
     *
1315
     *                      If FALSE is returned, check the {@link error} property to see the error code.
1316
     * @since  2.1
1317
     *
1318
     * @access private
1319
     *
1320
     */
1321
    private function _flip($orientation)
1322
    {
1323
        // if image resource was successfully created
1324
        if ($this->_create_from_source()) {
1325
            // prepare the target image
1326
            $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, -1);
1327
1328
            // flip according to $orientation
1329
            switch ($orientation) {
1330
                case 'horizontal':
1331
1332
                    imagecopyresampled(
1333
1334
                        $target_identifier,
1335
                        $this->source_identifier,
1336
                        0,
1337
                        0,
1338
                        ($this->source_width - 1),
1339
                        0,
1340
                        $this->source_width,
1341
                        $this->source_height,
1342
                        -$this->source_width,
1343
                        $this->source_height
1344
1345
                    );
1346
1347
                    break;
1348
1349
                case 'vertical':
1350
1351
                    imagecopyresampled(
1352
1353
                        $target_identifier,
1354
                        $this->source_identifier,
1355
                        0,
1356
                        0,
1357
                        0,
1358
                        ($this->source_height - 1),
1359
                        $this->source_width,
1360
                        $this->source_height,
1361
                        $this->source_width,
1362
                        -$this->source_height
1363
1364
                    );
1365
1366
                    break;
1367
1368
                case 'both':
1369
1370
                    imagecopyresampled(
1371
1372
                        $target_identifier,
1373
                        $this->source_identifier,
1374
                        0,
1375
                        0,
1376
                        ($this->source_width - 1),
1377
                        ($this->source_height - 1),
1378
                        $this->source_width,
1379
                        $this->source_height,
1380
                        -$this->source_width,
1381
                        -$this->source_height
1382
1383
                    );
1384
1385
                    break;
1386
            }
1387
1388
            // write image
1389
            return $this->_write_image($target_identifier);
1390
        }
1391
1392
        // if script gets this far, return false
1393
        // note that we do not set the error level as it has been already set
1394
        // by the _create_from_source() method earlier
1395
        return false;
1396
    }
1397
1398
    /**
1399
     *  Converts a hexadecimal representation of a color (i.e. #123456 or #AAA) to a RGB representation.
1400
     *
1401
     *  The RGB values will be a value between 0 and 255 each.
1402
     *
1403
     * @param string $color                 Hexadecimal representation of a color (i.e. #123456 or #AAA).
1404
     *
1405
     * @param string $default_on_error      (Optional) Hexadecimal representation of a color to be used in case $color
1406
     *                                      is not recognized as a hexadecimal color.
1407
     *
1408
     *                                      Default is #FFFFFF
1409
     *
1410
     * @return array                       Returns an associative array with the values of (R)ed, (G)reen and (B)lue
1411
     *
1412
     * @access private
1413
     */
1414
    private function _hex2rgb($color, $default_on_error = '#FFFFFF')
1415
    {
1416
        // if color is not formatted correctly
1417
        // use the default color
1418
        if (preg_match('/^#?([a-f]|[0-9]){3}(([a-f]|[0-9]){3})?$/i', $color) == 0) {
1419
            $color = $default_on_error;
1420
        }
1421
1422
        // trim off the "#" prefix from $background_color
1423
        $color = ltrim($color, '#');
1424
1425
        // if color is given using the shorthand (i.e. "FFF" instead of "FFFFFF")
1426
        if (strlen($color) == 3) {
1427
            $tmp = '';
1428
1429
            // take each value
1430
            // and duplicate it
1431
            for ($i = 0; $i < 3; $i++) {
1432
                $tmp .= str_repeat($color[$i], 2);
1433
            }
1434
1435
            // the color in it's full, 6 characters length notation
1436
            $color = $tmp;
1437
        }
1438
1439
        // decimal representation of the color
1440
        $int = hexdec($color);
1441
1442
        // extract and return the RGB values
1443
        return [
1444
1445
            'r' => 0xFF & ($int >> 0x10),
1446
            'g' => 0xFF & ($int >> 0x8),
1447
            'b' => 0xFF & $int,
1448
1449
        ];
1450
    }
1451
1452
    /**
1453
     *  Creates a blank image of given width, height and background color.
1454
     *
1455
     * @param integer $width            Width of the new image.
1456
     *
1457
     * @param integer $height           Height of the new image.
1458
     *
1459
     * @param string  $background_color (Optional) The hexadecimal color of the background.
1460
     *
1461
     *                                          Can also be -1 case in which the script will try to create a transparent
1462
     *                                          image, if possible.
1463
     *
1464
     *                                          Default is #FFFFFF.
1465
     *
1466
     * @return                                 Returns the identifier of the newly created image.
0 ignored issues
show
The type Returns was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1467
     *
1468
     * @access private
1469
     */
1470
    private function _prepare_image($width, $height, $background_color = '#FFFFFF')
1471
    {
1472
        // create a blank image
1473
        $identifier = imagecreatetruecolor((int)$width <= 0 ? 1 : (int)$width, (int)$height <= 0 ? 1 : (int)$height);
1474
1475
        // if we are creating a transparent image, and image type supports transparency
1476
        if ($background_color == -1 && $this->target_type != 'jpg') {
1477
            // disable blending
1478
            imagealphablending($identifier, false);
1479
1480
            // allocate a transparent color
1481
            $background_color = imagecolorallocatealpha($identifier, 0, 0, 0, 127);
1482
1483
            // we also need to set this for saving gifs
1484
            imagecolortransparent($identifier, $background_color);
1485
1486
            // save full alpha channel information
1487
            imagesavealpha($identifier, true);
1488
            // if we are not creating a transparent image
1489
        } else {
1490
            // convert hex color to rgb
1491
            $background_color = $this->_hex2rgb($background_color);
1492
1493
            // prepare the background color
1494
            $background_color = imagecolorallocate($identifier, $background_color['r'], $background_color['g'], $background_color['b']);
1495
        }
1496
1497
        // fill the image with the background color
1498
        imagefill($identifier, 0, 0, $background_color);
1499
1500
        // return the image's identifier
1501
        return $identifier;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $identifier returns the type GdImage|resource which is incompatible with the documented return type Returns.
Loading history...
1502
    }
1503
1504
    /**
1505
     *  Sharpens images. Useful when creating thumbnails.
1506
     *
1507
     *  Code taken from the comments at {@link http://docs.php.net/imageconvolution}.
1508
     *
1509
     *  <i>This function will yield a result only for PHP version 5.1.0+ and will leave the image unaltered for older
1510
     *  versions!</i>
1511
     *
1512
     * @param  $identifier identifier  An image identifier
0 ignored issues
show
The type identifier was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1513
     *
1514
     * @access private
1515
     */
1516
    private function _sharpen_image($image)
1517
    {
1518
        // if the "sharpen_images" is set to true and we're running an appropriate version of PHP
1519
        // (the "imageconvolution" is available only for PHP 5.1.0+)
1520
        if ($this->sharpen_images && version_compare(PHP_VERSION, '5.1.0') >= 0) {
1521
            // the convolution matrix as an array of three arrays of three floats
1522
            $matrix = [
1523
                [-1.2, -1, -1.2],
1524
                [-1, 20, -1],
1525
                [-1.2, -1, -1.2],
1526
            ];
1527
1528
            // the divisor of the matrix
1529
            $divisor = array_sum(array_map('array_sum', $matrix));
1530
1531
            // color offset
1532
            $offset = 0;
1533
1534
            // sharpen image
1535
            imageconvolution($image, $matrix, $divisor, $offset);
1536
        }
1537
1538
        // return the image's identifier
1539
        return $image;
1540
    }
1541
1542
    /**
1543
     *  Creates a new image from given image identifier having the extension as specified by {@link target_path}.
1544
     *
1545
     * @param  $identifier identifier  An image identifier
1546
     *
1547
     * @return boolean                 Returns TRUE on success or FALSE on error.
1548
     *
1549
     *                                  If FALSE is returned, check the {@link error} property to see the error code.
1550
     *
1551
     * @access private
1552
     */
1553
    private function _write_image($identifier)
1554
    {
1555
        // sharpen image if it's required
1556
        $this->_sharpen_image($identifier);
1557
1558
        // image saving process goes according to required extension
1559
        switch ($this->target_type) {
1560
            // if GIF
1561
            case 'gif':
1562
1563
                // if GD support for this file type is not available
1564
                // in version 1.6 of GD the support for GIF files was dropped see
1565
                // http://php.net/manual/en/function.imagegif.php#function.imagegif.notes
1566
                if (!function_exists('imagegif')) {
1567
                    // save the error level and stop the execution of the script
1568
                    $this->error = 6;
1569
1570
                    return false;
1571
                    // if, for some reason, file could not be created
1572
                } elseif (@!imagegif($identifier, $this->target_path)) {
1573
                    // save the error level and stop the execution of the script
1574
                    $this->error = 3;
1575
1576
                    return false;
1577
                }
1578
1579
                break;
1580
1581
            // if JPEG
1582
            case 'jpg':
1583
            case 'jpeg':
1584
1585
                // if GD support for this file type is not available
1586
                if (!function_exists('imagejpeg')) {
1587
                    // save the error level and stop the execution of the script
1588
                    $this->error = 6;
1589
1590
                    return false;
1591
                    // if, for some reason, file could not be created
1592
                } elseif (@!imagejpeg($identifier, $this->target_path, $this->jpeg_quality)) {
1593
                    // save the error level and stop the execution of the script
1594
                    $this->error = 3;
1595
1596
                    return false;
1597
                }
1598
1599
                break;
1600
1601
            // if PNG
1602
            case 'png':
1603
1604
                // if GD support for this file type is not available
1605
                if (!function_exists('imagepng')) {
1606
                    // save the error level and stop the execution of the script
1607
                    $this->error = 6;
1608
1609
                    return false;
1610
                    // if, for some reason, file could not be created
1611
                } elseif (@!imagepng($identifier, $this->target_path, $this->png_compression)) {
1612
                    // save the error level and stop the execution of the script
1613
                    $this->error = 3;
1614
1615
                    return false;
1616
                }
1617
1618
                break;
1619
1620
            // if not a supported file extension
1621
            default:
1622
1623
                // save the error level and stop the execution of the script
1624
                $this->error = 5;
1625
1626
                return false;
1627
        }
1628
1629
        // get a list of functions disabled via configuration
1630
        $disabled_functions = @ini_get('disable_functions');
1631
1632
        // if the 'chmod' function is not disabled via configuration
1633
        if ($disabled_functions == '' || strpos('chmod', $disabled_functions) === false) {
0 ignored issues
show
It seems like $disabled_functions can also be of type false; however, parameter $needle of strpos() 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

1633
        if ($disabled_functions == '' || strpos('chmod', /** @scrutinizer ignore-type */ $disabled_functions) === false) {
Loading history...
1634
            // chmod the file
1635
            chmod($this->target_path, intval($this->chmod_value, 8));
1636
            // save the error level
1637
        } else {
1638
            $this->error = 8;
1639
        }
1640
1641
        // if target file has to have the same timestamp as the source image
1642
        if ($this->preserve_time && isset($this->source_image_time)) {
1643
            // touch the newly created file
1644
            @touch($this->target_path, $this->source_image_time);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for touch(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1644
            /** @scrutinizer ignore-unhandled */ @touch($this->target_path, $this->source_image_time);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1645
        }
1646
1647
        // free memory
1648
        imagedestroy($this->source_identifier);
1649
        imagedestroy($identifier);
1650
1651
        // return true
1652
        return true;
1653
    }
1654
}
1655