elFinderVolumeFTP   D
last analyzed

Complexity

Total Complexity 325

Size/Duplication

Total Lines 1700
Duplicated Lines 8.82 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 0
Metric Value
dl 150
loc 1700
rs 4.4102
c 0
b 0
f 0
wmc 325
lcom 2
cbo 2

46 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 22 1
B netmountPrepare() 0 14 5
A umount() 0 4 2
C init() 9 51 10
D configure() 5 30 10
C connect() 8 56 11
A ftpRawList() 0 15 4
F parseRaw() 9 82 28
A normalizeRawWindows() 0 19 3
D parsePermissions() 0 16 14
F cacheDir() 12 80 20
A ftpMode() 0 4 2
A _dirname() 0 7 1
A _basename() 0 6 1
A _joinPath() 0 4 1
D _normpath() 18 46 16
A _relpath() 13 13 3
A _abspath() 0 13 3
A _path() 0 4 2
A _inpath() 0 4 2
F _stat() 23 166 48
B _subdirs() 3 18 9
A _dimensions() 0 9 2
A _scandir() 0 12 3
C _fopen() 0 31 8
A _fclose() 0 7 2
A _mkdir() 0 11 3
A _mkfile() 0 13 4
A _symlink() 0 4 1
A _copy() 4 17 4
A _move() 0 6 2
A _unlink() 0 4 1
A _rmdir() 0 4 1
A _save() 0 8 2
A _getContents() 0 14 3
B _filePutContents() 0 18 5
A _checkArchivers() 0 4 1
A _chmod() 0 6 2
A _findSymlinks() 0 4 1
F _extract() 43 154 39
B _archive() 0 33 6
C ftp_scan_dir() 3 42 12
B tempDir() 0 23 4
B ftp_download_files() 0 31 6
C deleteDir() 0 31 8
D listFilesInDirectory() 0 34 9

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

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 11 and the first side effect is on line 3.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
elFinder::$netDrivers['ftp'] = 'FTP';
4
5
/**
6
 * Simple elFinder driver for FTP.
7
 *
8
 * @author Dmitry (dio) Levashov
9
 * @author Cem (discofever)
10
 **/
11
class elFinderVolumeFTP extends elFinderVolumeDriver
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

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

namespace YourVendor;

class YourClass { }

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

Loading history...
12
{
13
    /**
14
     * Driver id
15
     * Must be started from letter and contains [a-z0-9]
16
     * Used as part of volume id.
17
     *
18
     * @var string
19
     **/
20
    protected $driverId = 'f';
21
22
    /**
23
     * FTP Connection Instance.
24
     *
25
     * @var ftp
26
     **/
27
    protected $connect = null;
28
29
    /**
30
     * Directory for tmp files
31
     * If not set driver will try to use tmbDir as tmpDir.
32
     *
33
     * @var string
34
     **/
35
    protected $tmpPath = '';
36
37
    /**
38
     * Last FTP error message.
39
     *
40
     * @var string
41
     **/
42
    protected $ftpError = '';
43
44
    /**
45
     * FTP server output list as ftp on linux.
46
     *
47
     * @var bool
48
     **/
49
    protected $ftpOsUnix;
50
51
    /**
52
     * FTP LIST command option.
53
     *
54
     * @var string
55
     */
56
    protected $ftpListOption = '-al';
57
58
    /**
59
     * Is connected server Pure FTPd?
60
     *
61
     * @var bool
62
     */
63
    protected $isPureFtpd = false;
64
65
    /**
66
     * Tmp folder path.
67
     *
68
     * @var string
69
     **/
70
    protected $tmp = '';
71
72
    /**
73
     * FTP command `MLST` support.
74
     *
75
     * @var bool
76
     */
77
    private $MLSTsupprt = false;
78
79
    /**
80
     * Calling cacheDir() target path with non-MLST.
81
     *
82
     * @var string
83
     */
84
    private $cacheDirTarget = '';
85
86
    /**
87
     * Constructor
88
     * Extend options with required fields.
89
     *
90
     * @author Dmitry (dio) Levashov
91
     * @author Cem (DiscoFever)
92
     */
93
    public function __construct()
94
    {
95
        $opts = [
96
            'host' => 'localhost',
97
            'user' => '',
98
            'pass' => '',
99
            'port' => 21,
100
            'mode' => 'passive',
101
            'path' => '/',
102
            'timeout' => 20,
103
            'owner' => true,
104
            'tmbPath' => '',
105
            'tmpPath' => '',
106
            'separator' => '/',
107
            'dirMode' => 0755,
108
            'fileMode' => 0644,
109
            'rootCssClass' => 'elfinder-navbar-root-ftp',
110
            'ftpListOption' => '-al',
111
        ];
112
        $this->options = array_merge($this->options, $opts);
113
        $this->options['mimeDetect'] = 'internal';
114
    }
115
116
    /**
117
     * Prepare
118
     * Call from elFinder::netmout() before volume->mount().
119
     *
120
     * @param $options
121
     * @return array
122
     * @author Naoki Sawada
123
     */
124
    public function netmountPrepare($options)
0 ignored issues
show
Coding Style introduced by
netmountPrepare uses the super-global variable $_REQUEST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
125
    {
126
        if (! empty($_REQUEST['encoding']) && iconv('UTF-8', $_REQUEST['encoding'], '') !== false) {
127
            $options['encoding'] = $_REQUEST['encoding'];
128
            if (! empty($_REQUEST['locale']) && setlocale(LC_ALL, $_REQUEST['locale'])) {
129
                setlocale(LC_ALL, elFinder::$locale);
130
                $options['locale'] = $_REQUEST['locale'];
131
            }
132
        }
133
        $options['statOwner'] = true;
134
        $options['allowChmodReadOnly'] = true;
135
136
        return $options;
137
    }
138
139
    /*********************************************************************/
140
    /*                               FS API                              */
141
    /*********************************************************************/
142
143
    /**
144
     * Close opened connection.
145
     *
146
     * @return void
147
     * @author Dmitry (dio) Levashov
148
     **/
149
    public function umount()
150
    {
151
        $this->connect && ftp_close($this->connect);
152
    }
153
154
    /*********************************************************************/
155
    /*                        INIT AND CONFIGURE                         */
156
    /*********************************************************************/
157
158
    /**
159
     * Prepare FTP connection
160
     * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn.
161
     *
162
     * @return bool
163
     * @author Dmitry (dio) Levashov
164
     * @author Cem (DiscoFever)
165
     **/
166
    protected function init()
167
    {
168 View Code Duplication
        if (! $this->options['host']
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...
169
        || ! $this->options['port']) {
170
            return $this->setError('Required options undefined.');
171
        }
172
173
        if (! $this->options['user']) {
174
            $this->options['user'] = 'anonymous';
175
            $this->options['pass'] = '';
176
        }
177
        if (! $this->options['path']) {
178
            $this->options['path'] = '/';
179
        }
180
181
        // make ney mount key
182
        $this->netMountKey = md5(implode('-', ['ftp', $this->options['host'], $this->options['port'], $this->options['path'], $this->options['user']]));
183
184
        if (! function_exists('ftp_connect')) {
185
            return $this->setError('FTP extension not loaded.');
186
        }
187
188
        // remove protocol from host
189
        $scheme = parse_url($this->options['host'], PHP_URL_SCHEME);
190
191
        if ($scheme) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $scheme of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
192
            $this->options['host'] = substr($this->options['host'], strlen($scheme) + 3);
193
        }
194
195
        // normalize root path
196
        $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
197
198 View Code Duplication
        if (empty($this->options['alias'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
199
            $this->options['alias'] = $this->options['user'].'@'.$this->options['host'];
200
            // $num = elFinder::$volumesCnt-1;
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% 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...
201
            // $this->options['alias'] = $this->root == '/' || $this->root == '.' ? 'FTP folder '.$num : basename($this->root);
0 ignored issues
show
Unused Code Comprehensibility introduced by
49% 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...
202
        }
203
204
        $this->rootName = $this->options['alias'];
205
        $this->options['separator'] = '/';
206
207
        if (is_null($this->options['syncChkAsTs'])) {
208
            $this->options['syncChkAsTs'] = true;
209
        }
210
211
        if (isset($this->options['ftpListOption'])) {
212
            $this->ftpListOption = $this->options['ftpListOption'];
213
        }
214
215
        return $this->connect();
216
    }
217
218
    /**
219
     * Configure after successfull mount.
220
     *
221
     * @return void
222
     * @author Dmitry (dio) Levashov
223
     **/
224
    protected function configure()
225
    {
226
        parent::configure();
227
228 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...
229
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) {
230
                $this->tmp = $this->options['tmpPath'];
231
            }
232
        }
233
        if (! $this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
234
            $this->tmp = $tmp;
235
        }
236
237
        // fallback of $this->tmp
238
        if (! $this->tmp && $this->tmbPathWritable) {
239
            $this->tmp = $this->tmbPath;
240
        }
241
242
        if (! $this->tmp) {
243
            $this->disabled[] = 'mkfile';
244
            $this->disabled[] = 'paste';
245
            $this->disabled[] = 'duplicate';
246
            $this->disabled[] = 'upload';
247
            $this->disabled[] = 'edit';
248
            $this->disabled[] = 'archive';
249
            $this->disabled[] = 'extract';
250
        }
251
252
        // echo $this->tmp;
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
253
    }
254
255
    /**
256
     * Connect to ftp server.
257
     *
258
     * @return bool
259
     * @author Dmitry (dio) Levashov
260
     **/
261
    protected function connect()
262
    {
263 View Code Duplication
        if (! ($this->connect = ftp_connect($this->options['host'], $this->options['port'], $this->options['timeout']))) {
0 ignored issues
show
Documentation Bug introduced by
It seems like ftp_connect($this->optio...is->options['timeout']) of type resource is incompatible with the declared type object<ftp> of property $connect.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

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...
264
            return $this->setError('Unable to connect to FTP server '.$this->options['host']);
265
        }
266 View Code Duplication
        if (! ftp_login($this->connect, $this->options['user'], $this->options['pass'])) {
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...
267
            $this->umount();
268
269
            return $this->setError('Unable to login into '.$this->options['host']);
270
        }
271
272
        // try switch utf8 mode
273
        if ($this->encoding) {
274
            ftp_raw($this->connect, 'OPTS UTF8 OFF');
275
        } else {
276
            ftp_raw($this->connect, 'OPTS UTF8 ON');
277
        }
278
279
        // switch off extended passive mode - may be usefull for some servers
280
        ftp_raw($this->connect, 'epsv4 off');
281
        // enter passive mode if required
282
        $pasv = ($this->options['mode'] == 'passive');
283
        if (! ftp_pasv($this->connect, $pasv)) {
284
            if ($pasv) {
285
                $this->options['mode'] = 'active';
286
            }
287
        }
288
289
        // enter root folder
290
        if (! ftp_chdir($this->connect, $this->root)
291
        || $this->root != ftp_pwd($this->connect)) {
292
            $this->umount();
293
294
            return $this->setError('Unable to open root folder.');
295
        }
296
297
        // check for MLST support
298
        $features = ftp_raw($this->connect, 'FEAT');
299
        if (! is_array($features)) {
300
            $this->umount();
301
302
            return $this->setError('Server does not support command FEAT.');
303
        }
304
305
        foreach ($features as $feat) {
306
            if (strpos(trim($feat), 'MLST') === 0) {
307
                $this->MLSTsupprt = true;
308
                break;
309
            }
310
        }
311
312
        $help = ftp_raw($this->connect, 'HELP');
313
        $this->isPureFtpd = stripos(implode(' ', $help), 'Pure-FTPd') !== false;
314
315
        return true;
316
    }
317
318
    /**
319
     * Call ftp_rawlist with option prefix.
320
     *
321
     * @param string $path
322
     * @return array
323
     */
324
    protected function ftpRawList($path)
325
    {
326
        if ($this->isPureFtpd) {
327
            $path = str_replace(' ', '\ ', $path);
328
        }
329
        if ($this->ftpListOption) {
330
            $path = $this->ftpListOption.' '.$path;
331
        }
332
        $res = ftp_rawlist($this->connect, $path);
333
        if ($res === false) {
334
            $res = [];
335
        }
336
337
        return $res;
338
    }
339
340
    /**
341
     * Parse line from ftp_rawlist() output and return file stat (array).
342
     *
343
     * @param  string $raw line from ftp_rawlist() output
344
     * @param $base
345
     * @param bool $nameOnly
346
     * @return array
347
     * @author Dmitry Levashov
348
     */
349
    protected function parseRaw($raw, $base, $nameOnly = false)
350
    {
351
        static $now;
352
        static $lastyear;
353
354
        if (! $now) {
355
            $now = time();
356
            $lastyear = date('Y') - 1;
357
        }
358
359
        $info = preg_split("/\s+/", $raw, 9);
360
        $stat = [];
361
362 View Code Duplication
        if (! isset($this->ftpOsUnix)) {
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...
363
            $this->ftpOsUnix = ! preg_match('/\d/', substr($info[0], 0, 1));
364
        }
365
        if (! $this->ftpOsUnix) {
366
            $info = $this->normalizeRawWindows($raw);
367
        }
368
369
        if (count($info) < 9 || $info[8] == '.' || $info[8] == '..') {
370
            return false;
371
        }
372
373
        $name = $info[8];
374
375
        if (preg_match('|(.+)\-\>(.+)|', $name, $m)) {
376
            $name = trim($m[1]);
377
            // check recursive processing
378
            if ($this->cacheDirTarget && $this->_joinPath($base, $name) !== $this->cacheDirTarget) {
379
                return [];
380
            }
381
            if (! $nameOnly) {
382
                $target = trim($m[2]);
383
                if (substr($target, 0, 1) !== $this->separator) {
384
                    $target = $this->getFullPath($target, $base);
385
                }
386
                $target = $this->_normpath($target);
387
                $stat['name'] = $name;
388
                $stat['target'] = $target;
389
390
                return $stat;
391
            }
392
        }
393
394
        if ($nameOnly) {
395
            return ['name' => $name];
396
        }
397
398
        if (is_numeric($info[5]) && ! $info[6] && ! $info[7]) {
399
            // by normalizeRawWindows()
400
            $stat['ts'] = $info[5];
401
        } else {
402
            $stat['ts'] = strtotime($info[5].' '.$info[6].' '.$info[7]);
403 View Code Duplication
            if ($stat['ts'] && $stat['ts'] > $now && strpos($info[7], ':') !== false) {
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...
404
                $stat['ts'] = strtotime($info[5].' '.$info[6].' '.$lastyear.' '.$info[7]);
405
            }
406
            if (empty($stat['ts'])) {
407
                $stat['ts'] = strtotime($info[6].' '.$info[5].' '.$info[7]);
408 View Code Duplication
                if ($stat['ts'] && $stat['ts'] > $now && strpos($info[7], ':') !== false) {
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...
409
                    $stat['ts'] = strtotime($info[6].' '.$info[5].' '.$lastyear.' '.$info[7]);
410
                }
411
            }
412
        }
413
414
        if ($this->options['statOwner']) {
415
            $stat['owner'] = $info[2];
416
            $stat['group'] = $info[3];
417
            $stat['perm'] = substr($info[0], 1);
418
            $stat['isowner'] = $stat['owner'] ? ($stat['owner'] == $this->options['user']) : $this->options['owner'];
419
        }
420
        $owner = isset($stat['owner']) ? $stat['owner'] : '';
421
422
        $perm = $this->parsePermissions($info[0], $owner);
423
        $stat['name'] = $name;
424
        $stat['mime'] = substr(strtolower($info[0]), 0, 1) == 'd' ? 'directory' : $this->mimetype($stat['name'], true);
425
        $stat['size'] = $stat['mime'] == 'directory' ? 0 : $info[4];
426
        $stat['read'] = $perm['read'];
427
        $stat['write'] = $perm['write'];
428
429
        return $stat;
430
    }
431
432
    /**
433
     * Normalize MS-DOS style FTP LIST Raw line.
434
     *
435
     * @param  string  $raw  line from FTP LIST (MS-DOS style)
436
     * @return array
437
     * @author Naoki Sawada
438
     **/
439
    protected function normalizeRawWindows($raw)
440
    {
441
        $info = array_pad([], 9, '');
442
        $item = preg_replace('#\s+#', ' ', trim($raw), 3);
443
        list($date, $time, $size, $name) = explode(' ', $item, 4);
444
        $format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i';
445
        $dateObj = DateTime::createFromFormat($format, $date.$time);
446
        $info[5] = strtotime($dateObj->format('Y-m-d H:i'));
447
        $info[8] = $name;
448
        if ($size === '<DIR>') {
449
            $info[4] = 0;
450
            $info[0] = 'drwxr-xr-x';
451
        } else {
452
            $info[4] = (int) $size;
453
            $info[0] = '-rw-r--r--';
454
        }
455
456
        return $info;
457
    }
458
459
    /**
460
     * Parse permissions string. Return array(read => true/false, write => true/false).
461
     *
462
     * @param  string $perm permissions string
463
     * @param string $user
464
     * @return string
465
     * @author Dmitry (dio) Levashov
466
     */
467
    protected function parsePermissions($perm, $user = '')
468
    {
469
        $res = [];
0 ignored issues
show
Unused Code introduced by
$res is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
470
        $parts = [];
471
        $owner = $user ? ($user == $this->options['user']) : $this->options['owner'];
472
        for ($i = 0, $l = strlen($perm); $i < $l; $i++) {
473
            $parts[] = substr($perm, $i, 1);
474
        }
475
476
        $read = ($owner && $parts[1] == 'r') || $parts[4] == 'r' || $parts[7] == 'r';
477
478
        return [
479
            'read' => $parts[0] == 'd' ? $read && (($owner && $parts[3] == 'x') || $parts[6] == 'x' || $parts[9] == 'x') : $read,
480
            'write' => ($owner && $parts[2] == 'w') || $parts[5] == 'w' || $parts[8] == 'w',
481
        ];
482
    }
483
484
    /**
485
     * Cache dir contents.
486
     *
487
     * @param  string  $path  dir path
488
     * @return void
489
     * @author Dmitry Levashov
490
     **/
491
    protected function cacheDir($path)
492
    {
493
        $this->dirsCache[$path] = [];
494
        $hasDir = false;
495
496
        $list = [];
497
        $encPath = $this->convEncIn($path);
498
        foreach ($this->ftpRawList($encPath) as $raw) {
499
            if (($stat = $this->parseRaw($raw, $encPath))) {
500
                $list[] = $stat;
501
            }
502
        }
503
        $list = $this->convEncOut($list);
504
        $prefix = ($path === $this->separator) ? $this->separator : $path.$this->separator;
505
        $targets = [];
506
        foreach ($list as $stat) {
507
            $p = $prefix.$stat['name'];
508 View Code Duplication
            if (isset($stat['target'])) {
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...
509
                // stat later
510
                $targets[$stat['name']] = $stat['target'];
511
            } else {
512
                $stat = $this->updateCache($p, $stat);
513
                if (empty($stat['hidden'])) {
514
                    if (! $hasDir && $stat['mime'] === 'directory') {
515
                        $hasDir = true;
516
                    }
517
                    $this->dirsCache[$path][] = $p;
518
                }
519
            }
520
        }
521
        // stat link targets
522
        foreach ($targets as $name => $target) {
523
            $stat = [];
524
            $stat['name'] = $name;
525
            $p = $prefix.$name;
526
            $cacheDirTarget = $this->cacheDirTarget;
527
            $this->cacheDirTarget = $this->convEncIn($target, true);
528
            if ($tstat = $this->stat($target)) {
529
                $stat['size'] = $tstat['size'];
530
                $stat['alias'] = $target;
531
                $stat['thash'] = $tstat['hash'];
532
                $stat['mime'] = $tstat['mime'];
533
                $stat['read'] = $tstat['read'];
534
                $stat['write'] = $tstat['write'];
535
536
                if (isset($tstat['ts'])) {
537
                    $stat['ts'] = $tstat['ts'];
538
                }
539
                if (isset($tstat['owner'])) {
540
                    $stat['owner'] = $tstat['owner'];
541
                }
542
                if (isset($tstat['group'])) {
543
                    $stat['group'] = $tstat['group'];
544
                }
545
                if (isset($tstat['perm'])) {
546
                    $stat['perm'] = $tstat['perm'];
547
                }
548
                if (isset($tstat['isowner'])) {
549
                    $stat['isowner'] = $tstat['isowner'];
550
                }
551
            } else {
552
                $stat['mime'] = 'symlink-broken';
553
                $stat['read'] = false;
554
                $stat['write'] = false;
555
                $stat['size'] = 0;
556
            }
557
            $this->cacheDirTarget = $cacheDirTarget;
558
            $stat = $this->updateCache($p, $stat);
559
            if (empty($stat['hidden'])) {
560
                if (! $hasDir && $stat['mime'] === 'directory') {
561
                    $hasDir = true;
562
                }
563
                $this->dirsCache[$path][] = $p;
564
            }
565
        }
566
567
        if (isset($this->sessionCache['subdirs'])) {
568
            $this->sessionCache['subdirs'][$path] = $hasDir;
569
        }
570
    }
571
572
    /**
573
     * Return ftp transfer mode for file.
574
     *
575
     * @param  string  $path  file path
576
     * @return string
577
     * @author Dmitry (dio) Levashov
578
     **/
579
    protected function ftpMode($path)
580
    {
581
        return strpos($this->mimetype($path), 'text/') === 0 ? FTP_ASCII : FTP_BINARY;
582
    }
583
584
    /*********************** paths/urls *************************/
585
586
    /**
587
     * Return parent directory path.
588
     *
589
     * @param  string  $path  file path
590
     * @return string
591
     * @author Naoki Sawada
592
     **/
593
    protected function _dirname($path)
594
    {
595
        $parts = explode($this->separator, trim($path, $this->separator));
596
        array_pop($parts);
597
598
        return $this->separator.implode($this->separator, $parts);
599
    }
600
601
    /**
602
     * Return file name.
603
     *
604
     * @param  string  $path  file path
605
     * @return string
606
     * @author Naoki Sawada
607
     **/
608
    protected function _basename($path)
609
    {
610
        $parts = explode($this->separator, trim($path, $this->separator));
611
612
        return array_pop($parts);
613
    }
614
615
    /**
616
     * Join dir name and file name and retur full path.
617
     *
618
     * @param  string  $dir
619
     * @param  string  $name
620
     * @return string
621
     * @author Dmitry (dio) Levashov
622
     **/
623
    protected function _joinPath($dir, $name)
624
    {
625
        return rtrim($dir, $this->separator).$this->separator.$name;
626
    }
627
628
    /**
629
     * Return normalized path, this works the same as os.path.normpath() in Python.
630
     *
631
     * @param  string  $path  path
632
     * @return string
633
     * @author Troex Nevelin
634
     **/
635
    protected function _normpath($path)
636
    {
637
        if (empty($path)) {
638
            $path = '.';
639
        }
640
        // path must be start with /
641
        $path = preg_replace('|^\.\/?|', $this->separator, $path);
642
        $path = preg_replace('/^([^\/])/', '/$1', $path);
643
644
        if ($path[0] === $this->separator) {
645
            $initial_slashes = true;
646
        } else {
647
            $initial_slashes = false;
648
        }
649
650 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...
651
        && (strpos($path, '//') === 0)
652
        && (strpos($path, '///') === false)) {
653
            $initial_slashes = 2;
654
        }
655
656
        $initial_slashes = (int) $initial_slashes;
657
658
        $comps = explode($this->separator, $path);
659
        $new_comps = [];
660 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...
661
            if (in_array($comp, ['', '.'])) {
662
                continue;
663
            }
664
665
            if (($comp != '..')
666
            || (! $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...
667
            || ($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...
668
                array_push($new_comps, $comp);
669
            } 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...
670
                array_pop($new_comps);
671
            }
672
        }
673
        $comps = $new_comps;
674
        $path = implode($this->separator, $comps);
675
        if ($initial_slashes) {
676
            $path = str_repeat($this->separator, $initial_slashes).$path;
677
        }
678
679
        return $path ? $path : '.';
680
    }
681
682
    /**
683
     * Return file path related to root dir.
684
     *
685
     * @param  string  $path  file path
686
     * @return string
687
     * @author Dmitry (dio) Levashov
688
     **/
689 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...
690
    {
691
        if ($path === $this->root) {
692
            return '';
693
        } else {
694
            if (strpos($path, $this->root) === 0) {
695
                return ltrim(substr($path, strlen($this->root)), $this->separator);
696
            } else {
697
                // for link
698
                return $path;
699
            }
700
        }
701
    }
702
703
    /**
704
     * Convert path related to root dir into real path.
705
     *
706
     * @param  string  $path  file path
707
     * @return string
708
     * @author Dmitry (dio) Levashov
709
     **/
710
    protected function _abspath($path)
711
    {
712
        if ($path === $this->separator) {
713
            return $this->root;
714
        } else {
715
            if ($path[0] === $this->separator) {
716
                // for link
717
                return $path;
718
            } else {
719
                return $this->_joinPath($this->root, $path);
720
            }
721
        }
722
    }
723
724
    /**
725
     * Return fake path started from root dir.
726
     *
727
     * @param  string  $path  file path
728
     * @return string
729
     * @author Dmitry (dio) Levashov
730
     **/
731
    protected function _path($path)
732
    {
733
        return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path));
734
    }
735
736
    /**
737
     * Return true if $path is children of $parent.
738
     *
739
     * @param  string  $path    path to check
740
     * @param  string  $parent  parent path
741
     * @return bool
742
     * @author Dmitry (dio) Levashov
743
     **/
744
    protected function _inpath($path, $parent)
745
    {
746
        return $path == $parent || strpos($path, $parent.$this->separator) === 0;
747
    }
748
749
    /***************** file stat ********************/
750
751
    /**
752
     * Return stat for given path.
753
     * Stat contains following fields:
754
     * - (int)    size    file size in b. required
755
     * - (int)    ts      file modification time in unix time. required
756
     * - (string) mime    mimetype. required for folders, others - optionally
757
     * - (bool)   read    read permissions. required
758
     * - (bool)   write   write permissions. required
759
     * - (bool)   locked  is object locked. optionally
760
     * - (bool)   hidden  is object hidden. optionally
761
     * - (string) alias   for symlinks - link target path relative to root path. optionally
762
     * - (string) target  for symlinks - link target path. optionally.
763
     *
764
     * If file does not exists - returns empty array or false.
765
     *
766
     * @param  string  $path    file path
767
     * @return array|false
768
     * @author Dmitry (dio) Levashov
769
     **/
770
    protected function _stat($path)
771
    {
772
        $outPath = $this->convEncOut($path);
773
        if (isset($this->cache[$outPath])) {
774
            return $this->convEncIn($this->cache[$outPath]);
775
        } else {
776
            $this->convEncIn();
777
        }
778
        if (! $this->MLSTsupprt) {
779
            if ($path === $this->root) {
780
                $res = [
781
                    'name' => $this->root,
782
                    'mime' => 'directory',
783
                    'dirs' => $this->_subdirs($path),
784
                ];
785
                if ($this->isMyReload()) {
786
                    $ts = 0;
787
                    foreach ($this->ftpRawList($path) as $str) {
788
                        if (($stat = $this->parseRaw($str, $path))) {
789
                            $ts = max($ts, $stat['ts']);
790
                        }
791
                    }
792
                    if ($ts) {
793
                        $res['ts'] = $ts;
794
                    }
795
                }
796
797
                return $res;
798
            }
799
            // stat of system root
800
            if ($path === $this->separator) {
801
                $res = [
802
                    'name' => $this->separator,
803
                    'mime' => 'directory',
804
                    'dirs' => 1,
805
                ];
806
                $this->cache[$outPath] = $res;
807
808
                return $res;
809
            }
810
            $parentSubdirs = null;
811
            $outParent = $this->convEncOut($this->_dirname($path));
812 View Code Duplication
            if (isset($this->sessionCache['subdirs']) && isset($this->sessionCache['subdirs'][$outParent])) {
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...
813
                $parentSubdirs = $this->sessionCache['subdirs'][$outParent];
814
            }
815
            $this->cacheDir($outParent);
816
            if ($parentSubdirs) {
817
                $this->sessionCache['subdirs'][$outParent] = $parentSubdirs;
818
            }
819
820
            $stat = $this->convEncIn(isset($this->cache[$outPath]) ? $this->cache[$outPath] : []);
821
            if (! $this->mounted) {
822
                // dispose incomplete cache made by calling `stat` by 'startPath' option
823
                $this->cache = [];
824
            }
825
826
            return $stat;
827
        }
828
        $raw = ftp_raw($this->connect, 'MLST '.$path);
829
        if (is_array($raw) && count($raw) > 1 && substr(trim($raw[0]), 0, 1) == 2) {
830
            $parts = explode(';', trim($raw[1]));
831
            array_pop($parts);
832
            $parts = array_map('strtolower', $parts);
833
            $stat = [];
834
            $mode = '';
835
            foreach ($parts as $part) {
836
                list($key, $val) = explode('=', $part);
837
838
                switch ($key) {
839
                    case 'type':
840
                        $stat['mime'] = strpos($val, 'dir') !== false ? 'directory' : $this->mimetype($path);
841
                        break;
842
843
                    case 'size':
844
                        $stat['size'] = $val;
845
                        break;
846
847
                    case 'modify':
848
                        $ts = mktime(intval(substr($val, 8, 2)), intval(substr($val, 10, 2)), intval(substr($val, 12, 2)), intval(substr($val, 4, 2)), intval(substr($val, 6, 2)), substr($val, 0, 4));
849
                        $stat['ts'] = $ts;
850
                        break;
851
852
                    case 'unix.mode':
853
                        $mode = strval($val);
854
                        break;
855
856
                    case 'unix.uid':
857
                        $stat['owner'] = $val;
858
                        break;
859
860
                    case 'unix.gid':
861
                        $stat['group'] = $val;
862
                        break;
863
864
                    case 'perm':
865
                        $val = strtolower($val);
866
                        $stat['read'] = (int) preg_match('/e|l|r/', $val);
867
                        $stat['write'] = (int) preg_match('/w|m|c/', $val);
868
                        if (! preg_match('/f|d/', $val)) {
869
                            $stat['locked'] = 1;
870
                        }
871
                        break;
872
                }
873
            }
874
            if (empty($stat['mime'])) {
875
                return [];
876
            }
877
            if ($stat['mime'] == 'directory') {
878
                $stat['size'] = 0;
879
            }
880
881
            if ($mode) {
882
                $stat['perm'] = '';
883
                if ($mode[0] === '0') {
884
                    $mode = substr($mode, 1);
885
                }
886
887
                $perm = [];
888
                for ($i = 0; $i <= 2; $i++) {
889
                    $perm[$i] = [false, false, false];
890
                    $n = isset($mode[$i]) ? $mode[$i] : 0;
891
892 View Code Duplication
                    if ($n - 4 >= 0) {
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...
893
                        $perm[$i][0] = true;
894
                        $n = $n - 4;
895
                        $stat['perm'] .= 'r';
896
                    } else {
897
                        $stat['perm'] .= '-';
898
                    }
899
900 View Code Duplication
                    if ($n - 2 >= 0) {
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...
901
                        $perm[$i][1] = true;
902
                        $n = $n - 2;
903
                        $stat['perm'] .= 'w';
904
                    } else {
905
                        $stat['perm'] .= '-';
906
                    }
907
908 View Code Duplication
                    if ($n - 1 == 0) {
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...
909
                        $perm[$i][2] = true;
910
                        $stat['perm'] .= 'x';
911
                    } else {
912
                        $stat['perm'] .= '-';
913
                    }
914
                }
915
916
                $stat['perm'] = trim($stat['perm']);
917
918
                $owner = $this->options['owner'];
919
                $read = ($owner && $perm[0][0]) || $perm[1][0] || $perm[2][0];
920
921
                $stat['read'] = $stat['mime'] == 'directory' ? $read && (($owner && $perm[0][2]) || $perm[1][2] || $perm[2][2]) : $read;
922
                $stat['write'] = ($owner && $perm[0][1]) || $perm[1][1] || $perm[2][1];
923
924
                if ($this->options['statOwner']) {
925
                    $stat['isowner'] = $owner;
926
                } else {
927
                    unset($stat['owner'], $stat['group'], $stat['perm']);
928
                }
929
            }
930
931
            return $stat;
932
        }
933
934
        return [];
935
    }
936
937
    /**
938
     * Return true if path is dir and has at least one childs directory.
939
     *
940
     * @param  string  $path  dir path
941
     * @return bool
942
     * @author Dmitry (dio) Levashov
943
     **/
944
    protected function _subdirs($path)
945
    {
946
        foreach ($this->ftpRawList($path) as $str) {
947
            $info = preg_split('/\s+/', $str, 9);
948 View Code Duplication
            if (! isset($this->ftpOsUnix)) {
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...
949
                $this->ftpOsUnix = ! preg_match('/\d/', substr($info[0], 0, 1));
950
            }
951
            if (! $this->ftpOsUnix) {
952
                $info = $this->normalizeRawWindows($str);
953
            }
954
            $name = isset($info[8]) ? trim($info[8]) : '';
955
            if ($name && $name !== '.' && $name !== '..' && substr(strtolower($info[0]), 0, 1) === 'd') {
956
                return true;
957
            }
958
        }
959
960
        return false;
961
    }
962
963
    /**
964
     * Return object width and height
965
     * Ususaly used for images, but can be realize for video etc...
966
     *
967
     * @param  string  $path  file path
968
     * @param  string  $mime  file mime type
969
     * @return string|false
970
     * @author Dmitry (dio) Levashov
971
     **/
972
    protected function _dimensions($path, $mime)
973
    {
974
        $ret = false;
975
        if ($imgsize = $this->getImageSize($path, $mime)) {
976
            $ret = $imgsize['dimensions'];
977
        }
978
979
        return $ret;
980
    }
981
982
    /******************** file/dir content *********************/
983
984
    /**
985
     * Return files list in directory.
986
     *
987
     * @param  string  $path  dir path
988
     * @return array
989
     * @author Dmitry (dio) Levashov
990
     * @author Cem (DiscoFever)
991
     **/
992
    protected function _scandir($path)
993
    {
994
        $files = [];
995
996
        foreach ($this->ftpRawList($path) as $str) {
997
            if (($stat = $this->parseRaw($str, $path, true))) {
998
                $files[] = $this->_joinPath($path, $stat['name']);
999
            }
1000
        }
1001
1002
        return $files;
1003
    }
1004
1005
    /**
1006
     * Open file and return file pointer.
1007
     *
1008
     * @param  string $path file path
1009
     * @param string $mode
1010
     * @return false|resource
1011
     * @internal param bool $write open file for writing
1012
     * @author Dmitry (dio) Levashov
1013
     */
1014
    protected function _fopen($path, $mode = 'rb')
1015
    {
1016
        // try ftp stream wrapper
1017
        if ($this->options['mode'] == 'passive' && ini_get('allow_url_fopen')) {
1018
            $url = 'ftp://'.$this->options['user'].':'.$this->options['pass'].'@'.$this->options['host'].':'.$this->options['port'].$path;
1019
            if (strtolower($mode[0]) === 'w') {
1020
                $context = stream_context_create(['ftp' => ['overwrite' => true]]);
1021
                $fp = fopen($url, $mode, false, $context);
1022
            } else {
1023
                $fp = fopen($url, $mode);
1024
            }
1025
            if ($fp) {
1026
                return $fp;
1027
            }
1028
        }
1029
1030
        if ($this->tmp) {
1031
            $local = $this->getTempFile($path);
1032
            $fp = fopen($local, 'wb');
1033
            if (ftp_fget($this->connect, $fp, $path, FTP_BINARY)) {
1034
                fclose($fp);
1035
                $fp = fopen($local, $mode);
1036
1037
                return $fp;
1038
            }
1039
            fclose($fp);
1040
            is_file($local) && unlink($local);
1041
        }
1042
1043
        return false;
1044
    }
1045
1046
    /**
1047
     * Close opened file.
1048
     *
1049
     * @param  resource $fp file pointer
1050
     * @param string $path
1051
     * @return bool
1052
     * @author Dmitry (dio) Levashov
1053
     */
1054
    protected function _fclose($fp, $path = '')
1055
    {
1056
        fclose($fp);
1057
        if ($path) {
1058
            unlink($this->getTempFile($path));
1059
        }
1060
    }
1061
1062
    /********************  file/dir manipulations *************************/
1063
1064
    /**
1065
     * Create dir and return created dir path or false on failed.
1066
     *
1067
     * @param  string  $path  parent dir path
1068
     * @param string  $name  new directory name
1069
     * @return string|bool
1070
     * @author Dmitry (dio) Levashov
1071
     **/
1072
    protected function _mkdir($path, $name)
1073
    {
1074
        $path = $this->_joinPath($path, $name);
1075
        if (ftp_mkdir($this->connect, $path) === false) {
1076
            return false;
1077
        }
1078
1079
        $this->options['dirMode'] && ftp_chmod($this->connect, $this->options['dirMode'], $path);
1080
1081
        return $path;
1082
    }
1083
1084
    /**
1085
     * Create file and return it's path or false on failed.
1086
     *
1087
     * @param  string  $path  parent dir path
1088
     * @param string  $name  new file name
1089
     * @return string|bool
1090
     * @author Dmitry (dio) Levashov
1091
     **/
1092
    protected function _mkfile($path, $name)
1093
    {
1094
        if ($this->tmp) {
1095
            $path = $this->_joinPath($path, $name);
1096
            $local = $this->getTempFile();
1097
            $res = touch($local) && ftp_put($this->connect, $path, $local, FTP_ASCII);
1098
            unlink($local);
1099
1100
            return $res ? $path : false;
1101
        }
1102
1103
        return false;
1104
    }
1105
1106
    /**
1107
     * Create symlink. FTP driver does not support symlinks.
1108
     *
1109
     * @param  string $target link target
1110
     * @param  string $path symlink path
1111
     * @param string $name
1112
     * @return bool
1113
     * @author Dmitry (dio) Levashov
1114
     */
1115
    protected function _symlink($target, $path, $name)
1116
    {
1117
        return false;
1118
    }
1119
1120
    /**
1121
     * Copy file into another file.
1122
     *
1123
     * @param  string  $source     source file path
1124
     * @param  string  $targetDir  target directory path
1125
     * @param  string  $name       new file name
1126
     * @return bool
1127
     * @author Dmitry (dio) Levashov
1128
     **/
1129
    protected function _copy($source, $targetDir, $name)
1130
    {
1131
        $res = false;
1132
1133
        if ($this->tmp) {
1134
            $local = $this->getTempFile();
1135
            $target = $this->_joinPath($targetDir, $name);
1136
1137 View Code Duplication
            if (ftp_get($this->connect, $local, $source, FTP_BINARY)
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...
1138
            && ftp_put($this->connect, $target, $local, $this->ftpMode($target))) {
1139
                $res = $target;
1140
            }
1141
            unlink($local);
1142
        }
1143
1144
        return $res;
1145
    }
1146
1147
    /**
1148
     * Move file into another parent dir.
1149
     * Return new file path or false.
1150
     *
1151
     * @param  string $source source file path
1152
     * @param $targetDir
1153
     * @param  string $name file name
1154
     * @return bool|string
1155
     * @internal param string $target target dir path
1156
     * @author Dmitry (dio) Levashov
1157
     */
1158
    protected function _move($source, $targetDir, $name)
1159
    {
1160
        $target = $this->_joinPath($targetDir, $name);
1161
1162
        return ftp_rename($this->connect, $source, $target) ? $target : false;
1163
    }
1164
1165
    /**
1166
     * Remove file.
1167
     *
1168
     * @param  string  $path  file path
1169
     * @return bool
1170
     * @author Dmitry (dio) Levashov
1171
     **/
1172
    protected function _unlink($path)
1173
    {
1174
        return ftp_delete($this->connect, $path);
1175
    }
1176
1177
    /**
1178
     * Remove dir.
1179
     *
1180
     * @param  string  $path  dir path
1181
     * @return bool
1182
     * @author Dmitry (dio) Levashov
1183
     **/
1184
    protected function _rmdir($path)
1185
    {
1186
        return ftp_rmdir($this->connect, $path);
1187
    }
1188
1189
    /**
1190
     * Create new file and write into it from file pointer.
1191
     * Return new file path or false on error.
1192
     *
1193
     * @param  resource  $fp   file pointer
1194
     * @param  string    $dir  target dir path
1195
     * @param  string    $name file name
1196
     * @param  array     $stat file stat (required by some virtual fs)
1197
     * @return bool|string
1198
     * @author Dmitry (dio) Levashov
1199
     **/
1200
    protected function _save($fp, $dir, $name, $stat)
1201
    {
1202
        $path = $this->_joinPath($dir, $name);
1203
1204
        return ftp_fput($this->connect, $path, $fp, $this->ftpMode($path))
1205
            ? $path
1206
            : false;
1207
    }
1208
1209
    /**
1210
     * Get file contents.
1211
     *
1212
     * @param  string  $path  file path
1213
     * @return string|false
1214
     * @author Dmitry (dio) Levashov
1215
     **/
1216
    protected function _getContents($path)
1217
    {
1218
        $contents = '';
1219
        if (($fp = $this->_fopen($path))) {
1220
            while (! feof($fp)) {
1221
                $contents .= fread($fp, 8192);
1222
            }
1223
            $this->_fclose($fp, $path);
1224
1225
            return $contents;
1226
        }
1227
1228
        return false;
1229
    }
1230
1231
    /**
1232
     * Write a string to a file.
1233
     *
1234
     * @param  string  $path     file path
1235
     * @param  string  $content  new file content
1236
     * @return bool
1237
     * @author Dmitry (dio) Levashov
1238
     **/
1239
    protected function _filePutContents($path, $content)
1240
    {
1241
        $res = false;
1242
1243
        if ($this->tmp) {
1244
            $local = $this->getTempFile();
1245
1246
            if (file_put_contents($local, $content, LOCK_EX) !== false
1247
            && ($fp = fopen($local, 'rb'))) {
1248
                clearstatcache();
1249
                $res = ftp_fput($this->connect, $path, $fp, $this->ftpMode($path));
1250
                fclose($fp);
1251
            }
1252
            file_exists($local) && unlink($local);
1253
        }
1254
1255
        return $res;
1256
    }
1257
1258
    /**
1259
     * Detect available archivers.
1260
     *
1261
     * @return void
1262
     **/
1263
    protected function _checkArchivers()
1264
    {
1265
        $this->archivers = $this->getArchivers();
1266
    }
1267
1268
    /**
1269
     * chmod availability.
1270
     *
1271
     * @param string $path
1272
     * @param string $mode
1273
     * @return bool
1274
     */
1275
    protected function _chmod($path, $mode)
1276
    {
1277
        $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf('%04o', $mode));
1278
1279
        return ftp_chmod($this->connect, $modeOct, $path);
1280
    }
1281
1282
    /**
1283
     * Recursive symlinks search.
1284
     *
1285
     * @param  string  $path  file/dir path
1286
     * @return bool
1287
     * @author Dmitry (dio) Levashov
1288
     **/
1289
    protected function _findSymlinks($path)
0 ignored issues
show
Unused Code introduced by
The parameter $path is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1290
    {
1291
        die('Not yet implemented. (_findSymlinks)');
0 ignored issues
show
Coding Style Compatibility introduced by
The method _findSymlinks() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1292
    }
1293
1294
    /**
1295
     * Extract files from archive.
1296
     *
1297
     * @param  string  $path  archive path
1298
     * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
1299
     * @return true
1300
     * @author Dmitry (dio) Levashov,
1301
     * @author Alexey Sukhotin
1302
     **/
1303
    protected function _extract($path, $arc)
1304
    {
1305
        $dir = $this->tempDir();
1306
        if (! $dir) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $dir of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1307
            return false;
1308
        }
1309
1310
        $basename = $this->_basename($path);
1311
        $localPath = $dir.DIRECTORY_SEPARATOR.$basename;
1312
1313
        if (! ftp_get($this->connect, $localPath, $path, FTP_BINARY)) {
1314
            //cleanup
1315
            $this->rmdirRecursive($dir);
1316
1317
            return false;
1318
        }
1319
1320
        $this->unpackArchive($localPath, $arc);
1321
1322
        $this->archiveSize = 0;
0 ignored issues
show
Bug introduced by
The property archiveSize 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...
1323
1324
        // find symlinks and check extracted items
1325
        $checkRes = $this->checkExtractItems($dir);
1326 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...
1327
            $this->rmdirRecursive($dir);
1328
1329
            return $this->setError(array_merge($this->error, [elFinder::ERROR_ARC_SYMLINKS]));
1330
        }
1331
        $this->archiveSize = $checkRes['totalSize'];
1332
        if ($checkRes['rmNames']) {
1333
            foreach ($checkRes['rmNames'] as $name) {
1334
                $this->addError(elFinder::ERROR_SAVE, $name);
1335
            }
1336
        }
1337
1338
        $filesToProcess = self::listFilesInDirectory($dir, true);
1339
1340
        // no files - extract error ?
1341
        if (empty($filesToProcess)) {
1342
            $this->rmdirRecursive($dir);
1343
1344
            return false;
1345
        }
1346
1347
        // check max files size
1348 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...
1349
            $this->rmdirRecursive($dir);
1350
1351
            return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
1352
        }
1353
1354
        $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...
1355
1356
        // archive contains one item - extract in archive dir
1357
        $name = '';
1358
        $src = $dir.DIRECTORY_SEPARATOR.$filesToProcess[0];
1359
        if (($extractTo === 'auto' || ! $extractTo) && count($filesToProcess) === 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...
1360
            $name = $filesToProcess[0];
1361
        } 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...
1362
            // for several files - create new directory
1363
            // create unique name for directory
1364
            $src = $dir;
1365
            $name = basename($path);
1366 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...
1367
                $name = substr($name, 0, strlen($name) - strlen($m[0]));
1368
            }
1369
            $test = $this->_joinPath(dirname($path), $name);
1370
            if ($this->stat($test)) {
1371
                $name = $this->uniqueName(dirname($path), $name, '-', false);
1372
            }
1373
        }
1374
1375
        if ($name !== '' && is_file($src)) {
1376
            $result = $this->_joinPath(dirname($path), $name);
1377
1378
            if (! ftp_put($this->connect, $result, $src, FTP_BINARY)) {
1379
                $this->rmdirRecursive($dir);
1380
1381
                return false;
1382
            }
1383
        } else {
1384
            $dstDir = $this->_dirname($path);
1385
            $result = [];
1386
            if (is_dir($src) && $name) {
1387
                $target = $this->_joinPath($dstDir, $name);
1388
                $_stat = $this->_stat($target);
1389 View Code Duplication
                if ($_stat) {
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...
1390
                    if (! $this->options['copyJoin']) {
1391
                        if ($_stat['mime'] === 'directory') {
1392
                            $this->delTree($target);
1393
                        } else {
1394
                            $this->_unlink($target);
1395
                        }
1396
                        $_stat = false;
1397
                    } else {
1398
                        $dstDir = $target;
1399
                    }
1400
                }
1401
                if (! $_stat && (! $dstDir = $this->_mkdir($dstDir, $name))) {
1402
                    $this->rmdirRecursive($dir);
1403
1404
                    return false;
1405
                }
1406
                $result[] = $dstDir;
1407
            }
1408
            foreach ($filesToProcess as $name) {
1409
                $name = rtrim($name, DIRECTORY_SEPARATOR);
1410
                $src = $dir.DIRECTORY_SEPARATOR.$name;
1411
                if (is_dir($src)) {
1412
                    $p = dirname($name);
1413
                    if ($p === '.') {
1414
                        $p = '';
1415
                    }
1416
                    $name = basename($name);
1417
                    $target = $this->_joinPath($this->_joinPath($dstDir, $p), $name);
0 ignored issues
show
Security Bug introduced by
It seems like $dstDir defined by $this->_mkdir($dstDir, $name) on line 1401 can also be of type false; however, elFinderVolumeFTP::_joinPath() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1418
                    $_stat = $this->_stat($target);
1419 View Code Duplication
                    if ($_stat) {
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...
1420
                        if (! $this->options['copyJoin']) {
1421
                            if ($_stat['mime'] === 'directory') {
1422
                                $this->delTree($target);
1423
                            } else {
1424
                                $this->_unlink($target);
1425
                            }
1426
                            $_stat = false;
1427
                        }
1428
                    }
1429
                    if (! $_stat && (! $target = $this->_mkdir($this->_joinPath($dstDir, $p), $name))) {
0 ignored issues
show
Security Bug introduced by
It seems like $dstDir defined by $this->_mkdir($dstDir, $name) on line 1401 can also be of type false; however, elFinderVolumeFTP::_joinPath() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1430
                        $this->rmdirRecursive($dir);
1431
1432
                        return false;
1433
                    }
1434 View Code Duplication
                } else {
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...
1435
                    $target = $this->_joinPath($dstDir, $name);
0 ignored issues
show
Security Bug introduced by
It seems like $dstDir defined by $this->_mkdir($dstDir, $name) on line 1401 can also be of type false; however, elFinderVolumeFTP::_joinPath() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1436
                    if (! ftp_put($this->connect, $target, $src, FTP_BINARY)) {
1437
                        $this->rmdirRecursive($dir);
1438
1439
                        return false;
1440
                    }
1441
                }
1442
                $result[] = $target;
1443
            }
1444
            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...
1445
                $this->rmdirRecursive($dir);
1446
1447
                return false;
1448
            }
1449
        }
1450
1451
        is_dir($dir) && $this->rmdirRecursive($dir);
1452
1453
        $this->clearcache();
1454
1455
        return $result ? $result : false;
1456
    }
1457
1458
    /**
1459
     * Create archive and return its path.
1460
     *
1461
     * @param  string  $dir    target dir
1462
     * @param  array   $files  files names list
1463
     * @param  string  $name   archive name
1464
     * @param  array   $arc    archiver options
1465
     * @return string|bool
1466
     * @author Dmitry (dio) Levashov,
1467
     * @author Alexey Sukhotin
1468
     **/
1469
    protected function _archive($dir, $files, $name, $arc)
1470
    {
1471
        // get current directory
1472
        $cwd = getcwd();
0 ignored issues
show
Unused Code introduced by
$cwd 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...
1473
1474
        $tmpDir = $this->tempDir();
1475
        if (! $tmpDir) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tmpDir of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1476
            return false;
1477
        }
1478
1479
        //download data
1480
        if (! $this->ftp_download_files($dir, $files, $tmpDir)) {
1481
            //cleanup
1482
            $this->rmdirRecursive($tmpDir);
1483
1484
            return false;
1485
        }
1486
1487
        $remoteArchiveFile = false;
1488
        if ($path = $this->makeArchive($tmpDir, $files, $name, $arc)) {
1489
            $remoteArchiveFile = $this->_joinPath($dir, $name);
1490
            if (! ftp_put($this->connect, $remoteArchiveFile, $path, FTP_BINARY)) {
1491
                $remoteArchiveFile = false;
1492
            }
1493
        }
1494
1495
        //cleanup
1496
        if (! $this->rmdirRecursive($tmpDir)) {
1497
            return false;
1498
        }
1499
1500
        return $remoteArchiveFile;
1501
    }
1502
1503
    /**
1504
     * Gets an array of absolute remote FTP paths of files and
1505
     * folders in $remote_directory omitting symbolic links.
1506
     *
1507
     * @param $remote_directory string remote FTP path to scan for file and folders recursively
1508
     * @param $targets          array  Array of target item. `null` is to get all of items
1509
     * @return array of elements each of which is an array of two elements:
1510
     * <ul>
1511
     * <li>$item['path'] - absolute remote FTP path</li>
1512
     * <li>$item['type'] - either 'f' for file or 'd' for directory</li>
1513
     * </ul>
1514
     */
1515
    protected function ftp_scan_dir($remote_directory, $targets = null)
1516
    {
1517
        $buff = $this->ftpRawList($remote_directory);
1518
        $items = [];
1519
        if ($targets && is_array($targets)) {
1520
            $targets = array_flip($targets);
1521
        } else {
1522
            $targets = false;
1523
        }
1524
        foreach ($buff as $str) {
1525
            $info = preg_split("/\s+/", $str, 9);
1526 View Code Duplication
            if (! isset($this->ftpOsUnix)) {
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...
1527
                $this->ftpOsUnix = ! preg_match('/\d/', substr($info[0], 0, 1));
1528
            }
1529
            if (! $this->ftpOsUnix) {
1530
                $info = $this->normalizeRawWindows($str);
1531
            }
1532
            $type = substr($info[0], 0, 1);
1533
            $name = trim($info[8]);
1534
            if ($name !== '.' && $name !== '..' && (! $targets || isset($targets[$name]))) {
1535
                switch ($type) {
1536
                    case 'l': //omit symbolic links
1537
                    case 'd':
1538
                        $remote_file_path = $this->_joinPath($remote_directory, $name);
1539
                        $item = [];
1540
                        $item['path'] = $remote_file_path;
1541
                        $item['type'] = 'd'; // normal file
1542
                        $items[] = $item;
1543
                        $items = array_merge($items, $this->ftp_scan_dir($remote_file_path));
1544
                        break;
1545
                    default:
1546
                        $remote_file_path = $this->_joinPath($remote_directory, $name);
1547
                        $item = [];
1548
                        $item['path'] = $remote_file_path;
1549
                        $item['type'] = 'f'; // normal file
1550
                        $items[] = $item;
1551
                }
1552
            }
1553
        }
1554
1555
        return $items;
1556
    }
1557
1558
    /**
1559
     * Create writable temporary directory and return path to it.
1560
     * @return string path to the new temporary directory or false in case of error.
1561
     */
1562
    private function tempDir()
1563
    {
1564
        $tempPath = tempnam($this->tmp, 'elFinder');
1565
        if (! $tempPath) {
1566
            $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp);
1567
1568
            return false;
1569
        }
1570
        $success = unlink($tempPath);
1571
        if (! $success) {
1572
            $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp);
1573
1574
            return false;
1575
        }
1576
        $success = mkdir($tempPath, 0700, true);
1577
        if (! $success) {
1578
            $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp);
1579
1580
            return false;
1581
        }
1582
1583
        return $tempPath;
1584
    }
1585
1586
    /**
1587
     * Downloads specified files from remote directory
1588
     * if there is a directory among files it is downloaded recursively (omitting symbolic links).
1589
     *
1590
     * @param $remote_directory string remote FTP path to a source directory to download from.
1591
     * @param array $files list of files to download from remote directory.
1592
     * @param $dest_local_directory string destination folder to store downloaded files.
1593
     * @return bool true on success and false on failure.
1594
     */
1595
    private function ftp_download_files($remote_directory, array $files, $dest_local_directory)
1596
    {
1597
        $contents = $this->ftp_scan_dir($remote_directory, $files);
1598
        if (! isset($contents)) {
1599
            $this->setError(elFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory);
1600
1601
            return false;
1602
        }
1603
        $remoteDirLen = strlen($remote_directory);
1604
        foreach ($contents as $item) {
1605
            $relative_path = substr($item['path'], $remoteDirLen);
1606
            $local_path = $dest_local_directory.DIRECTORY_SEPARATOR.$relative_path;
1607
            switch ($item['type']) {
1608
                case 'd':
1609
                    $success = mkdir($local_path);
1610
                    break;
1611
                case 'f':
1612
                    $success = ftp_get($this->connect, $local_path, $item['path'], FTP_BINARY);
1613
                    break;
1614
                default:
1615
                    $success = true;
1616
            }
1617
            if (! $success) {
1618
                $this->setError(elFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory);
1619
1620
                return false;
1621
            }
1622
        }
1623
1624
        return true;
1625
    }
1626
1627
    /**
1628
     * Delete local directory recursively.
1629
     * @param $dirPath string to directory to be erased.
1630
     * @return bool true on success and false on failure.
1631
     */
1632
    private function deleteDir($dirPath)
1633
    {
1634
        if (! is_dir($dirPath)) {
1635
            $success = unlink($dirPath);
1636
        } else {
1637
            $success = true;
1638
            foreach (array_reverse(self::listFilesInDirectory($dirPath, false)) as $path) {
1639
                $path = $dirPath.DIRECTORY_SEPARATOR.$path;
1640
                if (is_link($path)) {
1641
                    unlink($path);
1642
                } elseif (is_dir($path)) {
1643
                    $success = rmdir($path);
1644
                } else {
1645
                    $success = unlink($path);
1646
                }
1647
                if (! $success) {
1648
                    break;
1649
                }
1650
            }
1651
            if ($success) {
1652
                $success = rmdir($dirPath);
1653
            }
1654
        }
1655
        if (! $success) {
1656
            $this->setError(elFinder::ERROR_RM, $dirPath);
1657
1658
            return false;
1659
        }
1660
1661
        return $success;
1662
    }
1663
1664
    /**
1665
     * Returns array of strings containing all files and folders in the specified local directory.
1666
     * @param $dir
1667
     * @param $omitSymlinks
1668
     * @param string $prefix
1669
     * @return array array of files and folders names relative to the $path
1670
     * or an empty array if the directory $path is empty,
1671
     * <br />
1672
     * false if $path is not a directory or does not exist.
1673
     * @throws Exception
1674
     * @internal param string $path path to directory to scan.
1675
     */
1676
    private static function listFilesInDirectory($dir, $omitSymlinks, $prefix = '')
1677
    {
1678
        if (! is_dir($dir)) {
1679
            return false;
1680
        }
1681
        $excludes = ['.', '..'];
1682
        $result = [];
1683
        $files = self::localScandir($dir);
1684
        if (! $files) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $files 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...
1685
            return [];
1686
        }
1687
        foreach ($files as $file) {
1688
            if (! in_array($file, $excludes)) {
1689
                $path = $dir.DIRECTORY_SEPARATOR.$file;
1690
                if (is_link($path)) {
1691
                    if ($omitSymlinks) {
1692
                        continue;
1693
                    } else {
1694
                        $result[] = $prefix.$file;
1695
                    }
1696
                } elseif (is_dir($path)) {
1697
                    $result[] = $prefix.$file.DIRECTORY_SEPARATOR;
1698
                    $subs = self::listFilesInDirectory($path, $omitSymlinks, $prefix.$file.DIRECTORY_SEPARATOR);
1699
                    if ($subs) {
1700
                        $result = array_merge($result, $subs);
1701
                    }
1702
                } else {
1703
                    $result[] = $prefix.$file;
1704
                }
1705
            }
1706
        }
1707
1708
        return $result;
1709
    }
1710
} // END class
1711