Completed
Branch prettify (847d68)
by samayo
01:21
created

Image::getSize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * BULLETPROOF
4
 *
5
 * Bulletproof is a single-class PHP library to upload images securely.
6
 *
7
 * PHP support 5.3+
8
 *
9
 * @package     bulletproof
10
 * @version     3.2.1
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 The min and max image size allowed for upload (in bytes)
51
     */
52
    protected $size = array(100, 500000);
53
54
    /**
55
     * @var array The max height and width image allowed
56
     */
57
    protected $dimensions = array(5000, 5000);
58
59
    /**
60
     * @var array The mime types allowed for upload
61
     */
62
    protected $mimeTypes = array('jpeg', 'png', 'gif', 'jpg');
63
64
    /**
65
     * @var array list of known image types
66
     */
67
    protected $acceptedMimes = array(
68
        1 => 'gif', 'jpeg', 'png', 'swf', 'psd',
69
        'bmp', 'tiff', 'tiff', 'jpc', 'jp2', 'jpx',
70
        'jb2', 'swc', 'iff', 'wbmp', 'xbm', 'ico'
71
    );
72
    /**
73
     * @var array error messages strings
74
     */
75
    protected $commonUploadErrors = array(
76
        UPLOAD_ERR_OK         => '',
77
        UPLOAD_ERR_INI_SIZE   => 'Image is larger than the specified amount set by the server',
78
        UPLOAD_ERR_FORM_SIZE  => 'Image is larger than the specified amount specified by browser',
79
        UPLOAD_ERR_PARTIAL    => 'Image could not be fully uploaded. Please try again later',
80
        UPLOAD_ERR_NO_FILE    => 'Image is not found',
81
        UPLOAD_ERR_NO_TMP_DIR => 'Can\'t write to disk, due to server configuration ( No tmp dir found )',
82
        UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk. Please check you file permissions',
83
        UPLOAD_ERR_EXTENSION  => 'A PHP extension has halted this file upload process'
84
    );
85
    /**
86
     * @var array storage for the $_FILES global array
87
     */
88
    private $_files = array();
89
    /**
90
     * @var string storage for any errors
91
     */
92
    private $error = '';
93
94
    /**
95
     * @param array $_files represents the $_FILES array passed as dependency
96
     */
97
    public function __construct(array $_files = array())
98
    {
99
        /* check if php_exif is enabled */
100
        if (!function_exists('exif_imagetype')) {
101
            $this->error = 'Function \'exif_imagetype\' Not found. Please enable \'php_exif\' in your PHP.ini';
102
            return null;
103
        }
104
105
        $this->_files = $_files;
106
    }
107
108
    /**
109
     * @param mixed $offset
110
     * @param mixed $value
111
     * @return null
112
     */
113
    public function offsetSet($offset, $value){}
114
115
    /**
116
     * @param mixed $offset
117
     * @return null
118
     */
119
    public function offsetExists($offset){}
120
121
    /**
122
     * @param mixed $offset
123
     * @return null
124
     */
125
    public function offsetUnset($offset){}
126
127
    /**
128
     * Gets array value \ArrayAccess
129
     *
130
     * @param mixed $offset
131
     *
132
     * @return string|boolean
133
     */
134
    public function offsetGet($offset)
135
    {   
136
        // return error if requested
137
        if ($offset == 'error') {
138
            return $this->error;
139
        }
140
        
141
        // return false if $image['key'] isn't found
142
        if (!isset($this->_files[$offset])) {
143
            return false;
144
        }
145
146
        $this->_files = $this->_files[$offset];
147
148
        // check for common upload errors
149
        if(isset($this->_files['error'])){
150
            $this->error = $this->commonUploadErrors[$this->_files['error']];
151
        }
152
153
        return true;
154
    }
155
156
    /**
157
     * Sets max image height and width limit
158
     *
159
     * @param $maxWidth int max width value
160
     * @param $maxHeight int max height value
161
     *
162
     * @return $this
163
     */
164
    public function setDimension($maxWidth, $maxHeight)
165
    {
166
        $this->dimensions = array($maxWidth, $maxHeight);
167
        return $this;
168
    }
169
170
    /**
171
     * Returns the full path of the image ex 'location/image.mime'
172
     *
173
     * @return string
174
     */
175
    public function getFullPath()
176
    {
177
        $this->fullPath = $this->location . '/' . $this->name . '.' . $this->mime;
178
        return $this->fullPath;
179
    }
180
181
    /**
182
     * Returns the image size in bytes
183
     *
184
     * @return int
185
     */
186
    public function getSize()
187
    {
188
        return (int) $this->_files['size'];
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
     * Returns a JSON format of the image width, height, name, mime ...
207
     *
208
     * @return string
209
     */
210
    public function getJson()
211
    {
212
        /* gather image info for json storage */
213
        return json_encode (
214
            array(
215
                'name'      => $this->name,
216
                'mime'      => $this->mime,
217
                'height'    => $this->height,
218
                'width'     => $this->width,
219
                'size'      => $this->_files['size'],
220
                'location'  => $this->location,
221
                'fullpath'  => $this->fullPath
222
            )
223
        );
224
    }
225
226
    /**
227
     * Returns the image mime type
228
     *
229
     * @return null|string
230
     */
231
    public function getMime()
232
    {
233
        if (!$this->mime) {
234
            return $this->getImageMime($this->_files['tmp_name']);
235
        }
236
        return $this->mime;
237
    }
238
239
    /**
240
     * Define a mime type for uploading
241
     *
242
     * @param array $fileTypes
243
     *
244
     * @return $this
245
     */
246
    public function setMime(array $fileTypes)
247
    {
248
        $this->mimeTypes = $fileTypes;
249
        return $this;
250
    }
251
252
    /**
253
     * Gets the real image mime type
254
     *
255
     * @param $tmp_name string The upload tmp directory
256
     *
257
     * @return null|string
258
     */
259
    protected function getImageMime($tmp_name)
260
    {
261
        $mime = @ $this->acceptedMimes[exif_imagetype($tmp_name)];
262
263
        if (!$mime) {
264
            return null;
265
        }
266
267
        return $mime;
268
    }
269
270
    /**
271
     * Returns error string or false if no errors occurred
272
     *
273
     * @return string|false
274
     */
275
    public function getError()
276
    {
277
        return $this->error != '' ? $this->error : false;
278
    }
279
280
    /**
281
     * Returns the image name
282
     *
283
     * @return string
284
     */
285
    public function getName()
286
    {
287
        if (!$this->name) {
288
            return uniqid('', true) . '_' . str_shuffle(implode(range('e', 'q')));
289
        }
290
291
        return $this->name;
292
    }
293
294
    /**
295
     * Provide image name if not provided
296
     *
297
     * @param null $isNameProvided
298
     * @return $this
299
     */
300
    public function setName($isNameProvided = null)
301
    {
302
        if ($isNameProvided) {
303
            $this->name = filter_var($isNameProvided, FILTER_SANITIZE_STRING);
304
        }
305
306
        return $this;
307
    }
308
309
    /**
310
     * Returns the image width
311
     *
312
     * @return int
313
     */
314
    public function getWidth()
315
    {
316
        if ($this->width != null) {
317
            return $this->width;
318
        }
319
320
        list($width) = getImageSize($this->_files['tmp_name']);
321
        return $width;
322
    }
323
324
    /**
325
     * Returns the image height in pixels
326
     *
327
     * @return int
328
     */
329
    public function getHeight()
330
    {
331
        if ($this->height != null) {
332
            return $this->height;
333
        }
334
335
        list(, $height) = getImageSize($this->_files['tmp_name']);
336
        return $height;
337
    }
338
339
    /**
340
     * Returns the storage / folder name
341
     *
342
     * @return string
343
     */
344
    public function getLocation()
345
    {
346
        if (!$this->location) {
347
            $this->setLocation();
348
        }
349
350
        return $this->location;
351
    }
352
353
    /**
354
     * Creates a location for upload storage
355
     *
356
     * @param $dir string the folder name to create
357
     * @param int $permission chmod permission
358
     *
359
     * @return $this
360
     */
361
    public function setLocation($dir = 'bulletproof', $permission = 0666)
362
    {
363
        if (!file_exists($dir) && !is_dir($dir)) {
364
            $createFolder = @mkdir('' . $dir, (int) $permission, true);
365
            if (!$createFolder) {
366
                $this->error = 'Error! Folder ' . $dir . ' could not be created';
367
                return false;
368
            }
369
        }
370
371
        /* check if we can create a file in the directory */
372
        if (!is_writable($dir)) {
373
            $this->error = 'The images directory \'' . $dir . '\' is not writable!';
374
            return false;
375
        }
376
377
        $this->location = $dir;
378
        return $this;
379
    }
380
381
    /**
382
     * This methods validates and uploads the image
383
     * @return false|Image
384
     */
385
    public function upload()
386
    {
387
        /* modify variable names for convenience */
388
        $image = $this;
389
        $files = $this->_files;
390
391
        if ($this->error || !isset($files['tmp_name'])) {
392
            return false;
393
        }
394
395
        /* check image for valid mime types and return mime */
396
        $image->mime = $image->getImageMime($files['tmp_name']);
397
        /* validate image mime type */
398
        if (!in_array($image->mime, $image->mimeTypes)) {
399
            $ext = implode(', ', $image->mimeTypes);
400
            $image->error = sprintf('Invalid File! Only (%s) image types are allowed', $ext);
401
            return false;
402
        }
403
404
        /* initialize image properties */
405
        $image->name = $image->getName();
406
        $image->width = $image->getWidth();
407
        $image->height = $image->getHeight();
408
        $image->location = $image->getLocation();
409
        
410
        $image->getFullPath();
411
412
        /* get image sizes */
413
        list($minSize, $maxSize) = $image->size;
414
415
        /* check image size based on the settings */
416
        if ($files['size'] < $minSize || $files['size'] > $maxSize) {
417
            $min = intval($minSize / 1000) ?: 1;
418
            $max = intval($maxSize / 1000) ?: 1;
419
            $image->error = 'Image size should be at least ' . $min . ' KB, and no more than ' . $max . ' KB';
420
            return false;
421
        }
422
423
        /* check image dimension */
424
        list($allowedWidth, $allowedHeight) = $image->dimensions;
425
426
        if ($image->height > $allowedHeight || $image->width > $allowedWidth) {
427
            $image->error = 'Image height/width should be less than ' . $allowedHeight . '/' . $allowedWidth . ' pixels';
428
            return false;
429
        }
430
431
        if ($image->height < 2 || $image->width < 2) {
432
            $image->error = 'Image height/width too small or corrupted.';
433
            return false;
434
        }
435
436
        if ($image->error === '') {
437
            $moveUpload = $image->moveUploadedFile($files['tmp_name'], $image->fullPath);
438
            if (false !== $moveUpload) {
439
                return $image;
440
            }
441
        }
442
443
        $image->error = 'Upload failed, Unknown error occured';
444
        return false;
445
    }
446
447
448
    /**
449
     * Final upload method to be called, isolated for testing purposes
450
     *
451
     * @param $tmp_name int the temporary location of the image file
452
     * @param $destination int upload destination
453
     *
454
     * @return bool
455
     */
456
    public function moveUploadedFile($tmp_name, $destination)
457
    {
458
        return move_uploaded_file($tmp_name, $destination);
459
    }
460
}