GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

elFinderVolumeMySQL::query()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Simple elFinder driver for MySQL.
5
 *
6
 * @author Dmitry (dio) Levashov
7
 **/
8
class elFinderVolumeMySQL extends elFinderVolumeDriver {
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
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
60
	 * @author Dmitry (dio) Levashov
61
	 **/
62
	public function __construct() {
63
		$opts = array(
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
		);
74
		$this->options = array_merge($this->options, $opts);
75
		$this->options['mimeDetect'] = 'internal';
76
	}
77
	
78
	/*********************************************************************/
79
	/*                        INIT AND CONFIGURE                         */
80
	/*********************************************************************/
81
	
82
	/**
83
	 * Prepare driver before mount volume.
84
	 * Connect to db, check required tables and fetch root path
85
	 *
86
	 * @return bool
87
	 * @author Dmitry (dio) Levashov
88
	 **/
89
	protected function init() {
90
		
91
		if (!($this->options['host'] || $this->options['socket'])
92
		||  !$this->options['user'] 
93
		||  !$this->options['pass'] 
94
		||  !$this->options['db']
95
		||  !$this->options['path']
96
		||  !$this->options['files_table']) {
97
			return false;
98
		}
99
		
100
		
101
		$this->db = new mysqli($this->options['host'], $this->options['user'], $this->options['pass'], $this->options['db'], $this->options['port'], $this->options['socket']);
102
		if ($this->db->connect_error || @mysqli_connect_error()) {
103
			return false;
104
		}
105
		
106
		$this->db->set_charset('utf8');
107
108
		if ($res = $this->db->query('SHOW TABLES')) {
109
			while ($row = $res->fetch_array()) {
110
				if ($row[0] == $this->options['files_table']) {
111
					$this->tbf = $this->options['files_table'];
112
					break;
113
				}
114
			}
115
		}
116
117
		if (!$this->tbf) {
118
			return false;
119
		}
120
121
		$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...
122
123
		return true;
124
	}
125
126
127
128
	/**
129
	 * Set tmp path
130
	 *
131
	 * @return void
132
	 * @author Dmitry (dio) Levashov
133
	 **/
134
	protected function configure() {
135
		parent::configure();
136
137
		if (($tmp = $this->options['tmpPath'])) {
138 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...
139
				if (@mkdir($tmp)) {
140
					@chmod($tmp, $this->options['tmbPathMode']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
141
				}
142
			}
143
			
144
			$this->tmpPath = is_dir($tmp) && is_writable($tmp) ? $tmp : false;
145
		}
146
		
147
		if (!$this->tmpPath && $this->tmbPath && $this->tmbPathWritable) {
148
			$this->tmpPath = $this->tmbPath;
149
		}
150
151
		$this->mimeDetect = 'internal';
152
	}
153
	
154
	/**
155
	 * Close connection
156
	 *
157
	 * @return void
158
	 * @author Dmitry (dio) Levashov
159
	 **/
160
	public function umount() {
161
		$this->db->close();
162
	}
163
	
164
	/**
165
	 * Return debug info for client
166
	 *
167
	 * @return array
168
	 * @author Dmitry (dio) Levashov
169
	 **/
170
	public function debug() {
171
		$debug = parent::debug();
172
		$debug['sqlCount'] = $this->sqlCnt;
173
		if ($this->dbError) {
174
			$debug['dbError'] = $this->dbError;
175
		}
176
		return $debug;
177
	}
178
179
	/**
180
	 * Perform sql query and return result.
181
	 * Increase sqlCnt and save error if occured
182
	 *
183
	 * @param  string  $sql  query
184
	 * @return misc
185
	 * @author Dmitry (dio) Levashov
186
	 **/
187
	protected function query($sql) {
188
		$this->sqlCnt++;
189
		$res = $this->db->query($sql);
190
		if (!$res) {
191
			$this->dbError = $this->db->error;
192
		}
193
		return $res;
194
	}
195
196
	/**
197
	 * Create empty object with required mimetype
198
	 *
199
	 * @param  string  $path  parent dir path
200
	 * @param  string  $name  object name
201
	 * @param  string  $mime  mime type
202
	 * @return bool
203
	 * @author Dmitry (dio) Levashov
204
	 **/
205
	protected function make($path, $name, $mime) {
206
		$sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`) VALUES ("%s", "%s", 0, %d, "%s", "", "%d", "%d")';
207
		$sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write']);
208
		// echo $sql;
209
		return $this->query($sql) && $this->db->affected_rows > 0;
210
	}
211
212
	/**
213
	 * Search files
214
	 *
215
	 * @param  string  $q  search string
216
	 * @param  array   $mimes
217
	 * @return array
218
	 * @author Dmitry (dio) Levashov
219
	 **/
220
	public function search($q, $mimes) {
221
		$result = array();
222
223
		$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 
224
				FROM %s AS f 
225
				WHERE f.name RLIKE "%s"';
226
		
227
		$sql = sprintf($sql, $this->tbf, $this->db->real_escape_string($q));
228
		
229
		if (($res = $this->query($sql))) {
230
			while ($row = $res->fetch_assoc()) {
231
				if ($this->mimeAccepted($row['mime'], $mimes)) {
232
					$id = $row['id'];
233
					if ($row['parent_id']) {
234
						$row['phash'] = $this->encode($row['parent_id']);
235
					} 
236
237
					if ($row['mime'] == 'directory') {
238
						unset($row['width']);
239
						unset($row['height']);
240
					} else {
241
						unset($row['dirs']);
242
					}
243
244
					unset($row['id']);
245
					unset($row['parent_id']);
246
247
248
249
					if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) {
250
						$result[] = $stat;
251
					}
252
				}
253
			}
254
		}
255
		
256
		return $result;
257
	}
258
259
	/**
260
	 * Return temporary file path for required file
261
	 *
262
	 * @param  string  $path   file path
263
	 * @return string
264
	 * @author Dmitry (dio) Levashov
265
	 **/
266
	protected function tmpname($path) {
267
		return $this->tmpPath.DIRECTORY_SEPARATOR.md5($path);
268
	}
269
270
	/**
271
	 * Resize image
272
	 *
273
	 * @param  string   $hash    image file
274
	 * @param  int      $width   new width
275
	 * @param  int      $height  new height
276
	 * @param  bool     $crop    crop image
0 ignored issues
show
Bug introduced by
There is no parameter named $crop. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
277
	 * @return array|false
278
	 * @author Dmitry (dio) Levashov
279
	 * @author Alexey Sukhotin
280
	 **/
281
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) {
282
		if ($this->commandDisabled('resize')) {
283
			return $this->setError(elFinder::ERROR_PERM_DENIED);
284
		}
285
		
286
		if (($file = $this->file($hash)) == false) {
287
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
288
		}
289
		
290
		if (!$file['write'] || !$file['read']) {
291
			return $this->setError(elFinder::ERROR_PERM_DENIED);
292
		}
293
		
294
		$path = $this->decode($hash);
295
		
296
		if (!$this->canResize($path, $file)) {
0 ignored issues
show
Bug introduced by
It seems like $file defined by $this->file($hash) on line 286 can also be of type boolean; however, elFinderVolumeDriver::canResize() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
297
			return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
298
		}
299
300
		$img = $this->tmpname($path);
301
		
302
		if (!($fp = @fopen($img, 'w+'))) {
303
			return false;
304
		}
305
306
		if (($res = $this->query('SELECT content FROM '.$this->tbf.' WHERE id="'.$path.'"'))
307
		&& ($r = $res->fetch_assoc())) {
308
			fwrite($fp, $r['content']);
309
			rewind($fp);
310
			fclose($fp);
311
		} else {
312
			return false;
313
		}
314
315
316
		switch($mode) {
317
			
318
			case 'propresize':
319
				$result = $this->imgResize($img, $width, $height, true, true);
320
				break;
321
322
			case 'crop':
323
				$result = $this->imgCrop($img, $width, $height, $x, $y);
324
				break;
325
326
			case 'fitsquare':
327
				$result = $this->imgSquareFit($img, $width, $height, 'center', 'middle', $bg ? $bg : $this->options['tmbBgColor']);
328
				break;
329
			
330
			default:
331
				$result = $this->imgResize($img, $width, $height, false, true);
332
				break;				
333
    	}
334
		
335
		if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type false|string 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...
336
			
337
			$sql = sprintf('UPDATE %s SET content=LOAD_FILE("%s"), mtime=UNIX_TIMESTAMP() WHERE id=%d', $this->tbf, $this->loadFilePath($img), $path);
338
			
339
			if (!$this->query($sql)) {
340
				$content = file_get_contents($img);
341
				$sql = sprintf('UPDATE %s SET content="%s", mtime=UNIX_TIMESTAMP() WHERE id=%d', $this->tbf, $this->db->real_escape_string($content), $path);
342
				if (!$this->query($sql)) {
343
					@unlink($img);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
344
					return false;
345
				}
346
			}
347
			@unlink($img);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
348
			$this->rmTmb($file);
0 ignored issues
show
Documentation introduced by
$file is of type array|boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
349
			$this->clearcache();
350
			return $this->stat($path);
351
		}
352
		
353
   		return false;
354
	}
355
	
356
357
	/*********************************************************************/
358
	/*                               FS API                              */
359
	/*********************************************************************/
360
	
361
	/**
362
	 * Cache dir contents
363
	 *
364
	 * @param  string  $path  dir path
365
	 * @return void
366
	 * @author Dmitry Levashov
367
	 **/
368
	protected function cacheDir($path) {
369
		$this->dirsCache[$path] = array();
370
371
		$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 
372
				FROM '.$this->tbf.' AS f 
373
				LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime="directory"
374
				WHERE f.parent_id="'.$path.'"
375
				GROUP BY f.id';
376
				
377
		$res = $this->query($sql);
378
		if ($res) {
379
			while ($row = $res->fetch_assoc()) {
380
				// debug($row);
381
				$id = $row['id'];
382
				if ($row['parent_id']) {
383
					$row['phash'] = $this->encode($row['parent_id']);
384
				} 
385
				
386
				if ($row['mime'] == 'directory') {
387
					unset($row['width']);
388
					unset($row['height']);
389
				} else {
390
					unset($row['dirs']);
391
				}
392
				
393
				unset($row['id']);
394
				unset($row['parent_id']);
395
				
396
				
397
				
398
				if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) {
399
					$this->dirsCache[$path][] = $id;
400
				}
401
			}
402
		}
403
		
404
		return $this->dirsCache[$path];
405
	}
406
407
	/**
408
	 * Return array of parents paths (ids)
409
	 *
410
	 * @param  int   $path  file path (id)
411
	 * @return array
412
	 * @author Dmitry (dio) Levashov
413
	 **/
414
	protected function getParents($path) {
415
		$parents = array();
416
417
		while ($path) {
418
			if ($file = $this->stat($path)) {
419
				array_unshift($parents, $path);
420
				$path = isset($file['phash']) ? $this->decode($file['phash']) : false;
421
			}
422
		}
423
		
424
		if (count($parents)) {
425
			array_pop($parents);
426
		}
427
		return $parents;
428
	}
429
430
	/**
431
	 * Return correct file path for LOAD_FILE method
432
	 *
433
	 * @param  string $path  file path (id)
434
	 * @return string
435
	 * @author Troex Nevelin
436
	 **/
437
	protected function loadFilePath($path) {
438
		$realPath = realpath($path);
439
		if (DIRECTORY_SEPARATOR == '\\') { // windows
440
			$realPath = str_replace('\\', '\\\\', $realPath);
441
		}
442
		return $this->db->real_escape_string($realPath);
443
	}
444
445
	/**
446
	 * Recursive files search
447
	 *
448
	 * @param  string  $path   dir path
449
	 * @param  string  $q      search string
450
	 * @param  array   $mimes
451
	 * @return array
452
	 * @author Dmitry (dio) Levashov
453
	 **/
454
	protected function doSearch($path, $q, $mimes) {
455
		return array();
456
	}
457
458
459
	/*********************** paths/urls *************************/
460
	
461
	/**
462
	 * Return parent directory path
463
	 *
464
	 * @param  string  $path  file path
465
	 * @return string
466
	 * @author Dmitry (dio) Levashov
467
	 **/
468
	protected function _dirname($path) {
469
		return ($stat = $this->stat($path)) ? ($stat['phash'] ? $this->decode($stat['phash']) : $this->root) : false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression ($stat = $this->stat($pa... : $this->root : false; of type string|false adds false to the return on line 469 which is incompatible with the return type declared by the abstract method elFinderVolumeDriver::_dirname of type string. It seems like you forgot to handle an error condition.
Loading history...
470
	}
471
472
	/**
473
	 * Return file name
474
	 *
475
	 * @param  string  $path  file path
476
	 * @return string
477
	 * @author Dmitry (dio) Levashov
478
	 **/
479
	protected function _basename($path) {
480
		return ($stat = $this->stat($path)) ? $stat['name'] : false;
481
	}
482
483
	/**
484
	 * Join dir name and file name and return full path
485
	 *
486
	 * @param  string  $dir
487
	 * @param  string  $name
488
	 * @return string
489
	 * @author Dmitry (dio) Levashov
490
	 **/
491
	protected function _joinPath($dir, $name) {
492
		$sql = 'SELECT id FROM '.$this->tbf.' WHERE parent_id="'.$dir.'" AND name="'.$this->db->real_escape_string($name).'"';
493
494
		if (($res = $this->query($sql)) && ($r = $res->fetch_assoc())) {
495
			$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...
496
			return $r['id'];
497
		}
498
		return -1;
499
	}
500
	
501
	/**
502
	 * Return normalized path, this works the same as os.path.normpath() in Python
503
	 *
504
	 * @param  string  $path  path
505
	 * @return string
506
	 * @author Troex Nevelin
507
	 **/
508
	protected function _normpath($path) {
509
		return $path;
510
	}
511
	
512
	/**
513
	 * Return file path related to root dir
514
	 *
515
	 * @param  string  $path  file path
516
	 * @return string
517
	 * @author Dmitry (dio) Levashov
518
	 **/
519
	protected function _relpath($path) {
520
		return $path;
521
	}
522
	
523
	/**
524
	 * Convert path related to root dir into real path
525
	 *
526
	 * @param  string  $path  file path
527
	 * @return string
528
	 * @author Dmitry (dio) Levashov
529
	 **/
530
	protected function _abspath($path) {
531
		return $path;
532
	}
533
	
534
	/**
535
	 * Return fake path started from root dir
536
	 *
537
	 * @param  string  $path  file path
538
	 * @return string
539
	 * @author Dmitry (dio) Levashov
540
	 **/
541
	protected function _path($path) {
542
		if (($file = $this->stat($path)) == false) {
543
			return '';
544
		}
545
		
546
		$parentsIds = $this->getParents($path);
547
		$path = '';
548
		foreach ($parentsIds as $id) {
549
			$dir = $this->stat($id);
550
			$path .= $dir['name'].$this->separator;
551
		}
552
		return $path.$file['name'];
553
	}
554
	
555
	/**
556
	 * Return true if $path is children of $parent
557
	 *
558
	 * @param  string  $path    path to check
559
	 * @param  string  $parent  parent path
560
	 * @return bool
561
	 * @author Dmitry (dio) Levashov
562
	 **/
563
	protected function _inpath($path, $parent) {
564
		return $path == $parent
565
			? true
566
			: in_array($parent, $this->getParents($path));
567
	}
568
	
569
	/***************** file stat ********************/
570
	/**
571
	 * Return stat for given path.
572
	 * Stat contains following fields:
573
	 * - (int)    size    file size in b. required
574
	 * - (int)    ts      file modification time in unix time. required
575
	 * - (string) mime    mimetype. required for folders, others - optionally
576
	 * - (bool)   read    read permissions. required
577
	 * - (bool)   write   write permissions. required
578
	 * - (bool)   locked  is object locked. optionally
579
	 * - (bool)   hidden  is object hidden. optionally
580
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
581
	 * - (string) target  for symlinks - link target path. optionally
582
	 *
583
	 * If file does not exists - returns empty array or false.
584
	 *
585
	 * @param  string  $path    file path 
586
	 * @return array|false
587
	 * @author Dmitry (dio) Levashov
588
	 **/
589
	protected function _stat($path) {
590
		$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
591
				FROM '.$this->tbf.' AS f 
592
				LEFT JOIN '.$this->tbf.' AS p ON p.id=f.parent_id
593
				LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime="directory"
594
				WHERE f.id="'.$path.'"
595
				GROUP BY f.id';
596
597
		$res = $this->query($sql);
598
		
599
		if ($res) {
600
			$stat = $res->fetch_assoc();
601
			if ($stat['parent_id']) {
602
				$stat['phash'] = $this->encode($stat['parent_id']);
603
			} 
604
			if ($stat['mime'] == 'directory') {
605
				unset($stat['width']);
606
				unset($stat['height']);
607
			} else {
608
				unset($stat['dirs']);
609
			}
610
			unset($stat['id']);
611
			unset($stat['parent_id']);
612
			return $stat;
613
			
614
		}
615
		return array();
616
	}
617
	
618
	/**
619
	 * Return true if path is dir and has at least one childs directory
620
	 *
621
	 * @param  string  $path  dir path
622
	 * @return bool
623
	 * @author Dmitry (dio) Levashov
624
	 **/
625
	protected function _subdirs($path) {
626
		return ($stat = $this->stat($path)) && isset($stat['dirs']) ? $stat['dirs'] : false;
627
	}
628
	
629
	/**
630
	 * Return object width and height
631
	 * Usualy used for images, but can be realize for video etc...
632
	 *
633
	 * @param  string  $path  file path
634
	 * @param  string  $mime  file mime type
635
	 * @return string
636
	 * @author Dmitry (dio) Levashov
637
	 **/
638
	protected function _dimensions($path, $mime) {
639
		return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'].'x'.$stat['height'] : '';
640
	}
641
	
642
	/******************** file/dir content *********************/
643
		
644
	/**
645
	 * Return files list in directory.
646
	 *
647
	 * @param  string  $path  dir path
648
	 * @return array
649
	 * @author Dmitry (dio) Levashov
650
	 **/
651
	protected function _scandir($path) {
652
		return isset($this->dirsCache[$path])
653
			? $this->dirsCache[$path]
654
			: $this->cacheDir($path);
655
	}
656
		
657
	/**
658
	 * Open file and return file pointer
659
	 *
660
	 * @param  string  $path  file path
661
	 * @param  string  $mode  open file mode (ignored in this driver)
662
	 * @return resource|false
663
	 * @author Dmitry (dio) Levashov
664
	 **/
665
	protected function _fopen($path, $mode='rb') {
666
		$fp = $this->tmbPath
667
			? @fopen($this->tmpname($path), 'w+')
668
			: @tmpfile();
669
		
670
		
671
		if ($fp) {
672
			if (($res = $this->query('SELECT content FROM '.$this->tbf.' WHERE id="'.$path.'"'))
673
			&& ($r = $res->fetch_assoc())) {
674
				fwrite($fp, $r['content']);
675
				rewind($fp);
676
				return $fp;
677
			} else {
678
				$this->_fclose($fp, $path);
679
			}
680
		}
681
		
682
		return false;
683
	}
684
	
685
	/**
686
	 * Close opened file
687
	 *
688
	 * @param  resource  $fp  file pointer
689
	 * @return bool
690
	 * @author Dmitry (dio) Levashov
691
	 **/
692
	protected function _fclose($fp, $path='') {
693
		@fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
694
		if ($path) {
695
			@unlink($this->tmpname($path));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
696
		}
697
	}
698
	
699
	/********************  file/dir manipulations *************************/
700
	
701
	/**
702
	 * Create dir and return created dir path or false on failed
703
	 *
704
	 * @param  string  $path  parent dir path
705
	 * @param string  $name  new directory name
706
	 * @return string|bool
707
	 * @author Dmitry (dio) Levashov
708
	 **/
709
	protected function _mkdir($path, $name) {
710
		return $this->make($path, $name, 'directory') ? $this->_joinPath($path, $name) : false;
711
	}
712
	
713
	/**
714
	 * Create file and return it's path or false on failed
715
	 *
716
	 * @param  string  $path  parent dir path
717
	 * @param string  $name  new file name
718
	 * @return string|bool
719
	 * @author Dmitry (dio) Levashov
720
	 **/
721
	protected function _mkfile($path, $name) {
722
		return $this->make($path, $name, 'text/plain') ? $this->_joinPath($path, $name) : false;
723
	}
724
	
725
	/**
726
	 * Create symlink. FTP driver does not support symlinks.
727
	 *
728
	 * @param  string  $target  link target
729
	 * @param  string  $path    symlink path
730
	 * @return bool
731
	 * @author Dmitry (dio) Levashov
732
	 **/
733
	protected function _symlink($target, $path, $name) {
734
		return false;
735
	}
736
	
737
	/**
738
	 * Copy file into another file
739
	 *
740
	 * @param  string  $source     source file path
741
	 * @param  string  $targetDir  target directory path
742
	 * @param  string  $name       new file name
743
	 * @return bool
744
	 * @author Dmitry (dio) Levashov
745
	 **/
746
	protected function _copy($source, $targetDir, $name) {
747
		$this->clearcache();
748
		$id = $this->_joinPath($targetDir, $name);
749
750
		$sql = $id > 0
751
			? 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)
752
			: 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);
753
754
		return $this->query($sql);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->query($sql); (misc) is incompatible with the return type declared by the abstract method elFinderVolumeDriver::_copy of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
755
	}
756
	
757
	/**
758
	 * Move file into another parent dir.
759
	 * Return new file path or false.
760
	 *
761
	 * @param  string  $source  source file path
762
	 * @param  string  $target  target dir path
0 ignored issues
show
Documentation introduced by
There is no parameter named $target. Did you maybe mean $targetDir?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
763
	 * @param  string  $name    file name
764
	 * @return string|bool
765
	 * @author Dmitry (dio) Levashov
766
	 **/
767
	protected function _move($source, $targetDir, $name) {
768
		$sql = 'UPDATE %s SET parent_id=%d, name="%s" WHERE id=%d LIMIT 1';
769
		$sql = sprintf($sql, $this->tbf, $targetDir, $this->db->real_escape_string($name), $source);
770
		return $this->query($sql) && $this->db->affected_rows > 0 ? $source : false;
771
	}
772
		
773
	/**
774
	 * Remove file
775
	 *
776
	 * @param  string  $path  file path
777
	 * @return bool
778
	 * @author Dmitry (dio) Levashov
779
	 **/
780
	protected function _unlink($path) {
781
		return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime!="directory" LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows;
782
	}
783
784
	/**
785
	 * Remove dir
786
	 *
787
	 * @param  string  $path  dir path
788
	 * @return bool
789
	 * @author Dmitry (dio) Levashov
790
	 **/
791
	protected function _rmdir($path) {
792
		return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime="directory" LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows;
793
	}
794
	
795
	/**
796
	 * undocumented function
797
	 *
798
	 * @return void
799
	 * @author Dmitry Levashov
800
	 **/
801
	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...
802
		rewind($fp);
803
		$fstat = fstat($fp);
804
		$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...
805
		
806
		
807
	}
808
	
809
	/**
810
	 * Create new file and write into it from file pointer.
811
	 * Return new file path or false on error.
812
	 *
813
	 * @param  resource  $fp   file pointer
814
	 * @param  string    $dir  target dir path
815
	 * @param  string    $name file name
816
	 * @param  array     $stat file stat (required by some virtual fs)
817
	 * @return bool|string
818
	 * @author Dmitry (dio) Levashov
819
	 **/
820
	protected function _save($fp, $dir, $name, $stat) {
821
		$this->clearcache();
822
		
823
		$mime = $stat['mime'];
824
		$w = !empty($stat['width'])  ? $stat['width']  : 0;
825
		$h = !empty($stat['height']) ? $stat['height'] : 0;
826
		
827
		$id = $this->_joinPath($dir, $name);
828
		rewind($fp);
829
		$stat = fstat($fp);
830
		$size = $stat['size'];
831
		
832
		if (($tmpfile = tempnam($this->tmpPath, $this->id))) {
833
			if (($trgfp = fopen($tmpfile, 'wb')) == false) {
834
				unlink($tmpfile);
835
			} else {
836
				while (!feof($fp)) {
837
					fwrite($trgfp, fread($fp, 8192));
838
				}
839
				fclose($trgfp);
840
				
841
				$sql = $id > 0
842
					? '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)'
843
					: 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, "%s", LOAD_FILE("%s"), %d, %d, "%s", %d, %d)';
844
				$sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->loadFilePath($tmpfile), $size, time(), $mime, $w, $h);
845
846
				$res = $this->query($sql);
847
				unlink($tmpfile);
848
				
849
				if ($res) {
850
					return $id > 0 ? $id : $this->db->insert_id;
851
				}
852
			}
853
		}
854
855
		
856
		$content = '';
857
		rewind($fp);
858
		while (!feof($fp)) {
859
			$content .= fread($fp, 8192);
860
		}
861
		
862
		$sql = $id > 0
863
			? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, "%s", "%s", %d, %d, "%s", %d, %d)'
864
			: 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, "%s", "%s", %d, %d, "%s", %d, %d)';
865
		$sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->db->real_escape_string($content), $size, time(), $mime, $w, $h);
866
		
867
		unset($content);
868
869
		if ($this->query($sql)) {
870
			return $id > 0 ? $id : $this->db->insert_id;
871
		}
872
		
873
		return false;
874
	}
875
	
876
	/**
877
	 * Get file contents
878
	 *
879
	 * @param  string  $path  file path
880
	 * @return string|false
881
	 * @author Dmitry (dio) Levashov
882
	 **/
883
	protected function _getContents($path) {
884
		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...
885
	}
886
	
887
	/**
888
	 * Write a string to a file
889
	 *
890
	 * @param  string  $path     file path
891
	 * @param  string  $content  new file content
892
	 * @return bool
893
	 * @author Dmitry (dio) Levashov
894
	 **/
895
	protected function _filePutContents($path, $content) {
896
		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));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->query(spri...tent), time(), $path)); (misc) is incompatible with the return type declared by the abstract method elFinderVolumeDriver::_filePutContents of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
897
	}
898
899
	/**
900
	 * Detect available archivers
901
	 *
902
	 * @return void
903
	 **/
904
	protected function _checkArchivers() {
905
		return;
906
	}
907
908
	/**
909
	 * Unpack archive
910
	 *
911
	 * @param  string  $path  archive path
912
	 * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
913
	 * @return void
914
	 * @author Dmitry (dio) Levashov
915
	 * @author Alexey Sukhotin
916
	 **/
917
	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...
918
		return;
919
	}
920
921
	/**
922
	 * Recursive symlinks search
923
	 *
924
	 * @param  string  $path  file/dir path
925
	 * @return bool
926
	 * @author Dmitry (dio) Levashov
927
	 **/
928
	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...
929
		return false;
930
	}
931
932
	/**
933
	 * Extract files from archive
934
	 *
935
	 * @param  string  $path  archive path
936
	 * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
937
	 * @return true
938
	 * @author Dmitry (dio) Levashov, 
939
	 * @author Alexey Sukhotin
940
	 **/
941
	protected function _extract($path, $arc) {
942
		return false;
943
	}
944
	
945
	/**
946
	 * Create archive and return its path
947
	 *
948
	 * @param  string  $dir    target dir
949
	 * @param  array   $files  files names list
950
	 * @param  string  $name   archive name
951
	 * @param  array   $arc    archiver options
952
	 * @return string|bool
953
	 * @author Dmitry (dio) Levashov, 
954
	 * @author Alexey Sukhotin
955
	 **/
956
	protected function _archive($dir, $files, $name, $arc) {
957
		return false;
958
	}
959
	
960
} // END class 
961