Image::getHeight()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/*
4
 * CKFinder
5
 * ========
6
 * http://cksource.com/ckfinder
7
 * Copyright (C) 2007-2016, CKSource - Frederico Knabben. All rights reserved.
8
 *
9
 * The software, this file and its contents are subject to the CKFinder
10
 * License. Please read the license.txt file before using, installing, copying,
11
 * modifying or distribute this file or part of its contents. The contents of
12
 * this file is part of the Source Code of CKFinder.
13
 */
14
15
namespace CKSource\CKFinder;
16
17
use CKSource\CKFinder\Exception\CKFinderException;
18
19
/**
20
 * The Image class.
21
 *
22
 * The class used for image processing.
23
 *
24
 * @copyright 2016 CKSource - Frederico Knabben
25
 */
26
class Image
27
{
28
    protected static $supportedExtensions = array('jpg', 'jpeg', 'gif', 'png');
29
30
    /**
31
     * Image width.
32
     *
33
     * @var int $witdh
34
     */
35
    protected $width;
36
37
    /**
38
     * Image height.
39
     *
40
     * @var int $height
41
     */
42
    protected $height;
43
44
    /**
45
     * Image MIME type.
46
     *
47
     * @var string $mime
48
     */
49
    protected $mime;
50
51
    /**
52
     * Number of bits for each color.
53
     *
54
     * @var string $mime
55
     */
56
    protected $bits;
57
58
    /**
59
     * Number of color channels (i.e. 3 for RGB pictures and 4 for CMYK pictures).
60
     *
61
     * @var int $channels
62
     */
63
    protected $channels;
64
65
    /**
66
     * GD image.
67
     *
68
     * @var resource $gdImage
69
     */
70
    protected $gdImage;
71
72
    /**
73
     * The size of the image produced by the `getData()` method.
74
     *
75
     * @var int $dataSize
76
     */
77
    protected $dataSize;
78
79
    /**
80
     * Quality of rescaled image.
81
     *
82
     * @var int
83
     */
84
    protected $resizeQuality;
85
86
    /**
87
     * The factory method.
88
     *
89
     * @param string $data
90
     * @param bool   $bmpSupport
91
     *
92
     * @return Image
93
     */
94
    public static function create($data, $bmpSupport = false)
95
    {
96
        return new Image($data, $bmpSupport);
97
    }
98
99
    /**
100
     * Parses the image size from a string in the form of `[width]x[height]`,
101
     * for example 278x219.
102
     *
103
     * @param string $size WxH string
104
     *
105
     * @return array An array with width and height values array([width], [height]),
106
     *               for the example above: array(278, 219).
107
     */
108
    public static function parseSize($size)
109
    {
110
        $sizeParts = explode('x', trim($size));
111
112
        return count($sizeParts) === 2 ? array_map('intval', $sizeParts) : array(0, 0);
113
    }
114
115
    /**
116
     * Checks if a given exception is supported by the Image class.
117
     *
118
     * @param string $extension
119
     * @param bool   $bmpSupport
120
     *
121
     * @return bool
122
     */
123
    public static function isSupportedExtension($extension, $bmpSupport = false)
124
    {
125
        $supportedExtensions = static::$supportedExtensions;
126
127
        if ($bmpSupport) {
128
            $supportedExtensions[] = 'bmp';
129
        }
130
131
        return in_array(strtolower($extension), $supportedExtensions);
132
    }
133
134
    /**
135
     * Returns the MIME type for a given extension.
136
     *
137
     * @param string $extension
138
     *
139
     * @return string MIME type
140
     */
141
    public static function mimeTypeFromExtension($extension)
142
    {
143
        $extensionMimeTypeMap = array(
144
            'gif'  => 'image/gif',
145
            'jpg'  => 'image/jpeg',
146
            'jpeg' => 'image/jpeg',
147
            'bmp'  => 'image/bmp',
148
            'png'  => 'image/png',
149
            'wbmp' => 'image/wbmp'
150
        );
151
152
        $extension = strtolower($extension);
153
154
        return array_key_exists($extension, $extensionMimeTypeMap) ? $extensionMimeTypeMap[$extension] : null;
155
    }
156
157
    /**
158
     * Constructor.
159
     *
160
     * @param string $imageData  image data
161
     * @param bool   $bmpSupport `true` if bitmaps are supported (be aware of poor efficiency!).
162
     *
163
     * @throws CKFinderException in case the image could not be initialized properly.
164
     */
165
166
    public function __construct($imageData, $bmpSupport = false)
167
    {
168
        if (!extension_loaded('gd')) {
169
            throw new CKFinderException('PHP GD library not found');
170
        }
171
172
        $imgInfo = @getimagesizefromstring($imageData);
173
174
        if ($imgInfo === false) {
175
            throw new CKFinderException('Unsupported image type');
176
        }
177
178
        $this->width    = isset($imgInfo[0])          ? $imgInfo[0]          : 0;
179
        $this->height   = isset($imgInfo[1])          ? $imgInfo[1]          : 0;
180
        $this->mime     = isset($imgInfo['mime'])     ? $imgInfo['mime']     : '';
181
        $this->bits     = isset($imgInfo['bits'])     ? $imgInfo['bits']     : 8;
182
        $this->channels = isset($imgInfo['channels']) ? $imgInfo['channels'] : 3;
183
        $this->dataSize = strlen($imageData);
184
185
        if (!$this->width || !$this->height || !$this->mime) {
186
            throw new CKFinderException('Unsupported image type');
187
        }
188
189
        $this->setMemory($this->width, $this->height, $this->bits, $this->channels);
190
191
        $gdSupportedTypes = @imagetypes();
192
193
        $supportedFormats = array(
194
            'image/gif'      => $gdSupportedTypes & IMG_GIF,
195
            'image/jpeg'     => $gdSupportedTypes & IMG_JPG,
196
            'image/png'      => $gdSupportedTypes & IMG_PNG,
197
            'image/wbmp'     => $gdSupportedTypes & IMG_WBMP,
198
            'image/bmp'      => $bmpSupport && ($gdSupportedTypes & IMG_JPG),
199
            'image/x-ms-bmp' => $bmpSupport && ($gdSupportedTypes & IMG_JPG)
200
        );
201
202
        if (!array_key_exists($this->mime, $supportedFormats) || !$supportedFormats[$this->mime]) {
203
            throw new CKFinderException('Unsupported image type: ' . $this->mime);
204
        }
205
206
        if ($this->mime === 'image/bmp' || $this->mime === 'image/x-ms-bmp') {
207
            $this->gdImage = $this->createFromBmp($imageData);
208
        } else {
209
            $this->gdImage = imagecreatefromstring($imageData);
0 ignored issues
show
Documentation Bug introduced by
It seems like imagecreatefromstring($imageData) can also be of type GdImage. However, the property $gdImage is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
210
        }
211
212
        if (!is_resource($this->gdImage)) {
213
            throw new CKFinderException('Unsupported image type (not resource): ' . $this->mime);
214
        }
215
216
        unset($imageData);
217
    }
218
219
    /**
220
     * Returns the aspect ratio size as associative array:
221
     * @code
222
     * array
223
     * (
224
     *      [width]  => 80
225
     *      [heigth] => 120
226
     * )
227
     * @endcode
228
     *
229
     * @param int  $maxWidth        requested width
230
     * @param int  $maxHeight       requested height
231
     * @param int  $actualWidth     original width
232
     * @param int  $actualHeight    original height
233
     * @param bool $useHigherFactor defines which factor should be used to calculate the new
234
     *                              size. For example:
235
     *                              - original image size 800x400
236
     *                              - calculateAspectRatio(300, 200, 800, 400, false) will return 300x150
237
     *                              - calculateAspectRatio(300, 200, 800, 400, true) will return 400x200
238
     *
239
     * @return array
240
     */
241
    public static function calculateAspectRatio($maxWidth, $maxHeight, $actualWidth, $actualHeight, $useHigherFactor = false)
242
    {
243
        $oSize = array('width' => $maxWidth, 'height' => $maxHeight);
244
245
        // Calculates the X and Y resize factors
246
        $iFactorX = (float) $maxWidth / (float) $actualWidth;
247
        $iFactorY = (float) $maxHeight / (float) $actualHeight;
248
249
        // If some dimension have to be resized
250
        if ($iFactorX != 1 || $iFactorY != 1) {
251
            if ($useHigherFactor) {
252
                // Uses the higher Factor to change the opposite size
253
                if ($iFactorX > $iFactorY) {
254
                    $oSize['height'] = (int) round($actualHeight * $iFactorX);
255
                } elseif ($iFactorX < $iFactorY) {
256
                    $oSize['width'] = (int) round($actualWidth * $iFactorY);
257
                }
258
            } else {
259
                // Uses the lower Factor to change the opposite size
260
                if ($iFactorX < $iFactorY) {
261
                    $oSize['height'] = (int) round($actualHeight * $iFactorX);
262
                } elseif ($iFactorX > $iFactorY) {
263
                    $oSize['width'] = (int) round($actualWidth * $iFactorY);
264
                }
265
            }
266
        }
267
268
        if ($oSize['height'] <= 0) {
269
            $oSize['height'] = 1;
270
        }
271
272
        if ($oSize['width'] <= 0) {
273
            $oSize['width'] = 1;
274
        }
275
276
        // Returns the Size
277
        return $oSize;
278
    }
279
280
281
    /**
282
     * @link http://pl.php.net/manual/pl/function.imagecreatefromjpeg.php
283
     * function posted by e dot a dot schultz at gmail dot com
284
     *
285
     * @param $imageWidth
286
     * @param $imageHeight
287
     * @param $imageBits
288
     * @param $imageChannels
289
     *
290
     * @return bool
291
     */
292
    public function setMemory($imageWidth, $imageHeight, $imageBits, $imageChannels)
293
    {
294
        $MB = 1048576; // number of bytes in 1M
295
        $K64 = 65536; // number of bytes in 64K
296
        $TWEAKFACTOR = 2.4; // Or whatever works for you
297
        $memoryNeeded = round(($imageWidth * $imageHeight
298
                    * $imageBits
299
                    * $imageChannels / 8
300
                    + $K64
301
                ) * $TWEAKFACTOR
302
            ) + 3 * $MB;
303
304
        //ini_get('memory_limit') only works if compiled with "--enable-memory-limit" also
305
        //Default memory limit is 8MB so well stick with that.
306
        //To find out what yours is, view your php.ini file.
307
        $memoryLimit = Utils::returnBytes(@ini_get('memory_limit')) / $MB;
0 ignored issues
show
Bug introduced by
It seems like @ini_get('memory_limit') can also be of type false; however, parameter $val of CKSource\CKFinder\Utils::returnBytes() 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

307
        $memoryLimit = Utils::returnBytes(/** @scrutinizer ignore-type */ @ini_get('memory_limit')) / $MB;
Loading history...
308
        // There are no memory limits, nothing to do
309
        if ($memoryLimit == -1) {
310
            return true;
311
        }
312
        if (!$memoryLimit) {
313
            $memoryLimit = 8;
314
        }
315
316
        $memoryLimitMB = $memoryLimit * $MB;
317
        if (function_exists('memory_get_usage')) {
318
            if (memory_get_usage() + $memoryNeeded > $memoryLimitMB) {
319
                $newLimit = $memoryLimit + ceil((memory_get_usage()
320
                            + $memoryNeeded
321
                            - $memoryLimitMB
322
                        ) / $MB
323
                    );
324
                if (@ini_set('memory_limit', $newLimit . 'M') === false) {
325
                    return false;
326
                }
327
            }
328
        } else {
329
            if ($memoryNeeded + 3 * $MB > $memoryLimitMB) {
330
                $newLimit = $memoryLimit + ceil((3 * $MB
331
                            + $memoryNeeded
332
                            - $memoryLimitMB
333
                        ) / $MB
334
                    );
335
                if (false === @ini_set('memory_limit', $newLimit . 'M')) {
336
                    return false;
337
                }
338
            }
339
        }
340
341
        return true;
342
    }
343
344
    /**
345
     * @link http://pl.php.net/manual/en/function.imagecopyresampled.php
346
     * Replacement to `imagecopyresampled` that will deliver results that are almost identical except
347
     * MUCH faster (very typically 30 times faster).
348
     *
349
     * @static
350
     * @access public
351
     * @param resource $dstImage
352
     * @param resource $srcImage
353
     * @param int $dstX
354
     * @param int $dstY
355
     * @param int $srcX
356
     * @param int $srcY
357
     * @param int $dstW
358
     * @param int $dstH
359
     * @param int $srcW
360
     * @param int $srcH
361
     * @param int $quality
362
     *
363
     * @return boolean
364
     */
365
    public function fastCopyResampled(&$dstImage, $srcImage, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH, $quality = 3)
366
    {
367
        if (empty($srcImage) || empty($dstImage)) {
368
            return false;
369
        }
370
371
        if ($quality <= 1) {
372
            $temp = imagecreatetruecolor($dstW + 1, $dstH + 1);
373
            imagecopyresized($temp, $srcImage, $dstX, $dstY, $srcX, $srcY, $dstW + 1, $dstH + 1, $srcW, $srcH);
374
            imagecopyresized($dstImage, $temp, 0, 0, 0, 0, $dstW, $dstH, $dstW, $dstH);
375
            imagedestroy($temp);
376
        } elseif ($quality < 5 && (($dstW * $quality) < $srcW || ($dstH * $quality) < $srcH)) {
377
            $tmpW = $dstW * $quality;
378
            $tmpH = $dstH * $quality;
379
            $temp = imagecreatetruecolor($tmpW + 1, $tmpH + 1);
380
            imagecopyresized($temp, $srcImage, 0, 0, $srcX, $srcY, $tmpW + 1, $tmpH + 1, $srcW, $srcH);
381
            imagecopyresampled($dstImage, $temp, $dstX, $dstY, 0, 0, $dstW, $dstH, $tmpW, $tmpH);
382
            imagedestroy($temp);
383
        } else {
384
            imagecopyresampled($dstImage, $srcImage, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
385
        }
386
387
        return true;
388
    }
389
390
    /**
391
     * Source: http://pl.php.net/imagecreate
392
     * (optimized for speed and memory usage, but yet not very efficient).
393
     *
394
     * @param string $data bitmap data
395
     *
396
     * @return resource
397
     */
398
    public function createFromBmp($data)
399
    {
400
        $stream = fopen('php://temp', 'r+');
401
        fwrite($stream, $data);
402
        rewind($stream);
403
404
        //20 seconds seems to be a reasonable value to not kill a server and process images up to 1680x1050
405
        @set_time_limit(20);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for set_time_limit(). 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

405
        /** @scrutinizer ignore-unhandled */ @set_time_limit(20);

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...
406
407
        if (!is_resource($stream)) {
408
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type resource.
Loading history...
409
        }
410
411
        $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($stream, 14));
412
        if ($FILE['file_type'] != 19778) {
413
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type resource.
Loading history...
414
        }
415
416
        $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel' .
417
            '/Vcompression/Vsize_bitmap/Vhoriz_resolution' .
418
            '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($stream, 40));
419
420
        $BMP['colors'] = pow(2, $BMP['bits_per_pixel']);
421
422
        if ($BMP['size_bitmap'] == 0) {
423
            $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
424
        }
425
426
        $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel'] / 8;
427
        $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
428
        $BMP['decal'] = ($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
429
        $BMP['decal'] -= floor($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
430
        $BMP['decal'] = 4 - (4 * $BMP['decal']);
431
432
        if ($BMP['decal'] == 4) {
433
            $BMP['decal'] = 0;
434
        }
435
436
        $PALETTE = array();
437
        if ($BMP['colors'] < 16777216) {
438
            $PALETTE = unpack('V' . $BMP['colors'], fread($stream, $BMP['colors'] * 4));
439
        }
440
441
        //2048x1536px@24bit don't even try to process larger files as it will probably fail
442
        if ($BMP['size_bitmap'] > 3 * 2048 * 1536) {
443
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type resource.
Loading history...
444
        }
445
446
        $IMG = fread($stream, $BMP['size_bitmap']);
447
        fclose($stream);
448
        $VIDE = chr(0);
449
450
        $res = imagecreatetruecolor($BMP['width'], $BMP['height']);
451
        $P = 0;
452
        $Y = $BMP['height'] - 1;
453
454
        $line_length = $BMP['bytes_per_pixel'] * $BMP['width'];
455
456
        if ($BMP['bits_per_pixel'] == 24) {
457
            while ($Y >= 0) {
458
                $X = 0;
459
                $temp = unpack("C*", substr($IMG, $P, $line_length));
460
461
                while ($X < $BMP['width']) {
462
                    $offset = $X * 3;
463
                    imagesetpixel($res, $X++, $Y, ($temp[$offset + 3] << 16) + ($temp[$offset + 2] << 8) + $temp[$offset + 1]);
464
                }
465
                $Y--;
466
                $P += $line_length + $BMP['decal'];
467
            }
468
        } elseif ($BMP['bits_per_pixel'] == 8) {
469
            while ($Y >= 0) {
470
                $X = 0;
471
472
                $temp = unpack("C*", substr($IMG, $P, $line_length));
473
474
                while ($X < $BMP['width']) {
475
                    imagesetpixel($res, $X++, $Y, $PALETTE[$temp[$X] + 1]);
476
                }
477
                $Y--;
478
                $P += $line_length + $BMP['decal'];
479
            }
480
        } elseif ($BMP['bits_per_pixel'] == 4) {
481
            while ($Y >= 0) {
482
                $X = 0;
483
                $i = 1;
484
                $low = true;
485
486
                $temp = unpack("C*", substr($IMG, $P, $line_length));
487
488
                while ($X < $BMP['width']) {
489
                    if ($low) {
490
                        $index = $temp[$i] >> 4;
491
                    } else {
492
                        $index = $temp[$i++] & 0x0F;
493
                    }
494
                    $low = !$low;
0 ignored issues
show
introduced by
The condition $low is always true.
Loading history...
495
496
                    imagesetpixel($res, $X++, $Y, $PALETTE[$index + 1]);
497
                }
498
                $Y--;
499
                $P += $line_length + $BMP['decal'];
500
            }
501
        } elseif ($BMP['bits_per_pixel'] == 1) {
502
            $COLOR = unpack("n", $VIDE . substr($IMG, floor($P), 1));
0 ignored issues
show
Bug introduced by
floor($P) of type double is incompatible with the type integer expected by parameter $offset of substr(). ( Ignorable by Annotation )

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

502
            $COLOR = unpack("n", $VIDE . substr($IMG, /** @scrutinizer ignore-type */ floor($P), 1));
Loading history...
503
            if (($P * 8) % 8 == 0) {
504
                $COLOR[1] = $COLOR[1] >> 7;
505
            } elseif (($P * 8) % 8 == 1) {
506
                $COLOR[1] = ($COLOR[1] & 0x40) >> 6;
507
            } elseif (($P * 8) % 8 == 2) {
508
                $COLOR[1] = ($COLOR[1] & 0x20) >> 5;
509
            } elseif (($P * 8) % 8 == 3) {
510
                $COLOR[1] = ($COLOR[1] & 0x10) >> 4;
511
            } elseif (($P * 8) % 8 == 4) {
512
                $COLOR[1] = ($COLOR[1] & 0x8) >> 3;
513
            } elseif (($P * 8) % 8 == 5) {
514
                $COLOR[1] = ($COLOR[1] & 0x4) >> 2;
515
            } elseif (($P * 8) % 8 == 6) {
516
                $COLOR[1] = ($COLOR[1] & 0x2) >> 1;
517
            } elseif (($P * 8) % 8 == 7) {
518
                $COLOR[1] = ($COLOR[1] & 0x1);
519
            }
520
            $COLOR[1] = $PALETTE[$COLOR[1] + 1];
521
        } else {
522
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type resource.
Loading history...
523
        }
524
525
        return $res;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $res also could return the type GdImage which is incompatible with the documented return type resource.
Loading history...
526
    }
527
528
    /**
529
     * Resizes an image to a given size keeping the aspect ratio.
530
     *
531
     * @param int  $maxWidth        maximum width
532
     * @param int  $maxHeight       maximum height
533
     * @param int  $quality         quality
534
     * @param bool $useHigherFactor
535
     *
536
     * @return Image $this
537
     */
538
    public function resize($maxWidth, $maxHeight, $quality = 80, $useHigherFactor = false)
539
    {
540
        $this->resizeQuality = $quality;
541
542
        $maxWidth = (int) $maxWidth ?: $this->width;
543
        $maxHeight = (int) $maxHeight ?: $this->height;
544
545
        if ($this->width <= $maxWidth && $this->height <= $maxHeight) {
546
            return $this;
547
        }
548
549
        $targetSize = static::calculateAspectRatio($maxWidth, $maxHeight, $this->width, $this->height, $useHigherFactor);
550
551
        $targetWidth = $targetSize['width'];
552
        $targetHeight = $targetSize['height'];
553
554
        $targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
555
556
        if ($this->mime === 'image/png') {
557
            $bg = imagecolorallocatealpha($targetImage, 255, 255, 255, 127);
558
            imagefill($targetImage, 0, 0, $bg);
559
            imagealphablending($targetImage, false);
560
            imagesavealpha($targetImage, true);
561
        }
562
563
        $this->fastCopyResampled($targetImage, $this->gdImage, 0, 0, 0, 0, $targetWidth, $targetHeight,
0 ignored issues
show
Bug introduced by
It seems like $targetImage can also be of type GdImage; however, parameter $dstImage of CKSource\CKFinder\Image::fastCopyResampled() does only seem to accept resource, 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

563
        $this->fastCopyResampled(/** @scrutinizer ignore-type */ $targetImage, $this->gdImage, 0, 0, 0, 0, $targetWidth, $targetHeight,
Loading history...
564
                                 $this->width, $this->height, (int)max(floor($quality / 20), 6));
565
566
        imagedestroy($this->gdImage);
567
        $this->gdImage = $targetImage;
568
        $this->width = $targetWidth;
569
        $this->height = $targetHeight;
570
571
        return $this;
572
    }
573
574
    /**
575
     * Returns image data.
576
     *
577
     * @param string $format Returned image format mimetype (current image mimetype is used if not set).
578
     * @param int $quality   Image quelity (used for JPG images only)
579
     *
580
     * @return string image data
581
     */
582
    public function getData($format = null, $quality = 80)
583
    {
584
        $mime = $format ?: $this->mime;
585
586
        ob_start();
587
588
        switch ($mime) {
589
            case 'image/gif':
590
                imagegif($this->gdImage);
591
                break;
592
            case 'image/jpeg':
593
            case 'image/bmp':
594
            case 'image/x-ms-bmp':
595
                $quality = $this->resizeQuality ?: $quality;
596
                imagejpeg($this->gdImage, null, $quality);
597
                break;
598
            case 'image/png':
599
                imagealphablending($this->gdImage, false);
600
                imagesavealpha($this->gdImage, true);
601
                imagepng($this->gdImage);
602
                break;
603
            case 'image/wbmp':
604
                imagewbmp($this->gdImage);
605
                break;
606
        }
607
608
        $this->dataSize = ob_get_length();
609
610
        return ob_get_clean();
611
    }
612
613
    /**
614
     * Returns GD image resource.
615
     *
616
     * @return resource GD image resource
617
     */
618
    public function getGDImage()
619
    {
620
        return $this->gdImage;
621
    }
622
623
    /**
624
     * Returns the size of image data produced by the `getData()` method.
625
     *
626
     * @return int image data size in bytes
627
     */
628
    public function getDataSize()
629
    {
630
        return $this->dataSize;
631
    }
632
633
    /**
634
     * Returns image width in pixels.
635
     *
636
     * @return int image width
637
     */
638
    public function getWidth()
639
    {
640
        return $this->width;
641
    }
642
643
    /**
644
     * Returns image height in pixels.
645
     *
646
     * @return int image height
647
     */
648
    public function getHeight()
649
    {
650
        return $this->height;
651
    }
652
653
    /**
654
     * Returns image MIME type.
655
     *
656
     * @return string MIME type
657
     */
658
    public function getMimeType()
659
    {
660
        return $this->mime;
661
    }
662
663
    public function crop($x, $y, $width, $height)
664
    {
665
        $targetImage = imagecreatetruecolor($width, $height);
666
667
        if ($this->mime === 'image/png') {
668
            $bg = imagecolorallocatealpha($targetImage, 255, 255, 255, 127);
669
            imagefill($targetImage, 0, 0, $bg);
670
            imagealphablending($targetImage, false);
671
            imagesavealpha($targetImage, true);
672
        }
673
674
        imagecopy($targetImage, $this->gdImage, 0, 0, $x, $y, $width, $height);
675
676
        imagedestroy($this->gdImage);
677
        $this->gdImage = $targetImage;
0 ignored issues
show
Documentation Bug introduced by
It seems like $targetImage can also be of type GdImage. However, the property $gdImage is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
678
        $this->width = $width;
679
        $this->height = $height;
680
681
        return $this;
682
    }
683
684
    public function rotate($degrees, $bgcolor = 0)
685
    {
686
        if ($this->mime === 'image/png') {
687
            imagesavealpha($this->gdImage, true);
688
            $bgcolor = imagecolorallocatealpha($this->gdImage, 0, 0, 0, 127);
689
        }
690
691
        $this->gdImage = imagerotate($this->gdImage, $degrees, $bgcolor);
0 ignored issues
show
Documentation Bug introduced by
It seems like imagerotate($this->gdImage, $degrees, $bgcolor) can also be of type GdImage. However, the property $gdImage is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
692
        $this->width = imagesx($this->gdImage);
693
        $this->height = imagesy($this->gdImage);
694
695
        return $this;
696
    }
697
698
    public function getInfo()
699
    {
700
        $info = array(
701
            'width'  => $this->getWidth(),
702
            'height' => $this->getHeight(),
703
            'size'   => $this->getDataSize()
704
        );
705
706
        return $info;
707
    }
708
709
    public function __destruct()
710
    {
711
        if (is_resource($this->gdImage)) {
712
            imagedestroy($this->gdImage);
713
        }
714
    }
715
}
716