Completed
Push — master ( 61c24d...075a0d )
by recca
07:28
created

elFinderVolumeLocalFileSystem::_findSymlinks()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
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
        {
10
            $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...
11
            parent::__construct($iterator);
12
        }
13
14
        public function accept()
15
        {
16
            return call_user_func($this->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...
17
        }
18
19
        public function getChildren()
20
        {
21
            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: DoctrineTest\InstantiatorTestAsset\PharAsset, PHPUnit\Framework\TestSuiteIterator, PHPUnit\Runner\Filter\ExcludeGroupFilterIterator, PHPUnit\Runner\Filter\GroupFilterIterator, PHPUnit\Runner\Filter\IncludeGroupFilterIterator, PHPUnit\Runner\Filter\NameFilterIterator, ParentIterator, Phar, PharData, RecursiveArrayIterator, RecursiveCachingIterator, RecursiveCallbackFilterIterator, RecursiveCallbackFilterIterator, RecursiveDirectoryIterator, RecursiveFilterIterator, RecursiveRegexIterator, SebastianBergmann\CodeCoverage\Node\Iterator, SimpleXMLIterator, SplFileObject, SplTempFileObject, Symfony\Component\Finder...DirectoryFilterIterator, Symfony\Component\Finder...ursiveDirectoryIterator.

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...
22
        }
23
    }
24
}
25
26
/**
27
 * elFinder driver for local filesystem.
28
 *
29
 * @author Dmitry (dio) Levashov
30
 * @author Troex Nevelin
31
 **/
32
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
0 ignored issues
show
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...
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...
33
{
34
    /**
35
	 * Driver id
36
	 * Must be started from letter and contains [a-z0-9]
37
	 * Used as part of volume id.
38
	 *
39
	 * @var string
40
	 **/
41
	protected $driverId = 'l';
42
43
	/**
44
	 * Required to count total archive files size.
45
	 *
46
	 * @var int
47
	 **/
48
	protected $archiveSize = 0;
49
50
	/**
51
	 * Constructor
52
	 * Extend options with required fields.
53
	 *
54
	 * @author Dmitry (dio) Levashov
55
	 */
56
	public function __construct()
57
	{
58
	    $this->options['alias'] = '';              // alias to replace root dir name
59
		$this->options['dirMode'] = 0755;            // new dirs mode
60
		$this->options['fileMode'] = 0644;            // new files mode
61
		$this->options['quarantine'] = '.quarantine'; // quarantine folder name - required to check archive (must be hidden)
62
		$this->options['rootCssClass'] = 'elfinder-navbar-root-local';
63
	    $this->options['followSymLinks'] = true;
64
	    $this->options['detectDirIcon'] = '';         // file name that is detected as a folder icon e.g. '.diricon.png'
65
		$this->options['keepTimestamp'] = ['copy', 'move']; // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload'
66
	}
67
68
	/**
69
	 * Long pooling sync checker
70
	 * This function require server command `inotifywait`
71
	 * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php.
72
	 *
73
	 * @param string     $path
74
	 * @param int        $standby
75
	 * @param number     $compare
76
	 * @return number|bool
77
	 */
78
	public function localFileSystemInotify($path, $standby, $compare)
79
	{
80
	    if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
81
	        return false;
82
	    }
83
	    $path = realpath($path);
84
	    $mtime = filemtime($path);
85
	    if (! $mtime) {
86
	        return false;
87
	    }
88
	    if ($mtime != $compare) {
89
	        return $mtime;
90
	    }
91
	    $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
92
	    $standby = max(1, intval($standby));
93
	    $cmd = $inotifywait.' '.escapeshellarg($path).' -t '.$standby.' -e moved_to,moved_from,move,close_write,delete,delete_self';
94
	    $this->procExec($cmd, $o, $r);
95
	    if ($r === 0) {
96
	        // changed
97
			clearstatcache();
98
	        if (file_exists($path)) {
99
	            $mtime = filemtime($path); // error on busy?
100
				return $mtime ? $mtime : time();
101
	        } else {
102
	            // target was removed
103
				return 0;
104
	        }
105
	    } elseif ($r === 2) {
106
	        // not changed (timeout)
107
			return $compare;
108
	    }
109
		// error
110
		// cache to $_SESSION
111
		$this->sessionCache['localFileSystemInotify_disable'] = true;
112
	    $this->session->set($this->id, $this->sessionCache, true);
0 ignored issues
show
Unused Code introduced by
The call to elFinderSessionInterface::set() has too many arguments starting with true.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

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