Completed
Push — 2.x ( 6ff22e...55a998 )
by Naoki
04:44
created

elFinderVolumeLocalFileSystem::_abspath()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 12
rs 9.4286
cc 3
eloc 8
nc 3
nop 1
1
<?php
2
3
// Implement similar functionality in PHP 5.2 or 5.3
4
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
5
if (! class_exists('RecursiveCallbackFilterIterator', false)) {
6
	class RecursiveCallbackFilterIterator extends RecursiveFilterIterator {
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...
7
	   
8
	    public function __construct ( RecursiveIterator $iterator, $callback ) {
9
	        $this->callback = $callback;
0 ignored issues
show
Bug introduced by
The property callback 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...
10
	        parent::__construct($iterator);
11
	    }
12
	   
13
	    public function accept () {
14
	        $callback = $this->callback;
15
	        return $callback(parent::current(), parent::key(), parent::getInnerIterator());
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (current() instead of accept()). Are you sure this is correct? If so, you might want to change this to $this->current().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (key() instead of accept()). Are you sure this is correct? If so, you might want to change this to $this->key().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getInnerIterator() instead of accept()). Are you sure this is correct? If so, you might want to change this to $this->getInnerIterator().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
16
	    }
17
	   
18
	    public function getChildren () {
19
	        return new self($this->getInnerIterator()->getChildren(), $this->callback);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Iterator as the method getChildren() does only exist in the following implementations of said interface: ParentIterator, RecursiveArrayIterator, RecursiveCachingIterator, RecursiveCallbackFilterIterator, RecursiveDirectoryIterator, RecursiveFilterIterator, RecursiveRegexIterator, SimpleXMLIterator, SplFileObject, SplTempFileObject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
20
	    }
21
	}
22
}
23
24
/**
25
 * elFinder driver for local filesystem.
26
 *
27
 * @author Dmitry (dio) Levashov
28
 * @author Troex Nevelin
29
 **/
30
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...
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

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