GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

elFinder   F
last analyzed

Complexity

Total Complexity 217

Size/Duplication

Total Lines 1219
Duplicated Lines 7.96 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 97
loc 1219
rs 0.8
c 0
b 0
f 0
wmc 217
lcom 1
cbo 0

42 Methods

Rating   Name   Duplication   Size   Complexity  
F __construct() 0 53 18
A loaded() 0 3 1
A version() 0 3 1
A bind() 0 19 6
A unbind() 0 11 4
A commandExists() 0 3 3
A getVolume() 0 3 1
A commandArgsList() 0 3 2
A session_expires() 0 14 4
F exec() 0 78 21
A realpath() 0 6 2
A getNetVolumes() 0 3 3
A saveNetVolumes() 0 3 1
A error() 0 13 4
B netmount() 0 40 11
C open() 0 59 15
A ls() 9 9 3
A tree() 10 10 3
A parents() 10 10 3
A tmb() 0 13 4
C file() 9 61 12
A size() 0 14 5
A mkdir() 12 12 3
A mkfile() 12 12 3
A rename() 4 14 4
B duplicate() 5 22 7
A rm() 4 17 5
C upload() 0 41 12
B paste() 4 26 7
B get() 3 20 6
A put() 7 14 4
A extract() 4 14 6
A archive() 0 12 5
A search() 0 11 4
A info() 0 12 4
A dim() 0 9 3
A resize() 4 19 4
A volume() 0 8 3
A toArray() 0 3 3
A hashes() 0 7 2
A filter() 0 8 4
A utime() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like elFinder 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 elFinder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * elFinder - file manager for web.
5
 * Core class.
6
 *
7
 * @package elfinder
8
 * @author Dmitry (dio) Levashov
9
 * @author Troex Nevelin
10
 * @author Alexey Sukhotin
11
 **/
12
class elFinder {
13
	
14
	/**
15
	 * API version number
16
	 *
17
	 * @var string
18
	 **/
19
	protected $version = '2.0';
20
	
21
	/**
22
	 * Storages (root dirs)
23
	 *
24
	 * @var array
25
	 **/
26
	protected $volumes = array();
27
	
28
	public static $netDrivers = array();
29
30
	/**
31
	 * Mounted volumes count
32
	 * Required to create unique volume id
33
	 *
34
	 * @var int
35
	 **/
36
	public static $volumesCnt = 1;
37
	
38
	/**
39
	 * Default root (storage)
40
	 *
41
	 * @var elFinderStorageDriver
42
	 **/
43
	protected $default = null;
44
	
45
	/**
46
	 * Commands and required arguments list
47
	 *
48
	 * @var array
49
	 **/
50
	protected $commands = array(
51
		'open'      => array('target' => false, 'tree' => false, 'init' => false, 'mimes' => false),
52
		'ls'        => array('target' => true, 'mimes' => false),
53
		'tree'      => array('target' => true),
54
		'parents'   => array('target' => true),
55
		'tmb'       => array('targets' => true),
56
		'file'      => array('target' => true, 'download' => false),
57
		'size'      => array('targets' => true),
58
		'mkdir'     => array('target' => true, 'name' => true),
59
		'mkfile'    => array('target' => true, 'name' => true, 'mimes' => false),
60
		'rm'        => array('targets' => true),
61
		'rename'    => array('target' => true, 'name' => true, 'mimes' => false),
62
		'duplicate' => array('targets' => true, 'suffix' => false),
63
		'paste'     => array('dst' => true, 'targets' => true, 'cut' => false, 'mimes' => false),
64
		'upload'    => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false),
65
		'get'       => array('target' => true),
66
		'put'       => array('target' => true, 'content' => '', 'mimes' => false),
67
		'archive'   => array('targets' => true, 'type' => true, 'mimes' => false),
68
		'extract'   => array('target' => true, 'mimes' => false),
69
		'search'    => array('q' => true, 'mimes' => false),
70
		'info'      => array('targets' => true),
71
		'dim'       => array('target' => true),
72
		'resize'    => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false),
73
		'netmount'  => array('protocol' => true, 'host' => true, 'path' => false, 'port' => false, 'user' => true, 'pass' => true, 'alias' => false, 'options' => false)
74
	);
75
	
76
	/**
77
	 * Commands listeners
78
	 *
79
	 * @var array
80
	 **/
81
	protected $listeners = array();
82
	
83
	/**
84
	 * script work time for debug
85
	 *
86
	 * @var string
87
	 **/
88
	protected $time = 0;
89
	/**
90
	 * Is elFinder init correctly?
91
	 *
92
	 * @var bool
93
	 **/
94
	protected $loaded = false;
95
	/**
96
	 * Send debug to client?
97
	 *
98
	 * @var string
99
	 **/
100
	protected $debug = false;
101
102
	/**
103
	 * session expires timeout
104
	 *
105
	 * @var int
106
	 **/
107
	protected $timeout = 0;
108
	
109
	/**
110
	 * undocumented class variable
111
	 *
112
	 * @var string
113
	 **/
114
	protected $uploadDebug = '';
115
	
116
	/**
117
	 * Errors from not mounted volumes
118
	 *
119
	 * @var array
120
	 **/
121
	public $mountErrors = array();
122
	
123
	// Errors messages
124
	const ERROR_UNKNOWN           = 'errUnknown';
125
	const ERROR_UNKNOWN_CMD       = 'errUnknownCmd';
126
	const ERROR_CONF              = 'errConf';
127
	const ERROR_CONF_NO_JSON      = 'errJSON';
128
	const ERROR_CONF_NO_VOL       = 'errNoVolumes';
129
	const ERROR_INV_PARAMS        = 'errCmdParams';
130
	const ERROR_OPEN              = 'errOpen';
131
	const ERROR_DIR_NOT_FOUND     = 'errFolderNotFound';
132
	const ERROR_FILE_NOT_FOUND    = 'errFileNotFound';     // 'File not found.'
133
	const ERROR_TRGDIR_NOT_FOUND  = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.'
134
	const ERROR_NOT_DIR           = 'errNotFolder';
135
	const ERROR_NOT_FILE          = 'errNotFile';
136
	const ERROR_PERM_DENIED       = 'errPerm';
137
	const ERROR_LOCKED            = 'errLocked';        // '"$1" is locked and can not be renamed, moved or removed.'
138
	const ERROR_EXISTS            = 'errExists';        // 'File named "$1" already exists.'
139
	const ERROR_INVALID_NAME      = 'errInvName';       // 'Invalid file name.'
140
	const ERROR_MKDIR             = 'errMkdir';
141
	const ERROR_MKFILE            = 'errMkfile';
142
	const ERROR_RENAME            = 'errRename';
143
	const ERROR_COPY              = 'errCopy';
144
	const ERROR_MOVE              = 'errMove';
145
	const ERROR_COPY_FROM         = 'errCopyFrom';
146
	const ERROR_COPY_TO           = 'errCopyTo';
147
	const ERROR_COPY_ITSELF       = 'errCopyInItself';
148
	const ERROR_REPLACE           = 'errReplace';          // 'Unable to replace "$1".'
149
	const ERROR_RM                = 'errRm';               // 'Unable to remove "$1".'
150
	const ERROR_RM_SRC            = 'errRmSrc';            // 'Unable remove source file(s)'
151
	const ERROR_UPLOAD            = 'errUpload';           // 'Upload error.'
152
	const ERROR_UPLOAD_FILE       = 'errUploadFile';       // 'Unable to upload "$1".'
153
	const ERROR_UPLOAD_NO_FILES   = 'errUploadNoFiles';    // 'No files found for upload.'
154
	const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize';  // 'Data exceeds the maximum allowed size.'
155
	const ERROR_UPLOAD_FILE_SIZE  = 'errUploadFileSize';   // 'File exceeds maximum allowed size.'
156
	const ERROR_UPLOAD_FILE_MIME  = 'errUploadMime';       // 'File type not allowed.'
157
	const ERROR_UPLOAD_TRANSFER   = 'errUploadTransfer';   // '"$1" transfer error.'
158
	// const ERROR_ACCESS_DENIED     = 'errAccess';
159
	const ERROR_NOT_REPLACE       = 'errNotReplace';       // Object "$1" already exists at this location and can not be replaced with object of another type.
160
	const ERROR_SAVE              = 'errSave';
161
	const ERROR_EXTRACT           = 'errExtract';
162
	const ERROR_ARCHIVE           = 'errArchive';
163
	const ERROR_NOT_ARCHIVE       = 'errNoArchive';
164
	const ERROR_ARCHIVE_TYPE      = 'errArcType';
165
	const ERROR_ARC_SYMLINKS      = 'errArcSymlinks';
166
	const ERROR_ARC_MAXSIZE       = 'errArcMaxSize';
167
	const ERROR_RESIZE            = 'errResize';
168
	const ERROR_UNSUPPORT_TYPE    = 'errUsupportType';
169
	const ERROR_NOT_UTF8_CONTENT  = 'errNotUTF8Content';
170
	const ERROR_NETMOUNT          = 'errNetMount';
171
	const ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver';
172
	const ERROR_NETMOUNT_FAILED       = 'errNetMountFailed';
173
174
	const ERROR_SESSION_EXPIRES 	= 'errSessionExpires';
175
176
	const ERROR_CREATING_TEMP_DIR 	= 'errCreatingTempDir';
177
	const ERROR_FTP_DOWNLOAD_FILE 	= 'errFtpDownloadFile';
178
	const ERROR_FTP_UPLOAD_FILE 	= 'errFtpUploadFile';
179
	const ERROR_FTP_MKDIR 		= 'errFtpMkdir';
180
	const ERROR_ARCHIVE_EXEC 	= 'errArchiveExec';
181
	const ERROR_EXTRACT_EXEC 	= 'errExtractExec';
182
183
	/**
184
	 * Constructor
185
	 *
186
	 * @param  array  elFinder and roots configurations
187
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
188
	 * @author Dmitry (dio) Levashov
189
	 **/
190
	public function __construct($opts) {
191
		if (session_id() == '') {
192
			session_start();
193
		}
194
195
		$this->time  = $this->utime();
0 ignored issues
show
Documentation Bug introduced by
The property $time was declared of type string, but $this->utime() is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
196
		$this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false);
0 ignored issues
show
Documentation Bug introduced by
The property $debug was declared of type string, but isset($opts['debug']) &&...'debug'] ? true : false is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
197
		$this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0);
198
		
199
		setlocale(LC_ALL, !empty($opts['locale']) ? $opts['locale'] : 'en_US.UTF-8');
200
201
		// bind events listeners
202
		if (!empty($opts['bind']) && is_array($opts['bind'])) {
203
			foreach ($opts['bind'] as $cmd => $handler) {
204
				$this->bind($cmd, $handler);
205
			}
206
		}
207
208
		if (!isset($opts['roots']) || !is_array($opts['roots'])) {
209
			$opts['roots'] = array();
210
		}
211
212
		// check for net volumes stored in session
213
		foreach ($this->getNetVolumes() as $root) {
214
			$opts['roots'][] = $root;
215
		}
216
217
		// "mount" volumes
218
		foreach ($opts['roots'] as $i => $o) {
219
			$class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : '');
220
221
			if (class_exists($class)) {
222
				$volume = new $class();
223
224
				if ($volume->mount($o)) {
225
					// unique volume id (ends on "_") - used as prefix to files hash
226
					$id = $volume->id();
227
					
228
					$this->volumes[$id] = $volume;
229
					if (!$this->default && $volume->isReadable()) {
230
						$this->default = $this->volumes[$id]; 
231
					}
232
				} else {
233
					$this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error());
234
				}
235
			} else {
236
				$this->mountErrors[] = 'Driver "'.$class.'" does not exists';
237
			}
238
		}
239
240
		// if at least one redable volume - ii desu >_<
241
		$this->loaded = !empty($this->default);
242
	}
243
	
244
	/**
245
	 * Return true if fm init correctly
246
	 *
247
	 * @return bool
248
	 * @author Dmitry (dio) Levashov
249
	 **/
250
	public function loaded() {
251
		return $this->loaded;
252
	}
253
	
254
	/**
255
	 * Return version (api) number
256
	 *
257
	 * @return string
258
	 * @author Dmitry (dio) Levashov
259
	 **/
260
	public function version() {
261
		return $this->version;
262
	}
263
	
264
	/**
265
	 * Add handler to elFinder command
266
	 *
267
	 * @param  string  command name
268
	 * @param  string|array  callback name or array(object, method)
269
	 * @return elFinder
270
	 * @author Dmitry (dio) Levashov
271
	 **/
272
	public function bind($cmd, $handler) {
273
		$cmds = $cmd == '*'
274
			? array_keys($this->commands)
275
			: array_map('trim', explode(' ', $cmd));
276
		
277
		foreach ($cmds as $cmd) {
278
			if ($cmd) {
279
				if (!isset($this->listeners[$cmd])) {
280
					$this->listeners[$cmd] = array();
281
				}
282
283
				if (is_callable($handler)) {
284
					$this->listeners[$cmd][] = $handler;
285
				}
286
			}
287
		}
288
289
		return $this;
290
	}
291
	
292
	/**
293
	 * Remove event (command exec) handler
294
	 *
295
	 * @param  string  command name
296
	 * @param  string|array  callback name or array(object, method)
297
	 * @return elFinder
298
	 * @author Dmitry (dio) Levashov
299
	 **/
300
	public function unbind($cmd, $handler) {
301
		if (!empty($this->listeners[$cmd])) {
302
			foreach ($this->listeners[$cmd] as $i => $h) {
303
				if ($h === $handler) {
304
					unset($this->listeners[$cmd][$i]);
305
					return $this;
306
				}
307
			}
308
		}
309
		return $this;
310
	}
311
	
312
	/**
313
	 * Return true if command exists
314
	 *
315
	 * @param  string  command name
316
	 * @return bool
317
	 * @author Dmitry (dio) Levashov
318
	 **/
319
	public function commandExists($cmd) {
320
		return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd);
321
	}
322
	
323
	/**
324
	 * Return root - file's owner (public func of volume())
325
	 *
326
	 * @param  string  file hash
327
	 * @return elFinderStorageDriver
328
	 * @author Naoki Sawada
329
	 */
330
	public function getVolume($hash) {
331
		return $this->volume($hash);
332
	}
333
	
334
	/**
335
	 * Return command required arguments info
336
	 *
337
	 * @param  string  command name
338
	 * @return array
339
	 * @author Dmitry (dio) Levashov
340
	 **/
341
	public function commandArgsList($cmd) {
342
		return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
343
	}
344
345
	private function session_expires() {
346
		
347
		if (!isset($_SESSION['LAST_ACTIVITY'])) {
348
			$_SESSION['LAST_ACTIVITY'] = time();
349
			return false;
350
		}
351
352
		if ( ($this->timeout > 0) && (time() - $_SESSION['LAST_ACTIVITY'] > $this->timeout) ) {
353
			return true;
354
		}
355
356
		$_SESSION['LAST_ACTIVITY'] = time();
357
		return false;	
358
	}
359
	
360
	/**
361
	 * Exec command and return result
362
	 *
363
	 * @param  string  $cmd  command name
364
	 * @param  array   $args command arguments
365
	 * @return array
366
	 * @author Dmitry (dio) Levashov
367
	 **/
368
	public function exec($cmd, $args) {
369
		
370
		if (!$this->loaded) {
371
			return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL));
372
		}
373
374
		if ($this->session_expires()) {
375
			return array('error' => $this->error(self::ERROR_SESSION_EXPIRES));
376
		}
377
		
378
		if (!$this->commandExists($cmd)) {
379
			return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
380
		}
381
		
382
		if (!empty($args['mimes']) && is_array($args['mimes'])) {
383
			foreach ($this->volumes as $id => $v) {
384
				$this->volumes[$id]->setMimesFilter($args['mimes']);
385
			}
386
		}
387
		
388
		$result = $this->$cmd($args);
389
		
390
		if (isset($result['removed'])) {
391
			foreach ($this->volumes as $volume) {
392
				$result['removed'] = array_merge($result['removed'], $volume->removed());
393
				$volume->resetRemoved();
394
			}
395
		}
396
		
397
		// call handlers for this command
398
		if (!empty($this->listeners[$cmd])) {
399
			foreach ($this->listeners[$cmd] as $handler) {
400
				if (call_user_func_array($handler,array($cmd,&$result,$args,$this))) {
401
					// handler return true to force sync client after command completed
402
					$result['sync'] = true;
403
				}
404
			}
405
		}
406
		
407
		// replace removed files info with removed files hashes
408
		if (!empty($result['removed'])) {
409
			$removed = array();
410
			foreach ($result['removed'] as $file) {
411
				$removed[] = $file['hash'];
412
			}
413
			$result['removed'] = array_unique($removed);
414
		}
415
		// remove hidden files and filter files by mimetypes
416
		if (!empty($result['added'])) {
417
			$result['added'] = $this->filter($result['added']);
418
		}
419
		// remove hidden files and filter files by mimetypes
420
		if (!empty($result['changed'])) {
421
			$result['changed'] = $this->filter($result['changed']);
422
		}
423
		
424
		if ($this->debug || !empty($args['debug'])) {
425
			$result['debug'] = array(
426
				'connector' => 'php', 
427
				'phpver'    => PHP_VERSION,
428
				'time'      => $this->utime() - $this->time,
429
				'memory'    => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'),
430
				'upload'    => $this->uploadDebug,
431
				'volumes'   => array(),
432
				'mountErrors' => $this->mountErrors
433
				);
434
			
435
			foreach ($this->volumes as $id => $volume) {
436
				$result['debug']['volumes'][] = $volume->debug();
437
			}
438
		}
439
		
440
		foreach ($this->volumes as $volume) {
441
			$volume->umount();
442
		}
443
		
444
		return $result;
445
	}
446
	
447
	/**
448
	 * Return file real path
449
	 *
450
	 * @param  string  $hash  file hash
451
	 * @return string
452
	 * @author Dmitry (dio) Levashov
453
	 **/
454
	public function realpath($hash)	{
455
		if (($volume = $this->volume($hash)) == false) {
456
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by elFinder::realpath of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
457
		}
458
		return $volume->realpath($hash);
459
	}
460
	
461
	/**
462
	 * Return network volumes config.
463
	 *
464
	 * @return array
465
	 * @author Dmitry (dio) Levashov
466
	 */
467
	protected function getNetVolumes() {
468
		return isset($_SESSION['elFinderNetVolumes']) && is_array($_SESSION['elFinderNetVolumes']) ? $_SESSION['elFinderNetVolumes'] : array();
469
	}
470
471
	/**
472
	 * Save network volumes config.
473
	 *
474
	 * @param  array  $volumes  volumes config
475
	 * @return void
476
	 * @author Dmitry (dio) Levashov
477
	 */
478
	protected function saveNetVolumes($volumes) {
479
		$_SESSION['elFinderNetVolumes'] = $volumes;
480
	}
481
482
	/***************************************************************************/
483
	/*                                 commands                                */
484
	/***************************************************************************/
485
	
486
	/**
487
	 * Normalize error messages
488
	 *
489
	 * @return array
490
	 * @author Dmitry (dio) Levashov
491
	 **/
492
	public function error() {
493
		$errors = array();
494
495
		foreach (func_get_args() as $msg) {
496
			if (is_array($msg)) {
497
				$errors = array_merge($errors, $msg);
498
			} else {
499
				$errors[] = $msg;
500
			}
501
		}
502
		
503
		return count($errors) ? $errors : array(self::ERROR_UNKNOWN);
504
	}
505
	
506
	protected function netmount($args) {
507
		$options  = array();
508
		$protocol = $args['protocol'];
509
		$driver   = isset(self::$netDrivers[$protocol]) ? $protocol : '';
510
		$class    = 'elfindervolume'.$protocol;
511
512
		if (!$driver) {
513
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER));
514
		}
515
516
		if (!$args['path']) {
517
			$args['path'] = '/';
518
		}
519
520
		foreach ($args as $k => $v) {
521
			if ($k != 'options' && $k != 'protocol' && $v) {
522
				$options[$k] = $v;
523
			}
524
		}
525
526
		if (is_array($args['options'])) {
527
			foreach ($args['options'] as $key => $value) {
528
				$options[$key] = $value;
529
			}
530
		}
531
532
		$volume = new $class();
533
534
		if ($volume->mount($options)) {
535
			$netVolumes        = $this->getNetVolumes();
536
			$options['driver'] = $driver;
537
			$netVolumes[]      = $options;
538
			$netVolumes        = array_unique($netVolumes);
539
			$this->saveNetVolumes($netVolumes);
540
			return array('sync' => true);
541
		} else {
542
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error())));
543
		}
544
545
	}
546
547
	/**
548
	 * "Open" directory
549
	 * Return array with following elements
550
	 *  - cwd          - opened dir info
551
	 *  - files        - opened dir content [and dirs tree if $args[tree]]
552
	 *  - api          - api version (if $args[init])
553
	 *  - uplMaxSize   - if $args[init]
554
	 *  - error        - on failed
555
	 *
556
	 * @param  array  command arguments
557
	 * @return array
558
	 * @author Dmitry (dio) Levashov
559
	 **/
560
	protected function open($args) {
561
		$target = $args['target'];
562
		$init   = !empty($args['init']);
563
		$tree   = !empty($args['tree']);
0 ignored issues
show
Unused Code introduced by
$tree is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
564
		$volume = $this->volume($target);
565
		$cwd    = $volume ? $volume->dir($target, true) : false;
566
		$hash   = $init ? 'default folder' : '#'.$target;
567
568
		// on init request we can get invalid dir hash -
569
		// dir which can not be opened now, but remembered by client,
570
		// so open default dir
571
		if ((!$cwd || !$cwd['read']) && $init) {
572
			$volume = $this->default;
573
			$cwd    = $volume->dir($volume->defaultPath(), true);
574
		}
575
		
576
		if (!$cwd) {
577
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND));
578
		}
579
		if (!$cwd['read']) {
580
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED));
581
		}
582
583
		$files = array();
584
585
		// get folders trees
586
		if ($args['tree']) {
587
			foreach ($this->volumes as $id => $v) {
588
				if (($tree = $v->tree('', 0, $cwd['hash'])) != false) {
589
					$files = array_merge($files, $tree);
590
				}
591
			}
592
		}
593
594
		// get current working directory files list and add to $files if not exists in it
595
		if (($ls = $volume->scandir($cwd['hash'])) === false) {
596
			return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
597
		}
598
		
599
		foreach ($ls as $file) {
600
			if (!in_array($file, $files)) {
601
				$files[] = $file;
602
			}
603
		}
604
		
605
		$result = array(
606
			'cwd'     => $cwd,
607
			'options' => $volume->options($cwd['hash']),
608
			'files'   => $files
609
		);
610
611
		if (!empty($args['init'])) {
612
			$result['api'] = $this->version;
613
			$result['uplMaxSize'] = ini_get('upload_max_filesize');
614
			$result['netDrivers'] = array_keys(self::$netDrivers);
615
		}
616
		
617
		return $result;
618
	}
619
	
620
	/**
621
	 * Return dir files names list
622
	 *
623
	 * @param  array  command arguments
624
	 * @return array
625
	 * @author Dmitry (dio) Levashov
626
	 **/
627 View Code Duplication
	protected function ls($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
628
		$target = $args['target'];
629
		
630
		if (($volume = $this->volume($target)) == false
631
		|| ($list = $volume->ls($target)) === false) {
632
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
633
		}
634
		return array('list' => $list);
635
	}
636
	
637
	/**
638
	 * Return subdirs for required directory
639
	 *
640
	 * @param  array  command arguments
641
	 * @return array
642
	 * @author Dmitry (dio) Levashov
643
	 **/
644 View Code Duplication
	protected function tree($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
645
		$target = $args['target'];
646
		
647
		if (($volume = $this->volume($target)) == false
648
		|| ($tree = $volume->tree($target)) == false) {
649
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
650
		}
651
652
		return array('tree' => $tree);
653
	}
654
	
655
	/**
656
	 * Return parents dir for required directory
657
	 *
658
	 * @param  array  command arguments
659
	 * @return array
660
	 * @author Dmitry (dio) Levashov
661
	 **/
662 View Code Duplication
	protected function parents($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
663
		$target = $args['target'];
664
		
665
		if (($volume = $this->volume($target)) == false
666
		|| ($tree = $volume->parents($target)) == false) {
667
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
668
		}
669
670
		return array('tree' => $tree);
671
	}
672
	
673
	/**
674
	 * Return new created thumbnails list
675
	 *
676
	 * @param  array  command arguments
677
	 * @return array
678
	 * @author Dmitry (dio) Levashov
679
	 **/
680
	protected function tmb($args) {
681
		
682
		$result  = array('images' => array());
683
		$targets = $args['targets'];
684
		
685
		foreach ($targets as $target) {
686
			if (($volume = $this->volume($target)) != false
687
			&& (($tmb = $volume->tmb($target)) != false)) {
688
				$result['images'][$target] = $tmb;
689
			}
690
		}
691
		return $result;
692
	}
693
	
694
	/**
695
	 * Required to output file in browser when volume URL is not set 
696
	 * Return array contains opened file pointer, root itself and required headers
697
	 *
698
	 * @param  array  command arguments
699
	 * @return array
700
	 * @author Dmitry (dio) Levashov
701
	 **/
702
	protected function file($args) {
703
		$target   = $args['target'];
704
		$download = !empty($args['download']);
705
		$h403     = 'HTTP/1.x 403 Access Denied';
706
		$h404     = 'HTTP/1.x 404 Not Found';
707
708 View Code Duplication
		if (($volume = $this->volume($target)) == false) { 
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
709
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
710
		}
711
		
712 View Code Duplication
		if (($file = $volume->file($target)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
713
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
714
		}
715
		
716
		if (!$file['read']) {
717
			return array('error' => 'Access denied', 'header' => $h403, 'raw' => true);
718
		}
719
		
720 View Code Duplication
		if (($fp = $volume->open($target)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
721
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
722
		}
723
724
		if ($download) {
725
			$disp = 'attachment';
726
			$mime = 'application/octet-stream';
727
		} else {
728
			$disp  = preg_match('/^(image|text)/i', $file['mime']) || $file['mime'] == 'application/x-shockwave-flash' 
729
					? 'inline' 
730
					: 'attachment';
731
			$mime = $file['mime'];
732
		}
733
		
734
		$filenameEncoded = rawurlencode($file['name']);
735
		if (strpos($filenameEncoded, '%') === false) { // ASCII only
736
			$filename = 'filename="'.$file['name'].'"';
737
		} else {
738
			$ua = $_SERVER["HTTP_USER_AGENT"];
739
			if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987)
740
				$filename = 'filename="'.$filenameEncoded.'"';
741
			} elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false) { // Safari
742
				$filename = 'filename="'.str_replace('"', '', $file['name']).'"';
743
			} else { // RFC 6266 (RFC 2231/RFC 5987)
744
				$filename = 'filename*=UTF-8\'\''.$filenameEncoded;
745
			}
746
		}
747
		
748
		$result = array(
749
			'volume'  => $volume,
750
			'pointer' => $fp,
751
			'info'    => $file,
752
			'header'  => array(
753
				'Content-Type: '.$mime, 
754
				'Content-Disposition: '.$disp.'; '.$filename,
755
				'Content-Location: '.$file['name'],
756
				'Content-Transfer-Encoding: binary',
757
				'Content-Length: '.$file['size'],
758
				'Connection: close'
759
			)
760
		);
761
		return $result;
762
	}
763
	
764
	/**
765
	 * Count total files size
766
	 *
767
	 * @param  array  command arguments
768
	 * @return array
769
	 * @author Dmitry (dio) Levashov
770
	 **/
771
	protected function size($args) {
772
		$size = 0;
773
		
774
		foreach ($args['targets'] as $target) {
775
			if (($volume = $this->volume($target)) == false
776
			|| ($file = $volume->file($target)) == false
777
			|| !$file['read']) {
778
				return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
779
			}
780
			
781
			$size += $volume->size($target);
782
		}
783
		return array('size' => $size);
784
	}
785
	
786
	/**
787
	 * Create directory
788
	 *
789
	 * @param  array  command arguments
790
	 * @return array
791
	 * @author Dmitry (dio) Levashov
792
	 **/
793 View Code Duplication
	protected function mkdir($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
794
		$target = $args['target'];
795
		$name   = $args['name'];
796
		
797
		if (($volume = $this->volume($target)) == false) {
798
			return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
799
		}
800
801
		return ($dir = $volume->mkdir($target, $name)) == false
802
			? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error()))
803
			: array('added' => array($dir));
804
	}
805
	
806
	/**
807
	 * Create empty file
808
	 *
809
	 * @param  array  command arguments
810
	 * @return array
811
	 * @author Dmitry (dio) Levashov
812
	 **/
813 View Code Duplication
	protected function mkfile($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
814
		$target = $args['target'];
815
		$name   = $args['name'];
816
		
817
		if (($volume = $this->volume($target)) == false) {
818
			return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
819
		}
820
821
		return ($file = $volume->mkfile($target, $args['name'])) == false
822
			? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error()))
823
			: array('added' => array($file));
824
	}
825
	
826
	/**
827
	 * Rename file
828
	 *
829
	 * @param  array  $args
830
	 * @return array
831
	 * @author Dmitry (dio) Levashov
832
	 **/
833
	protected function rename($args) {
834
		$target = $args['target'];
835
		$name   = $args['name'];
836
		
837 View Code Duplication
		if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
838
		||  ($rm  = $volume->file($target)) == false) {
839
			return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND));
840
		}
841
		$rm['realpath'] = $volume->realpath($target);
842
		
843
		return ($file = $volume->rename($target, $name)) == false
844
			? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error()))
845
			: array('added' => array($file), 'removed' => array($rm));
846
	}
847
	
848
	/**
849
	 * Duplicate file - create copy with "copy %d" suffix
850
	 *
851
	 * @param array  $args  command arguments
852
	 * @return array
853
	 * @author Dmitry (dio) Levashov
854
	 **/
855
	protected function duplicate($args) {
856
		$targets = is_array($args['targets']) ? $args['targets'] : array();
857
		$result  = array('added' => array());
858
		$suffix  = empty($args['suffix']) ? 'copy' : $args['suffix'];
859
		
860
		foreach ($targets as $target) {
861 View Code Duplication
			if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
862
			|| ($src = $volume->file($target)) == false) {
863
				$result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND);
864
				break;
865
			}
866
			
867
			if (($file = $volume->duplicate($target, $suffix)) == false) {
868
				$result['warning'] = $this->error($volume->error());
869
				break;
870
			}
871
			
872
			$result['added'][] = $file;
873
		}
874
		
875
		return $result;
876
	}
877
		
878
	/**
879
	 * Remove dirs/files
880
	 *
881
	 * @param array  command arguments
882
	 * @return array
883
	 * @author Dmitry (dio) Levashov
884
	 **/
885
	protected function rm($args) {
886
		$targets = is_array($args['targets']) ? $args['targets'] : array();
887
		$result  = array('removed' => array());
888
		
889
		foreach ($targets as $target) {
890 View Code Duplication
			if (($volume = $this->volume($target)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
891
				$result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND);
892
				return $result;
893
			}
894
			if (!$volume->rm($target)) {
895
				$result['warning'] = $this->error($volume->error());
896
				return $result;
897
			}
898
		}
899
900
		return $result;
901
	}
902
	
903
	/**
904
	 * Save uploaded files
905
	 *
906
	 * @param  array
907
	 * @return array
908
	 * @author Dmitry (dio) Levashov
909
	 **/
910
	protected function upload($args) {
911
		$target = $args['target'];
912
		$volume = $this->volume($target);
913
		$files  = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
914
		$result = array('added' => array(), 'header' => empty($args['html']) ? false : 'Content-Type: text/html; charset=utf-8');
915
		
916
		if (empty($files)) {
917
			return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES), 'header' => $header);
0 ignored issues
show
Bug introduced by
The variable $header does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
918
		}
919
		
920
		if (!$volume) {
921
			return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target), 'header' => $header);
922
		}
923
		
924
		foreach ($files['name'] as $i => $name) {
925
			if (($error = $files['error'][$i]) > 0) {				
926
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER);
927
				$this->uploadDebug = 'Upload error code: '.$error;
928
				break;
929
			}
930
			
931
			$tmpname = $files['tmp_name'][$i];
932
			
933
			if (($fp = fopen($tmpname, 'rb')) == false) {
934
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);
935
				$this->uploadDebug = 'Upload error: unable open tmp file';
936
				break;
937
			}
938
			
939
			if (($file = $volume->upload($fp, $target, $name, $tmpname)) === false) {
940
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());
941
				fclose($fp);
942
				break;
943
			}
944
			
945
			fclose($fp);
946
			$result['added'][] = $file;
947
		}
948
		
949
		return $result;
950
	}
951
		
952
	/**
953
	 * Copy/move files into new destination
954
	 *
955
	 * @param  array  command arguments
956
	 * @return array
957
	 * @author Dmitry (dio) Levashov
958
	 **/
959
	protected function paste($args) {
960
		$dst     = $args['dst'];
961
		$targets = is_array($args['targets']) ? $args['targets'] : array();
962
		$cut     = !empty($args['cut']);
963
		$error   = $cut ? self::ERROR_MOVE : self::ERROR_COPY;
964
		$result  = array('added' => array(), 'removed' => array());
965
		
966
		if (($dstVolume = $this->volume($dst)) == false) {
967
			return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst));
968
		}
969
		
970
		foreach ($targets as $target) {
971 View Code Duplication
			if (($srcVolume = $this->volume($target)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
972
				$result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND);
973
				break;
974
			}
975
			
976
			if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut)) == false) {
977
				$result['warning'] = $this->error($dstVolume->error());
978
				break;
979
			}
980
			
981
			$result['added'][] = $file;
982
		}
983
		return $result;
984
	}
985
	
986
	/**
987
	 * Return file content
988
	 *
989
	 * @param  array  $args  command arguments
990
	 * @return array
991
	 * @author Dmitry (dio) Levashov
992
	 **/
993
	protected function get($args) {
994
		$target = $args['target'];
995
		$volume = $this->volume($target);
996
		
997
		if (!$volume || ($file = $volume->file($target)) == false) {
998
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND));
999
		}
1000
		
1001 View Code Duplication
		if (($content = $volume->getContents($target)) === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1002
			return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error()));
1003
		}
1004
		
1005
		$json = json_encode($content);
1006
1007
		if ($json == 'null' && strlen($json) < strlen($content)) {
1008
			return array('error' => $this->error(self::ERROR_NOT_UTF8_CONTENT, $volume->path($target)));
1009
		}
1010
		
1011
		return array('content' => $content);
1012
	}
1013
	
1014
	/**
1015
	 * Save content into text file
1016
	 *
1017
	 * @return array
1018
	 * @author Dmitry (dio) Levashov
1019
	 **/
1020
	protected function put($args) {
1021
		$target = $args['target'];
1022
		
1023 View Code Duplication
		if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1024
		|| ($file = $volume->file($target)) == false) {
1025
			return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1026
		}
1027
		
1028 View Code Duplication
		if (($file = $volume->putContents($target, $args['content'])) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1029
			return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error()));
1030
		}
1031
		
1032
		return array('changed' => array($file));
1033
	}
1034
1035
	/**
1036
	 * Extract files from archive
1037
	 *
1038
	 * @param  array  $args  command arguments
1039
	 * @return array
1040
	 * @author Dmitry (dio) Levashov, 
1041
	 * @author Alexey Sukhotin
1042
	 **/
1043
	protected function extract($args) {
1044
		$target = $args['target'];
1045
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
0 ignored issues
show
Unused Code introduced by
$mimes is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1046
		$error  = array(self::ERROR_EXTRACT, '#'.$target);
0 ignored issues
show
Unused Code introduced by
$error is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1047
1048 View Code Duplication
		if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1049
		|| ($file = $volume->file($target)) == false) {
1050
			return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1051
		}  
1052
1053
		return ($file = $volume->extract($target))
1054
			? array('added' => array($file))
1055
			: array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()));
1056
	}
1057
	
1058
	/**
1059
	 * Create archive
1060
	 *
1061
	 * @param  array  $args  command arguments
1062
	 * @return array
1063
	 * @author Dmitry (dio) Levashov, 
1064
	 * @author Alexey Sukhotin
1065
	 **/
1066
	protected function archive($args) {
1067
		$type    = $args['type'];
0 ignored issues
show
Unused Code introduced by
$type is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1068
		$targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
1069
	
1070
		if (($volume = $this->volume($targets[0])) == false) {
1071
			return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
1072
		}
1073
	
1074
		return ($file = $volume->archive($targets, $args['type']))
1075
			? array('added' => array($file))
1076
			: array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error()));
1077
	}
1078
	
1079
	/**
1080
	 * Search files
1081
	 *
1082
	 * @param  array  $args  command arguments
1083
	 * @return array
1084
	 * @author Dmitry Levashov
1085
	 **/
1086
	protected function search($args) {
1087
		$q      = trim($args['q']);
1088
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
1089
		$result = array();
1090
1091
		foreach ($this->volumes as $volume) {
1092
			$result = array_merge($result, $volume->search($q, $mimes));
1093
		}
1094
		
1095
		return array('files' => $result);
1096
	}
1097
	
1098
	/**
1099
	 * Return file info (used by client "places" ui)
1100
	 *
1101
	 * @param  array  $args  command arguments
1102
	 * @return array
1103
	 * @author Dmitry Levashov
1104
	 **/
1105
	protected function info($args) {
1106
		$files = array();
1107
		
1108
		foreach ($args['targets'] as $hash) {
1109
			if (($volume = $this->volume($hash)) != false
1110
			&& ($info = $volume->file($hash)) != false) {
1111
				$files[] = $info;
1112
			}
1113
		}
1114
		
1115
		return array('files' => $files);
1116
	}
1117
	
1118
	/**
1119
	 * Return image dimmensions
1120
	 *
1121
	 * @param  array  $args  command arguments
1122
	 * @return array
1123
	 * @author Dmitry (dio) Levashov
1124
	 **/
1125
	protected function dim($args) {
1126
		$target = $args['target'];
1127
		
1128
		if (($volume = $this->volume($target)) != false) {
1129
			$dim = $volume->dimensions($target);
1130
			return $dim ? array('dim' => $dim) : array();
1131
		}
1132
		return array();
1133
	}
1134
	
1135
	/**
1136
	 * Resize image
1137
	 *
1138
	 * @param  array  command arguments
1139
	 * @return array
1140
	 * @author Dmitry (dio) Levashov
1141
	 * @author Alexey Sukhotin
1142
	 **/
1143
	protected function resize($args) {
1144
		$target = $args['target'];
1145
		$width  = $args['width'];
1146
		$height = $args['height'];
1147
		$x      = (int)$args['x'];
1148
		$y      = (int)$args['y'];
1149
		$mode   = $args['mode'];
1150
		$bg     = null;
1151
		$degree = (int)$args['degree'];
1152
		
1153 View Code Duplication
		if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1154
		|| ($file = $volume->file($target)) == false) {
1155
			return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1156
		}
1157
1158
		return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree))
1159
			? array('changed' => array($file))
1160
			: array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error()));
1161
	}
1162
	
1163
	/***************************************************************************/
1164
	/*                                   utils                                 */
1165
	/***************************************************************************/
1166
	
1167
	/**
1168
	 * Return root - file's owner
1169
	 *
1170
	 * @param  string  file hash
1171
	 * @return elFinderStorageDriver
1172
	 * @author Dmitry (dio) Levashov
1173
	 **/
1174
	protected function volume($hash) {
1175
		foreach ($this->volumes as $id => $v) {
1176
			if (strpos(''.$hash, $id) === 0) {
1177
				return $this->volumes[$id];
1178
			} 
1179
		}
1180
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by elFinder::volume of type elFinderStorageDriver.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1181
	}
1182
	
1183
	/**
1184
	 * Return files info array 
1185
	 *
1186
	 * @param  array  $data  one file info or files info
1187
	 * @return array
1188
	 * @author Dmitry (dio) Levashov
1189
	 **/
1190
	protected function toArray($data) {
1191
		return isset($data['hash']) || !is_array($data) ? array($data) : $data;
1192
	}
1193
	
1194
	/**
1195
	 * Return fils hashes list
1196
	 *
1197
	 * @param  array  $files  files info
1198
	 * @return array
1199
	 * @author Dmitry (dio) Levashov
1200
	 **/
1201
	protected function hashes($files) {
1202
		$ret = array();
1203
		foreach ($files as $file) {
1204
			$ret[] = $file['hash'];
1205
		}
1206
		return $ret;
1207
	}
1208
	
1209
	/**
1210
	 * Remove from files list hidden files and files with required mime types
1211
	 *
1212
	 * @param  array  $files  files info
1213
	 * @return array
1214
	 * @author Dmitry (dio) Levashov
1215
	 **/
1216
	protected function filter($files) {
1217
		foreach ($files as $i => $file) {
1218
			if (!empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) {
1219
				unset($files[$i]);
1220
			}
1221
		}
1222
		return array_merge($files, array());
1223
	}
1224
	
1225
	protected function utime() {
1226
		$time = explode(" ", microtime());
1227
		return (double)$time[1] + (double)$time[0];
1228
	}
1229
	
1230
} // END class
1231