elFinderVolumeLocalFileSystem   D
last analyzed

Complexity

Total Complexity 274

Size/Duplication

Total Lines 1296
Duplicated Lines 11.19 %

Coupling/Cohesion

Components 2
Dependencies 4

Importance

Changes 0
Metric Value
dl 145
loc 1296
rs 4.4102
c 0
b 0
f 0
wmc 274
lcom 2
cbo 4

39 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
D localFileSystemInotify() 0 38 9
B localFileSystemSearchIteratorFilter() 10 20 8
C init() 0 37 13
F configure() 5 75 24
A _dirname() 0 4 1
A _basename() 0 4 1
A _joinPath() 0 4 1
F _normpath() 18 57 20
A _relpath() 13 13 3
A _abspath() 0 13 3
A _path() 0 4 2
A _inpath() 0 11 4
F _stat() 3 72 23
D getOwnerStat() 18 42 10
C _subdirs() 0 36 8
A _dimensions() 0 8 4
A readlink() 0 16 4
F _scandir() 3 99 27
A _fopen() 0 4 1
A _fclose() 0 4 2
A _mkdir() 13 13 2
A _mkfile() 14 14 2
A _symlink() 0 4 1
A _copy() 11 11 4
B _move() 11 11 5
A _unlink() 0 7 2
A _rmdir() 0 7 2
C _save() 0 37 15
A _getContents() 0 4 1
A _filePutContents() 0 10 2
A _checkArchivers() 0 4 1
A _chmod() 0 8 3
A _findSymlinks() 0 4 1
F _extract() 13 105 32
A _archive() 0 4 1
A getWorkFile() 0 4 1
A delTree() 0 4 1
D doSearch() 13 81 29

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complex Class

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

Complex classes like elFinderVolumeLocalFileSystem often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use elFinderVolumeLocalFileSystem, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
// 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: 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