Completed
Push — master ( 647094...69506e )
by samayo
01:22
created

Image::offsetGet()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.0534
c 0
b 0
f 0
cc 4
eloc 9
nc 4
nop 1
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.0
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, 500000);
58
59
    /**
60
     * @var array The max height and width image allowed
61
     */
62
    protected $dimensions = array(5000, 5000);
63
64
    /**
65
     * @var array The mime types allowed for upload
66
     */
67
    protected $mimeTypes = array('jpeg', 'png', 'gif', 'jpg');
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
     * @var array error messages strings
79
     */
80
    protected $common_upload_errors = array(
81
        UPLOAD_ERR_OK         => '',
82
        UPLOAD_ERR_INI_SIZE   => 'Image is larger than the specified amount set by the server',
83
        UPLOAD_ERR_FORM_SIZE  => 'Image is larger than the specified amount specified by browser',
84
        UPLOAD_ERR_PARTIAL    => 'Image could not be fully uploaded. Please try again later',
85
        UPLOAD_ERR_NO_FILE    => 'Image is not found',
86
        UPLOAD_ERR_NO_TMP_DIR => 'Can\'t write to disk, due to server configuration ( No tmp dir found )',
87
        UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk. Please check you file permissions',
88
        UPLOAD_ERR_EXTENSION  => 'A PHP extension has halted this file upload process'
89
    );
90
    /**
91
     * @var array storage for the $_FILES global array
92
     */
93
    private $_files = array();
94
    /**
95
     * @var string storage for any errors
96
     */
97
    private $error = '';
98
99
    /**
100
     * @param array $_files represents the $_FILES array passed as dependency
101
     */
102
    public function __construct(array $_files = array())
103
    {
104
        /* check if php_exif is enabled */
105
        if (!function_exists('exif_imagetype')) {
106
            $this->error = 'Function \'exif_imagetype\' Not found. Please enable \'php_exif\' in your PHP.ini';
107
            return null;
108
        }
109
110
        $this->_files = $_files;
111
    }
112
113
    /**
114
     * @param mixed $offset
115
     * @param mixed $value
116
     */
117
    public function offsetSet($offset, $value)
118
    {
119
    }
120
121
    /**
122
     * @param mixed $offset
123
     * @return null
124
     */
125
    public function offsetExists($offset)
126
    {
127
    }
128
129
    /**
130
     * @param mixed $offset
131
     */
132
    public function offsetUnset($offset)
133
    {
134
    }
135
136
    /**
137
     * Gets array value \ArrayAccess
138
     *
139
     * @param mixed $offset
140
     *
141
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be string|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
142
     */
143
    public function offsetGet($offset)
144
    {   
145
        /* return error if requested */
146
        if ($offset == 'error') {
147
            return $this->error;
148
        }
149
        
150
        /* return false if $image['key'] isn't found */
151
        if (!isset($this->_files[$offset])) {
152
            return false;
153
        }
154
155
        $this->_files = $this->_files[$offset];
156
157
        /* check for common upload errors */
158
        if (isset($this->_files['error'])) {
159
            $this->error = $this->commonUploadErrors($this->_files['error']);
160
        }
161
162
        return true;
163
    }
164
165
    /**
166
     * Checks for the common upload errors
167
     *
168
     * @param $errors int error constant
169
     *
170
     * @return string
171
     */
172
    protected function commonUploadErrors($errors)
173
    {
174
        return $this->common_upload_errors[$errors];
175
    }
176
177
    /**
178
     * Sets acceptable max image height and width
179
     *
180
     * @param $maxWidth int max width value
181
     * @param $maxHeight int max height value
182
     *
183
     * @return $this
184
     */
185
    public function setDimension($maxWidth, $maxHeight)
186
    {
187
        $this->dimensions = array($maxWidth, $maxHeight);
188
        return $this;
189
    }
190
191
    /**
192
     * Returns the full path of the image ex 'location/image.mime'
193
     *
194
     * @return string
195
     */
196
    public function getFullPath()
197
    {
198
        $this->fullPath = $this->location . '/' . $this->name . '.' . $this->mime;
199
        return $this->fullPath;
200
    }
201
202
    /**
203
     * Returns the image size in bytes
204
     *
205
     * @return int
206
     */
207
    public function getSize()
208
    {
209
        return (int)$this->_files['size'];
210
    }
211
212
    /**
213
     * Define a min and max image size for uploading
214
     *
215
     * @param $min int minimum value in bytes
216
     * @param $max int maximum value in bytes
217
     *
218
     * @return $this
219
     */
220
    public function setSize($min, $max)
221
    {
222
        $this->size = array($min, $max);
223
        return $this;
224
    }
225
226
    /**
227
     * Returns a JSON format of the image width, height, name, mime ...
228
     *
229
     * @return string
230
     */
231
    public function getJson()
232
    {
233
        return json_encode($this->serialize);
234
    }
235
236
    /**
237
     * Returns the image mime type
238
     *
239
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
240
     */
241
    public function getMime()
242
    {
243
        if (!$this->mime) {
244
            return $this->getImageMime($this->_files['tmp_name']);
245
        }
246
        return $this->mime;
247
    }
248
249
    /**
250
     * Define a mime type for uploading
251
     *
252
     * @param array $fileTypes
253
     *
254
     * @return $this
255
     */
256
    public function setMime(array $fileTypes)
257
    {
258
        $this->mimeTypes = $fileTypes;
259
        return $this;
260
    }
261
262
    /**
263
     * Gets the real image mime type
264
     *
265
     * @param $tmp_name string The upload tmp directory
266
     *
267
     * @return null|string
268
     */
269
    protected function getImageMime($tmp_name)
270
    {
271
        $mime = @$this->imageMimes[exif_imagetype($tmp_name)];
272
273
        if (!$mime) {
274
            return null;
275
        }
276
277
        return $mime;
278
    }
279
280
    /**
281
     * Returns error string or false if no errors occurred
282
     *
283
     * @return string|bool
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
284
     */
285
    public function getError()
286
    {
287
        return $this->error != '' ? $this->error : false;
288
    }
289
290
    /**
291
     * This methods validates and uploads the image
292
     * @return false|Image
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null|Image?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
293
     */
294
    public function upload()
295
    {
296
        /* modify variable names for convenience */
297
        $image = $this;
298
        $files = $this->_files;
299
300
        if ($this->error) {
301
            return false;
302
        }
303
304
        if (!isset($files['tmp_name'])) {
305
            return false;
306
        }
307
308
        /* check image for valid mime types and return mime */
309
        $image->mime = $image->getImageMime($files['tmp_name']);
310
        /* validate image mime type */
311
        if (!in_array($image->mime, $image->mimeTypes)) {
312
            $ext = implode(', ', $image->mimeTypes);
313
            $image->error = sprintf('Invalid File! Only (%s) image types are allowed', $ext);
314
            return false;
315
        }
316
317
        /* initialize image properties */
318
        $image->name = $image->getName();
319
        $image->width = $image->getWidth();
320
        $image->height = $image->getHeight();
321
        $image->location = $image->getLocation();
322
323
        /* get image sizes */
324
        list($minSize, $maxSize) = $image->size;
325
326
        /* check image size based on the settings */
327
        if ($files['size'] < $minSize || $files['size'] > $maxSize) {
328
            $min = intval($minSize / 1000) ?: 1;
329
            $max = intval($maxSize / 1000) ?: 1;
330
            $image->error = "Image size should be at least " . $min . " KB, and no more than " . $max . " KB";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal Image size should be at least does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal KB, and no more than does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal KB does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
331
            return null;
332
        }
333
334
        /* check image dimension */
335
        list($allowedWidth, $allowedHeight) = $image->dimensions;
336
337
        if ($image->height > $allowedHeight || $image->width > $allowedWidth) {
338
            $image->error = 'Image height/width should be less than ' . $allowedHeight . '/' . $allowedWidth . ' pixels';
339
            return false;
340
        }
341
342
        if ($image->height < 2 || $image->width < 2) {
343
            $image->error = 'Image height/width too small or corrupted.';
344
            return false;
345
        }
346
347
        /* set and get folder name */
348
        $image->fullPath = $image->location . '/' . $image->name . '.' . $image->mime;
349
350
        /* gather image info for json storage */
351
        $image->serialize = array(
352
            'name' => $image->name,
353
            'mime' => $image->mime,
354
            'height' => $image->height,
355
            'width' => $image->width,
356
            'size' => $files['size'],
357
            'location' => $image->location,
358
            'fullpath' => $image->fullPath
359
        );
360
361
        if ($image->error === '') {
362
            $moveUpload = $image->moveUploadedFile($files['tmp_name'], $image->fullPath);
363
            if (false !== $moveUpload) {
364
                return $image;
365
            }
366
        }
367
368
        $image->error = 'Upload failed, Unknown error occured';
369
        return false;
370
    }
371
372
    /**
373
     * Returns the image name
374
     *
375
     * @return string
376
     */
377
    public function getName()
378
    {
379
        if (!$this->name) {
380
            return uniqid('', true) . '_' . str_shuffle(implode(range('e', 'q')));
381
        }
382
383
        return $this->name;
384
    }
385
386
    /**
387
     * Provide image name if not provided
388
     *
389
     * @param null $isNameProvided
390
     * @return $this
391
     */
392
    public function setName($isNameProvided = null)
393
    {
394
        if ($isNameProvided) {
395
            $this->name = filter_var($isNameProvided, FILTER_SANITIZE_STRING);
396
        }
397
398
        return $this;
399
    }
400
401
    /**
402
     * Returns the image width
403
     *
404
     * @return int
405
     */
406
    public function getWidth()
407
    {
408
        if ($this->width != null) {
409
            return $this->width;
410
        }
411
412
        list($width) = getImageSize($this->_files['tmp_name']);
413
        return $width;
414
    }
415
416
    /**
417
     * Returns the image height in pixels
418
     *
419
     * @return int
420
     */
421
    public function getHeight()
422
    {
423
        if ($this->height != null) {
424
            return $this->height;
425
        }
426
427
        list(, $height) = getImageSize($this->_files['tmp_name']);
428
        return $height;
429
    }
430
431
    /**
432
     * Returns the storage / folder name
433
     *
434
     * @return string
435
     */
436
    public function getLocation()
437
    {
438
        if (!$this->location) {
439
            $this->setLocation();
440
        }
441
442
        return $this->location;
443
    }
444
445
    /**
446
     * Creates a location for upload storage
447
     *
448
     * @param $dir string the folder name to create
449
     * @param int $permission chmod permission
450
     *
451
     * @return $this
452
     */
453
    public function setLocation($dir = 'bulletproof', $permission = 0666)
454
    {
455
456
        if (!file_exists($dir) && !is_dir($dir) && !$this->location) {
457
            $createFolder = @mkdir('' . $dir, (int)$permission, true);
458
            if (!$createFolder) {
459
                $this->error = 'Error! Folder ' . $dir . ' could not be created';
460
                return false;
461
            }
462
        }
463
464
        /* check if we can create a file in the directory */
465
        if (!is_writable($dir)) {
466
            $this->error = "The images directory \"" . $dir . "\" is not writable!";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal The images directory \" does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal \" is not writable! does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
467
            return false;
468
        }
469
470
        $this->location = $dir;
471
        return $this;
472
    }
473
474
    /**
475
     * Final upload method to be called, isolated for testing purposes
476
     *
477
     * @param $tmp_name int the temporary location of the image file
478
     * @param $destination int upload destination
479
     *
480
     * @return bool
481
     */
482
    public function moveUploadedFile($tmp_name, $destination)
483
    {
484
        return move_uploaded_file($tmp_name, $destination);
485
    }
486
}
0 ignored issues
show
Coding Style introduced by
As per coding style, files should not end with a newline character.

This check marks files that end in a newline character, i.e. an empy line.

Loading history...
487