Completed
Push — 2.x ( 5243ac...ccb6e2 )
by Naoki
03:52
created

elFinderVolumeLocalFileSystem   D

Complexity

Total Complexity 230

Size/Duplication

Total Lines 1137
Duplicated Lines 7.92 %

Coupling/Cohesion

Components 2
Dependencies 2
Metric Value
wmc 230
lcom 2
cbo 2
dl 90
loc 1137
rs 4

38 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
C init() 0 36 13
F configure() 5 62 20
D localFileSystemInotify() 0 39 9
A _dirname() 0 3 1
A _basename() 0 3 1
A _joinPath() 0 3 1
D _normpath() 18 51 18
A _relpath() 12 12 3
A _abspath() 0 12 3
A _path() 0 3 2
A _inpath() 0 9 4
F _stat() 0 63 18
D getOwnerStat() 18 41 10
A _subdirs() 0 8 2
A _dimensions() 0 6 3
A readlink() 0 15 4
F _scandir() 0 83 21
A _fopen() 0 3 1
A _fclose() 0 3 1
A _mkdir() 11 11 2
A _mkfile() 11 11 2
A _symlink() 0 3 1
A _copy() 0 5 2
A _move() 0 6 3
A _unlink() 0 5 2
A _rmdir() 0 5 2
C _save() 0 24 11
A _getContents() 0 3 1
A _filePutContents() 0 7 2
A _checkArchivers() 0 4 1
A _chmod() 0 6 3
C _findSymlinks() 0 29 14
F _extract() 11 105 29
A _archive() 0 3 1
A getWorkFile() 0 3 1
A delTree() 0 3 1
C doSearch() 4 59 16

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 elFinderVolumeLocalFileSystem 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 elFinderVolumeLocalFileSystem, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * elFinder driver for local filesystem.
5
 *
6
 * @author Dmitry (dio) Levashov
7
 * @author Troex Nevelin
8
 **/
9
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
10
	
11
	/**
12
	 * Driver id
13
	 * Must be started from letter and contains [a-z0-9]
14
	 * Used as part of volume id
15
	 *
16
	 * @var string
17
	 **/
18
	protected $driverId = 'l';
19
	
20
	/**
21
	 * Required to count total archive files size
22
	 *
23
	 * @var int
24
	 **/
25
	protected $archiveSize = 0;
26
	
27
	/**
28
	 * Constructor
29
	 * Extend options with required fields
30
	 *
31
	 * @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...
32
	 * @author Dmitry (dio) Levashov
33
	 **/
34
	public function __construct() {
35
		$this->options['alias']    = '';              // alias to replace root dir name
36
		$this->options['dirMode']  = 0755;            // new dirs mode
37
		$this->options['fileMode'] = 0644;            // new files mode
38
		$this->options['quarantine'] = '.quarantine';  // quarantine folder name - required to check archive (must be hidden)
39
		$this->options['maxArcFilesSize'] = 0;        // max allowed archive files size (0 - no limit)
40
		$this->options['rootCssClass'] = 'elfinder-navbar-root-local';
41
	}
42
	
43
	/*********************************************************************/
44
	/*                        INIT AND CONFIGURE                         */
45
	/*********************************************************************/
46
	
47
	/**
48
	 * Prepare driver before mount volume.
49
	 * Return true if volume is ready.
50
	 *
51
	 * @return bool
52
	 **/
53
	protected function init() {
54
		// Normalize directory separator for windows
55
		if (DIRECTORY_SEPARATOR !== '/') {
56
			foreach(array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
57
				if (!empty($this->options[$key])) {
58
					$this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
59
				}
60
			}
61
		}
62
		if (!$cwd = getcwd()) {
63
			return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
64
		}
65
		// detect systemRoot
66
		if (!isset($this->options['systemRoot'])) {
67
			if ($cwd[0] === $this->separator || $this->root[0] === $this->separator) {
68
				$this->systemRoot = $this->separator;
69
			} else if (preg_match('/^([a-zA-Z]:'.preg_quote($this->separator, '/').')/', $this->root, $m)) {
70
				$this->systemRoot = $m[1];
71
			} else if (preg_match('/^([a-zA-Z]:'.preg_quote($this->separator, '/').')/', $cwd, $m)) {
72
				$this->systemRoot = $m[1];
73
			}
74
		}
75
		$this->root = $this->getFullPath($this->root, $cwd);
76
		if (!empty($this->options['startPath'])) {
77
			$this->options['startPath'] = $this->getFullPath($this->options['startPath'], $cwd);
78
		}
79
		
80
		if (is_null($this->options['syncChkAsTs'])) {
81
			$this->options['syncChkAsTs'] = true;
82
		}
83
		if (is_null($this->options['syncCheckFunc'])) {
84
			$this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
85
		}
86
		
87
		return true;
88
	}
89
	
90
	/**
91
	 * Configure after successfull mount.
92
	 *
93
	 * @return void
94
	 * @author Dmitry (dio) Levashov
95
	 **/
96
	protected function configure() {
97
		$root = $this->stat($this->root);
98
		
99
		// chek thumbnails path
100
		if ($this->options['tmbPath']) {
101
			$this->options['tmbPath'] = strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false
102
				// tmb path set as dirname under root dir
103
				? $this->_abspath($this->options['tmbPath'])
104
				// tmb path as full path
105
				: $this->_normpath($this->options['tmbPath']);
106
		}
107
108
		parent::configure();
109
		
110
		// set $this->tmp by options['tmpPath']
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
111 View Code Duplication
		if (!empty($this->options['tmpPath'])) {
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...
112
			if ((is_dir($this->options['tmpPath']) || @mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) {
113
				$this->tmp = $this->options['tmpPath'];
0 ignored issues
show
Bug introduced by
The property tmp does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
114
			}
115
		}
116
		
117
		// if no thumbnails url - try detect it
118
		if ($root['read'] && !$this->tmbURL && $this->URL) {
119
			if (strpos($this->tmbPath, $this->root) === 0) {
120
				$this->tmbURL = $this->URL.str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root)+1));
121
				if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
122
					$this->tmbURL .= '/';
123
				}
124
			}
125
		}
126
127
		// check quarantine dir
128
		$this->quarantine = '';
0 ignored issues
show
Bug introduced by
The property quarantine does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
129
		if (!empty($this->options['quarantine'])) {
130
			if (is_dir($this->options['quarantine'])) {
131
				if (is_writable($this->options['quarantine'])) {
132
					$this->quarantine = $this->options['quarantine'];
133
				}
134
				$this->options['quarantine'] = '';
135
			} else {
136
				$this->quarantine = $this->_abspath($this->options['quarantine']);
137
				if ((!is_dir($this->quarantine) && !$this->_mkdir($this->root, $this->options['quarantine'])) || !is_writable($this->quarantine)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_mkdir($this->roo...>options['quarantine']) of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
138
					$this->options['quarantine'] = $this->quarantine = '';
139
				}
140
			}
141
		}
142
		
143
		if (!$this->quarantine) {
144
			$this->archivers['extract'] = array();
145
			$this->disabled[] = 'extract';
146
		}
147
		
148
		if ($this->options['quarantine']) {
149
			$this->attributes[] = array(
150
					'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR.$this->options['quarantine']).'$~',
151
					'read'    => false,
152
					'write'   => false,
153
					'locked'  => true,
154
					'hidden'  => true
155
			);
156
		}
157
	}
158
	
159
	/**
160
	 * Long pooling sync checker
161
	 * This function require server command `inotifywait`
162
	 * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
163
	 * 
164
	 * @param string     $path
165
	 * @param int        $standby
166
	 * @param number     $compare
167
	 * @return number|bool
168
	 */
169
	public function localFileSystemInotify($path, $standby, $compare) {
0 ignored issues
show
Coding Style introduced by
localFileSystemInotify uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
170
		if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
171
			return false;
172
		}
173
		$path = realpath($path);
174
		$mtime = filemtime($path);
175
		if ($mtime != $compare) {
176
			return $mtime;
177
		}
178
		$inotifywait = defined('ELFINER_INOTIFYWAIT_PATH')? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
179
		$path = escapeshellarg($path);
180
		$standby = max(1, intval($standby));
181
		$cmd = $inotifywait.' '.$path.' -t '.$standby.' -e moved_to,moved_from,move,create,delete,delete_self';
182
		$o = $r = '';
183
		$this->procExec($cmd , $o, $r);
0 ignored issues
show
Documentation introduced by
$o is of type string, but the function expects a null|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
184
		if ($r === 0) {
185
			// changed
186
			clearstatcache();
187
			return filemtime($path);
188
		} else if ($r === 2) {
189
			// not changed (timeout)
190
			return $compare;
191
		}
192
		// error
193
		// cache to $_SESSION
194
		$sessionClose = true;;
195
		try {
196
			$sessionStart = session_start();
197
		} catch (Exception $e) {
198
			$sessionClose = false;
199
		}
200
		if ($sessionStart) {
201
			$this->sessionCache = &$_SESSION[elFinder::$sessionCacheKey][$this->id];
202
			$this->sessionCache['localFileSystemInotify_disable'] = true;
203
			$sessionClose && session_write_close();
204
		}
205
		
206
		return false;
207
	}
208
	
209
	/*********************************************************************/
210
	/*                               FS API                              */
211
	/*********************************************************************/
212
213
	/*********************** paths/urls *************************/
214
	
215
	/**
216
	 * Return parent directory path
217
	 *
218
	 * @param  string  $path  file path
219
	 * @return string
220
	 * @author Dmitry (dio) Levashov
221
	 **/
222
	protected function _dirname($path) {
223
		return dirname($path);
224
	}
225
226
	/**
227
	 * Return file name
228
	 *
229
	 * @param  string  $path  file path
230
	 * @return string
231
	 * @author Dmitry (dio) Levashov
232
	 **/
233
	protected function _basename($path) {
234
		return basename($path);
235
	}
236
237
	/**
238
	 * Join dir name and file name and retur full path
239
	 *
240
	 * @param  string  $dir
241
	 * @param  string  $name
242
	 * @return string
243
	 * @author Dmitry (dio) Levashov
244
	 **/
245
	protected function _joinPath($dir, $name) {
246
		return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
247
	}
248
	
249
	/**
250
	 * Return normalized path, this works the same as os.path.normpath() in Python
251
	 *
252
	 * @param  string  $path  path
253
	 * @return string
254
	 * @author Troex Nevelin
255
	 **/
256
	protected function _normpath($path) {
257
		if (empty($path)) {
258
			return '.';
259
		}
260
		
261
		$changeSep = (DIRECTORY_SEPARATOR !== '/');
262
		if ($changeSep) {
263
			$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
264
		}
265
266
		if (strpos($path, '/') === 0) {
267
			$initial_slashes = true;
268
		} else {
269
			$initial_slashes = false;
270
		}
271
			
272 View Code Duplication
		if (($initial_slashes) 
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...
273
		&& (strpos($path, '//') === 0) 
274
		&& (strpos($path, '///') === false)) {
275
			$initial_slashes = 2;
276
		}
277
			
278
		$initial_slashes = (int) $initial_slashes;
279
280
		$comps = explode('/', $path);
281
		$new_comps = array();
282 View Code Duplication
		foreach ($comps as $comp) {
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...
283
			if (in_array($comp, array('', '.'))) {
284
				continue;
285
			}
286
				
287
			if (($comp != '..') 
288
			|| (!$initial_slashes && !$new_comps) 
0 ignored issues
show
Bug Best Practice introduced by
The expression $new_comps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
289
			|| ($new_comps && (end($new_comps) == '..'))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $new_comps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
290
				array_push($new_comps, $comp);
291
			} elseif ($new_comps) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $new_comps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
292
				array_pop($new_comps);
293
			}
294
		}
295
		$comps = $new_comps;
296
		$path = implode('/', $comps);
297
		if ($initial_slashes) {
298
			$path = str_repeat('/', $initial_slashes) . $path;
299
		}
300
		
301
		if ($changeSep) {
302
			$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
303
		}
304
		
305
		return $path ? $path : '.';
306
	}
307
	
308
	/**
309
	 * Return file path related to root dir
310
	 *
311
	 * @param  string  $path  file path
312
	 * @return string
313
	 * @author Dmitry (dio) Levashov
314
	 **/
315 View Code Duplication
	protected function _relpath($path) {
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...
316
		if ($path === $this->root) {
317
			return '';
318
		} else {
319
			if (strpos($path, $this->root) === 0) {
320
				return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
321
			} else {
322
				// for link
323
				return $path;
324
			}
325
		}
326
	}
327
	
328
	/**
329
	 * Convert path related to root dir into real path
330
	 *
331
	 * @param  string  $path  file path
332
	 * @return string
333
	 * @author Dmitry (dio) Levashov
334
	 **/
335
	protected function _abspath($path) {
336
		if ($path === DIRECTORY_SEPARATOR) {
337
			return $this->root;
338
		} else {
339
			if ($path[0] === DIRECTORY_SEPARATOR) {
340
				// for link
341
				return $path;
342
			} else {
343
				return $this->_joinPath($this->root, $path);
344
			}
345
		}
346
	}
347
	
348
	/**
349
	 * Return fake path started from root dir
350
	 *
351
	 * @param  string  $path  file path
352
	 * @return string
353
	 * @author Dmitry (dio) Levashov
354
	 **/
355
	protected function _path($path) {
356
		return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path));
357
	}
358
	
359
	/**
360
	 * Return true if $path is children of $parent
361
	 *
362
	 * @param  string  $path    path to check
363
	 * @param  string  $parent  parent path
364
	 * @return bool
365
	 * @author Dmitry (dio) Levashov
366
	 **/
367
	protected function _inpath($path, $parent) {
368
		$cwd = getcwd();
369
		$real_path   = $this->getFullPath($path,   $cwd);
370
		$real_parent = $this->getFullPath($parent, $cwd);
371
		if ($real_path && $real_parent) {
372
			return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR) === 0;
373
		}
374
		return false;
375
	}
376
	
377
	
378
	
379
	/***************** file stat ********************/
380
381
	/**
382
	 * Return stat for given path.
383
	 * Stat contains following fields:
384
	 * - (int)    size    file size in b. required
385
	 * - (int)    ts      file modification time in unix time. required
386
	 * - (string) mime    mimetype. required for folders, others - optionally
387
	 * - (bool)   read    read permissions. required
388
	 * - (bool)   write   write permissions. required
389
	 * - (bool)   locked  is object locked. optionally
390
	 * - (bool)   hidden  is object hidden. optionally
391
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
392
	 * - (string) target  for symlinks - link target path. optionally
393
	 *
394
	 * If file does not exists - returns empty array or false.
395
	 *
396
	 * @param  string  $path    file path 
397
	 * @return array|false
398
	 * @author Dmitry (dio) Levashov
399
	 **/
400
	protected function _stat($path) {
401
		
402
		static $statOwner;
403
		if (is_null($statOwner)) {
404
			$statOwner = (!empty($this->options['statOwner']));
405
		}
406
		
407
		$stat = array();
408
409
		if (!file_exists($path) && !is_link($path)) {
410
			return $stat;
411
		}
412
413
		//Verifies the given path is the root or is inside the root. Prevents directory traveral.
414
		if (!$this->_inpath($path, $this->root)) {
415
			return $stat;
416
		}
417
418
		$gid = $uid = 0;
0 ignored issues
show
Unused Code introduced by
$uid 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...
Unused Code introduced by
$gid 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...
419
		$stat['isowner'] = false;
420
		$linkreadable = false;
421
		if ($path != $this->root && is_link($path)) {
422
			if (!($target = $this->readlink($path))
423
			|| $target == $path) {
424
				if (is_null($target)) {
425
					$stat = array();
426
					return $stat;
427
				} else {
428
					$stat['mime']  = 'symlink-broken';
429
					$target = readlink($path);
430
					$lstat = lstat($path);
431
					$ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
432
					$linkreadable = !empty($ostat['isowner']);
433
				}
434
			}
435
			$stat['alias'] = $this->_path($target);
436
			$stat['target'] = $target;
437
		}
438
		$size = sprintf('%u', @filesize($path));
439
		$stat['ts'] = filemtime($path);
440
		if ($statOwner) {
441
			$fstat = stat($path);
442
			$uid = $fstat['uid'];
443
			$gid = $fstat['gid'];
444
			$stat['perm'] = substr((string)decoct($fstat['mode']), -4);
445
			$stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
446
		}
447
		
448
		$dir = is_dir($path);
449
		
450
		if (!isset($stat['mime'])) {
451
			$stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
452
		}
453
		//logical rights first
454
		$stat['read'] = ($linkreadable || is_readable($path))? null : false;
455
		$stat['write'] = is_writable($path)? null : false;
456
457
		if (is_null($stat['read'])) {
458
			$stat['size'] = $dir ? 0 : $size;
459
		}
460
		
461
		return $stat;
462
	}
463
	
464
	/**
465
	 * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
466
	 * Sub-fuction of _stat() and _scandir()
467
	 * 
468
	 * @param integer $uid
469
	 * @param integer $gid
470
	 * @return array  stat
471
	 */
472
	protected function getOwnerStat($uid, $gid) {
473
		static $names = null;
474
		static $phpuid = null;
475
		
476
		if (is_null($names)) {
477
			$names = array('uid' => array(), 'gid' =>array());
478
		}
479
		if (is_null($phpuid)) {
480
			if (is_callable('posix_getuid')) {
481
				$phpuid = posix_getuid();
482
			} else {
483
				$phpuid = 0;
484
			}
485
		}
486
		
487
		$stat = array();
488
		
489
		if ($uid) {
490
			$stat['isowner'] = ($phpuid == $uid);
491 View Code Duplication
			if (isset($names['uid'][$uid])) {
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...
492
				$stat['owner'] = $names['uid'][$uid];
493
			} else if (is_callable('posix_getpwuid')) {
494
				$pwuid = posix_getpwuid($uid);
495
				$stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
496
			} else {
497
				$stat['owner'] = $names['uid'][$uid] = $uid;
498
			}
499
		}
500 View Code Duplication
		if ($gid) {
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...
501
			if (isset($names['gid'][$gid])) {
502
				$stat['group'] = $names['gid'][$gid];
503
			} else if (is_callable('posix_getgrgid')) {
504
				$grgid = posix_getgrgid($gid);
505
				$stat['group'] = $names['gid'][$gid] = $grgid['name'];
506
			} else {
507
				$stat['group'] = $names['gid'][$gid] = $gid;
508
			}
509
		}
510
		
511
		return $stat;
512
	}
513
514
	/**
515
	 * Return true if path is dir and has at least one childs directory
516
	 *
517
	 * @param  string  $path  dir path
518
	 * @return bool
519
	 * @author Dmitry (dio) Levashov
520
	 **/
521
	protected function _subdirs($path) {
522
523
		if (is_dir($path)) {
524
			$path = strtr($path, array('['  => '\\[', ']'  => '\\]', '*'  => '\\*', '?'  => '\\?'));
525
			return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
526
		}
527
		return false;
528
	}
529
	
530
	/**
531
	 * Return object width and height
532
	 * Usualy used for images, but can be realize for video etc...
533
	 *
534
	 * @param  string  $path  file path
535
	 * @param  string  $mime  file mime type
536
	 * @return string
537
	 * @author Dmitry (dio) Levashov
538
	 **/
539
	protected function _dimensions($path, $mime) {
540
		clearstatcache();
541
		return strpos($mime, 'image') === 0 && ($s = @getimagesize($path)) !== false 
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression strpos($mime, 'image') =... . 'x' . $s[1] : false; of type string|false adds false to the return on line 541 which is incompatible with the return type declared by the abstract method elFinderVolumeDriver::_dimensions of type string. It seems like you forgot to handle an error condition.
Loading history...
542
			? $s[0].'x'.$s[1] 
0 ignored issues
show
Bug introduced by
The variable $s does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
543
			: false;
544
	}
545
	/******************** file/dir content *********************/
546
	
547
	/**
548
	 * Return symlink target file
549
	 *
550
	 * @param  string  $path  link path
551
	 * @return string
552
	 * @author Dmitry (dio) Levashov
553
	 **/
554
	protected function readlink($path) {
555
		if (!($target = @readlink($path))) {
556
			return null;
557
		}
558
559
		if (strpos($target, $this->systemRoot) !== 0) {
560
			$target = $this->_joinPath(dirname($path), $target);
561
		}
562
563
		if (!file_exists($target)) {
564
			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 elFinderVolumeLocalFileSystem::readlink 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...
565
		}
566
		
567
		return $target;
568
	}
569
		
570
	/**
571
	 * Return files list in directory.
572
	 *
573
	 * @param  string  $path  dir path
574
	 * @return array
575
	 * @author Dmitry (dio) Levashov
576
	 **/
577
	protected function _scandir($path) {
578
		$files = array();
579
		$cache = array();
580
		$statOwner = (!empty($this->options['statOwner']));
581
		$dirItr = array();
582
		try {
583
			$dirItr = new DirectoryIterator($path);
584
		} catch (UnexpectedValueException $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
585
		
586
		foreach ($dirItr as $file) {
587
			try {
588
				if ($file->isDot()) { continue; }
589
				
590
				$files[] = $fpath = $file->getPathname();
591
				
592
				$br = false;
593
				$stat = array();
594
				
595
				$gid = $uid = 0;
596
				$stat['isowner'] = false;
597
				$linkreadable = false;
598
				if ($file->isLink()) {
599
					if (!($target = $this->readlink($fpath))
600
					|| $target == $fpath) {
601
						if (is_null($target)) {
602
							$stat = array();
603
							$br = true;
604
						} else {
605
							$_path = $fpath;
606
							$stat['mime']  = 'symlink-broken';
607
							$target = readlink($_path);
608
							$lstat = lstat($_path);
609
							$ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
610
							$linkreadable = !empty($ostat['isowner']);
611
							$dir = false;
612
							$stat['alias'] = $this->_path($target);
613
							$stat['target'] = $target;
614
						}
615
					} else {
616
						$dir = is_dir($target);
617
						$stat['alias'] = $this->_path($target);
618
						$stat['target'] = $target;
619
						$stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
620
					}
621
				} else {
622
					$dir = $file->isDir();
623
					$stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
624
				}
625
				$size = sprintf('%u', $file->getSize());
626
				$stat['ts'] = $file->getMTime();
627
				if (!$br) {
628
					if ($statOwner && !$linkreadable) {
629
						$uid = $file->getOwner();
630
						$gid = $file->getGroup();
631
						$stat['perm'] = substr((string)decoct($file->getPerms()), -4);
632
						$stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
633
					}
634
					
635
					//logical rights first
636
					$stat['read'] = ($linkreadable || $file->isReadable())? null : false;
637
					$stat['write'] = $file->isWritable()? null : false;
638
					
639
					if (is_null($stat['read'])) {
640
						$stat['size'] = $dir ? 0 : $size;
0 ignored issues
show
Bug introduced by
The variable $dir does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
641
					}
642
					
643
				}
644
				
645
				$cache[] = array($fpath, $stat);
646
			} catch (RuntimeException $e) {
647
				continue;
648
			}
649
		}
650
		
651
		if ($cache) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cache of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
652
			$cache = $this->convEncOut($cache, false);
653
			foreach($cache as $d) {
654
				$this->updateCache($d[0], $d[1]);
655
			}
656
		}
657
		
658
		return $files;
659
	}
660
		
661
	/**
662
	 * Open file and return file pointer
663
	 *
664
	 * @param  string  $path  file path
665
	 * @param  bool    $write open file for writing
0 ignored issues
show
Bug introduced by
There is no parameter named $write. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
666
	 * @return resource|false
667
	 * @author Dmitry (dio) Levashov
668
	 **/
669
	protected function _fopen($path, $mode='rb') {
670
		return @fopen($path, $mode);
671
	}
672
	
673
	/**
674
	 * Close opened file
675
	 *
676
	 * @param  resource  $fp  file pointer
677
	 * @return bool
678
	 * @author Dmitry (dio) Levashov
679
	 **/
680
	protected function _fclose($fp, $path='') {
681
		return @fclose($fp);
682
	}
683
	
684
	/********************  file/dir manipulations *************************/
685
	
686
	/**
687
	 * Create dir and return created dir path or false on failed
688
	 *
689
	 * @param  string  $path  parent dir path
690
	 * @param string  $name  new directory name
691
	 * @return string|bool
692
	 * @author Dmitry (dio) Levashov
693
	 **/
694 View Code Duplication
	protected function _mkdir($path, $name) {
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...
695
		$path = $this->_joinPath($path, $name);
696
697
		if (@mkdir($path)) {
698
			@chmod($path, $this->options['dirMode']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
699
			clearstatcache();
700
			return $path;
701
		}
702
703
		return false;
704
	}
705
	
706
	/**
707
	 * Create file and return it's path or false on failed
708
	 *
709
	 * @param  string  $path  parent dir path
710
	 * @param string  $name  new file name
711
	 * @return string|bool
712
	 * @author Dmitry (dio) Levashov
713
	 **/
714 View Code Duplication
	protected function _mkfile($path, $name) {
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...
715
		$path = $this->_joinPath($path, $name);
716
		
717
		if (($fp = @fopen($path, 'w'))) {
718
			@fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
719
			@chmod($path, $this->options['fileMode']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
720
			clearstatcache();
721
			return $path;
722
		}
723
		return false;
724
	}
725
	
726
	/**
727
	 * Create symlink
728
	 *
729
	 * @param  string  $source     file to link to
730
	 * @param  string  $targetDir  folder to create link in
731
	 * @param  string  $name       symlink name
732
	 * @return bool
733
	 * @author Dmitry (dio) Levashov
734
	 **/
735
	protected function _symlink($source, $targetDir, $name) {
736
		return @symlink($source, $this->_joinPath($targetDir, $name));
737
	}
738
	
739
	/**
740
	 * Copy file into another file
741
	 *
742
	 * @param  string  $source     source file path
743
	 * @param  string  $targetDir  target directory path
744
	 * @param  string  $name       new file name
745
	 * @return bool
746
	 * @author Dmitry (dio) Levashov
747
	 **/
748
	protected function _copy($source, $targetDir, $name) {
749
		$ret = copy($source, $this->_joinPath($targetDir, $name));
750
		$ret && clearstatcache();
751
		return $ret;
752
	}
753
	
754
	/**
755
	 * Move file into another parent dir.
756
	 * Return new file path or false.
757
	 *
758
	 * @param  string  $source  source file path
759
	 * @param  string  $target  target dir path
0 ignored issues
show
Documentation introduced by
There is no parameter named $target. Did you maybe mean $targetDir?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
760
	 * @param  string  $name    file name
761
	 * @return string|bool
762
	 * @author Dmitry (dio) Levashov
763
	 **/
764
	protected function _move($source, $targetDir, $name) {
765
		$target = $this->_joinPath($targetDir, $name);
766
		$ret = @rename($source, $target) ? $target : false;
767
		$ret && clearstatcache();
0 ignored issues
show
Bug Best Practice introduced by
The expression $ret of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
768
		return $ret;
769
	}
770
		
771
	/**
772
	 * Remove file
773
	 *
774
	 * @param  string  $path  file path
775
	 * @return bool
776
	 * @author Dmitry (dio) Levashov
777
	 **/
778
	protected function _unlink($path) {
779
		$ret = @unlink($path);
780
		$ret && clearstatcache();
781
		return $ret;
782
	}
783
784
	/**
785
	 * Remove dir
786
	 *
787
	 * @param  string  $path  dir path
788
	 * @return bool
789
	 * @author Dmitry (dio) Levashov
790
	 **/
791
	protected function _rmdir($path) {
792
		$ret = @rmdir($path);
793
		$ret && clearstatcache();
794
		return $ret;
795
	}
796
	
797
	/**
798
	 * Create new file and write into it from file pointer.
799
	 * Return new file path or false on error.
800
	 *
801
	 * @param  resource  $fp   file pointer
802
	 * @param  string    $dir  target dir path
803
	 * @param  string    $name file name
804
	 * @param  array     $stat file stat (required by some virtual fs)
805
	 * @return bool|string
806
	 * @author Dmitry (dio) Levashov
807
	 **/
808
	protected function _save($fp, $dir, $name, $stat) {
809
		$path = $this->_joinPath($dir, $name);
810
811
		$meta = stream_get_meta_data($fp);
812
		$uri = isset($meta['uri'])? $meta['uri'] : '';
813
		if ($uri && @is_file($uri)) {
814
			fclose($fp);
815
			$isCmdPaste = ($this->ARGS['cmd'] === 'paste');
816
			$isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
817
			if (($isCmdCopy || !@rename($uri, $path)) && !@copy($uri, $path)) {
818
				return false;
819
			}
820
			// re-create the source file for remove processing of paste command
821
			$isCmdPaste && !$isCmdCopy && touch($uri);
822
		} else {
823
			if (@file_put_contents($path, $fp, LOCK_EX) === false) {
824
				return false;
825
			}
826
		}
827
828
		@chmod($path, $this->options['fileMode']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
829
		clearstatcache();
830
		return $path;
831
	}
832
	
833
	/**
834
	 * Get file contents
835
	 *
836
	 * @param  string  $path  file path
837
	 * @return string|false
838
	 * @author Dmitry (dio) Levashov
839
	 **/
840
	protected function _getContents($path) {
841
		return file_get_contents($path);
842
	}
843
	
844
	/**
845
	 * Write a string to a file
846
	 *
847
	 * @param  string  $path     file path
848
	 * @param  string  $content  new file content
849
	 * @return bool
850
	 * @author Dmitry (dio) Levashov
851
	 **/
852
	protected function _filePutContents($path, $content) {
853
		if (@file_put_contents($path, $content, LOCK_EX) !== false) {
854
			clearstatcache();
855
			return true;
856
		}
857
		return false;
858
	}
859
860
	/**
861
	 * Detect available archivers
862
	 *
863
	 * @return void
864
	 **/
865
	protected function _checkArchivers() {
866
		$this->archivers = $this->getArchivers();
867
		return;
868
	}
869
870
	/**
871
	 * chmod availability
872
	 *
873
	 * @return bool
874
	 **/
875
	protected function _chmod($path, $mode) {
876
		$modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o",$mode));
877
		$ret = @chmod($path, $modeOct);
878
		$ret && clearstatcache();
879
		return  $ret;
880
	}
881
882
	/**
883
	 * Recursive symlinks search
884
	 *
885
	 * @param  string  $path  file/dir path
886
	 * @return bool
887
	 * @author Dmitry (dio) Levashov
888
	 **/
889
	protected function _findSymlinks($path) {
890
		if (is_link($path)) {
891
			return true;
892
		}
893
		
894
		if (is_dir($path)) {
895
			foreach (scandir($path) as $name) {
896
				if ($name != '.' && $name != '..') {
897
					$p = $path.DIRECTORY_SEPARATOR.$name;
898
					if (is_link($p) || !$this->nameAccepted($name)
899
						||
900
					(($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)) && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
901
						$this->setError(elFinder::ERROR_SAVE, $name);
902
						return true;
903
					}
904
					if (is_dir($p) && $this->_findSymlinks($p)) {
905
						return true;
906
					} elseif (is_file($p)) {
907
						$this->archiveSize += sprintf('%u', filesize($p));
908
					}
909
				}
910
			}
911
		} else {
912
			
913
			$this->archiveSize += sprintf('%u', filesize($path));
914
		}
915
		
916
		return false;
917
	}
918
919
	/**
920
	 * Extract files from archive
921
	 *
922
	 * @param  string  $path  archive path
923
	 * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
924
	 * @return true
925
	 * @author Dmitry (dio) Levashov, 
926
	 * @author Alexey Sukhotin
927
	 **/
928
	protected function _extract($path, $arc) {
929
		
930
		if ($this->quarantine) {
931
932
			$dir     = $this->quarantine.DIRECTORY_SEPARATOR.md5(basename($path).mt_rand());
933
			$archive = $dir.DIRECTORY_SEPARATOR.basename($path);
934
			
935
			if (!@mkdir($dir)) {
936
				return false;
937
			}
938
			
939
			// insurance unexpected shutdown
940
			register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));
941
			
942
			chmod($dir, 0777);
943
			
944
			// copy in quarantine
945
			if (!copy($path, $archive)) {
946
				return false;
947
			}
948
			
949
			// extract in quarantine
950
			$this->unpackArchive($archive, $arc);
951
			
952
			// get files list
953
			$ls = array();
954
			foreach (scandir($dir) as $i => $name) {
955
				if ($name != '.' && $name != '..') {
956
					$ls[] = $name;
957
				}
958
			}
959
			
960
			// no files - extract error ?
961
			if (empty($ls)) {
962
				return false;
963
			}
964
			
965
			$this->archiveSize = 0;
966
			
967
			// find symlinks
968
			$symlinks = $this->_findSymlinks($dir);
969
			
970 View Code Duplication
			if ($symlinks) {
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...
971
				$this->delTree($dir);
972
				return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
973
			}
974
975
			// check max files size
976 View Code Duplication
			if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
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...
977
				$this->delTree($dir);
978
				return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
979
			}
980
			
981
			$extractTo = $this->extractToNewdir; // 'auto', ture or false
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
982
			
983
			// archive contains one item - extract in archive dir
984
			$name = '';
985
			$src = $dir.DIRECTORY_SEPARATOR.$ls[0];
986
			if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $extractTo (integer) and 'auto' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
987
				$name = $ls[0];
988
			} else if ($extractTo === 'auto' || $extractTo) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $extractTo (integer) and 'auto' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
989
				// for several files - create new directory
990
				// create unique name for directory
991
				$src = $dir;
992
				$name = basename($path);
993 View Code Duplication
				if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
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...
994
					$name = substr($name, 0,  strlen($name)-strlen($m[0]));
995
				}
996
				$test = dirname($path).DIRECTORY_SEPARATOR.$name;
997
				if (file_exists($test) || is_link($test)) {
998
					$name = $this->uniqueName(dirname($path), $name, '-', false);
999
				}
1000
			}
1001
			
1002
			if ($name !== '') {
1003
				$result  = dirname($path).DIRECTORY_SEPARATOR.$name;
1004
1005
				if (! @rename($src, $result)) {
1006
					$this->delTree($dir);
1007
					return false;
1008
				}
1009
			} else {
1010
				$dstDir = dirname($path);
1011
				$res = false;
0 ignored issues
show
Unused Code introduced by
$res 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...
1012
				$result = array();
1013
				foreach($ls as $name) {
1014
					$target = $dstDir.DIRECTORY_SEPARATOR.$name;
1015
					if (is_dir($target)) {
1016
						$this->delTree($target);
1017
					}
1018
					if (@rename($dir.DIRECTORY_SEPARATOR.$name, $target)) {
1019
						$result[] = $target;
1020
					}
1021
				}
1022
				if (!$result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1023
					$this->delTree($dir);
1024
					return false;
1025
				}
1026
			}
1027
			
1028
			is_dir($dir) && $this->delTree($dir);
1029
			
1030
			return (is_array($result) || file_exists($result)) ? $result : false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return is_array($result)...ult) ? $result : false; (array|string|false) is incompatible with the return type declared by the abstract method elFinderVolumeDriver::_extract of type boolean.

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...
1031
		}
1032
	}
1033
	
1034
	/**
1035
	 * Create archive and return its path
1036
	 *
1037
	 * @param  string  $dir    target dir
1038
	 * @param  array   $files  files names list
1039
	 * @param  string  $name   archive name
1040
	 * @param  array   $arc    archiver options
1041
	 * @return string|bool
1042
	 * @author Dmitry (dio) Levashov, 
1043
	 * @author Alexey Sukhotin
1044
	 **/
1045
	protected function _archive($dir, $files, $name, $arc) {
1046
		return $this->makeArchive($dir, $files, $name, $arc);
1047
	}
1048
	
1049
	/******************** Over write functions *************************/
1050
	
1051
	/**
1052
	 * File path of local server side work file path
1053
	 *
1054
	 * @param  string $path
1055
	 * @return string
1056
	 * @author Naoki Sawada
1057
	 */
1058
	protected function getWorkFile($path) {
1059
		return $path;
1060
	}
1061
1062
	/**
1063
	 * Delete dirctory trees
1064
	 *
1065
	 * @param string $localpath path need convert encoding to server encoding
1066
	 * @return boolean
1067
	 * @author Naoki Sawada
1068
	 */
1069
	protected function delTree($localpath) {
1070
		return $this->rmdirRecursive($localpath);
1071
	}
1072
1073
	/******************** Over write (Optimized) functions *************************/
1074
	
1075
	/**
1076
	 * Recursive files search
1077
	 *
1078
	 * @param  string  $path   dir path
1079
	 * @param  string  $q      search string
1080
	 * @param  array   $mimes
1081
	 * @return array
1082
	 * @author Dmitry (dio) Levashov
1083
	 * @author Naoki Sawada
1084
	 **/
1085
	protected function doSearch($path, $q, $mimes) {
1086
		if ($this->encoding) {
1087
			// non UTF-8 has problem of glob() results
1088
			return parent::doSearch($path, $q, $mimes);
1089
		}
1090
		
1091
		static $escaper;
1092
		if (!$escaper) {
1093
			$escaper = array(
1094
				'['  => '\\[',
1095
				']'  => '\\]',
1096
				'*'  => '\\*',
1097
				'?'  => '\\?'
1098
			);
1099
		}
1100
		
1101
		$result = array();
1102
		
1103
		$path = strtr($path, $escaper);
1104
		$_q = strtr($q, $escaper);
1105
		$dirs = glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
1106
		$match = glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*'.$_q.'*', GLOB_NOSORT);
1107
		if ($match) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $match of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1108
			foreach($match as $p) {
1109
				$stat = $this->stat($p);
1110
		
1111
				if (!$stat) { // invalid links
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1112
					continue;
1113
				}
1114
		
1115
				if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
1116
					continue;
1117
				}
1118
					
1119
				$name = $stat['name'];
0 ignored issues
show
Unused Code introduced by
$name 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...
1120
		
1121
				if ((!$mimes || $stat['mime'] !== 'directory')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mimes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1122
					$stat['path'] = $this->path($stat['hash']);
1123
					if ($this->URL && !isset($stat['url'])) {
1124
						$path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
1125
						$stat['url'] = $this->URL . $path;
1126
					}
1127
		
1128
					$result[] = $stat;
1129
				}
1130
			}
1131
		}
1132
		if ($dirs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $dirs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1133
			foreach($dirs as $dir) {
1134
				$stat = $this->stat($dir);
1135 View Code Duplication
				if ($stat['read'] && !isset($stat['alias'])) {
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...
1136
					@set_time_limit(30);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1137
					$result = array_merge($result, $this->doSearch($dir, $q, $mimes));
1138
				}
1139
			}
1140
		}
1141
	
1142
		return $result;
1143
	}
1144
	
1145
} // END class 
1146