ImgPicker::resizeImage()   C
last analyzed

Complexity

Conditions 14
Paths 72

Size

Total Lines 52
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 14
eloc 40
nc 72
nop 8
dl 0
loc 52
rs 6.2666
c 1
b 0
f 1

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * ImgPicker
5
 */
6
class ImgPicker
7
{
8
    protected $options;
9
10
    protected $error_messages = [
11
        // File upload errors codes
12
        // http://www.php.net/manual/en/features.file-upload.errors.php
13
        1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
14
        2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
15
        3 => 'The uploaded file was only partially uploaded',
16
        4 => 'No file was uploaded',
17
        6 => 'Missing a temporary folder',
18
        7 => 'Failed to write file to disk',
19
        8 => 'A PHP extension stopped the file upload',
20
        'gd' => 'PHP GD library is NOT installed on your web server',
21
        'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
22
        'max_file_size' => 'File is too big',
23
        'min_file_size' => 'File is too small',
24
        'accept_file_types' => 'Filetype not allowed',
25
        'max_width' => 'Image exceeds maximum width of ',
26
        'min_width' => 'Image requires a minimum width of ',
27
        'max_height' => 'Image exceeds maximum height of ',
28
        'min_height' => 'Image requires a minimum height of ',
29
        'upload_failed' => 'Failed to upload the file',
30
        'move_failed' => 'Failed to upload the file',
31
        'invalid_image' => 'Invalid image',
32
        'image_resize' => 'Failed to resize image',
33
        'not_exists' => 'Failed to load the image',
34
    ];
35
36
    /**
37
     *    Constructor
38
     *
39
     * @param array $options
40
     * @param array $error_messages
41
     * @return void
42
     */
43
    public function __construct($options = array(), $error_messages = array())
44
    {
45
        $this->options = array(
46
            // Upload directory path:
47
            'upload_dir' => dirname(__FILE__) . '/files/',
48
49
            // Upload directory url:
50
            'upload_url' => $this->getFullUrl() . '/files/',
51
52
            // Accepted file types:
53
            'accept_file_types' => 'png|jpg|jpeg|gif',
54
55
            // Directory mode:
56
            'mkdir_mode' => 0755,
57
58
            // File size restrictions (in bytes):
59
            'max_file_size' => null,
60
            'min_file_size' => 1,
61
62
            // Image resolution restrictions (in px):
63
            'max_width' => null,
64
            'max_height' => null,
65
            'min_width' => 1,
66
            'min_height' => 1,
67
68
            // Auto orient image based on EXIF data:
69
            'auto_orient' => true,
70
71
            // Image versions
72
            'versions' => array(
73
                //'' => array(
74
                //'upload_dir' => '',
75
                //'upload_url' => '',
76
                // Create square images
77
                //    'crop' => true,
78
                //	'max_width' => 200,
79
                //     'max_height' => 200,
80
                //),
81
82
                // 'avatar' => array(
83
                // 	'crop' => true,
84
                // 	'max_width' => 200,
85
                // 	'max_height' => 200
86
                // ),
87
88
                // 'small' => array(
89
                // 	'crop' => true,
90
                // 	'max_width' => 100
91
                // )
92
            ),
93
        );
94
95
        $this->options = $options + $this->options;
96
        $this->error_messages = $error_messages + $this->error_messages;
97
98
        $this->initialize();
99
    }
100
101
    /**
102
     *    Initialize upload and crop actions
103
     *
104
     * @return void
105
     */
106
    protected function initialize()
107
    {
108
        // GD extension check
109
        // http://www.php.net/manual/en/book.image.php
110
        if (!extension_loaded('gd') || !function_exists('gd_info')) {
111
            $this->error = $this->getErrorMessage('gd');
0 ignored issues
show
Bug Best Practice introduced by
The property error does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
112
113
            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 void.
Loading history...
114
        }
115
116
        switch (@$_POST['action']) {
117
            case 'upload':
118
                $this->upload();
119
                break;
120
121
            case 'crop':
122
                $this->crop();
123
                break;
124
125
            case 'delete':
126
                $this->delete();
127
                break;
128
        }
129
130
        switch (@$_GET['action']) {
131
            case 'preview':
132
                $this->preview();
133
                break;
134
135
            case 'load':
136
                $this->load();
137
                break;
138
        }
139
    }
140
141
    /**
142
     *    Load action
143
     *
144
     * @return void
145
     */
146
    protected function load()
147
    {
148
        if (!isset($this->options['load'])) {
149
            return;
150
        }
151
152
        $files = call_user_func($this->options['load'], $this);
153
154
        if (!$files) {
155
            return;
156
        }
157
158
        if (!is_array($files)) {
159
            $files = array($files);
160
            $single = true;
161
        }
162
163
        $images = array();
164
165
        foreach ($files as $file) {
166
            $image = new stdClass();
167
            $image->path = $this->getUploadPath($file);
168
169
            if (!file_exists($image->path)) {
170
                continue;
171
            }
172
173
            $image->name = $file;
174
            $image->type = $this->getFileExtension($image->name);
175
            $image->url = $this->getUploadUrl($image->name);
176
177
            list($image->width, $image->height) = @getimagesize($image->path);
178
179
            foreach ($this->options['versions'] as $version => $options) {
180
                $filename = $this->getVersionFilename($image->name, $version);
181
                $filepath = $this->getUploadPath($filename, $version);
182
183
                list($width, $height) = @getimagesize($filepath);
184
185
                $image->versions[$version] = array(
186
                    'url' => $this->getUploadUrl($filename, $version),
187
                    'width' => $width,
188
                    'height' => $height,
189
                );
190
            }
191
192
            unset($image->path);
193
194
            if (isset($single)) {
195
                $images = $image;
196
            } else {
197
                $images[] = $image;
198
            }
199
        }
200
201
        $this->generateResponse($images);
0 ignored issues
show
Bug introduced by
It seems like $images can also be of type stdClass; however, parameter $response of ImgPicker::generateResponse() does only seem to accept array, 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

201
        $this->generateResponse(/** @scrutinizer ignore-type */ $images);
Loading history...
202
    }
203
204
    /**
205
     *    Preview action
206
     *
207
     * @return void
208
     */
209
    protected function preview()
210
    {
211
        $filename = basename(@$_GET['file']);
212
        $width = @$_GET['width'];
213
        $rotate = @$_GET['rotate'];
214
215
        $filepath = $this->getUploadPath($filename);
216
        $filetype = $this->getFileExtension($filename);
217
218
        if (file_exists($filepath)) {
219
            list($src_w, $src_h) = @getimagesize($filepath);
220
221
            $dst_w = $src_w;
222
            $dst_h = $src_h;
223
224
            if (is_numeric($width) && $width > 0) {
225
                $dst_w = $width;
226
                $dst_h = $src_h / $src_w * $width;
227
            }
228
229
            $dst_path = $this->getUploadPath(md5($filename) . '.' . $filetype);
230
231
            $this->resizeImage($filepath, $dst_path, 0, 0, $dst_w, $dst_h, $src_w, $src_h);
232
233
            if (in_array(abs($rotate), array(90, 180, 270))) {
234
                $angle = ($rotate < 0) ? abs($rotate) : 360 - $rotate;
235
                $this->rotateImage($dst_path, $angle);
0 ignored issues
show
Bug introduced by
It seems like $angle can also be of type double; however, parameter $angle of ImgPicker::rotateImage() does only seem to accept integer, 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

235
                $this->rotateImage($dst_path, /** @scrutinizer ignore-type */ $angle);
Loading history...
236
            }
237
238
            header('Content-Type: image/jpeg');
239
            header('Content-Length: ' . filesize($dst_path));
240
            readfile($dst_path);
241
            @unlink($dst_path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). 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

241
            /** @scrutinizer ignore-unhandled */ @unlink($dst_path);

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...
242
        }
243
    }
244
245
    /**
246
     *    Delete action
247
     *
248
     * @return void
249
     */
250
    protected function delete()
251
    {
252
        if (!isset($this->options['delete'])) {
253
            return;
254
        }
255
256
        $filename = basename(@$_POST['file']);
257
        $filepath = $this->getUploadPath($filename);
258
259
        if (file_exists($filepath) && call_user_func($this->options['delete'], $filename, $this)) {
260
            foreach ($this->options['versions'] as $version => $options) {
261
                $name = $this->getVersionFilename($filename, $version);
262
                $path = $this->getUploadPath($name, $version);
0 ignored issues
show
Unused Code introduced by
The assignment to $path is dead and can be removed.
Loading history...
263
            }
264
            @unlink($filepath);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). 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

264
            /** @scrutinizer ignore-unhandled */ @unlink($filepath);

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...
265
        }
266
    }
267
268
    /**
269
     *    Upload action
270
     *
271
     * @return void
272
     */
273
    protected function upload()
274
    {
275
        $upload = isset($_FILES['file']) ? $_FILES['file'] : null;
276
277
        $file = $this->handleFileUpload(
278
            @$upload['tmp_name'],
279
            @$upload['name'] == 'blob' ? $this->getRandFilename() . '.jpg' : @$upload['name'],
280
            @$upload['size'],
281
            @$upload['error']
282
        );
283
284
        $this->generateResponse($file);
0 ignored issues
show
Bug introduced by
$file of type stdClass is incompatible with the type array expected by parameter $response of ImgPicker::generateResponse(). ( Ignorable by Annotation )

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

284
        $this->generateResponse(/** @scrutinizer ignore-type */ $file);
Loading history...
285
    }
286
287
    /**
288
     *    Handle file upload
289
     *
290
     * @param string $uploaded_file
291
     * @param string $name
292
     * @param integer $size
293
     * @param integer $error
294
     * @return stdClass
295
     */
296
    protected function handleFileUpload($uploaded_file, $name, $size, $error)
297
    {
298
        $image = new stdClass();
299
        $image->name = $this->getFilename($name);
300
        $image->type = $this->getFileExtension($name);
301
        $image->size = $this->fixIntOverflow(intval($size));
302
        $image->path = $this->getUploadPath($image->name);
303
        $image->url = $this->getUploadUrl($image->name);
304
        list($image->width, $image->height) = @getimagesize($uploaded_file);
305
306
        if (!$this->validate($uploaded_file, $image, $error)) {
307
            return $image;
308
        }
309
310
        $upload_dir = $this->getUploadPath();
311
        if (!is_dir($upload_dir)) {
312
            mkdir($upload_dir, $this->options['mkdir_mode'], true);
313
        }
314
315
        //Upload start callback
316
        if (isset($this->options['upload_start'])) {
317
            call_user_func($this->options['upload_start'], $image, $this);
318
        }
319
320
        $image->path = $this->getUploadPath($image->name);
321
        $image->url = $this->getUploadUrl($image->name);
322
323
        if (!move_uploaded_file($uploaded_file, $image->path)) {
324
            $image->error = $this->getErrorMessage('move_failed');
325
326
            return $image;
327
        }
328
329
        // Orient the image
330
        if (!empty($this->options['auto_orient'])) {
331
            $this->orientImage($image->path);
332
        }
333
334
        // Generate image versions
335
        $image->versions = $this->generateVersions($image, true);
336
337
        // Upload complete callback
338
        if (isset($this->options['upload_complete'])) {
339
            call_user_func($this->options['upload_complete'], $image, $this);
340
        }
341
342
        unset($image->path);
343
344
        return $image;
345
    }
346
347
    /**
348
     *    Crop action
349
     *
350
     * @return void
351
     */
352
    protected function crop()
353
    {
354
        $filename = basename(@$_POST['image']);
355
        $rotate = @$_POST['rotate'];
356
357
        $image = new stdClass();
358
        $image->name = $filename;
359
        $image->type = $this->getFileExtension($image->name);
360
        $image->path = $this->getUploadPath($image->name);
361
        $image->url = $this->getUploadUrl($image->name);
362
363
        if (!file_exists($image->path)) {
364
            return $this->generateResponse(array('error' => $this->getErrorMessage('not_exists')));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->generateRe...Message('not_exists'))) returns the type string which is incompatible with the documented return type void.
Loading history...
365
        }
366
367
        if (!preg_match('/.(' . $this->options['accept_file_types'] . ')+$/i', $image->name)) {
368
            return;
369
        }
370
371
        list($image->width, $image->height) = @getimagesize($image->path);
372
373
        @list($src_x, $src_y, $x2, $y2, $src_w, $src_h) = @array_values(@$_POST['coords']);
374
375
        if (isset($this->options['crop_start'])) {
376
            call_user_func($this->options['crop_start'], $image, $this);
377
        }
378
379
        $image->url = $this->getUploadUrl($image->name);
380
381
        if (empty($src_w) || empty($src_h)) {
382
            $src_w = $image->width;
383
            $src_h = $image->height;
384
        }
385
386
        if (empty($src_x) && empty($src_y)) {
387
            $src_x = $src_y = 0;
388
        }
389
390
        $dst_w = $src_w;
391
        $dst_h = $src_h;
392
393
        $tmp = clone $image;
394
        $tmp->path = $this->getUploadPath(md5($tmp->name) . '.' . $tmp->type);
395
396
        @copy($image->path, $tmp->path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for copy(). 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

396
        /** @scrutinizer ignore-unhandled */ @copy($image->path, $tmp->path);

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...
397
398
        if (in_array(abs($rotate), array(90, 180, 270))) {
399
            $angle = ($rotate < 0) ? abs($rotate) : 360 - $rotate;
400
            $this->rotateImage($tmp->path, $angle);
0 ignored issues
show
Bug introduced by
It seems like $angle can also be of type double; however, parameter $angle of ImgPicker::rotateImage() does only seem to accept integer, 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

400
            $this->rotateImage($tmp->path, /** @scrutinizer ignore-type */ $angle);
Loading history...
401
        }
402
403
        $this->resizeImage($tmp->path, null, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
404
405
        list($tmp->width, $tmp->height) = @getimagesize($tmp->path);
406
407
        $image->versions = $this->generateVersions($tmp);
408
409
        @unlink($tmp->path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). 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

409
        /** @scrutinizer ignore-unhandled */ @unlink($tmp->path);

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...
410
411
        if (!isset($this->options['versions'][''])) {
412
            @rename($image->path, $this->getUploadPath($image->name));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). 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

412
            /** @scrutinizer ignore-unhandled */ @rename($image->path, $this->getUploadPath($image->name));

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...
413
        }
414
415
        list($image->width, $image->height) = @getimagesize($this->getUploadPath($image->name));
416
417
        if ($image->path != $this->getUploadPath($image->name)) {
418
            foreach ($this->options['versions'] as $version => $options) {
419
                $filename = $this->getVersionFilename(basename($image->path), $version);
420
                @unlink($this->getUploadPath($filename, $version));
421
            }
422
        }
423
424
        // Crop complete callback
425
        if (isset($this->options['crop_complete'])) {
426
            call_user_func($this->options['crop_complete'], $image, $this);
427
        }
428
429
        unset($image->path);
430
431
        // Generate json response
432
        $this->generateResponse($image);
0 ignored issues
show
Bug introduced by
$image of type stdClass is incompatible with the type array expected by parameter $response of ImgPicker::generateResponse(). ( Ignorable by Annotation )

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

432
        $this->generateResponse(/** @scrutinizer ignore-type */ $image);
Loading history...
433
    }
434
435
    /**
436
     *    Generate image versions
437
     *
438
     * @param stdClass $image
439
     * @param bool $is_upload
440
     * @return array
441
     */
442
    protected function generateVersions($image, $is_upload = false)
443
    {
444
        $versions = array();
445
        foreach ($this->options['versions'] as $version => $options) {
446
            $dst_w = $src_w = $image->width;
447
            $dst_h = $src_h = $image->height;
448
            $src_x = $src_y = 0;
449
450
            $max_width = @$options['max_width'];
451
            $max_height = @$options['max_height'];
452
            $crop = isset($options['crop']) && $options['crop'] === true;
453
454
            if ($crop) {
455
                $min = min($src_w, $src_h);
456
                $src_x = ($src_w - $min) / 2;
457
                $src_y = ($src_h - $min) / 2;
458
                $dst_w = $dst_h = $src_w = $src_h = $min;
459
            }
460
461
            if (!empty($max_width) && $src_w > $max_width || ($src_w < $max_width && $crop)) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($max_width) && ...w < $max_width && $crop, Probably Intended Meaning: ! empty($max_width) && (... < $max_width && $crop)
Loading history...
462
                $dst_w = $max_width;
463
                $dst_h = $src_h / $src_w * $max_width;
464
            } else {
465
                if (!empty($max_height) && $src_h > $max_height || ($src_h < $max_height && $crop)) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($max_height) &&... < $max_height && $crop, Probably Intended Meaning: ! empty($max_height) && ...< $max_height && $crop)
Loading history...
466
                    $dst_h = $max_height;
467
                    $dst_w = $src_w / $src_h * $max_height;
468
                }
469
            }
470
471
            $filename = $this->getVersionFilename($image->name, $version);
472
            $filepath = $this->getUploadPath($filename, $version);
473
            $upload_dir = $this->getUploadPath('', $version);
474
475
            if (!is_dir($upload_dir)) {
476
                mkdir($upload_dir, $this->options['mkdir_mode'], true);
477
            }
478
479
            if (!$is_upload || ($is_upload && $version != '')) {
480
//                var_dump($image->path, $filepath, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
481
                $success = $this->resizeImage($image->path, $filepath, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
482
            }
483
484
            if (!empty($success)) {
485
                $versions[$version] = array(
486
                    'url' => $this->getUploadUrl($filename, $version),
487
                    'width' => $dst_w,
488
                    'height' => $dst_h,
489
                );
490
            }
491
        }
492
493
        return $versions;
494
    }
495
496
    /**
497
     *    Validate uploaded file
498
     *
499
     * @param string $uploaded_file
500
     * @param stdClass $name
501
     * @param string $error
502
     * @return boolean
503
     */
504
    protected function validate($uploaded_file, $file, $error)
505
    {
506
        if (!$uploaded_file) {
507
            $file->error = $this->getErrorMessage(4);
508
509
            return false;
510
        }
511
512
        if ($error) {
513
            $file->error = $this->getErrorMessage($error);
514
515
            return false;
516
        }
517
518
        $content_length = $this->fixIntOverflow(intval($_SERVER['CONTENT_LENGTH']));
519
        $post_max_size = $this->getConfigBytes(ini_get('post_max_size'));
520
521
        if ($post_max_size && $content_length > $post_max_size) {
522
            $file->error = $this->getErrorMessage('post_max_size');
523
524
            return false;
525
        }
526
527
        if ($this->options['max_file_size'] && $file->size > $this->options['max_file_size']) {
528
            $file->error = $this->getErrorMessage('max_file_size');
529
530
            return false;
531
        }
532
533
        if ($this->options['min_file_size'] && $file->size < $this->options['min_file_size']) {
534
            $file->error = $this->getErrorMessage('min_file_size');
535
536
            return false;
537
        }
538
539
        if (!preg_match('/.(' . $this->options['accept_file_types'] . ')+$/i', $file->name)) {
540
            $file->error = $this->getErrorMessage('accept_file_types');
541
542
            return false;
543
        }
544
545
        if (empty($file->width) || empty($file->height)) {
546
            $file->error = $this->getErrorMessage('invalid_image');
547
548
            return false;
549
        }
550
551
        $max_width = @$this->options['max_width'];
552
        $max_height = @$this->options['max_height'];
553
        $min_width = @$this->options['min_width'];
554
        $min_height = @$this->options['min_height'];
555
556
        if ($max_width || $max_height || $min_width || $min_height) {
557
            if ($max_width && $file->width > $max_width) {
558
                $file->error = $this->getErrorMessage('max_width') . $max_width . 'px';
559
560
                return false;
561
            }
562
            if ($max_height && $file->height > $max_height) {
563
                $file->error = $this->getErrorMessage('max_height') . $max_height . 'px';
564
565
                return false;
566
            }
567
            if ($min_width && $file->width < $min_width) {
568
                $file->error = $this->getErrorMessage('min_width') . $min_width . 'px';
569
570
                return false;
571
            }
572
            if ($min_height && $file->height < $min_height) {
573
                $file->error = $this->getErrorMessage('min_height') . $min_height . 'px';
574
575
                return false;
576
            }
577
        }
578
579
        return true;
580
    }
581
582
    /**
583
     *    Get upload directory path
584
     *
585
     * @param string $filename
586
     * @param string $version
587
     * @return string
588
     */
589
    public function getUploadPath($filename = '', $version = '')
590
    {
591
        $upload_dir = $this->options['upload_dir'];
592
593
        if ($version != '') {
594
            $dir = @$this->options['versions'][$version]['upload_dir'];
595
            if (!empty($dir)) {
596
                $upload_dir = $dir;
597
            }
598
        }
599
600
        return $upload_dir . $filename;
601
    }
602
603
    /**
604
     *    Get upload directory url
605
     *
606
     * @param string $filename
607
     * @param string $version
608
     * @return string
609
     */
610
    public function getUploadUrl($filename = '', $version = '')
611
    {
612
        $upload_url = $this->options['upload_url'];
613
614
        if ($version != '') {
615
            $url = @$this->options['versions'][$version]['upload_url'];
616
            if (!empty($url)) {
617
                $upload_url = $url;
618
            }
619
        }
620
621
        return $upload_url . $filename;
622
    }
623
624
    /**
625
     *    Get full url
626
     *
627
     * @return string
628
     */
629
    protected function getFullUrl()
630
    {
631
        $https = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'on') === 0;
632
633
        return
634
            ($https ? 'https://' : 'http://') .
635
            (!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] . '@' : '') .
636
            (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME'] .
637
                ($https && $_SERVER['SERVER_PORT'] === 443 ||
638
                $_SERVER['SERVER_PORT'] === 80 ? '' : ':' . $_SERVER['SERVER_PORT']))) .
639
            substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/'));
640
    }
641
642
    /**
643
     *    Get file name
644
     *
645
     * @param string
646
     * @return string
647
     */
648
    public function getFilename($name)
649
    {
650
        return $this->getUniqueFilename($name);
651
    }
652
653
    /**
654
     *    Get version name
655
     *
656
     * @param string
657
     * @return string
658
     */
659
    public function getVersionFilename($filename, $version)
660
    {
661
        $ext = $this->getFileExtension($filename);
662
        if ($version == '') {
663
            return $filename;
664
        }
665
666
        return str_replace('.' . $ext, "-$version.$ext", $filename);
667
    }
668
669
    /**
670
     *    Get unique file name
671
     *
672
     * @param string
673
     * @return string
674
     */
675
    public function getUniqueFilename($name)
676
    {
677
        while (is_dir($this->getUploadPath($name))) {
678
            $name = $this->upcountName($name);
679
        }
680
        while (is_file($this->getUploadPath($name))) {
681
            $name = $this->upcountName($name);
682
        }
683
684
        return $name;
685
    }
686
687
    /**
688
     *    Generate random file name
689
     *
690
     * @return string
691
     */
692
    public function getRandFilename()
693
    {
694
        return md5(time() . rand());
695
    }
696
697
    /**
698
     *    Get file extension
699
     *
700
     * @param string $filename
701
     * @return string
702
     */
703
    public function getFileExtension($filename)
704
    {
705
        return pathinfo(strtolower($filename), PATHINFO_EXTENSION);
0 ignored issues
show
Bug Best Practice introduced by
The expression return pathinfo(strtolow...e), PATHINFO_EXTENSION) also could return the type array which is incompatible with the documented return type string.
Loading history...
706
    }
707
708
    /**
709
     *    Generate json response
710
     *
711
     * @param array $response
712
     * @return string
713
     */
714
    public function generateResponse($response)
715
    {
716
        echo json_encode($response);
717
    }
718
719
    /**
720
     *    Get error message
721
     *
722
     * @param string $error
723
     * @return string
724
     */
725
    public function getErrorMessage($error)
726
    {
727
        return array_key_exists($error, $this->error_messages) ?
728
            $this->error_messages[$error] : $error;
729
    }
730
731
    /**
732
     *    Resize image
733
     *
734
     * @param string $src_path Source image path
735
     * @param string|null $dst_path Destination image path
736
     * @param integer $src_x x-coordinate of source point
737
     * @param integer $src_y y-coordinate of source point
738
     * @param integer $dst_w Destination width
739
     * @param integer $dst_h Destination height
740
     * @param integer $src_w Source width
741
     * @param integer $src_h Source height
742
     * @return bool
743
     */
744
    public function resizeImage($src_path, $dst_path = null, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
745
    {
746
        $src_x = ceil($src_x);
747
        $src_y = ceil($src_y);
748
        $dst_w = ceil($dst_w);
749
        $dst_h = ceil($dst_h);
750
        $src_w = ceil($src_w);
751
        $src_h = ceil($src_h);
752
753
        $dst_path = ($dst_path) ? $dst_path : $src_path;
754
        $dst_image = imagecreatetruecolor($dst_w, $dst_h);
0 ignored issues
show
Bug introduced by
$dst_h of type double is incompatible with the type integer expected by parameter $height of imagecreatetruecolor(). ( Ignorable by Annotation )

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

754
        $dst_image = imagecreatetruecolor($dst_w, /** @scrutinizer ignore-type */ $dst_h);
Loading history...
Bug introduced by
$dst_w of type double is incompatible with the type integer expected by parameter $width of imagecreatetruecolor(). ( Ignorable by Annotation )

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

754
        $dst_image = imagecreatetruecolor(/** @scrutinizer ignore-type */ $dst_w, $dst_h);
Loading history...
755
        $extension = $this->getFileExtension($src_path);
756
757
        if (!$dst_image) {
758
            return false;
759
        }
760
761
        switch ($extension) {
762
            case 'gif':
763
                $src_image = imagecreatefromgif($src_path);
764
                break;
765
            case 'jpeg':
766
            case 'jpg':
767
                $src_image = imagecreatefromjpeg($src_path);
768
                break;
769
            case 'png':
770
                imagealphablending($dst_image, false);
771
                imagesavealpha($dst_image, true);
772
                $src_image = imagecreatefrompng($src_path);
773
                @imagealphablending($src_image, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for imagealphablending(). 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

773
                /** @scrutinizer ignore-unhandled */ @imagealphablending($src_image, true);

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...
774
                break;
775
        }
776
777
        if (isset($src_image) && !$src_image) {
778
            return false;
779
        }
780
781
        if (!imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $src_image does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug introduced by
$src_h of type double is incompatible with the type integer expected by parameter $src_h of imagecopyresampled(). ( Ignorable by Annotation )

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

781
        if (!imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, /** @scrutinizer ignore-type */ $src_h)) {
Loading history...
Bug introduced by
$src_x of type double is incompatible with the type integer expected by parameter $src_x of imagecopyresampled(). ( Ignorable by Annotation )

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

781
        if (!imagecopyresampled($dst_image, $src_image, 0, 0, /** @scrutinizer ignore-type */ $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)) {
Loading history...
Bug introduced by
$src_w of type double is incompatible with the type integer expected by parameter $src_w of imagecopyresampled(). ( Ignorable by Annotation )

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

781
        if (!imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, /** @scrutinizer ignore-type */ $src_w, $src_h)) {
Loading history...
Bug introduced by
$dst_h of type double is incompatible with the type integer expected by parameter $dst_h of imagecopyresampled(). ( Ignorable by Annotation )

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

781
        if (!imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, $dst_w, /** @scrutinizer ignore-type */ $dst_h, $src_w, $src_h)) {
Loading history...
Bug introduced by
$dst_w of type double is incompatible with the type integer expected by parameter $dst_w of imagecopyresampled(). ( Ignorable by Annotation )

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

781
        if (!imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, /** @scrutinizer ignore-type */ $dst_w, $dst_h, $src_w, $src_h)) {
Loading history...
Bug introduced by
$src_y of type double is incompatible with the type integer expected by parameter $src_y of imagecopyresampled(). ( Ignorable by Annotation )

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

781
        if (!imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, /** @scrutinizer ignore-type */ $src_y, $dst_w, $dst_h, $src_w, $src_h)) {
Loading history...
782
            return false;
783
        }
784
785
        switch ($extension) {
786
            case 'gif':
787
                return imagegif($dst_image, $dst_path);
788
                break;
0 ignored issues
show
Unused Code introduced by
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...
789
            case 'jpeg':
790
            case 'jpg':
791
                return imagejpeg($dst_image, $dst_path);
792
                break;
793
            case 'png':
794
                return imagepng($dst_image, $dst_path);
795
                break;
796
        }
797
    }
798
799
    /**
800
     *    Rotate image
801
     *
802
     * @param string $src_path
803
     * @param integer $angle
804
     * @return void
805
     */
806
    public function rotateImage($src_path, $angle)
807
    {
808
        $type = $this->getFileExtension($src_path);
809
810
        switch ($type) {
811
            case 'gif':
812
                $source = imagecreatefromgif($src_path);
813
                break;
814
            case 'jpeg':
815
            case 'jpg':
816
                $source = imagecreatefromjpeg($src_path);
817
                break;
818
            case 'png':
819
                $source = imagecreatefrompng($src_path);
820
                break;
821
        }
822
823
        $image = imagerotate($source, $angle, 0);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $source does not seem to be defined for all execution paths leading up to this point.
Loading history...
824
825
        switch ($type) {
826
            case 'gif':
827
                imagegif($image, $src_path);
828
                break;
829
            case 'jpeg':
830
            case 'jpg':
831
                imagejpeg($image, $src_path);
832
                break;
833
            case 'png':
834
                imagepng($image, $src_path);
835
                break;
836
        }
837
838
        imagedestroy($source);
839
        imagedestroy($image);
840
    }
841
842
    /**
843
     *    Orient image based on EXIF orientation data
844
     *
845
     * @param string $filepath
846
     * @return void
847
     */
848
    protected function orientImage($filepath)
849
    {
850
        if (!preg_match('/\.(jpe?g)$/i', $filepath)) {
851
            return;
852
        }
853
854
        $exif = @exif_read_data($filepath);
855
        if (!empty($exif['Orientation'])) {
856
            switch ($exif['Orientation']) {
857
                case 3:
858
                    $angle = 180;
859
                    break;
860
                case 6:
861
                    $angle = -90;
862
                    break;
863
                case 8:
864
                    $angle = 90;
865
                    break;
866
            }
867
868
            if (isset($angle)) {
869
                $this->rotateImage($filepath, $angle);
870
            }
871
        }
872
    }
873
874
    protected function upcountName($name)
875
    {
876
        return preg_replace_callback(
877
            '/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/',
878
            array($this, 'upcountNameCallback'),
879
            $name,
880
            1
881
        );
882
    }
883
884
    protected function upcountNameCallback($matches)
885
    {
886
        $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
887
        $ext = isset($matches[2]) ? $matches[2] : '';
888
889
        return ' (' . $index . ')' . $ext;
890
    }
891
892
    protected function getConfigBytes($val)
893
    {
894
        $val = trim($val);
895
        $last = strtolower($val[strlen($val) - 1]);
896
        switch ($last) {
897
            case 'g':
898
                $val *= 1024;
899
                // no break
900
            case 'm':
901
                $val *= 1024;
902
                // no break
903
            case 'k':
904
                $val *= 1024;
905
        }
906
907
        return $this->fixIntOverflow($val);
908
    }
909
910
    protected function fixIntOverflow($size)
911
    {
912
        if ($size < 0) {
913
            $size += 2.0 * (PHP_INT_MAX + 1);
914
        }
915
916
        return $size;
917
    }
918
}
919