upload   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 514
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 46
c 1
b 0
f 0
lcom 1
cbo 0
dl 0
loc 514
rs 8.3999

28 Methods

Rating   Name   Duplication   Size   Complexity  
A factory() 0 4 1
A __construct() 0 11 2
A set_filename() 0 4 1
A upload() 0 8 2
A save() 0 5 1
A check() 0 13 1
A get_state() 0 4 1
B save_file() 0 24 3
A set_file_data() 0 15 1
A set_error() 0 4 1
A get_errors() 0 4 1
A callbacks() 0 13 3
A validate() 0 16 2
A execute_callbacks() 0 6 2
A check_mime_type() 0 8 3
A set_allowed_mime_types() 0 6 1
A check_file_size() 0 9 3
A set_max_file_size() 0 6 1
A file() 0 4 1
A set_file_array() 0 17 2
B check_file_array() 0 8 5
A get_file_mime() 0 4 1
A get_file_size() 0 4 1
A set_destination() 0 5 2
A destination_exist() 0 4 1
A create_destination() 0 4 1
A create_new_filename() 0 5 1
A bytes_to_mb() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like upload often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use upload, and based on these observations, apply Extract Interface, too.

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 string[] $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 double
519
	 */
520
	protected function bytes_to_mb($bytes)
521
	{
522
		return round(($bytes / 1048576), 2);
523
	}
524
525
526
} // end of Upload