Completed
Push — 2.x ( c8a66f...9c9617 )
by Naoki
12:26
created

elFinderVolumeLocalFileSystem::init()   C

Complexity

Conditions 11
Paths 22

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 28
rs 5.2653
cc 11
eloc 18
nc 22
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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', 'tmpPath', 'quarantine') as $key) {
57
				if ($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
		return true;
80
	}
81
	
82
	/**
83
	 * Configure after successfull mount.
84
	 *
85
	 * @return void
86
	 * @author Dmitry (dio) Levashov
87
	 **/
88
	protected function configure() {
89
		$root = $this->stat($this->root);
90
		
91
		// chek thumbnails path
92
		if ($this->options['tmbPath']) {
93
			$this->options['tmbPath'] = strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false
94
				// tmb path set as dirname under root dir
95
				? $this->_abspath($this->options['tmbPath'])
96
				// tmb path as full path
97
				: $this->_normpath($this->options['tmbPath']);
98
		}
99
100
		parent::configure();
101
		
102
		// if no thumbnails url - try detect it
103
		if ($root['read'] && !$this->tmbURL && $this->URL) {
104
			if (strpos($this->tmbPath, $this->root) === 0) {
105
				$this->tmbURL = $this->URL.str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root)+1));
106
				if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
107
					$this->tmbURL .= '/';
108
				}
109
			}
110
		}
111
112
		// check quarantine dir
113
		$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...
114
		if (!empty($this->options['quarantine'])) {
115
			if (is_dir($this->options['quarantine'])) {
116
				if (is_writable($this->options['quarantine'])) {
117
					$this->quarantine = $this->options['quarantine'];
118
				}
119
				$this->options['quarantine'] = '';
120
			} else {
121
				$this->quarantine = $this->_abspath($this->options['quarantine']);
122
				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...
123
					$this->options['quarantine'] = $this->quarantine = '';
124
				}
125
			}
126
		}
127
		
128
		if (!$this->quarantine) {
129
			$this->archivers['extract'] = array();
130
			$this->disabled[] = 'extract';
131
		}
132
		
133
		if ($this->options['quarantine']) {
134
			$this->attributes[] = array(
135
					'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR.$this->options['quarantine']).'$~',
136
					'read'    => false,
137
					'write'   => false,
138
					'locked'  => true,
139
					'hidden'  => true
140
			);
141
		}
142
	}
143
	
144
	/*********************************************************************/
145
	/*                               FS API                              */
146
	/*********************************************************************/
147
148
	/*********************** paths/urls *************************/
149
	
150
	/**
151
	 * Return parent directory path
152
	 *
153
	 * @param  string  $path  file path
154
	 * @return string
155
	 * @author Dmitry (dio) Levashov
156
	 **/
157
	protected function _dirname($path) {
158
		return dirname($path);
159
	}
160
161
	/**
162
	 * Return file name
163
	 *
164
	 * @param  string  $path  file path
165
	 * @return string
166
	 * @author Dmitry (dio) Levashov
167
	 **/
168
	protected function _basename($path) {
169
		return basename($path);
170
	}
171
172
	/**
173
	 * Join dir name and file name and retur full path
174
	 *
175
	 * @param  string  $dir
176
	 * @param  string  $name
177
	 * @return string
178
	 * @author Dmitry (dio) Levashov
179
	 **/
180
	protected function _joinPath($dir, $name) {
181
		return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
182
	}
183
	
184
	/**
185
	 * Return normalized path, this works the same as os.path.normpath() in Python
186
	 *
187
	 * @param  string  $path  path
188
	 * @return string
189
	 * @author Troex Nevelin
190
	 **/
191
	protected function _normpath($path) {
192
		if (empty($path)) {
193
			return '.';
194
		}
195
		
196
		$changeSep = (DIRECTORY_SEPARATOR !== '/');
197
		if ($changeSep) {
198
			$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
199
		}
200
201
		if (strpos($path, '/') === 0) {
202
			$initial_slashes = true;
203
		} else {
204
			$initial_slashes = false;
205
		}
206
			
207 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...
208
		&& (strpos($path, '//') === 0) 
209
		&& (strpos($path, '///') === false)) {
210
			$initial_slashes = 2;
211
		}
212
			
213
		$initial_slashes = (int) $initial_slashes;
214
215
		$comps = explode('/', $path);
216
		$new_comps = array();
217 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...
218
			if (in_array($comp, array('', '.'))) {
219
				continue;
220
			}
221
				
222
			if (($comp != '..') 
223
			|| (!$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...
224
			|| ($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...
225
				array_push($new_comps, $comp);
226
			} 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...
227
				array_pop($new_comps);
228
			}
229
		}
230
		$comps = $new_comps;
231
		$path = implode('/', $comps);
232
		if ($initial_slashes) {
233
			$path = str_repeat('/', $initial_slashes) . $path;
234
		}
235
		
236
		if ($changeSep) {
237
			$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
238
		}
239
		
240
		return $path ? $path : '.';
241
	}
242
	
243
	/**
244
	 * Return file path related to root dir
245
	 *
246
	 * @param  string  $path  file path
247
	 * @return string
248
	 * @author Dmitry (dio) Levashov
249
	 **/
250 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...
251
		if ($path === $this->root) {
252
			return '';
253
		} else {
254
			if (strpos($path, $this->root) === 0) {
255
				return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
256
			} else {
257
				// for link
258
				return $path;
259
			}
260
		}
261
	}
262
	
263
	/**
264
	 * Convert path related to root dir into real path
265
	 *
266
	 * @param  string  $path  file path
267
	 * @return string
268
	 * @author Dmitry (dio) Levashov
269
	 **/
270
	protected function _abspath($path) {
271
		if ($path === DIRECTORY_SEPARATOR) {
272
			return $this->root;
273
		} else {
274
			if ($path[0] === DIRECTORY_SEPARATOR) {
275
				// for link
276
				return $path;
277
			} else {
278
				return $this->_joinPath($this->root, $path);
279
			}
280
		}
281
	}
282
	
283
	/**
284
	 * Return fake path started from root dir
285
	 *
286
	 * @param  string  $path  file path
287
	 * @return string
288
	 * @author Dmitry (dio) Levashov
289
	 **/
290
	protected function _path($path) {
291
		return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path));
292
	}
293
	
294
	/**
295
	 * Return true if $path is children of $parent
296
	 *
297
	 * @param  string  $path    path to check
298
	 * @param  string  $parent  parent path
299
	 * @return bool
300
	 * @author Dmitry (dio) Levashov
301
	 **/
302
	protected function _inpath($path, $parent) {
303
		$cwd = getcwd();
304
		$real_path   = $this->getFullPath($path,   $cwd);
305
		$real_parent = $this->getFullPath($parent, $cwd);
306
		if ($real_path && $real_parent) {
307
			return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR) === 0;
308
		}
309
		return false;
310
	}
311
	
312
	
313
	
314
	/***************** file stat ********************/
315
316
	/**
317
	 * Return stat for given path.
318
	 * Stat contains following fields:
319
	 * - (int)    size    file size in b. required
320
	 * - (int)    ts      file modification time in unix time. required
321
	 * - (string) mime    mimetype. required for folders, others - optionally
322
	 * - (bool)   read    read permissions. required
323
	 * - (bool)   write   write permissions. required
324
	 * - (bool)   locked  is object locked. optionally
325
	 * - (bool)   hidden  is object hidden. optionally
326
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
327
	 * - (string) target  for symlinks - link target path. optionally
328
	 *
329
	 * If file does not exists - returns empty array or false.
330
	 *
331
	 * @param  string  $path    file path 
332
	 * @return array|false
333
	 * @author Dmitry (dio) Levashov
334
	 **/
335
	protected function _stat($path) {
336
		
337
		static $statOwner;
338
		if (is_null($statOwner)) {
339
			$statOwner = (!empty($this->options['statOwner']));
340
		}
341
		
342
		$stat = array();
343
344
		if (!file_exists($path) && !is_link($path)) {
345
			return $stat;
346
		}
347
348
		//Verifies the given path is the root or is inside the root. Prevents directory traveral.
349
		if (!$this->_inpath($path, $this->root)) {
350
			return $stat;
351
		}
352
353
		$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...
354
		$stat['isowner'] = false;
355
		$linkreadable = false;
356
		if ($path != $this->root && is_link($path)) {
357
			if (!($target = $this->readlink($path))
358
			|| $target == $path) {
359
				if (is_null($target)) {
360
					$stat = array();
361
					return $stat;
362
				} else {
363
					$stat['mime']  = 'symlink-broken';
364
					$target = readlink($path);
365
					$lstat = lstat($path);
366
					$ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
367
					$linkreadable = !empty($ostat['isowner']);
368
				}
369
			}
370
			$stat['alias'] = $this->_path($target);
371
			$stat['target'] = $target;
372
		}
373
		$size = sprintf('%u', @filesize($path));
374
		$stat['ts'] = filemtime($path);
375
		if ($statOwner) {
376
			$fstat = stat($path);
377
			$uid = $fstat['uid'];
378
			$gid = $fstat['gid'];
379
			$stat['perm'] = substr((string)decoct($fstat['mode']), -4);
380
			$stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
381
		}
382
		
383
		$dir = is_dir($path);
384
		
385
		if (!isset($stat['mime'])) {
386
			$stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
387
		}
388
		//logical rights first
389
		$stat['read'] = ($linkreadable || is_readable($path))? null : false;
390
		$stat['write'] = is_writable($path)? null : false;
391
392
		if (is_null($stat['read'])) {
393
			$stat['size'] = $dir ? 0 : $size;
394
		}
395
		
396
		return $stat;
397
	}
398
	
399
	/**
400
	 * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
401
	 * Sub-fuction of _stat() and _scandir()
402
	 * 
403
	 * @param integer $uid
404
	 * @param integer $gid
405
	 * @return array  stat
406
	 */
407
	protected function getOwnerStat($uid, $gid) {
408
		static $names = null;
409
		static $phpuid = null;
410
		
411
		if (is_null($names)) {
412
			$names = array('uid' => array(), 'gid' =>array());
413
		}
414
		if (is_null($phpuid)) {
415
			if (is_callable('posix_getuid')) {
416
				$phpuid = posix_getuid();
417
			} else {
418
				$phpuid = 0;
419
			}
420
		}
421
		
422
		$stat = array();
423
		
424
		if ($uid) {
425
			$stat['isowner'] = ($phpuid == $uid);
426 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...
427
				$stat['owner'] = $names['uid'][$uid];
428
			} else if (is_callable('posix_getpwuid')) {
429
				$pwuid = posix_getpwuid($uid);
430
				$stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
431
			} else {
432
				$stat['owner'] = $names['uid'][$uid] = $uid;
433
			}
434
		}
435 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...
436
			if (isset($names['gid'][$gid])) {
437
				$stat['group'] = $names['gid'][$gid];
438
			} else if (is_callable('posix_getgrgid')) {
439
				$grgid = posix_getgrgid($gid);
440
				$stat['group'] = $names['gid'][$gid] = $grgid['name'];
441
			} else {
442
				$stat['group'] = $names['gid'][$gid] = $gid;
443
			}
444
		}
445
		
446
		return $stat;
447
	}
448
449
	/**
450
	 * Return true if path is dir and has at least one childs directory
451
	 *
452
	 * @param  string  $path  dir path
453
	 * @return bool
454
	 * @author Dmitry (dio) Levashov
455
	 **/
456
	protected function _subdirs($path) {
457
458
		if (is_dir($path)) {
459
			$path = strtr($path, array('['  => '\\[', ']'  => '\\]', '*'  => '\\*', '?'  => '\\?'));
460
			return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
461
		}
462
		return false;
463
	}
464
	
465
	/**
466
	 * Return object width and height
467
	 * Usualy used for images, but can be realize for video etc...
468
	 *
469
	 * @param  string  $path  file path
470
	 * @param  string  $mime  file mime type
471
	 * @return string
472
	 * @author Dmitry (dio) Levashov
473
	 **/
474
	protected function _dimensions($path, $mime) {
475
		clearstatcache();
476
		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 476 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...
477
			? $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...
478
			: false;
479
	}
480
	/******************** file/dir content *********************/
481
	
482
	/**
483
	 * Return symlink target file
484
	 *
485
	 * @param  string  $path  link path
486
	 * @return string
487
	 * @author Dmitry (dio) Levashov
488
	 **/
489
	protected function readlink($path) {
490
		if (!($target = @readlink($path))) {
491
			return null;
492
		}
493
494
		if (strpos($target, $this->systemRoot) !== 0) {
495
			$target = $this->_joinPath(dirname($path), $target);
496
		}
497
498
		if (!file_exists($target)) {
499
			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...
500
		}
501
		
502
		return $target;
503
	}
504
		
505
	/**
506
	 * Return files list in directory.
507
	 *
508
	 * @param  string  $path  dir path
509
	 * @return array
510
	 * @author Dmitry (dio) Levashov
511
	 **/
512
	protected function _scandir($path) {
513
		$files = array();
514
		$cache = array();
515
		$statOwner = (!empty($this->options['statOwner']));
516
		$dirItr = array();
517
		try {
518
			$dirItr = new DirectoryIterator($path);
519
		} catch (UnexpectedValueException $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
520
		
521
		foreach ($dirItr as $file) {
522
			try {
523
				if ($file->isDot()) { continue; }
524
				
525
				$files[] = $fpath = $file->getPathname();
526
				
527
				$br = false;
528
				$stat = array();
529
				
530
				$gid = $uid = 0;
531
				$stat['isowner'] = false;
532
				$linkreadable = false;
533
				if ($file->isLink()) {
534
					if (!($target = $this->readlink($fpath))
535
					|| $target == $fpath) {
536
						if (is_null($target)) {
537
							$stat = array();
538
							$br = true;
539
						} else {
540
							$_path = $fpath;
541
							$stat['mime']  = 'symlink-broken';
542
							$target = readlink($_path);
543
							$lstat = lstat($_path);
544
							$ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
545
							$linkreadable = !empty($ostat['isowner']);
546
							$dir = false;
547
							$stat['alias'] = $this->_path($target);
548
							$stat['target'] = $target;
549
						}
550
					} else {
551
						$dir = is_dir($target);
552
						$stat['alias'] = $this->_path($target);
553
						$stat['target'] = $target;
554
						$stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
555
					}
556
				} else {
557
					$dir = $file->isDir();
558
					$stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
559
				}
560
				$size = sprintf('%u', $file->getSize());
561
				$stat['ts'] = $file->getMTime();
562
				if (!$br) {
563
					if ($statOwner && !$linkreadable) {
564
						$uid = $file->getOwner();
565
						$gid = $file->getGroup();
566
						$stat['perm'] = substr((string)decoct($file->getPerms()), -4);
567
						$stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
568
					}
569
					
570
					//logical rights first
571
					$stat['read'] = ($linkreadable || $file->isReadable())? null : false;
572
					$stat['write'] = $file->isWritable()? null : false;
573
					
574
					if (is_null($stat['read'])) {
575
						$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...
576
					}
577
					
578
				}
579
				
580
				$cache[] = array($fpath, $stat);
581
			} catch (RuntimeException $e) {
582
				continue;
583
			}
584
		}
585
		
586
		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...
587
			$cache = $this->convEncOut($cache, false);
588
			foreach($cache as $d) {
589
				$this->updateCache($d[0], $d[1]);
590
			}
591
		}
592
		
593
		return $files;
594
	}
595
		
596
	/**
597
	 * Open file and return file pointer
598
	 *
599
	 * @param  string  $path  file path
600
	 * @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...
601
	 * @return resource|false
602
	 * @author Dmitry (dio) Levashov
603
	 **/
604
	protected function _fopen($path, $mode='rb') {
605
		return @fopen($path, $mode);
606
	}
607
	
608
	/**
609
	 * Close opened file
610
	 *
611
	 * @param  resource  $fp  file pointer
612
	 * @return bool
613
	 * @author Dmitry (dio) Levashov
614
	 **/
615
	protected function _fclose($fp, $path='') {
616
		return @fclose($fp);
617
	}
618
	
619
	/********************  file/dir manipulations *************************/
620
	
621
	/**
622
	 * Create dir and return created dir path or false on failed
623
	 *
624
	 * @param  string  $path  parent dir path
625
	 * @param string  $name  new directory name
626
	 * @return string|bool
627
	 * @author Dmitry (dio) Levashov
628
	 **/
629 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...
630
		$path = $this->_joinPath($path, $name);
631
632
		if (@mkdir($path)) {
633
			@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...
634
			clearstatcache();
635
			return $path;
636
		}
637
638
		return false;
639
	}
640
	
641
	/**
642
	 * Create file and return it's path or false on failed
643
	 *
644
	 * @param  string  $path  parent dir path
645
	 * @param string  $name  new file name
646
	 * @return string|bool
647
	 * @author Dmitry (dio) Levashov
648
	 **/
649 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...
650
		$path = $this->_joinPath($path, $name);
651
		
652
		if (($fp = @fopen($path, 'w'))) {
653
			@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...
654
			@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...
655
			clearstatcache();
656
			return $path;
657
		}
658
		return false;
659
	}
660
	
661
	/**
662
	 * Create symlink
663
	 *
664
	 * @param  string  $source     file to link to
665
	 * @param  string  $targetDir  folder to create link in
666
	 * @param  string  $name       symlink name
667
	 * @return bool
668
	 * @author Dmitry (dio) Levashov
669
	 **/
670
	protected function _symlink($source, $targetDir, $name) {
671
		return @symlink($source, $this->_joinPath($targetDir, $name));
672
	}
673
	
674
	/**
675
	 * Copy file into another file
676
	 *
677
	 * @param  string  $source     source file path
678
	 * @param  string  $targetDir  target directory path
679
	 * @param  string  $name       new file name
680
	 * @return bool
681
	 * @author Dmitry (dio) Levashov
682
	 **/
683
	protected function _copy($source, $targetDir, $name) {
684
		$ret = copy($source, $this->_joinPath($targetDir, $name));
685
		$ret && clearstatcache();
686
		return $ret;
687
	}
688
	
689
	/**
690
	 * Move file into another parent dir.
691
	 * Return new file path or false.
692
	 *
693
	 * @param  string  $source  source file path
694
	 * @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...
695
	 * @param  string  $name    file name
696
	 * @return string|bool
697
	 * @author Dmitry (dio) Levashov
698
	 **/
699
	protected function _move($source, $targetDir, $name) {
700
		$target = $this->_joinPath($targetDir, $name);
701
		$ret = @rename($source, $target) ? $target : false;
702
		$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...
703
		return $ret;
704
	}
705
		
706
	/**
707
	 * Remove file
708
	 *
709
	 * @param  string  $path  file path
710
	 * @return bool
711
	 * @author Dmitry (dio) Levashov
712
	 **/
713
	protected function _unlink($path) {
714
		$ret = @unlink($path);
715
		$ret && clearstatcache();
716
		return $ret;
717
	}
718
719
	/**
720
	 * Remove dir
721
	 *
722
	 * @param  string  $path  dir path
723
	 * @return bool
724
	 * @author Dmitry (dio) Levashov
725
	 **/
726
	protected function _rmdir($path) {
727
		$ret = @rmdir($path);
728
		$ret && clearstatcache();
729
		return $ret;
730
	}
731
	
732
	/**
733
	 * Create new file and write into it from file pointer.
734
	 * Return new file path or false on error.
735
	 *
736
	 * @param  resource  $fp   file pointer
737
	 * @param  string    $dir  target dir path
738
	 * @param  string    $name file name
739
	 * @param  array     $stat file stat (required by some virtual fs)
740
	 * @return bool|string
741
	 * @author Dmitry (dio) Levashov
742
	 **/
743
	protected function _save($fp, $dir, $name, $stat) {
744
		$path = $this->_joinPath($dir, $name);
745
746
		$meta = stream_get_meta_data($fp);
747
		$uri = isset($meta['uri'])? $meta['uri'] : '';
748
		if ($uri && @is_file($uri)) {
749
			fclose($fp);
750
			$isCmdPaste = ($this->ARGS['cmd'] === 'paste');
751
			$isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
752
			if (($isCmdCopy || !@rename($uri, $path)) && !@copy($uri, $path)) {
753
				return false;
754
			}
755
			// re-create the source file for remove processing of paste command
756
			$isCmdPaste && !$isCmdCopy && touch($uri);
757
		} else {
758
			if (@file_put_contents($path, $fp, LOCK_EX) === false) {
759
				return false;
760
			}
761
		}
762
763
		@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...
764
		clearstatcache();
765
		return $path;
766
	}
767
	
768
	/**
769
	 * Get file contents
770
	 *
771
	 * @param  string  $path  file path
772
	 * @return string|false
773
	 * @author Dmitry (dio) Levashov
774
	 **/
775
	protected function _getContents($path) {
776
		return file_get_contents($path);
777
	}
778
	
779
	/**
780
	 * Write a string to a file
781
	 *
782
	 * @param  string  $path     file path
783
	 * @param  string  $content  new file content
784
	 * @return bool
785
	 * @author Dmitry (dio) Levashov
786
	 **/
787
	protected function _filePutContents($path, $content) {
788
		if (@file_put_contents($path, $content, LOCK_EX) !== false) {
789
			clearstatcache();
790
			return true;
791
		}
792
		return false;
793
	}
794
795
	/**
796
	 * Detect available archivers
797
	 *
798
	 * @return void
799
	 **/
800
	protected function _checkArchivers() {
801
		$this->archivers = $this->getArchivers();
802
		return;
803
	}
804
805
	/**
806
	 * chmod availability
807
	 *
808
	 * @return bool
809
	 **/
810
	protected function _chmod($path, $mode) {
811
		$modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o",$mode));
812
		$ret = @chmod($path, $modeOct);
813
		$ret && clearstatcache();
814
		return  $ret;
815
	}
816
817
	/**
818
	 * Recursive symlinks search
819
	 *
820
	 * @param  string  $path  file/dir path
821
	 * @return bool
822
	 * @author Dmitry (dio) Levashov
823
	 **/
824
	protected function _findSymlinks($path) {
825
		if (is_link($path)) {
826
			return true;
827
		}
828
		
829
		if (is_dir($path)) {
830
			foreach (scandir($path) as $name) {
831
				if ($name != '.' && $name != '..') {
832
					$p = $path.DIRECTORY_SEPARATOR.$name;
833
					if (is_link($p) || !$this->nameAccepted($name)
834
						||
835
					(($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)) && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
836
						$this->setError(elFinder::ERROR_SAVE, $name);
837
						return true;
838
					}
839
					if (is_dir($p) && $this->_findSymlinks($p)) {
840
						return true;
841
					} elseif (is_file($p)) {
842
						$this->archiveSize += sprintf('%u', filesize($p));
843
					}
844
				}
845
			}
846
		} else {
847
			
848
			$this->archiveSize += sprintf('%u', filesize($path));
849
		}
850
		
851
		return false;
852
	}
853
854
	/**
855
	 * Extract files from archive
856
	 *
857
	 * @param  string  $path  archive path
858
	 * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
859
	 * @return true
860
	 * @author Dmitry (dio) Levashov, 
861
	 * @author Alexey Sukhotin
862
	 **/
863
	protected function _extract($path, $arc) {
864
		
865
		if ($this->quarantine) {
866
867
			$dir     = $this->quarantine.DIRECTORY_SEPARATOR.md5(basename($path).mt_rand());
868
			$archive = $dir.DIRECTORY_SEPARATOR.basename($path);
869
			
870
			if (!@mkdir($dir)) {
871
				return false;
872
			}
873
			
874
			// insurance unexpected shutdown
875
			register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));
876
			
877
			chmod($dir, 0777);
878
			
879
			// copy in quarantine
880
			if (!copy($path, $archive)) {
881
				return false;
882
			}
883
			
884
			// extract in quarantine
885
			$this->unpackArchive($archive, $arc);
886
			
887
			// get files list
888
			$ls = array();
889
			foreach (scandir($dir) as $i => $name) {
890
				if ($name != '.' && $name != '..') {
891
					$ls[] = $name;
892
				}
893
			}
894
			
895
			// no files - extract error ?
896
			if (empty($ls)) {
897
				return false;
898
			}
899
			
900
			$this->archiveSize = 0;
901
			
902
			// find symlinks
903
			$symlinks = $this->_findSymlinks($dir);
904
			
905 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...
906
				$this->delTree($dir);
907
				return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
908
			}
909
910
			// check max files size
911 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...
912
				$this->delTree($dir);
913
				return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
914
			}
915
			
916
			$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...
917
			
918
			// archive contains one item - extract in archive dir
919
			$name = '';
920
			$src = $dir.DIRECTORY_SEPARATOR.$ls[0];
921
			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...
922
				$name = $ls[0];
923
			} 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...
924
				// for several files - create new directory
925
				// create unique name for directory
926
				$src = $dir;
927
				$name = basename($path);
928 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...
929
					$name = substr($name, 0,  strlen($name)-strlen($m[0]));
930
				}
931
				$test = dirname($path).DIRECTORY_SEPARATOR.$name;
932
				if (file_exists($test) || is_link($test)) {
933
					$name = $this->uniqueName(dirname($path), $name, '-', false);
934
				}
935
			}
936
			
937
			if ($name !== '') {
938
				$result  = dirname($path).DIRECTORY_SEPARATOR.$name;
939
940
				if (! @rename($src, $result)) {
941
					$this->delTree($dir);
942
					return false;
943
				}
944
			} else {
945
				$dstDir = dirname($path);
946
				$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...
947
				$result = array();
948
				foreach($ls as $name) {
949
					$target = $dstDir.DIRECTORY_SEPARATOR.$name;
950
					if (is_dir($target)) {
951
						$this->delTree($target);
952
					}
953
					if (@rename($dir.DIRECTORY_SEPARATOR.$name, $target)) {
954
						$result[] = $target;
955
					}
956
				}
957
				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...
958
					$this->delTree($dir);
959
					return false;
960
				}
961
			}
962
			
963
			is_dir($dir) && $this->delTree($dir);
964
			
965
			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...
966
		}
967
	}
968
	
969
	/**
970
	 * Create archive and return its path
971
	 *
972
	 * @param  string  $dir    target dir
973
	 * @param  array   $files  files names list
974
	 * @param  string  $name   archive name
975
	 * @param  array   $arc    archiver options
976
	 * @return string|bool
977
	 * @author Dmitry (dio) Levashov, 
978
	 * @author Alexey Sukhotin
979
	 **/
980
	protected function _archive($dir, $files, $name, $arc) {
981
		return $this->makeArchive($dir, $files, $name, $arc);
982
	}
983
	
984
	/******************** Over write functions *************************/
985
	
986
	/**
987
	 * File path of local server side work file path
988
	 *
989
	 * @param  string $path
990
	 * @return string
991
	 * @author Naoki Sawada
992
	 */
993
	protected function getWorkFile($path) {
994
		return $path;
995
	}
996
997
	/**
998
	 * Delete dirctory trees
999
	 *
1000
	 * @param string $localpath path need convert encoding to server encoding
1001
	 * @return boolean
1002
	 * @author Naoki Sawada
1003
	 */
1004
	protected function delTree($localpath) {
1005
		return $this->rmdirRecursive($localpath);
1006
	}
1007
1008
	/******************** Over write (Optimized) functions *************************/
1009
	
1010
	/**
1011
	 * Recursive files search
1012
	 *
1013
	 * @param  string  $path   dir path
1014
	 * @param  string  $q      search string
1015
	 * @param  array   $mimes
1016
	 * @return array
1017
	 * @author Dmitry (dio) Levashov
1018
	 * @author Naoki Sawada
1019
	 **/
1020
	protected function doSearch($path, $q, $mimes) {
1021
		if ($this->encoding) {
1022
			// non UTF-8 has problem of glob() results
1023
			return parent::doSearch($path, $q, $mimes);
1024
		}
1025
		
1026
		static $escaper;
1027
		if (!$escaper) {
1028
			$escaper = array(
1029
				'['  => '\\[',
1030
				']'  => '\\]',
1031
				'*'  => '\\*',
1032
				'?'  => '\\?'
1033
			);
1034
		}
1035
		
1036
		$result = array();
1037
		
1038
		$path = strtr($path, $escaper);
1039
		$_q = strtr($q, $escaper);
1040
		$dirs = glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
1041
		$match = glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*'.$_q.'*', GLOB_NOSORT);
1042
		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...
1043
			foreach($match as $p) {
1044
				$stat = $this->stat($p);
1045
		
1046
				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...
1047
					continue;
1048
				}
1049
		
1050
				if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
1051
					continue;
1052
				}
1053
					
1054
				$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...
1055
		
1056
				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...
1057
					$stat['path'] = $this->path($stat['hash']);
1058
					if ($this->URL && !isset($stat['url'])) {
1059
						$path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
1060
						$stat['url'] = $this->URL . $path;
1061
					}
1062
		
1063
					$result[] = $stat;
1064
				}
1065
			}
1066
		}
1067
		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...
1068
			foreach($dirs as $dir) {
1069
				$stat = $this->stat($dir);
1070 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...
1071
					@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...
1072
					$result = array_merge($result, $this->doSearch($dir, $q, $mimes));
1073
				}
1074
			}
1075
		}
1076
	
1077
		return $result;
1078
	}
1079
	
1080
} // END class 
1081