Completed
Pull Request — master (#55)
by
unknown
02:16
created

Image::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
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.0
11
 * @author      Samayo  /@sama_io
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 dependancy
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
     * array offset \ArrayAccess
135
     * unused
136
     */
137
    public function offsetSet($offset, $value){}
138
    public function offsetExists($offset){}
139
    public function offsetUnset($offset){}
140
141
    /**
142
     * Gets array value \ArrayAccess
143
     *
144
     * @param mixed $offset
145
     *
146
     * @return bool|mixed
147
     */
148
    public function offsetGet($offset)
149
    {
150
        if ($offset == "error") {
151
            return $this->error;
152
        }
153
154
        if (isset($this->_files[$offset]) && file_exists($this->_files[$offset]["tmp_name"])) {
155
            $this->_files = $this->_files[$offset];
156
            return true;
157
        }
158
        
159
        return false;
160
    }
161
162
    /**
163
     * Renames image
164
     *
165
     * @param null $isNameGiven if null, image will be auto-generated
0 ignored issues
show
Bug introduced by
There is no parameter named $isNameGiven. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
166
     *
167
     * @return $this
168
     */
169
    public function setName($isNameProvided = null)
170
    {
171
        if ($isNameProvided) {
172
            $this->name = filter_var($isNameProvided, FILTER_SANITIZE_STRING);
173
        }
174
        
175
        return $this;
176
    }
177
178
    /**
179
     * Define a mime type for uploading
180
     *
181
     * @param array $fileTypes
182
     *
183
     * @return $this
184
     */
185
    public function setMime(array $fileTypes)
186
    {
187
        $this->mimeTypes = $fileTypes;
188
        return $this;
189
    }
190
191
    /**
192
     * Define a min and max image size for uploading
193
     *
194
     * @param $min int minimum value in bytes
195
     * @param $max int maximum value in bytes
196
     *
197
     * @return $this
198
     */
199
    public function setSize($min, $max)
200
    {
201
        $this->size = array($min, $max);
202
        return $this;
203
    }
204
205
    /**
206
     * Creates a location for upload storage
207
     *
208
     * @param $dir string the folder name to create
209
     * @param int $permission chmod permission
210
     *
211
     * @return $this
212
     */
213
    public function setLocation($dir = "bulletproof", $permission = 0666)
214
    {
215
        if (!file_exists($dir) && !is_dir($dir) && !$this->location) {
216
            $createFolder = @mkdir("" . $dir, (int) $permission, true);
217
            if (!$createFolder) {
218
                $this->error = sprintf($this->error_messages['location'], $dir);
219
                return;
220
            }
221
        }
222
223
        $this->location = $dir;
224
        return $this;
225
    }
226
227
    /**
228
     * Sets acceptable max image height and width
229
     *
230
     * @param $maxWidth int max width value
231
     * @param $maxHeight int max height value
232
     *
233
     * @return $this
234
     */
235
    public function setDimension($maxWidth, $maxHeight)
236
    {
237
        $this->dimensions = array($maxWidth, $maxHeight);
238
        return $this;
239
    }
240
241
	/**
242
	 * Replace error_messages array values with values of given array
243
	 *
244
	 * @param $new_error_messages array Array containing new error messages
245
	 *
246
	 * @return $this
247
	 */
248
	public function setErrorMessages($new_error_messages)
249
	{
250
		if($new_array = array_replace_recursive($this->error_messages, $new_error_messages)) {
251
			$this->error_messages = $new_array;
252
		};
253
		return $this;
254
	}
255
256
    /**
257
     * Returns the image name
258
     *
259
     * @return string
260
     */
261
    public function getName()
262
    {
263
        if (!$this->name) {
264
           return  uniqid(true) . "_" . str_shuffle(implode(range("e", "q")));
265
        }
266
267
        return $this->name;
268
    }
269
270
    /**
271
     * Returns the full path of the image ex "location/image.mime"
272
     *
273
     * @return string
274
     */
275
    public function getFullPath()
276
    {
277
        $this->fullPath = $this->location . "/" . $this->name . "." . $this->mime;
278
        return $this->fullPath;
279
    }
280
281
    /**
282
     * Returns the image size in bytes
283
     *
284
     * @return int
285
     */
286
    public function getSize()
287
    {
288
        return (int) $this->_files["size"];
289
    }
290
291
    /**
292
     * Returns the image height in pixels
293
     *
294
     * @return int
295
     */
296
    public function getHeight()
297
    {
298
        if ($this->height != null) {
299
            return $this->height;
300
        }
301
302
        list(, $height) = getImageSize($this->_files["tmp_name"]); 
303
        return $height;
304
    }
305
306
    /**
307
     * Returns the image width
308
     *
309
     * @return int
310
     */
311
    public function getWidth()
312
    {
313
        if ($this->width != null) {
314
            return $this->width;
315
        }
316
317
        list($width) = getImageSize($this->_files["tmp_name"]); 
318
        return $width;
319
    }
320
321
    /**
322
     * Returns the storage / folder name
323
     *
324
     * @return string
325
     */
326
    public function getLocation()
327
    {
328
        if(!$this->location){
329
            $this->setLocation(); 
330
        }
331
332
        return $this->location; 
333
    }
334
335
    /**
336
     * Returns a JSON format of the image width, height, name, mime ...
337
     *
338
     * @return string
339
     */
340
    public function getJson()
341
    {
342
        return json_encode($this->serialize);
343
    }
344
345
    /**
346
     * Returns the image mime type
347
     *
348
     * @return string
349
     */
350
    public function getMime()
351
    {
352
        return $this->mime;
353
    }
354
355
    /**
356
     * Returns error string or false if no errors occurred
357
     *
358
     * @return string|bool
359
     */
360
    public function getError(){
361
        return $this->error != "" ? $this->error : false;
362
    }
363
364
    /**
365
     * Checks for the common upload errors
366
     *
367
     * @param $e int error constant
368
     */
369
    protected function uploadErrors($e)
370
    {
371
        $errors = $this->error_messages['upload'];
372
        return $errors[$e];
373
    }
374
375
    /**
376
     * Main upload method.
377
     * This is where all the monkey business happens
378
     *
379
     * @return $this|bool
380
     */
381
    public function upload()
382
    {
383
        /* modify variable names for convenience */
384
        $image = $this; 
385
        $files = $this->_files;
386
387
        /* initialize image properties */
388
        $image->name     = $image->getName();
389
        $image->width    = $image->getWidth();
390
        $image->height   = $image->getHeight(); 
391
        $image->location = $image->getLocation();
392
393
        /* get image sizes */
394
        list($minSize, $maxSize) = $image->size;
395
396
        /* check for common upload errors */
397
        if($image->error = $image->uploadErrors($files["error"])){
398
            return ;
399
        }
400
401
        /* check image for valid mime types and return mime */
402
        $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...
403
404
        /* validate image mime type */
405
        if (!in_array($image->mime, $image->mimeTypes)) {
406
            $ext = implode(", ", $image->mimeTypes);
407
            $image->error = sprintf($this->error_messages['mime_type'], $ext);
408
            return ;
409
        }
410
411
        /* check image size based on the settings */
412
        if ($files["size"] < $minSize || $files["size"] > $maxSize) {
413
            $min = intval($minSize / 1000) ?: 1; $max = intval($maxSize / 1000);
414
415
            $image->error = sprintf($this->error_messages['file_size'], $min, $max);
416
            return ;
417
        }
418
419
        /* check image dimension */
420
        list($allowedWidth, $allowedHeight) = $image->dimensions;
421
422
        if ($image->height > $allowedHeight || $image->width > $allowedWidth) {
423
            $image->error = sprintf($this->error_messages['dimensions'], $allowedHeight, $allowedWidth);
424
            return ;
425
        }
426
427
        if($image->height < 4 || $image->width < 4){
428
            $image->error = $this->error_messages['too_small'];
429
            return ;
430
        }
431
 
432
        /* set and get folder name */
433
        $image->fullPath = $image->location. "/" . $image->name . "." . $image->mime;
434
435
        /* gather image info for json storage */ 
436
        $image->serialize = array(
437
            "name"     => $image->name,
438
            "mime"     => $image->mime,
439
            "height"   => $image->height,
440
            "width"    => $image->width,
441
            "size"     => $files["size"],
442
            "location" => $image->location,
443
            "fullpath" => $image->fullPath
444
        );
445
446
        if ($image->error === "") {
447
            $moveUpload = $image->moveUploadedFile($files["tmp_name"], $image->fullPath);
448
            if (false !== $moveUpload) {
449
                return $image;
450
            }
451
        }
452
453
        $image->error =  $this->error_messages['unknown'];
454
        return false;
455
    }
456
457
    /**
458
     * Final upload method to be called, isolated for testing purposes
459
     *
460
     * @param $tmp_name int the temporary location of the image file
461
     * @param $destination int upload destination
462
     *
463
     * @return bool
464
     */
465
    public function moveUploadedFile($tmp_name, $destination)
466
    {
467
        return move_uploaded_file($tmp_name, $destination);
468
    }
469
}
470