Completed
Push — master ( 9d86c3...798c64 )
by Florin
02:33
created

upload::upload()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
1
<?php
2
3
namespace florinp\messenger\libs;
4
5
use finfo;
6
use Exception;
7
8
/**
9
 * Simple PHP upload class
10
 *
11
 * @author Aivis Silins
12
 */
13
class upload
14
{
15
16
    /**
17
     * Default directory persmissions (destination dir)
18
     */
19
    protected $default_permissions = 750;
20
21
22
    /**
23
     * File post array
24
     *
25
     * @var array
26
     */
27
    protected $files_post = array();
28
29
30
    /**
31
     * Destination directory
32
     *
33
     * @var string
34
     */
35
    protected $destination;
36
37
38
    /**
39
     * Fileinfo
40
     *
41
     * @var object
42
     */
43
    protected $finfo;
44
45
46
    /**
47
     * Data about file
48
     *
49
     * @var array
50
     */
51
    public $file = array();
52
53
54
    /**
55
     * Max. file size
56
     *
57
     * @var int
58
     */
59
    protected $max_file_size;
60
61
62
    /**
63
     * Allowed mime types
64
     *
65
     * @var array
66
     */
67
    protected $mimes = array();
68
69
70
    /**
71
     * External callback object
72
     *
73
     * @var obejct
74
     */
75
    protected $external_callback_object;
76
77
78
    /**
79
     * External callback methods
80
     *
81
     * @var array
82
     */
83
    protected $external_callback_methods = array();
84
85
86
    /**
87
     * Temp path
88
     *
89
     * @var string
90
     */
91
    protected $tmp_name;
92
93
94
    /**
95
     * Validation errors
96
     *
97
     * @var array
98
     */
99
    protected $validation_errors = array();
100
101
102
    /**
103
     * Filename (new)
104
     *
105
     * @var string
106
     */
107
    protected $filename;
108
109
    /**
110
     * File extension
111
     * @var string
112
     */
113
    protected $extension;
114
115
    /**
116
     * Internal callbacks (filesize check, mime, etc)
117
     *
118
     * @var array
119
     */
120
    private $callbacks = array();
121
122
    /**
123
     * Root dir
124
     *
125
     * @var string
126
     */
127
    protected $root;
128
129
    /**
130
     * Return upload object
131
     *
132
     * $destination = 'path/to/your/file/destination/folder';
133
     *
134
     * @param string $phpbb_root_path
135
     * @return Upload
136
     */
137
    public static function factory($phpbb_root_path)
138
    {
139
        return new Upload($phpbb_root_path);
140
    }
141
142
    /**
143
     *  Define ROOT constant and set & create destination path
144
     *
145
     * @param string $phpbb_root_path
146
     * @throws Exception
147
     */
148
    public function __construct($phpbb_root_path)
149
    {
150
            $phpbb_root_path = $phpbb_root_path . 'store/messenger/files';
151
        // set & create destination path
152
        if (!$this->set_destination($phpbb_root_path)) {
153
            throw new Exception('Upload: Can\'t create destination. ' . $this->root . $this->destination);
154
        }
155
156
        //create finfo object
157
        $this->finfo = new finfo();
158
    }
159
160
    /**
161
     * Set target filename
162
     *
163
     * @param string $filename
164
     */
165
    public function set_filename($filename)
166
    {
167
        $this->filename = $filename;
168
    }
169
170
    /**
171
     * Check & Save file
172
     *
173
     * Return data about current upload
174
     *
175
     * @return array
176
     */
177
    public function upload($filename = '')
0 ignored issues
show
Unused Code introduced by
The parameter $filename is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
178
    {
179
        if ($this->check()) {
180
            $this->save();
181
        }
182
        // return state data
183
        return $this->get_state();
184
    }
185
186
187
    /**
188
     * Save file on server
189
     *
190
     * Return state data
191
     *
192
     * @return array
193
     */
194
    public function save()
195
    {
196
        $this->save_file();
197
        return $this->get_state();
198
    }
199
200
201
    /**
202
     * Validate file (execute callbacks)
203
     *
204
     * Returns TRUE if validation successful
205
     *
206
     * @return bool
207
     */
208
    public function check()
209
    {
210
        //execute callbacks (check filesize, mime, also external callbacks
211
        $this->validate();
212
213
        //add error messages
214
        $this->file['errors'] = $this->get_errors();
215
216
        //change file validation status
217
        $this->file['status'] = empty($this->validation_errors);
218
219
        return $this->file['status'];
220
    }
221
222
    /**
223
     * Get current state data
224
     *
225
     * @return array
226
     */
227
    public function get_state()
228
    {
229
        return $this->file;
230
    }
231
232
233
    /**
234
     * Save file on server
235
     */
236
    protected function save_file()
237
    {
238
        //create & set new filename
239
        if (empty($this->filename)) {
240
            $this->create_new_filename();
241
        }
242
243
        //set filename
244
        $this->file['filename'] = $this->filename;
245
246
        //set full path
247
        $this->file['full_path'] = $this->root . $this->destination . $this->filename;
248
        $this->file['path'] = $this->destination . $this->filename;
249
250
        $status = move_uploaded_file($this->tmp_name, $this->file['full_path']);
251
252
        //checks whether upload successful
253
        if (!$status) {
254
            throw new Exception('Upload: Can\'t upload file.');
255
        }
256
257
        //done
258
        $this->file['status'] = true;
259
    }
260
261
    /**
262
     * Set data about file
263
     */
264
    protected function set_file_data()
265
    {
266
        $file_size = $this->get_file_size();
267
268
        $this->file = array(
269
            'status' => false,
270
            'destination' => $this->destination,
271
            'size_in_bytes' => $file_size,
272
            'size_in_mb' => $this->bytes_to_mb($file_size),
273
            'mime' => $this->get_file_mime(),
274
            'original_filename' => $this->file_post['name'],
0 ignored issues
show
Bug introduced by
The property file_post does not seem to exist. Did you mean files_post?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
275
            'tmp_name' => $this->file_post['tmp_name'],
0 ignored issues
show
Bug introduced by
The property file_post does not seem to exist. Did you mean files_post?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
276
            'post_data' => $this->file_post,
0 ignored issues
show
Bug introduced by
The property file_post does not seem to exist. Did you mean files_post?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
277
        );
278
    }
279
280
    /**
281
     * Set validation error
282
     *
283
     * @param string $message
284
     */
285
    public function set_error($message)
286
    {
287
        $this->validation_errors[] = $message;
288
    }
289
290
    /**
291
     * Return validation errors
292
     *
293
     * @return array
294
     */
295
    public function get_errors()
296
    {
297
        return $this->validation_errors;
298
    }
299
300
    /**
301
     * Set external callback methods
302
     *
303
     * @param object $instance_of_callback_object
304
     * @param array $callback_methods
305
     * @throws Exception
306
     */
307
    public function callbacks($instance_of_callback_object, $callback_methods)
308
    {
309
        if (empty($instance_of_callback_object)) {
310
            throw new Exception('Upload: $instance_of_callback_object can\'t be empty.');
311
        }
312
313
        if (!is_array($callback_methods)) {
314
            throw new Exception('Upload: $callback_methods data type need to be array.');
315
        }
316
317
        $this->external_callback_object = $instance_of_callback_object;
318
        $this->external_callback_methods = $callback_methods;
319
    }
320
321
    /**
322
     * Execute callbacks
323
     */
324
    protected function validate()
325
    {
326
        //get curent errors
327
        $errors = $this->get_errors();
328
329
        if (empty($errors)) {
330
            //set data about current file
331
            $this->set_file_data();
332
333
            //execute internal callbacks
334
            $this->execute_callbacks($this->callbacks, $this);
335
336
            //execute external callbacks
337
            $this->execute_callbacks($this->external_callback_methods, $this->external_callback_object);
338
        }
339
    }
340
341
    /**
342
     * Execute callbacks
343
     */
344
    protected function execute_callbacks($callbacks, $object)
345
    {
346
        foreach ($callbacks as $method) {
347
            $object->$method($this);
348
        }
349
    }
350
351
    /**
352
     * File mime type validation callback
353
     *
354
     * @param mixed $object
355
     */
356
    protected function check_mime_type($object)
357
    {
358
        if (!empty($object->mimes)) {
359
            if (!in_array($object->file['mime'], $object->mimes)) {
360
                $object->set_error('Mime type not allowed.');
361
            }
362
        }
363
    }
364
365
    /**
366
     * Set allowed mime types
367
     *
368
     * @param array $mimes
369
     */
370
    public function set_allowed_mime_types($mimes)
371
    {
372
        $this->mimes = $mimes;
373
        //if mime types is set -> set callback
374
        $this->callbacks[] = 'check_mime_type';
375
    }
376
377
    /**
378
     * File size validation callback
379
     *
380
     * @param object $object
381
     */
382
    protected function check_file_size($object)
383
    {
384
        if (!empty($object->max_file_size)) {
385
            $file_size_in_mb = $this->bytes_to_mb($object->file['size_in_bytes']);
386
            if ($object->max_file_size <= $file_size_in_mb) {
387
                $object->set_error('File is too big.');
388
            }
389
        }
390
    }
391
392
    /**
393
     * Set max. file size
394
     *
395
     * @param int $size
396
     */
397
    public function set_max_file_size($size)
398
    {
399
        $this->max_file_size = $size;
400
        //if max file size is set -> set callback
401
        $this->callbacks[] = 'check_file_size';
402
    }
403
404
    /**
405
     * Set File array to object
406
     *
407
     * @param array $file
408
     */
409
    public function file($file)
410
    {
411
        $this->set_file_array($file);
412
    }
413
414
    /**
415
     * Set file array
416
     *
417
     * @param array $file
418
     */
419
    protected function set_file_array($file)
420
    {
421
        //checks whether file array is valid
422
        if (!$this->check_file_array($file)) {
423
            //file not selected or some bigger problems (broken files array)
424
            $this->set_error('Please select file.');
425
        }
426
427
        //set file data
428
        $this->file_post = $file;
0 ignored issues
show
Bug introduced by
The property file_post does not seem to exist. Did you mean files_post?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
429
430
        //set tmp path
431
        $this->tmp_name = $file['tmp_name'];
432
433
        // set file extension
434
        $this->extension = pathinfo($file['name'], PATHINFO_EXTENSION);
435
    }
436
437
    /**
438
     * Checks whether Files post array is valid
439
     *
440
     * @return bool
441
     */
442
    protected function check_file_array($file)
443
    {
444
        return isset($file['error'])
445
        && !empty($file['name'])
446
        && !empty($file['type'])
447
        && !empty($file['tmp_name'])
448
        && !empty($file['size']);
449
    }
450
451
    /**
452
     * Get file mime type
453
     *
454
     * @return string
455
     */
456
    protected function get_file_mime()
457
    {
458
        return $this->finfo->file($this->tmp_name, FILEINFO_MIME_TYPE);
459
    }
460
461
    /**
462
     * Get file size
463
     *
464
     * @return int
465
     */
466
    protected function get_file_size()
467
    {
468
        return filesize($this->tmp_name);
469
    }
470
471
    /**
472
     * Set destination path (return TRUE on success)
473
     *
474
     * @param string $destination
475
     * @return bool
476
     */
477
    protected function set_destination($destination)
478
    {
479
        $this->destination = $destination . DIRECTORY_SEPARATOR;
480
        return $this->destination_exist() ? TRUE : $this->create_destination();
481
    }
482
483
    /**
484
     * Checks whether destination folder exists
485
     *
486
     * @return bool
487
     */
488
    protected function destination_exist()
489
    {
490
        return is_writable($this->root . $this->destination);
491
    }
492
493
    /**
494
     * Create path to destination
495
     *
496
     * @return bool
497
     */
498
    protected function create_destination()
499
    {
500
        return mkdir($this->root . $this->destination, $this->default_permissions, true);
501
    }
502
503
    /**
504
     * Set unique filename
505
     *
506
     * @return string
507
     */
508
    protected function create_new_filename()
509
    {
510
        $filename = sha1(mt_rand(1, 9999) . $this->destination . uniqid()) . time() . '.' . $this->extension;
511
        $this->set_filename($filename);
512
    }
513
514
    /**
515
     * Convert bytes to mb.
516
     *
517
     * @param int $bytes
518
     * @return int
519
     */
520
    protected function bytes_to_mb($bytes)
521
    {
522
        return round(($bytes / 1048576), 2);
523
    }
524
525
526
} // end of Upload