Completed
Pull Request — master (#93)
by
unknown
04:28
created

Image::setLanguage()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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