Issues (82)

legacy/Library/ImgPicker.php (29 issues)

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
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
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
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
$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
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
$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
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
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
$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...
$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...
$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...
$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...
$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...
$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...
$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...
$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
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