Completed
Push — master ( 714fec...f6d32c )
by samayo
01:59
created

Image::upload()   C

Complexity

Conditions 13
Paths 10

Size

Total Lines 81
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 44
nc 10
nop 0
dl 0
loc 81
rs 5.0112
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

1
<?php
2
/**
3
 * BulletProof
4
 *
5
 * A single class PHP-library for secure image uploading.
6
 *
7
 * PHP support 5.3+
8
 *
9
 * @package     BulletProof
10
 * @version     2.0.2
11
 * @author      https://twitter.com/_samayo
12
 * @link        https://github.com/samayo/bulletproof
13
 * @license     MIT
14
 */
15
namespace BulletProof;
16
17
class Image implements \ArrayAccess
18
{
19
    /**
20
     * @var string The new image name, to be provided or will be generated.
21
     */
22
    protected $name;
23
24
    /**
25
     * @var int The image width in pixels
26
     */
27
    protected $width;
28
29
    /**
30
     * @var int The image height in pixels
31
     */
32
    protected $height;
33
34
    /**
35
     * @var string The image mime type (extension)
36
     */
37
    protected $mime;
38
39
    /**
40
     * @var string The full image path (dir + image + mime)
41
     */
42
    protected $fullPath;
43
44
    /**
45
     * @var string The folder or image storage location
46
     */
47
    protected $location;
48
49
    /**
50
     * @var array A json format of all information about an image
51
     */
52
    protected $serialize = array();
53
54
    /**
55
     * @var array The min and max image size allowed for upload (in bytes)
56
     */
57
    protected $size = array(100, 50000);
58
59
    /**
60
     * @var array The max height and width image allowed
61
     */
62
    protected $dimensions = array(500, 5000);
63
64
    /**
65
     * @var array The mime types allowed for upload
66
     */
67
    protected $mimeTypes = array("jpeg", "png", "gif");
68
69
    /**
70
     * @var array list of known image types
71
     */
72
    protected $imageMimes = array(
73
        1 => "gif", "jpeg", "png", "swf", "psd",
74
        "bmp", "tiff", "tiff", "jpc", "jp2", "jpx",
75
        "jb2", "swc", "iff", "wbmp", "xbm", "ico"
76
    );
77
78
    /**
79
     * @var array storage for the $_FILES global array
80
     */
81
    private $_files = array();
82
83
    /**
84
     * @var string storage for any errors
85
     */
86
    private $error = "";
87
88
	/**
89
     * @var array error messages strings
90
     */
91
    private $error_messages = array(
92
		'upload' => array(
93
            UPLOAD_ERR_OK           => "",
94
            UPLOAD_ERR_INI_SIZE     => "Image is larger than the specified amount set by the server",
95
            UPLOAD_ERR_FORM_SIZE    => "Image is larger than the specified amount specified by browser",
96
            UPLOAD_ERR_PARTIAL      => "Image could not be fully uploaded. Please try again later",
97
            UPLOAD_ERR_NO_FILE      => "Image is not found",
98
            UPLOAD_ERR_NO_TMP_DIR   => "Can't write to disk, due to server configuration ( No tmp dir found )",
99
            UPLOAD_ERR_CANT_WRITE   => "Failed to write file to disk. Please check you file permissions",
100
            UPLOAD_ERR_EXTENSION    => "A PHP extension has halted this file upload process"
101
        ),
102
		'location'                  => "Folder %s could not be created",
103
		'mime_type'                 => "Invalid File! Only (%s) image types are allowed",
104
		'file_size'                 => "Image size should be atleast more than min: %s and less than max: %s kb",
105
		'dimensions'                => "Image height/width should be less than ' %s \ %s ' pixels",
106
		'too_small'                 => "Invalid! Image height/width is too small or maybe corrupted",
107
		'unknown'                   => "Upload failed, Unknown error occured"
108
	);
109
110
    /**
111
     * @param array $_files represents the $_FILES array passed as dependency
112
     */
113
    public function __construct(array $_files = [])
114
    {
115
        $this->_files = $_files;
116
    }
117
118
    /**
119
     * Gets the real image mime type
120
     *
121
     * @param $tmp_name string The upload tmp directory
122
     *
123
     * @return bool|string
124
     */
125
    protected function getImageMime($tmp_name)
126
    {
127
        if (isset($this->imageMimes[exif_imagetype($tmp_name)])) {
128
            return $this->imageMimes [exif_imagetype($tmp_name)];
129
        }
130
        return false;
131
    }
132
133
    /**
134
     * @param mixed $offset
135
     * @param mixed $value
136
     */
137
    public function offsetSet($offset, $value){}
138
139
    /**
140
     * @param mixed $offset
141
     * @return null
142
     */
143
    public function offsetExists($offset){}
144
145
    /**
146
     * @param mixed $offset
147
     */
148
    public function offsetUnset($offset){}
149
150
    /**
151
     * Gets array value \ArrayAccess
152
     *
153
     * @param mixed $offset
154
     *
155
     * @return bool|mixed
156
     */
157
    public function offsetGet($offset)
158
    {
159
        if ($offset == "error") {
160
            return $this->error;
161
        }
162
163
        if (isset($this->_files[$offset]) && file_exists($this->_files[$offset]["tmp_name"])) {
164
            $this->_files = $this->_files[$offset];
165
            return true;
166
        }
167
        
168
        return false;
169
    }
170
171
172
    /**
173
     * Provide image name if not provided
174
     *
175
     * @param null $isNameProvided
176
     * @return $this
177
     */
178
    public function setName($isNameProvided = null)
179
    {
180
        if ($isNameProvided) {
181
            $this->name = filter_var($isNameProvided, FILTER_SANITIZE_STRING);
182
        }
183
        
184
        return $this;
185
    }
186
187
    /**
188
     * Define a mime type for uploading
189
     *
190
     * @param array $fileTypes
191
     *
192
     * @return $this
193
     */
194
    public function setMime(array $fileTypes)
195
    {
196
        $this->mimeTypes = $fileTypes;
197
        return $this;
198
    }
199
200
    /**
201
     * Define a min and max image size for uploading
202
     *
203
     * @param $min int minimum value in bytes
204
     * @param $max int maximum value in bytes
205
     *
206
     * @return $this
207
     */
208
    public function setSize($min, $max)
209
    {
210
        $this->size = array($min, $max);
211
        return $this;
212
    }
213
214
    /**
215
     * Creates a location for upload storage
216
     *
217
     * @param $dir string the folder name to create
218
     * @param int $permission chmod permission
219
     *
220
     * @return $this
221
     */
222
    public function setLocation($dir = "bulletproof", $permission = 0666)
223
    {
224
        if (!file_exists($dir) && !is_dir($dir) && !$this->location) {
225
            $createFolder = @mkdir("" . $dir, (int) $permission, true);
226
            if (!$createFolder) {
227
                $this->error = sprintf($this->error_messages['location'], $dir);
228
                return;
229
            }
230
        }
231
232
        $this->location = $dir;
233
        return $this;
234
    }
235
236
    /**
237
     * Sets acceptable max image height and width
238
     *
239
     * @param $maxWidth int max width value
240
     * @param $maxHeight int max height value
241
     *
242
     * @return $this
243
     */
244
    public function setDimension($maxWidth, $maxHeight)
245
    {
246
        $this->dimensions = array($maxWidth, $maxHeight);
247
        return $this;
248
    }
249
250
	/**
251
	 * Replace error_messages array values with values of given array
252
	 *
253
	 * @param $new_error_messages array Array containing new error messages
254
	 *
255
	 * @return $this
256
	 */
257
	public function setErrorMessages($new_error_messages)
258
	{
259
		if($new_array = array_replace_recursive($this->error_messages, $new_error_messages)) {
260
			$this->error_messages = $new_array;
261
		};
262
		return $this;
263
	}
264
265
    /**
266
     * Returns the image name
267
     *
268
     * @return string
269
     */
270
    public function getName()
271
    {
272
        if (!$this->name) {
273
           return  uniqid(true) . "_" . str_shuffle(implode(range("e", "q")));
274
        }
275
276
        return $this->name;
277
    }
278
279
    /**
280
     * Returns the full path of the image ex "location/image.mime"
281
     *
282
     * @return string
283
     */
284
    public function getFullPath()
285
    {
286
        $this->fullPath = $this->location . "/" . $this->name . "." . $this->mime;
287
        return $this->fullPath;
288
    }
289
290
    /**
291
     * Returns the image size in bytes
292
     *
293
     * @return int
294
     */
295
    public function getSize()
296
    {
297
        return (int) $this->_files["size"];
298
    }
299
300
    /**
301
     * Returns the image height in pixels
302
     *
303
     * @return int
304
     */
305
    public function getHeight()
306
    {
307
        if ($this->height != null) {
308
            return $this->height;
309
        }
310
311
        list(, $height) = getImageSize($this->_files["tmp_name"]); 
312
        return $height;
313
    }
314
315
    /**
316
     * Returns the image width
317
     *
318
     * @return int
319
     */
320
    public function getWidth()
321
    {
322
        if ($this->width != null) {
323
            return $this->width;
324
        }
325
326
        list($width) = getImageSize($this->_files["tmp_name"]); 
327
        return $width;
328
    }
329
330
    /**
331
     * Returns the storage / folder name
332
     *
333
     * @return string
334
     */
335
    public function getLocation()
336
    {
337
        if(!$this->location){
338
            $this->setLocation(); 
339
        }
340
341
        return $this->location; 
342
    }
343
344
    /**
345
     * Returns a JSON format of the image width, height, name, mime ...
346
     *
347
     * @return string
348
     */
349
    public function getJson()
350
    {
351
        return json_encode($this->serialize);
352
    }
353
354
    /**
355
     * Returns the image mime type
356
     *
357
     * @return string
358
     */
359
    public function getMime()
360
    {
361
        return $this->mime;
362
    }
363
364
    /**
365
     * Returns error string or false if no errors occurred
366
     *
367
     * @return string|bool
368
     */
369
    public function getError(){
370
        return $this->error != "" ? $this->error : false;
371
    }
372
373
    /**
374
     * Checks for the common upload errors
375
     *
376
     * @param $e int error constant
377
     */
378
    protected function uploadErrors($e)
379
    {
380
        $errors = $this->error_messages['upload'];
381
        return $errors[$e];
382
    }
383
384
385
    /**
386
     * This methods validates and uploads the image
387
     * @return bool|Image|null
388
     * @throws ImageUploaderException
389
     */
390
    public function upload()
391
    {
392
        /* modify variable names for convenience */
393
        $image = $this;
394
        $files = $this->_files;
395
396
        /* check if php_exif is enabled */
397
        if(!function_exists('exif_imagetype')){
398
            $image->error = "Function 'exif_imagetype' Not found. Please enable \"php_exif\" in your PHP.ini";
399
            return null;
400
        }
401
402
        /* initialize image properties */
403
        $image->name     = $image->getName();
404
        $image->width    = $image->getWidth();
405
        $image->height   = $image->getHeight(); 
406
        $image->location = $image->getLocation();
407
408
        /* get image sizes */
409
        list($minSize, $maxSize) = $image->size;
410
411
        /* check for common upload errors */
412
        if($image->error = $image->uploadErrors($files["error"])){
413
            return null;
414
        }
415
416
        /* check image for valid mime types and return mime */
417
        $image->mime = $image->getImageMime($files["tmp_name"]);
0 ignored issues
show
Documentation Bug introduced by
It seems like $image->getImageMime($files['tmp_name']) can also be of type boolean. However, the property $mime is declared as type string. 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...
418
419
        /* validate image mime type */
420
        if (!in_array($image->mime, $image->mimeTypes)) {
421
            $ext = implode(", ", $image->mimeTypes);
422
            $image->error = sprintf($this->error_messages['mime_type'], $ext);
423
            return null;
424
        }
425
426
        /* check image size based on the settings */
427
        if ($files["size"] < $minSize || $files["size"] > $maxSize) {
428
            $min = intval($minSize / 1000) ?: 1; $max = intval($maxSize / 1000);
429
430
            $image->error = sprintf($this->error_messages['file_size'], $min, $max);
431
            return null;
432
        }
433
434
        /* check image dimension */
435
        list($allowedWidth, $allowedHeight) = $image->dimensions;
436
437
        if ($image->height > $allowedHeight || $image->width > $allowedWidth) {
438
            $image->error = sprintf($this->error_messages['dimensions'], $allowedHeight, $allowedWidth);
439
            return null;
440
        }
441
442
        if($image->height < 4 || $image->width < 4){
443
            $image->error = $this->error_messages['too_small'];
444
            return null;
445
        }
446
 
447
        /* set and get folder name */
448
        $image->fullPath = $image->location. "/" . $image->name . "." . $image->mime;
449
450
        /* gather image info for json storage */ 
451
        $image->serialize = array(
452
            "name"     => $image->name,
453
            "mime"     => $image->mime,
454
            "height"   => $image->height,
455
            "width"    => $image->width,
456
            "size"     => $files["size"],
457
            "location" => $image->location,
458
            "fullpath" => $image->fullPath
459
        );
460
461
        if ($image->error === "") {
462
            $moveUpload = $image->moveUploadedFile($files["tmp_name"], $image->fullPath);
463
            if (false !== $moveUpload) {
464
                return $image;
465
            }
466
        }
467
468
        $image->error =  $this->error_messages['unknown'];
469
        return false;
470
    }
471
472
    /**
473
     * Final upload method to be called, isolated for testing purposes
474
     *
475
     * @param $tmp_name int the temporary location of the image file
476
     * @param $destination int upload destination
477
     *
478
     * @return bool
479
     */
480
    public function moveUploadedFile($tmp_name, $destination)
481
    {
482
        return move_uploaded_file($tmp_name, $destination);
483
    }
484
}
485