elFinderVolumeMySQL   D
last analyzed

Complexity

Total Complexity 140

Size/Duplication

Total Lines 949
Duplicated Lines 2.32 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 22
loc 949
rs 4.4444
c 0
b 0
f 0
wmc 140
lcom 1
cbo 2

42 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
A umount() 0 4 1
A debug() 0 10 2
C init() 0 35 14
C configure() 5 24 10
A query() 0 10 2
A make() 0 7 2
C cacheDir() 7 37 7
B getParents() 0 17 5
A loadFilePath() 0 9 2
D doSearch() 10 81 19
A _dirname() 0 4 3
A _basename() 0 4 2
A _joinPath() 0 12 3
A _normpath() 0 4 1
A _relpath() 0 4 1
A _abspath() 0 4 1
A _path() 0 15 3
A _inpath() 0 6 2
B _stat() 0 34 5
A _subdirs() 0 4 3
A _dimensions() 0 4 4
A _scandir() 0 6 2
B _fopen() 0 20 5
A _fclose() 0 7 2
A _mkdir() 0 4 2
A _mkfile() 0 4 2
A _symlink() 0 4 1
A _copy() 0 11 2
A _move() 0 7 3
A _unlink() 0 4 2
A _rmdir() 0 4 2
A _setContent() 0 6 1
C _save() 0 56 13
A _getContents() 0 4 3
A _filePutContents() 0 4 1
A _checkArchivers() 0 3 1
A _chmod() 0 4 1
A _unpack() 0 3 1
A _findSymlinks() 0 4 1
A _extract() 0 4 1
A _archive() 0 4 1

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

1
<?php
2
3
/**
4
 * Simple elFinder driver for MySQL.
5
 *
6
 * @author Dmitry (dio) Levashov
7
 **/
8
class elFinderVolumeMySQL 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...
9
{
10
    /**
11
     * Driver id
12
     * Must be started from letter and contains [a-z0-9]
13
     * Used as part of volume id.
14
     *
15
     * @var string
16
     **/
17
    protected $driverId = 'm';
18
19
    /**
20
     * Database object.
21
     *
22
     * @var mysqli
23
     **/
24
    protected $db = null;
25
26
    /**
27
     * Tables to store files.
28
     *
29
     * @var string
30
     **/
31
    protected $tbf = '';
32
33
    /**
34
     * Directory for tmp files
35
     * If not set driver will try to use tmbDir as tmpDir.
36
     *
37
     * @var string
38
     **/
39
    protected $tmpPath = '';
40
41
    /**
42
     * Numbers of sql requests (for debug).
43
     *
44
     * @var int
45
     **/
46
    protected $sqlCnt = 0;
47
48
    /**
49
     * Last db error message.
50
     *
51
     * @var string
52
     **/
53
    protected $dbError = '';
54
55
    /**
56
     * Constructor
57
     * Extend options with required fields.
58
     *
59
     * @author Dmitry (dio) Levashov
60
     */
61
    public function __construct()
62
    {
63
        $opts = [
64
            'host' => 'localhost',
65
            'user' => '',
66
            'pass' => '',
67
            'db' => '',
68
            'port' => null,
69
            'socket' => null,
70
            'files_table' => 'elfinder_file',
71
            'tmbPath' => '',
72
            'tmpPath' => '',
73
            'rootCssClass' => 'elfinder-navbar-root-sql',
74
            'noSessionCache' => ['hasdirs'],
75
        ];
76
        $this->options = array_merge($this->options, $opts);
77
        $this->options['mimeDetect'] = 'internal';
78
    }
79
80
    /**
81
     * Close connection.
82
     *
83
     * @return void
84
     * @author Dmitry (dio) Levashov
85
     **/
86
    public function umount()
87
    {
88
        $this->db->close();
89
    }
90
91
    /**
92
     * Return debug info for client.
93
     *
94
     * @return array
95
     * @author Dmitry (dio) Levashov
96
     **/
97
    public function debug()
98
    {
99
        $debug = parent::debug();
100
        $debug['sqlCount'] = $this->sqlCnt;
101
        if ($this->dbError) {
102
            $debug['dbError'] = $this->dbError;
103
        }
104
105
        return $debug;
106
    }
107
108
    /*********************************************************************/
109
    /*                        INIT AND CONFIGURE                         */
110
    /*********************************************************************/
111
112
    /**
113
     * Prepare driver before mount volume.
114
     * Connect to db, check required tables and fetch root path.
115
     *
116
     * @return bool
117
     * @author Dmitry (dio) Levashov
118
     **/
119
    protected function init()
120
    {
121
        if (! ($this->options['host'] || $this->options['socket'])
122
        || ! $this->options['user']
123
        || ! $this->options['pass']
124
        || ! $this->options['db']
125
        || ! $this->options['path']
126
        || ! $this->options['files_table']) {
127
            return false;
128
        }
129
130
        $this->db = new mysqli($this->options['host'], $this->options['user'], $this->options['pass'], $this->options['db'], $this->options['port'], $this->options['socket']);
131
        if ($this->db->connect_error || mysqli_connect_error()) {
132
            return false;
133
        }
134
135
        $this->db->set_charset('utf8');
136
137
        if ($res = $this->db->query('SHOW TABLES')) {
138
            while ($row = $res->fetch_array()) {
139
                if ($row[0] == $this->options['files_table']) {
140
                    $this->tbf = $this->options['files_table'];
141
                    break;
142
                }
143
            }
144
        }
145
146
        if (! $this->tbf) {
147
            return false;
148
        }
149
150
        $this->updateCache($this->options['path'], $this->_stat($this->options['path']));
0 ignored issues
show
Security Bug introduced by
It seems like $this->_stat($this->options['path']) targeting elFinderVolumeMySQL::_stat() can also be of type false; however, elFinderVolumeDriver::updateCache() does only seem to accept array, did you maybe forget to handle an error condition?
Loading history...
151
152
        return true;
153
    }
154
155
    /**
156
     * Set tmp path.
157
     *
158
     * @return void
159
     * @author Dmitry (dio) Levashov
160
     **/
161
    protected function configure()
162
    {
163
        parent::configure();
164
165
        if (($tmp = $this->options['tmpPath'])) {
166 View Code Duplication
            if (! file_exists($tmp)) {
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...
167
                if (mkdir($tmp)) {
168
                    chmod($tmp, $this->options['tmbPathMode']);
169
                }
170
            }
171
172
            $this->tmpPath = is_dir($tmp) && is_writable($tmp) ? $tmp : false;
173
        }
174
        if (! $this->tmpPath && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
175
            $this->tmpPath = $tmp;
176
        }
177
178
        // fallback of $this->tmp
179
        if (! $this->tmpPath && $this->tmbPathWritable) {
180
            $this->tmpPath = $this->tmbPath;
181
        }
182
183
        $this->mimeDetect = 'internal';
184
    }
185
186
    /**
187
     * Perform sql query and return result.
188
     * Increase sqlCnt and save error if occured.
189
     *
190
     * @param  string  $sql  query
191
     * @return misc
192
     * @author Dmitry (dio) Levashov
193
     **/
194
    protected function query($sql)
195
    {
196
        $this->sqlCnt++;
197
        $res = $this->db->query($sql);
198
        if (! $res) {
199
            $this->dbError = $this->db->error;
200
        }
201
202
        return $res;
203
    }
204
205
    /**
206
     * Create empty object with required mimetype.
207
     *
208
     * @param  string  $path  parent dir path
209
     * @param  string  $name  object name
210
     * @param  string  $mime  mime type
211
     * @return bool
212
     * @author Dmitry (dio) Levashov
213
     **/
214
    protected function make($path, $name, $mime)
215
    {
216
        $sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`) VALUES (\'%s\', \'%s\', 0, %d, \'%s\', \'\', \'%d\', \'%d\')';
217
        $sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write']);
218
        // echo $sql;
219
        return $this->query($sql) && $this->db->affected_rows > 0;
220
    }
221
222
    /*********************************************************************/
223
    /*                               FS API                              */
224
    /*********************************************************************/
225
226
    /**
227
     * Cache dir contents.
228
     *
229
     * @param  string  $path  dir path
230
     * @return string
231
     * @author Dmitry Levashov
232
     **/
233
    protected function cacheDir($path)
234
    {
235
        $this->dirsCache[$path] = [];
236
237
        $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs 
238
				FROM '.$this->tbf.' AS f 
239
				LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime=\'directory\'
240
				WHERE f.parent_id=\''.$path.'\'
241
				GROUP BY f.id';
242
243
        $res = $this->query($sql);
244
        if ($res) {
245
            while ($row = $res->fetch_assoc()) {
246
                $id = $row['id'];
247
                if ($row['parent_id']) {
248
                    $row['phash'] = $this->encode($row['parent_id']);
249
                }
250
251 View Code Duplication
                if ($row['mime'] == 'directory') {
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...
252
                    unset($row['width']);
253
                    unset($row['height']);
254
                    $row['size'] = 0;
255
                } else {
256
                    unset($row['dirs']);
257
                }
258
259
                unset($row['id']);
260
                unset($row['parent_id']);
261
262
                if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) {
263
                    $this->dirsCache[$path][] = $id;
264
                }
265
            }
266
        }
267
268
        return $this->dirsCache[$path];
269
    }
270
271
    /**
272
     * Return array of parents paths (ids).
273
     *
274
     * @param  int   $path  file path (id)
275
     * @return array
276
     * @author Dmitry (dio) Levashov
277
     **/
278
    protected function getParents($path)
279
    {
280
        $parents = [];
281
282
        while ($path) {
283
            if ($file = $this->stat($path)) {
284
                array_unshift($parents, $path);
285
                $path = isset($file['phash']) ? $this->decode($file['phash']) : false;
286
            }
287
        }
288
289
        if (count($parents)) {
290
            array_pop($parents);
291
        }
292
293
        return $parents;
294
    }
295
296
    /**
297
     * Return correct file path for LOAD_FILE method.
298
     *
299
     * @param  string $path  file path (id)
300
     * @return string
301
     * @author Troex Nevelin
302
     **/
303
    protected function loadFilePath($path)
304
    {
305
        $realPath = realpath($path);
306
        if (DIRECTORY_SEPARATOR == '\\') { // windows
307
            $realPath = str_replace('\\', '\\\\', $realPath);
308
        }
309
310
        return $this->db->real_escape_string($realPath);
311
    }
312
313
    /**
314
     * Recursive files search.
315
     *
316
     * @param  string  $path   dir path
317
     * @param  string  $q      search string
318
     * @param  array   $mimes
319
     * @return array
320
     * @author Dmitry (dio) Levashov
321
     **/
322
    protected function doSearch($path, $q, $mimes)
323
    {
324
        $dirs = [];
325
        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
326
327
        if ($path != $this->root) {
328
            $dirs = $inpath = [intval($path)];
329
            while ($inpath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $inpath 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...
330
                $in = '('.implode(',', $inpath).')';
331
                $inpath = [];
332
                $sql = 'SELECT f.id FROM %s AS f WHERE f.parent_id IN '.$in.' AND `mime` = \'directory\'';
333
                $sql = sprintf($sql, $this->tbf);
334
                if ($res = $this->query($sql)) {
335
                    $_dir = [];
0 ignored issues
show
Unused Code introduced by
$_dir 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...
336
                    while ($dat = $res->fetch_assoc()) {
337
                        $inpath[] = $dat['id'];
338
                    }
339
                    $dirs = array_merge($dirs, $inpath);
340
                }
341
            }
342
        }
343
344
        $result = [];
345
346
        if ($mimes) {
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...
347
            $whrs = [];
348
            foreach ($mimes as $mime) {
349
                if (strpos($mime, '/') === false) {
350
                    $whrs[] = sprintf('f.mime LIKE \'%s/%%\'', $this->db->real_escape_string($mime));
351
                } else {
352
                    $whrs[] = sprintf('f.mime = \'%s\'', $this->db->real_escape_string($mime));
353
                }
354
            }
355
            $whr = implode(' OR ', $whrs);
356
        } else {
357
            $whr = sprintf('f.name RLIKE \'%s\'', $this->db->real_escape_string($q));
358
        }
359
        if ($dirs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $dirs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
360
            $whr = '('.$whr.') AND (`parent_id` IN ('.implode(',', $dirs).'))';
361
        }
362
363
        $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, 0 AS dirs 
364
				FROM %s AS f 
365
				WHERE %s';
366
367
        $sql = sprintf($sql, $this->tbf, $whr);
368
369
        if (($res = $this->query($sql))) {
370
            while ($row = $res->fetch_assoc()) {
371 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...
372
                    $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
373
                    break;
374
                }
375
376
                if (! $this->mimeAccepted($row['mime'], $mimes)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->mimeAccepted($row['mime'], $mimes) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
377
                    continue;
378
                }
379
                $id = $row['id'];
380
                if ($row['parent_id']) {
381
                    $row['phash'] = $this->encode($row['parent_id']);
382
                }
383
                $row['path'] = $this->_path($id);
384
385 View Code Duplication
                if ($row['mime'] == 'directory') {
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...
386
                    unset($row['width']);
387
                    unset($row['height']);
388
                } else {
389
                    unset($row['dirs']);
390
                }
391
392
                unset($row['id']);
393
                unset($row['parent_id']);
394
395
                if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) {
396
                    $result[] = $stat;
397
                }
398
            }
399
        }
400
401
        return $result;
402
    }
403
404
    /*********************** paths/urls *************************/
405
406
    /**
407
     * Return parent directory path.
408
     *
409
     * @param  string  $path  file path
410
     * @return string
411
     * @author Dmitry (dio) Levashov
412
     **/
413
    protected function _dirname($path)
414
    {
415
        return ($stat = $this->stat($path)) ? (! empty($stat['phash']) ? $this->decode($stat['phash']) : $this->root) : false;
416
    }
417
418
    /**
419
     * Return file name.
420
     *
421
     * @param  string  $path  file path
422
     * @return string
423
     * @author Dmitry (dio) Levashov
424
     **/
425
    protected function _basename($path)
426
    {
427
        return ($stat = $this->stat($path)) ? $stat['name'] : false;
428
    }
429
430
    /**
431
     * Join dir name and file name and return full path.
432
     *
433
     * @param  string  $dir
434
     * @param  string  $name
435
     * @return string
436
     * @author Dmitry (dio) Levashov
437
     **/
438
    protected function _joinPath($dir, $name)
439
    {
440
        $sql = 'SELECT id FROM '.$this->tbf.' WHERE parent_id=\''.$dir.'\' AND name=\''.$this->db->real_escape_string($name).'\'';
441
442
        if (($res = $this->query($sql)) && ($r = $res->fetch_assoc())) {
443
            $this->updateCache($r['id'], $this->_stat($r['id']));
0 ignored issues
show
Security Bug introduced by
It seems like $this->_stat($r['id']) targeting elFinderVolumeMySQL::_stat() can also be of type false; however, elFinderVolumeDriver::updateCache() does only seem to accept array, did you maybe forget to handle an error condition?
Loading history...
444
445
            return $r['id'];
446
        }
447
448
        return -1;
449
    }
450
451
    /**
452
     * Return normalized path, this works the same as os.path.normpath() in Python.
453
     *
454
     * @param  string  $path  path
455
     * @return string
456
     * @author Troex Nevelin
457
     **/
458
    protected function _normpath($path)
459
    {
460
        return $path;
461
    }
462
463
    /**
464
     * Return file path related to root dir.
465
     *
466
     * @param  string  $path  file path
467
     * @return string
468
     * @author Dmitry (dio) Levashov
469
     **/
470
    protected function _relpath($path)
471
    {
472
        return $path;
473
    }
474
475
    /**
476
     * Convert path related to root dir into real path.
477
     *
478
     * @param  string  $path  file path
479
     * @return string
480
     * @author Dmitry (dio) Levashov
481
     **/
482
    protected function _abspath($path)
483
    {
484
        return $path;
485
    }
486
487
    /**
488
     * Return fake path started from root dir.
489
     *
490
     * @param  string  $path  file path
491
     * @return string
492
     * @author Dmitry (dio) Levashov
493
     **/
494
    protected function _path($path)
495
    {
496
        if (($file = $this->stat($path)) == false) {
497
            return '';
498
        }
499
500
        $parentsIds = $this->getParents($path);
501
        $path = '';
502
        foreach ($parentsIds as $id) {
503
            $dir = $this->stat($id);
504
            $path .= $dir['name'].$this->separator;
505
        }
506
507
        return $path.$file['name'];
508
    }
509
510
    /**
511
     * Return true if $path is children of $parent.
512
     *
513
     * @param  string  $path    path to check
514
     * @param  string  $parent  parent path
515
     * @return bool
516
     * @author Dmitry (dio) Levashov
517
     **/
518
    protected function _inpath($path, $parent)
519
    {
520
        return $path == $parent
521
            ? true
522
            : in_array($parent, $this->getParents($path));
523
    }
524
525
    /***************** file stat ********************/
526
527
    /**
528
     * Return stat for given path.
529
     * Stat contains following fields:
530
     * - (int)    size    file size in b. required
531
     * - (int)    ts      file modification time in unix time. required
532
     * - (string) mime    mimetype. required for folders, others - optionally
533
     * - (bool)   read    read permissions. required
534
     * - (bool)   write   write permissions. required
535
     * - (bool)   locked  is object locked. optionally
536
     * - (bool)   hidden  is object hidden. optionally
537
     * - (string) alias   for symlinks - link target path relative to root path. optionally
538
     * - (string) target  for symlinks - link target path. optionally.
539
     *
540
     * If file does not exists - returns empty array or false.
541
     *
542
     * @param  string  $path    file path
543
     * @return array|false
544
     * @author Dmitry (dio) Levashov
545
     **/
546
    protected function _stat($path)
547
    {
548
        $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs
549
				FROM '.$this->tbf.' AS f 
550
				LEFT JOIN '.$this->tbf.' AS p ON p.id=f.parent_id
551
				LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime=\'directory\'
552
				WHERE f.id=\''.$path.'\'
553
				GROUP BY f.id';
554
555
        $res = $this->query($sql);
556
557
        if ($res) {
558
            $stat = $res->fetch_assoc();
559
            if ($stat['parent_id']) {
560
                $stat['phash'] = $this->encode($stat['parent_id']);
561
            }
562
            if ($stat['mime'] == 'directory') {
563
                unset($stat['width']);
564
                unset($stat['height']);
565
                $stat['size'] = 0;
566
            } else {
567
                if (! $stat['mime']) {
568
                    unset($stat['mime']);
569
                }
570
                unset($stat['dirs']);
571
            }
572
            unset($stat['id']);
573
            unset($stat['parent_id']);
574
575
            return $stat;
576
        }
577
578
        return [];
579
    }
580
581
    /**
582
     * Return true if path is dir and has at least one childs directory.
583
     *
584
     * @param  string  $path  dir path
585
     * @return bool
586
     * @author Dmitry (dio) Levashov
587
     **/
588
    protected function _subdirs($path)
589
    {
590
        return ($stat = $this->stat($path)) && isset($stat['dirs']) ? $stat['dirs'] : false;
591
    }
592
593
    /**
594
     * Return object width and height
595
     * Usualy used for images, but can be realize for video etc...
596
     *
597
     * @param  string  $path  file path
598
     * @param  string  $mime  file mime type
599
     * @return string
600
     * @author Dmitry (dio) Levashov
601
     **/
602
    protected function _dimensions($path, $mime)
603
    {
604
        return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'].'x'.$stat['height'] : '';
605
    }
606
607
    /******************** file/dir content *********************/
608
609
    /**
610
     * Return files list in directory.
611
     *
612
     * @param  string  $path  dir path
613
     * @return array
614
     * @author Dmitry (dio) Levashov
615
     **/
616
    protected function _scandir($path)
617
    {
618
        return isset($this->dirsCache[$path])
619
            ? $this->dirsCache[$path]
620
            : $this->cacheDir($path);
621
    }
622
623
    /**
624
     * Open file and return file pointer.
625
     *
626
     * @param  string  $path  file path
627
     * @param  string  $mode  open file mode (ignored in this driver)
628
     * @return resource|false
629
     * @author Dmitry (dio) Levashov
630
     **/
631
    protected function _fopen($path, $mode = 'rb')
632
    {
633
        $fp = $this->tmbPath
634
            ? fopen($this->getTempFile($path), 'w+')
635
            : tmpfile();
636
637
        if ($fp) {
638
            if (($res = $this->query('SELECT content FROM '.$this->tbf.' WHERE id=\''.$path.'\''))
639
            && ($r = $res->fetch_assoc())) {
640
                fwrite($fp, $r['content']);
641
                rewind($fp);
642
643
                return $fp;
644
            } else {
645
                $this->_fclose($fp, $path);
646
            }
647
        }
648
649
        return false;
650
    }
651
652
    /**
653
     * Close opened file.
654
     *
655
     * @param  resource $fp file pointer
656
     * @param string $path
657
     * @return bool
658
     * @author Dmitry (dio) Levashov
659
     */
660
    protected function _fclose($fp, $path = '')
661
    {
662
        fclose($fp);
663
        if ($path) {
664
            unlink($this->getTempFile($path));
665
        }
666
    }
667
668
    /********************  file/dir manipulations *************************/
669
670
    /**
671
     * Create dir and return created dir path or false on failed.
672
     *
673
     * @param  string  $path  parent dir path
674
     * @param string  $name  new directory name
675
     * @return string|bool
676
     * @author Dmitry (dio) Levashov
677
     **/
678
    protected function _mkdir($path, $name)
679
    {
680
        return $this->make($path, $name, 'directory') ? $this->_joinPath($path, $name) : false;
681
    }
682
683
    /**
684
     * Create file and return it's path or false on failed.
685
     *
686
     * @param  string  $path  parent dir path
687
     * @param string  $name  new file name
688
     * @return string|bool
689
     * @author Dmitry (dio) Levashov
690
     **/
691
    protected function _mkfile($path, $name)
692
    {
693
        return $this->make($path, $name, '') ? $this->_joinPath($path, $name) : false;
694
    }
695
696
    /**
697
     * Create symlink. FTP driver does not support symlinks.
698
     *
699
     * @param  string $target link target
700
     * @param  string $path symlink path
701
     * @param string $name
702
     * @return bool
703
     * @author Dmitry (dio) Levashov
704
     */
705
    protected function _symlink($target, $path, $name)
706
    {
707
        return false;
708
    }
709
710
    /**
711
     * Copy file into another file.
712
     *
713
     * @param  string  $source     source file path
714
     * @param  string  $targetDir  target directory path
715
     * @param  string  $name       new file name
716
     * @return bool
717
     * @author Dmitry (dio) Levashov
718
     **/
719
    protected function _copy($source, $targetDir, $name)
720
    {
721
        $this->clearcache();
722
        $id = $this->_joinPath($targetDir, $name);
723
724
        $sql = $id > 0
725
            ? sprintf('REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) (SELECT %d, %d, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d)', $this->tbf, $id, $this->_dirname($id), $this->tbf, $source)
726
            : sprintf('INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) SELECT %d, \'%s\', content, size, %d, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d', $this->tbf, $targetDir, $this->db->real_escape_string($name), time(), $this->tbf, $source);
727
728
        return $this->query($sql);
729
    }
730
731
    /**
732
     * Move file into another parent dir.
733
     * Return new file path or false.
734
     *
735
     * @param  string $source source file path
736
     * @param $targetDir
737
     * @param  string $name file name
738
     * @return bool|string
739
     * @internal param string $target target dir path
740
     * @author Dmitry (dio) Levashov
741
     */
742
    protected function _move($source, $targetDir, $name)
743
    {
744
        $sql = 'UPDATE %s SET parent_id=%d, name=\'%s\' WHERE id=%d LIMIT 1';
745
        $sql = sprintf($sql, $this->tbf, $targetDir, $this->db->real_escape_string($name), $source);
746
747
        return $this->query($sql) && $this->db->affected_rows > 0 ? $source : false;
748
    }
749
750
    /**
751
     * Remove file.
752
     *
753
     * @param  string  $path  file path
754
     * @return bool
755
     * @author Dmitry (dio) Levashov
756
     **/
757
    protected function _unlink($path)
758
    {
759
        return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime!=\'directory\' LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows;
760
    }
761
762
    /**
763
     * Remove dir.
764
     *
765
     * @param  string  $path  dir path
766
     * @return bool
767
     * @author Dmitry (dio) Levashov
768
     **/
769
    protected function _rmdir($path)
770
    {
771
        return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime=\'directory\' LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows;
772
    }
773
774
    /**
775
     * undocumented function.
776
     *
777
     * @param $path
778
     * @param $fp
779
     * @author Dmitry Levashov
780
     */
781
    protected function _setContent($path, $fp)
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...
782
    {
783
        elFinder::rewind($fp);
784
        $fstat = fstat($fp);
785
        $size = $fstat['size'];
0 ignored issues
show
Unused Code introduced by
$size 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...
786
    }
787
788
    /**
789
     * Create new file and write into it from file pointer.
790
     * Return new file path or false on error.
791
     *
792
     * @param  resource  $fp   file pointer
793
     * @param  string    $dir  target dir path
794
     * @param  string    $name file name
795
     * @param  array     $stat file stat (required by some virtual fs)
796
     * @return bool|string
797
     * @author Dmitry (dio) Levashov
798
     **/
799
    protected function _save($fp, $dir, $name, $stat)
800
    {
801
        $this->clearcache();
802
803
        $mime = $stat['mime'];
804
        $w = ! empty($stat['width']) ? $stat['width'] : 0;
805
        $h = ! empty($stat['height']) ? $stat['height'] : 0;
806
807
        $id = $this->_joinPath($dir, $name);
808
        elFinder::rewind($fp);
809
        $stat = fstat($fp);
810
        $size = $stat['size'];
811
812
        if (($tmpfile = tempnam($this->tmpPath, $this->id))) {
813
            if (($trgfp = fopen($tmpfile, 'wb')) == false) {
814
                unlink($tmpfile);
815
            } else {
816
                while (! feof($fp)) {
817
                    fwrite($trgfp, fread($fp, 8192));
818
                }
819
                fclose($trgfp);
820
                chmod($tmpfile, 0644);
821
822
                $sql = $id > 0
823
                    ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, \'%s\', LOAD_FILE(\'%s\'), %d, %d, \'%s\', %d, %d)'
824
                    : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, \'%s\', LOAD_FILE(\'%s\'), %d, %d, \'%s\', %d, %d)';
825
                $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->loadFilePath($tmpfile), $size, time(), $mime, $w, $h);
826
827
                $res = $this->query($sql);
828
                unlink($tmpfile);
829
830
                if ($res) {
831
                    return $id > 0 ? $id : $this->db->insert_id;
832
                }
833
            }
834
        }
835
836
        $content = '';
837
        elFinder::rewind($fp);
838
        while (! feof($fp)) {
839
            $content .= fread($fp, 8192);
840
        }
841
842
        $sql = $id > 0
843
            ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, \'%s\', \'%s\', %d, %d, \'%s\', %d, %d)'
844
            : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, \'%s\', \'%s\', %d, %d, \'%s\', %d, %d)';
845
        $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->db->real_escape_string($content), $size, time(), $mime, $w, $h);
846
847
        unset($content);
848
849
        if ($this->query($sql)) {
850
            return $id > 0 ? $id : $this->db->insert_id;
851
        }
852
853
        return false;
854
    }
855
856
    /**
857
     * Get file contents.
858
     *
859
     * @param  string  $path  file path
860
     * @return string|false
861
     * @author Dmitry (dio) Levashov
862
     **/
863
    protected function _getContents($path)
864
    {
865
        return ($res = $this->query(sprintf('SELECT content FROM %s WHERE id=%d', $this->tbf, $path))) && ($r = $res->fetch_assoc()) ? $r['content'] : false;
0 ignored issues
show
Bug introduced by
The variable $r 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...
866
    }
867
868
    /**
869
     * Write a string to a file.
870
     *
871
     * @param  string  $path     file path
872
     * @param  string  $content  new file content
873
     * @return bool
874
     * @author Dmitry (dio) Levashov
875
     **/
876
    protected function _filePutContents($path, $content)
877
    {
878
        return $this->query(sprintf('UPDATE %s SET content=\'%s\', size=%d, mtime=%d WHERE id=%d LIMIT 1', $this->tbf, $this->db->real_escape_string($content), strlen($content), time(), $path));
879
    }
880
881
    /**
882
     * Detect available archivers.
883
     *
884
     * @return void
885
     **/
886
    protected function _checkArchivers()
887
    {
888
    }
889
890
    /**
891
     * chmod implementation.
892
     *
893
     * @param string $path
894
     * @param string $mode
895
     * @return bool
896
     */
897
    protected function _chmod($path, $mode)
898
    {
899
        return false;
900
    }
901
902
    /**
903
     * Unpack archive.
904
     *
905
     * @param  string  $path  archive path
906
     * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
907
     * @return void
908
     * @author Dmitry (dio) Levashov
909
     * @author Alexey Sukhotin
910
     **/
911
    protected function _unpack($path, $arc)
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...
Unused Code introduced by
The parameter $arc 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...
912
    {
913
    }
914
915
    /**
916
     * Recursive symlinks search.
917
     *
918
     * @param  string  $path  file/dir path
919
     * @return bool
920
     * @author Dmitry (dio) Levashov
921
     **/
922
    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...
923
    {
924
        return false;
925
    }
926
927
    /**
928
     * Extract files from archive.
929
     *
930
     * @param  string  $path  archive path
931
     * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
932
     * @return true
933
     * @author Dmitry (dio) Levashov,
934
     * @author Alexey Sukhotin
935
     **/
936
    protected function _extract($path, $arc)
937
    {
938
        return false;
939
    }
940
941
    /**
942
     * Create archive and return its path.
943
     *
944
     * @param  string  $dir    target dir
945
     * @param  array   $files  files names list
946
     * @param  string  $name   archive name
947
     * @param  array   $arc    archiver options
948
     * @return string|bool
949
     * @author Dmitry (dio) Levashov,
950
     * @author Alexey Sukhotin
951
     **/
952
    protected function _archive($dir, $files, $name, $arc)
953
    {
954
        return false;
955
    }
956
} // END class
957