Completed
Pull Request — master (#93)
by
unknown
01:17
created

Image::getLocation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
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
        'ERROR_09' => 'Error! the language could not found',
100
      ),
101
    );
102
103
    /**
104
     * @var array storage for the global array
105
     */
106
    private $_files = array();
107
108
    /**
109
     * @var string storage for any errors
110
     */
111
    private $error = '';
112
113
    /**
114
     * @param array $_files represents the $_FILES array passed as dependency
115
     */
116
    public function __construct(array $_files = array())
117
    {
118
      if (!function_exists('exif_imagetype')) {
119
        $this->error = $this->commonUploadErrors[$this->language]['ERROR_01'];
120
      }
121
122
      $this->_files = $_files;
123
    }
124
125
    /**
126
     * \ArrayAccess unused method
127
     * 
128
     * @param mixed $offset
129
     * @param mixed $value
130
     */
131
    public function offsetSet($offset, $value) {}
132
133
    /**
134
     * \ArrayAccess unused method
135
     * 
136
     * @param mixed $offset
137
     */
138
    public function offsetExists($offset){}
139
140
    /**
141
     * \ArrayAccess unused method
142
     * 
143
     * @param mixed $offset
144
     */
145
    public function offsetUnset($offset){}
146
147
    /**
148
     * \ArrayAccess - get array value from object
149
     *
150
     * @param mixed $offset
151
     *
152
     * @return string|bool
153
     */
154
    public function offsetGet($offset)
155
    {
156
      // return false if $_FILES['key'] isn't found
157
      if (!isset($this->_files[$offset])) {
158
        $this->error = sprintf($this->commonUploadErrors[$this->language]['ERROR_02'], $offset);
159
        return false;
160
      }
161
162
      $this->_files = $this->_files[$offset];
163
164
      // check for common upload errors
165
      if (isset($this->_files['error'])) {
166
        $this->error = $this->commonUploadErrors[$this->language][$this->_files['error']];
167
      }
168
169
      return true;
170
    }
171
172
    /**
173
     * Sets max image height and width limit.
174
     *
175
     * @param $maxWidth int max width value
176
     * @param $maxHeight int max height value
177
     *
178
     * @return $this
179
     */
180
    public function setDimension($maxWidth, $maxHeight)
181
    {
182
      if ( (int) $maxWidth && (int) $maxHeight) {
183
        $this->dimensions = array($maxWidth, $maxHeight);
184
      } else {
185
        $this->error = $this->commonUploadErrors[$this->language]['ERROR_03'];
186
      }
187
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
      return $this->fullPath = $this->getLocation().'/'.$this->getName().'.'.$this->getMime();
199
    }
200
201
    /**
202
     * Define a language
203
     *
204
     * @param $lang string language code 
205
     *
206
     * @return $this
207
     */
208
    public function setLanguage($lang)
209
    {
210
      if (isset($this->commonUploadErrors[$lang])) {
211
        $this->language = $lang;
212
      } else {
213
        $this->error = $this->commonUploadErrors[$this->language]['ERROR_09'];
214
      }
215
216
      return $this;
217
    }
218
219
    /**
220
     * Returns the image size in bytes.
221
     *
222
     * @return int
223
     */
224
    public function getSize()
225
    {
226
      return (int) $this->_files['size'];
227
    }
228
229
    /**
230
     * Define a min and max image size for uploading.
231
     *
232
     * @param $min int minimum value in bytes
233
     * @param $max int maximum value in bytes
234
     *
235
     * @return $this
236
     */
237
    public function setSize($min, $max)
238
    {
239
      $this->size = array($min, $max);
240
      return $this;
241
    }
242
243
    /**
244
     * Returns a JSON format of the image width, height, name, mime ...
245
     *
246
     * @return string
247
     */
248
    public function getJson()
249
    {
250
      return json_encode(
251
        array(
252
          'name' => $this->name,
253
          'mime' => $this->mime,
254
          'height' => $this->height,
255
          'width' => $this->width,
256
          'size' => $this->_files['size'],
257
          'location' => $this->location,
258
          'fullpath' => $this->fullPath,
259
        )
260
      );
261
    }
262
263
    /**
264
     * Returns the image mime type.
265
     *
266
     * @return null|string
267
     */
268
    public function getMime()
269
    {
270
      if (!$this->mime) {
271
        $this->mime = $this->getImageMime($this->_files['tmp_name']);
272
      }
273
274
      return $this->mime;
275
    }
276
277
    /**
278
     * Define a mime type for uploading.
279
     *
280
     * @param array $fileTypes
281
     *
282
     * @return $this
283
     */
284
    public function setMime(array $fileTypes)
285
    {
286
      $this->mimeTypes = $fileTypes;
287
      return $this;
288
    }
289
290
    /**
291
     * Gets the real image mime type.
292
     *
293
     * @param $tmp_name string The upload tmp directory
294
     *
295
     * @return null|string
296
     */
297
    protected function getImageMime($tmp_name)
298
    {
299
      $this->mime = @$this->acceptedMimes[exif_imagetype($tmp_name)];
300
      if (!$this->mime) {
301
        return null;
302
      }
303
304
      return $this->mime;
305
    }
306
307
    /**
308
     * Returns error string or false if no errors occurred.
309
     *
310
     * @return string|false
311
     */
312
    public function getError()
313
    {
314
      return $this->error;
315
    }
316
317
    /**
318
     * Returns the image name.
319
     *
320
     * @return string
321
     */
322
    public function getName()
323
    {
324
      if (!$this->name) {
325
        $this->name = uniqid('', true).'_'.str_shuffle(implode(range('e', 'q')));
326
      }
327
328
      return $this->name;
329
    }
330
331
    /**
332
     * Provide image name if not provided.
333
     *
334
     * @param null $isNameProvided
335
     *
336
     * @return $this
337
     */
338
    public function setName($isNameProvided = null)
339
    {
340
      if ($isNameProvided) {
341
        $this->name = filter_var($isNameProvided, FILTER_SANITIZE_STRING);
342
      }
343
344
      return $this;
345
    }
346
347
    /**
348
     * Returns the image width.
349
     *
350
     * @return int
351
     */
352
    public function getWidth()
353
    {
354
      if ($this->width != null) {
355
        return $this->width;
356
      }
357
358
      list($width) = getimagesize($this->_files['tmp_name']);
359
360
      return $width;
361
    }
362
363
    /**
364
     * Returns the image height in pixels.
365
     *
366
     * @return int
367
     */
368
    public function getHeight()
369
    {
370
      if ($this->height != null) {
371
        return $this->height;
372
      }
373
374
      list(, $height) = getimagesize($this->_files['tmp_name']);
375
376
      return $height;
377
    }
378
379
    /**
380
     * Returns the storage / folder name.
381
     *
382
     * @return string
383
     */
384
    public function getLocation()
385
    {
386
      if (!$this->location) {
387
        $this->setLocation();
388
      }
389
390
      return $this->location;
391
    }
392
393
    /**
394
     * Validate directory/permission before creating a folder.
395
     *
396
     * @param $dir string the folder name to check
397
     *
398
     * @return bool
399
     */
400
    private function isDirectoryValid($dir)
401
    {
402
      return !file_exists($dir) && !is_dir($dir) || is_writable($dir);
403
    }
404
405
    /**
406
     * Creates a location for upload storage.
407
     *
408
     * @param $dir string the folder name to create
409
     * @param int $permission chmod permission
410
     *
411
     * @return $this
412
     */
413
    public function setLocation($dir = 'bulletproof', $permission = 0666)
414
    {
415
      $isDirectoryValid = $this->isDirectoryValid($dir);
416
417
      if (!$isDirectoryValid) {
418
        $this->error = sprintf($this->commonUploadErrors[$this->language]['ERROR_04'], $dir);
419
        return false;
420
      }
421
422
      $create = !is_dir($dir) ? @mkdir('' . $dir, (int) $permission, true) : true;
423
424
      if (!$create) {
425
        $this->error = sprintf($this->commonUploadErrors[$this->language]['ERROR_05'], $dir);
426
        return false;
427
      }
428
429
      $this->location = $dir;
430
431
      return $this;
432
    }
433
434
    /**
435
     * Validate image size, dimension or mimetypes
436
     *
437
     * @return boolean
438
     */
439
    protected function contraintsValidator()
440
    {
441
      /* check image for valid mime types and return mime */
442
      $this->getImageMime($this->_files['tmp_name']);
443
      /* validate image mime type */
444
      if (!in_array($this->mime, $this->mimeTypes)) {
445
        $this->error = sprintf($this->commonUploadErrors[$this->language]['ERROR_06'], implode(', ', $this->mimeTypes));
446
        return false;
447
      }
448
449
      /* get image sizes */
450
      list($minSize, $maxSize) = $this->size;
451
452
      /* check image size based on the settings */
453
      if ($this->_files['size'] < $minSize || $this->_files['size'] > $maxSize) {
454
        $min = $minSize.' bytes ('.intval($minSize / 1000).' kb)';
455
        $max = $maxSize.' bytes ('.intval($maxSize / 1000).' kb)';
456
        $this->error = sprintf($this->commonUploadErrors[$this->language]['ERROR_07'], $min, $max);
457
        return false;
458
      }
459
460
      /* check image dimension */
461
      list($maxWidth, $maxHeight) = $this->dimensions;
462
      $this->width = $this->getWidth();
463
      $this->height = $this->getHeight();
464
465
      if ($this->height > $maxHeight || $this->width > $maxWidth) {
466
        $this->error = sprintf($this->commonUploadErrors[$this->language]['ERROR_08'], $maxHeight, $maxWidth);
467
        return false;
468
      }
469
470
      return true;
471
    }
472
473
    /**
474
     * Validate and save (upload) file
475
     *
476
     * @return false|Image
477
     */
478
    public function upload()
479
    {
480
      if ($this->error) {
481
        return false;
482
      }
483
484
      $isValid = $this->contraintsValidator();
485
486
      $isSuccess = $isValid && $this->isSaved($this->_files['tmp_name'], $this->getFullPath());
487
488
      return $isSuccess ? $this : false;
489
    }
490
491
    /**
492
     * Final upload method to be called, isolated for testing purposes.
493
     *
494
     * @param $tmp_name int the temporary location of the image file
495
     * @param $destination int upload destination
496
     *
497
     * @return bool
498
     */
499
    protected function isSaved($tmp_name, $destination)
500
    {
501
      return move_uploaded_file($tmp_name, $destination);
502
    }
503
}