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.

elFinderVolumeDriver   F
last analyzed

Complexity

Total Complexity 634

Size/Duplication

Total Lines 3607
Duplicated Lines 6.1 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 220
loc 3607
rs 0.8
c 0
b 0
f 0
wmc 634
lcom 1
cbo 1

114 Methods

Rating   Name   Duplication   Size   Complexity  
_dirname() 0 1 ?
_basename() 0 1 ?
_joinPath() 0 1 ?
_normpath() 0 1 ?
_relpath() 0 1 ?
_abspath() 0 1 ?
_path() 0 1 ?
_inpath() 0 1 ?
_stat() 0 1 ?
_subdirs() 0 1 ?
_dimensions() 0 1 ?
_scandir() 0 1 ?
_fopen() 0 1 ?
_fclose() 0 1 ?
_mkdir() 0 1 ?
_mkfile() 0 1 ?
_symlink() 0 1 ?
_copy() 0 1 ?
_move() 0 1 ?
_unlink() 0 1 ?
_rmdir() 0 1 ?
_save() 0 1 ?
_getContents() 0 1 ?
_filePutContents() 0 1 ?
_extract() 0 1 ?
_archive() 0 1 ?
_checkArchivers() 0 1 ?
A init() 0 3 1
B configure() 7 30 11
A driverId() 0 3 1
A id() 0 3 1
A debug() 0 8 1
F mount() 38 235 90
A umount() 0 2 1
A error() 0 3 1
A setMimesFilter() 0 5 2
A root() 0 3 1
A defaultPath() 0 3 2
A options() 0 16 5
A commandDisabled() 0 3 1
B mimeAccepted() 0 11 7
A isReadable() 0 4 1
A copyFromAllowed() 0 3 1
A path() 0 3 1
A realpath() 0 4 2
A removed() 0 3 1
A resetRemoved() 0 3 1
A closest() 0 3 2
A file() 0 13 4
B dir() 0 13 7
A scandir() 0 9 3
A ls() 0 16 6
A tree() 3 11 6
B parents() 0 27 11
A tmb() 0 9 3
A size() 0 3 1
A open() 4 8 3
A close() 0 3 1
B mkdir() 3 26 7
B mkfile() 3 26 8
B rename() 0 41 9
A duplicate() 3 21 5
F upload() 3 84 23
D paste() 3 89 28
A getContents() 0 17 4
A putContents() 0 17 5
B extract() 0 26 8
B archive() 0 39 11
C resize() 22 50 13
A rm() 0 5 2
A search() 0 3 1
A dimensions() 0 7 2
A setError() 0 15 3
A encode() 0 21 3
A decode() 0 12 2
A crypt() 0 3 1
A uncrypt() 0 3 1
A nameAccepted() 0 11 3
B uniqueName() 0 28 7
B attr() 14 34 11
B allowCreate() 13 23 8
A stat() 0 8 3
F updateCache() 0 102 38
A cacheDir() 5 9 4
A clearcache() 0 3 1
C mimetype() 0 35 13
A mimetypeInternalDetect() 0 6 3
B countSize() 0 25 10
A isSameType() 0 3 4
A closestByAttr() 0 17 5
A childsByAttr() 0 8 3
A getScandir() 5 13 5
B gettree() 0 18 9
B doSearch() 0 31 11
C copy() 3 39 12
A move() 0 14 2
C copyFrom() 3 50 14
B remove() 0 35 11
A tmbname() 0 3 1
A gettmb() 0 14 5
B canCreateTmb() 0 7 8
A canResize() 0 3 1
D createTmb() 0 65 20
C imgResize() 16 82 14
C imgCrop() 16 57 12
C imgSquareFit() 0 52 11
B imgRotate() 12 41 7
A procExec() 0 30 2
B rmTmb() 0 12 8
A gdImageCreate() 0 16 5
B gdImage() 0 12 7
A gdImageBackground() 0 13 2
A stripos() 0 8 4
F getArchivers() 44 129 35

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

1
<?php
2
/**
3
 * Base class for elFinder volume.
4
 * Provide 2 layers:
5
 *  1. Public API (commands)
6
 *  2. abstract fs API
7
 *
8
 * All abstract methods begin with "_"
9
 *
10
 * @author Dmitry (dio) Levashov
11
 * @author Troex Nevelin
12
 * @author Alexey Sukhotin
13
 **/
14
abstract class elFinderVolumeDriver {
15
16
	/**
17
	 * Driver id
18
	 * Must be started from letter and contains [a-z0-9]
19
	 * Used as part of volume id
20
	 *
21
	 * @var string
22
	 **/
23
	protected $driverId = 'a';
24
25
	/**
26
	 * Volume id - used as prefix for files hashes
27
	 *
28
	 * @var string
29
	 **/
30
	protected $id = '';
31
32
	/**
33
	 * Flag - volume "mounted" and available
34
	 *
35
	 * @var bool
36
	 **/
37
	protected $mounted = false;
38
39
	/**
40
	 * Root directory path
41
	 *
42
	 * @var string
43
	 **/
44
	protected $root = '';
45
46
	/**
47
	 * Root basename | alias
48
	 *
49
	 * @var string
50
	 **/
51
	protected $rootName = '';
52
53
	/**
54
	 * Default directory to open
55
	 *
56
	 * @var string
57
	 **/
58
	protected $startPath = '';
59
60
	/**
61
	 * Base URL
62
	 *
63
	 * @var string
64
	 **/
65
	protected $URL = '';
66
67
	/**
68
	 * Thumbnails dir path
69
	 *
70
	 * @var string
71
	 **/
72
	protected $tmbPath = '';
73
74
	/**
75
	 * Is thumbnails dir writable
76
	 *
77
	 * @var bool
78
	 **/
79
	protected $tmbPathWritable = false;
80
81
	/**
82
	 * Thumbnails base URL
83
	 *
84
	 * @var string
85
	 **/
86
	protected $tmbURL = '';
87
88
	/**
89
	 * Thumbnails size in px
90
	 *
91
	 * @var int
92
	 **/
93
	protected $tmbSize = 48;
94
95
	/**
96
	 * Image manipulation lib name
97
	 * auto|imagick|mogtify|gd
98
	 *
99
	 * @var string
100
	 **/
101
	protected $imgLib = 'auto';
102
103
	/**
104
	 * Library to crypt files name
105
	 *
106
	 * @var string
107
	 **/
108
	protected $cryptLib = '';
109
110
	/**
111
	 * Archivers config
112
	 *
113
	 * @var array
114
	 **/
115
	protected $archivers = array(
116
		'create'  => array(),
117
		'extract' => array()
118
	);
119
120
	/**
121
	 * How many subdirs levels return for tree
122
	 *
123
	 * @var int
124
	 **/
125
	protected $treeDeep = 1;
126
127
	/**
128
	 * Errors from last failed action
129
	 *
130
	 * @var array
131
	 **/
132
	protected $error = array();
133
134
	/**
135
	 * Today 24:00 timestamp
136
	 *
137
	 * @var int
138
	 **/
139
	protected $today = 0;
140
141
	/**
142
	 * Yesterday 24:00 timestamp
143
	 *
144
	 * @var int
145
	 **/
146
	protected $yesterday = 0;
147
148
	/**
149
	 * Object configuration
150
	 *
151
	 * @var array
152
	 **/
153
	protected $options = array(
154
		'id'              => '',
155
		// root directory path
156
		'path'            => '',
157
		// open this path on initial request instead of root path
158
		'startPath'       => '',
159
		// how many subdirs levels return per request
160
		'treeDeep'        => 1,
161
		// root url, not set to disable sending URL to client (replacement for old "fileURL" option)
162
		'URL'             => '',
163
		// directory separator. required by client to show paths correctly
164
		'separator'       => DIRECTORY_SEPARATOR,
165
		// library to crypt/uncrypt files names (not implemented)
166
		'cryptLib'        => '',
167
		// how to detect files mimetypes. (auto/internal/finfo/mime_content_type)
168
		'mimeDetect'      => 'auto',
169
		// mime.types file path (for mimeDetect==internal)
170
		'mimefile'        => '',
171
		// directory for thumbnails
172
		'tmbPath'         => '.tmb',
173
		// mode to create thumbnails dir
174
		'tmbPathMode'     => 0777,
175
		// thumbnails dir URL. Set it if store thumbnails outside root directory
176
		'tmbURL'          => '',
177
		// thumbnails size (px)
178
		'tmbSize'         => 48,
179
		// thumbnails crop (true - crop, false - scale image to fit thumbnail size)
180
		'tmbCrop'         => true,
181
		// thumbnails background color (hex #rrggbb or 'transparent')
182
		'tmbBgColor'      => '#ffffff',
183
		// image manipulations library
184
		'imgLib'          => 'auto',
185
		// on paste file -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
186
		'copyOverwrite'   => true,
187
		// if true - join new and old directories content on paste
188
		'copyJoin'        => true,
189
		// on upload -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
190
		'uploadOverwrite' => true,
191
		// mimetypes allowed to upload
192
		'uploadAllow'     => array(),
193
		// mimetypes not allowed to upload
194
		'uploadDeny'      => array(),
195
		// order to proccess uploadAllow and uploadDeny options
196
		'uploadOrder'     => array('deny', 'allow'),
197
		// maximum upload file size. NOTE - this is size for every uploaded files
198
		'uploadMaxSize'   => 0,
199
		// files dates format
200
		'dateFormat'      => 'j M Y H:i',
201
		// files time format
202
		'timeFormat'      => 'H:i',
203
		// if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders
204
		'checkSubfolders' => true,
205
		// allow to copy from this volume to other ones?
206
		'copyFrom'        => true,
207
		// allow to copy from other volumes to this one?
208
		'copyTo'          => true,
209
		// list of commands disabled on this root
210
		'disabled'        => array(),
211
		// regexp or function name to validate new file name
212
		'acceptedName'    => '/^[^\.].*/', //<-- DONT touch this! Use constructor options to overwrite it!
213
		// function/class method to control files permissions
214
		'accessControl'   => null,
215
		// some data required by access control
216
		'accessControlData' => null,
217
		// default permissions. not set hidden/locked here - take no effect
218
		'defaults'     => array(
219
			'read'   => true,
220
			'write'  => true
221
		),
222
		// files attributes
223
		'attributes'   => array(),
224
		// Allowed archive's mimetypes to create. Leave empty for all available types.
225
		'archiveMimes' => array(),
226
		// Manual config for archivers. See example below. Leave empty for auto detect
227
		'archivers'    => array(),
228
		// required to fix bug on macos
229
		'utf8fix'      => false,
230
		 //                           й                 ё              Й               Ё              Ø         Å
231
		'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"),
232
		'utf8replace'  => array("\u0439",        "\u0451",       "\u0419",       "\u0401",       "\u00d8", "\u00c5")
233
	);
234
235
	/**
236
	 * Defaults permissions
237
	 *
238
	 * @var array
239
	 **/
240
	protected $defaults = array(
241
		'read'   => true,
242
		'write'  => true,
243
		'locked' => false,
244
		'hidden' => false
245
	);
246
247
	/**
248
	 * Access control function/class
249
	 *
250
	 * @var mixed
251
	 **/
252
	protected $attributes = array();
253
254
	/**
255
	 * Access control function/class
256
	 *
257
	 * @var mixed
258
	 **/
259
	protected $access = null;
260
261
	/**
262
	 * Mime types allowed to upload
263
	 *
264
	 * @var array
265
	 **/
266
	protected $uploadAllow = array();
267
268
	/**
269
	 * Mime types denied to upload
270
	 *
271
	 * @var array
272
	 **/
273
	protected $uploadDeny = array();
274
275
	/**
276
	 * Order to validate uploadAllow and uploadDeny
277
	 *
278
	 * @var array
279
	 **/
280
	protected $uploadOrder = array();
281
282
	/**
283
	 * Maximum allowed upload file size.
284
	 * Set as number or string with unit - "10M", "500K", "1G"
285
	 *
286
	 * @var int|string
287
	 **/
288
	protected $uploadMaxSize = 0;
289
290
	/**
291
	 * Mimetype detect method
292
	 *
293
	 * @var string
294
	 **/
295
	protected $mimeDetect = 'auto';
296
297
	/**
298
	 * Flag - mimetypes from externail file was loaded
299
	 *
300
	 * @var bool
301
	 **/
302
	private static $mimetypesLoaded = false;
303
304
	/**
305
	 * Finfo object for mimeDetect == 'finfo'
306
	 *
307
	 * @var object
308
	 **/
309
	protected $finfo = null;
310
311
	/**
312
	 * List of disabled client's commands
313
	 *
314
	 * @var array
315
	 **/
316
	protected $disabled = array();
317
318
	/**
319
	 * default extensions/mimetypes for mimeDetect == 'internal'
320
	 *
321
	 * @var array
322
	 **/
323
	protected static $mimetypes = array(
324
		// applications
325
		'ai'    => 'application/postscript',
326
		'eps'   => 'application/postscript',
327
		'exe'   => 'application/x-executable',
328
		'doc'   => 'application/vnd.ms-word',
329
		'xls'   => 'application/vnd.ms-excel',
330
		'ppt'   => 'application/vnd.ms-powerpoint',
331
		'pps'   => 'application/vnd.ms-powerpoint',
332
		'pdf'   => 'application/pdf',
333
		'xml'   => 'application/xml',
334
		'swf'   => 'application/x-shockwave-flash',
335
		'torrent' => 'application/x-bittorrent',
336
		'jar'   => 'application/x-jar',
337
		// open office (finfo detect as application/zip)
338
		'odt'   => 'application/vnd.oasis.opendocument.text',
339
		'ott'   => 'application/vnd.oasis.opendocument.text-template',
340
		'oth'   => 'application/vnd.oasis.opendocument.text-web',
341
		'odm'   => 'application/vnd.oasis.opendocument.text-master',
342
		'odg'   => 'application/vnd.oasis.opendocument.graphics',
343
		'otg'   => 'application/vnd.oasis.opendocument.graphics-template',
344
		'odp'   => 'application/vnd.oasis.opendocument.presentation',
345
		'otp'   => 'application/vnd.oasis.opendocument.presentation-template',
346
		'ods'   => 'application/vnd.oasis.opendocument.spreadsheet',
347
		'ots'   => 'application/vnd.oasis.opendocument.spreadsheet-template',
348
		'odc'   => 'application/vnd.oasis.opendocument.chart',
349
		'odf'   => 'application/vnd.oasis.opendocument.formula',
350
		'odb'   => 'application/vnd.oasis.opendocument.database',
351
		'odi'   => 'application/vnd.oasis.opendocument.image',
352
		'oxt'   => 'application/vnd.openofficeorg.extension',
353
		// MS office 2007 (finfo detect as application/zip)
354
		'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
355
		'docm'  => 'application/vnd.ms-word.document.macroEnabled.12',
356
		'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
357
		'dotm'  => 'application/vnd.ms-word.template.macroEnabled.12',
358
		'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
359
		'xlsm'  => 'application/vnd.ms-excel.sheet.macroEnabled.12',
360
		'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
361
		'xltm'  => 'application/vnd.ms-excel.template.macroEnabled.12',
362
		'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
363
		'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
364
		'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
365
		'pptm'  => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
366
		'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
367
		'ppsm'  => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
368
		'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
369
		'potm'  => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
370
		'ppam'  => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
371
		'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
372
		'sldm'  => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
373
		// archives
374
		'gz'    => 'application/x-gzip',
375
		'tgz'   => 'application/x-gzip',
376
		'bz'    => 'application/x-bzip2',
377
		'bz2'   => 'application/x-bzip2',
378
		'tbz'   => 'application/x-bzip2',
379
		'zip'   => 'application/zip',
380
		'rar'   => 'application/x-rar',
381
		'tar'   => 'application/x-tar',
382
		'7z'    => 'application/x-7z-compressed',
383
		// texts
384
		'txt'   => 'text/plain',
385
		'php'   => 'text/x-php',
386
		'html'  => 'text/html',
387
		'htm'   => 'text/html',
388
		'js'    => 'text/javascript',
389
		'css'   => 'text/css',
390
		'rtf'   => 'text/rtf',
391
		'rtfd'  => 'text/rtfd',
392
		'py'    => 'text/x-python',
393
		'java'  => 'text/x-java-source',
394
		'rb'    => 'text/x-ruby',
395
		'sh'    => 'text/x-shellscript',
396
		'pl'    => 'text/x-perl',
397
		'xml'   => 'text/xml',
398
		'sql'   => 'text/x-sql',
399
		'c'     => 'text/x-csrc',
400
		'h'     => 'text/x-chdr',
401
		'cpp'   => 'text/x-c++src',
402
		'hh'    => 'text/x-c++hdr',
403
		'log'   => 'text/plain',
404
		'csv'   => 'text/x-comma-separated-values',
405
		// images
406
		'bmp'   => 'image/x-ms-bmp',
407
		'jpg'   => 'image/jpeg',
408
		'jpeg'  => 'image/jpeg',
409
		'gif'   => 'image/gif',
410
		'png'   => 'image/png',
411
		'tif'   => 'image/tiff',
412
		'tiff'  => 'image/tiff',
413
		'tga'   => 'image/x-targa',
414
		'psd'   => 'image/vnd.adobe.photoshop',
415
		'ai'    => 'image/vnd.adobe.photoshop',
416
		'xbm'   => 'image/xbm',
417
		'pxm'   => 'image/pxm',
418
		//audio
419
		'mp3'   => 'audio/mpeg',
420
		'mid'   => 'audio/midi',
421
		'ogg'   => 'audio/ogg',
422
		'oga'   => 'audio/ogg',
423
		'm4a'   => 'audio/x-m4a',
424
		'wav'   => 'audio/wav',
425
		'wma'   => 'audio/x-ms-wma',
426
		// video
427
		'avi'   => 'video/x-msvideo',
428
		'dv'    => 'video/x-dv',
429
		'mp4'   => 'video/mp4',
430
		'mpeg'  => 'video/mpeg',
431
		'mpg'   => 'video/mpeg',
432
		'mov'   => 'video/quicktime',
433
		'wm'    => 'video/x-ms-wmv',
434
		'flv'   => 'video/x-flv',
435
		'mkv'   => 'video/x-matroska',
436
		'webm'  => 'video/webm',
437
		'ogv'   => 'video/ogg',
438
		'ogm'   => 'video/ogg'
439
		);
440
441
	/**
442
	 * Directory separator - required by client
443
	 *
444
	 * @var string
445
	 **/
446
	protected $separator = DIRECTORY_SEPARATOR;
447
448
	/**
449
	 * Mimetypes allowed to display
450
	 *
451
	 * @var array
452
	 **/
453
	protected $onlyMimes = array();
454
455
	/**
456
	 * Store files moved or overwrited files info
457
	 *
458
	 * @var array
459
	 **/
460
	protected $removed = array();
461
462
	/**
463
	 * Cache storage
464
	 *
465
	 * @var array
466
	 **/
467
	protected $cache = array();
468
469
	/**
470
	 * Cache by folders
471
	 *
472
	 * @var array
473
	 **/
474
	protected $dirsCache = array();
475
476
	/*********************************************************************/
477
	/*                            INITIALIZATION                         */
478
	/*********************************************************************/
479
480
	/**
481
	 * Prepare driver before mount volume.
482
	 * Return true if volume is ready.
483
	 *
484
	 * @return bool
485
	 * @author Dmitry (dio) Levashov
486
	 **/
487
	protected function init() {
488
		return true;
489
	}
490
491
	/**
492
	 * Configure after successfull mount.
493
	 * By default set thumbnails path and image manipulation library.
494
	 *
495
	 * @return void
496
	 * @author Dmitry (dio) Levashov
497
	 **/
498
	protected function configure() {
499
		// set thumbnails path
500
		$path = $this->options['tmbPath'];
501
		if ($path) {
502 View Code Duplication
			if (!file_exists($path)) {
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...
503
				if (@mkdir($path)) {
504
					chmod($path, $this->options['tmbPathMode']);
505
				} else {
506
					$path = '';
507
				}
508
			}
509
510
			if (is_dir($path) && is_readable($path)) {
511
				$this->tmbPath = $path;
512
				$this->tmbPathWritable = is_writable($path);
513
			}
514
		}
515
516
		// set image manipulation library
517
		$type = preg_match('/^(imagick|gd|auto)$/i', $this->options['imgLib'])
518
			? strtolower($this->options['imgLib'])
519
			: 'auto';
520
521
		if (($type == 'imagick' || $type == 'auto') && extension_loaded('imagick')) {
522
			$this->imgLib = 'imagick';
523
		} else {
524
			$this->imgLib = function_exists('gd_info') ? 'gd' : '';
525
		}
526
527
	}
528
529
530
	/*********************************************************************/
531
	/*                              PUBLIC API                           */
532
	/*********************************************************************/
533
534
	/**
535
	 * Return driver id. Used as a part of volume id.
536
	 *
537
	 * @return string
538
	 * @author Dmitry (dio) Levashov
539
	 **/
540
	public function driverId() {
541
		return $this->driverId;
542
	}
543
544
	/**
545
	 * Return volume id
546
	 *
547
	 * @return string
548
	 * @author Dmitry (dio) Levashov
549
	 **/
550
	public function id() {
551
		return $this->id;
552
	}
553
554
	/**
555
	 * Return debug info for client
556
	 *
557
	 * @return array
558
	 * @author Dmitry (dio) Levashov
559
	 **/
560
	public function debug() {
561
		return array(
562
			'id'         => $this->id(),
563
			'name'       => strtolower(substr(get_class($this), strlen('elfinderdriver'))),
564
			'mimeDetect' => $this->mimeDetect,
565
			'imgLib'     => $this->imgLib
566
		);
567
	}
568
569
	/**
570
	 * "Mount" volume.
571
	 * Return true if volume available for read or write,
572
	 * false - otherwise
573
	 *
574
	 * @return bool
575
	 * @author Dmitry (dio) Levashov
576
	 * @author Alexey Sukhotin
577
	 **/
578
	public function mount(array $opts) {
579
		if (!isset($opts['path']) || $opts['path'] === '') {
580
			return $this->setError('Path undefined.');;
581
		}
582
583
		$this->options = array_merge($this->options, $opts);
584
		$this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
585
		$this->root = $this->_normpath($this->options['path']);
586
		$this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
587
588
		// default file attribute
589
		$this->defaults = array(
590
			'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
591
			'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
592
			'locked'  => false,
593
			'hidden'  => false
594
		);
595
596
		// root attributes
597
		$this->attributes[] = array(
598
			'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
599
			'locked'  => true,
600
			'hidden'  => false
601
		);
602
		// set files attributes
603 View Code Duplication
		if (!empty($this->options['attributes']) && is_array($this->options['attributes'])) {
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...
604
605
			foreach ($this->options['attributes'] as $a) {
606
				// attributes must contain pattern and at least one rule
607
				if (!empty($a['pattern']) || count($a) > 1) {
608
					$this->attributes[] = $a;
609
				}
610
			}
611
		}
612
613
		if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
614
			$this->access = $this->options['accessControl'];
615
		}
616
617
		$this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
618
		$this->yesterday = $this->today-86400;
619
620
		// debug($this->attributes);
621
		if (!$this->init()) {
622
			return false;
623
		}
624
625
		// check some options is arrays
626
		$this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
627
			? $this->options['uploadAllow']
628
			: array();
629
630
		$this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
631
			? $this->options['uploadDeny']
632
			: array();
633
634
		if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
635
			$parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
636
			$this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
637
		} else { // telephat_mode off
638
			$this->uploadOrder = $this->options['uploadOrder'];
639
		}
640
641
		if (!empty($this->options['uploadMaxSize'])) {
642
			$size = ''.$this->options['uploadMaxSize'];
643
			$unit = strtolower(substr($size, strlen($size) - 1));
644
			$n = 1;
645
			switch ($unit) {
646
				case 'k':
647
					$n = 1024;
648
					break;
649
				case 'm':
650
					$n = 1048576;
651
					break;
652
				case 'g':
653
					$n = 1073741824;
654
			}
655
			$this->uploadMaxSize = intval($size)*$n;
656
		}
657
658
		$this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
659
			? $this->options['disabled']
660
			: array();
661
662
		$this->cryptLib   = $this->options['cryptLib'];
663
		$this->mimeDetect = $this->options['mimeDetect'];
664
665
		// find available mimetype detect method
666
		$type = strtolower($this->options['mimeDetect']);
667
		$type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
668
		$regexp = '/text\/x\-(php|c\+\+)/';
669
670
		if (($type == 'finfo' || $type == 'auto')
671
		&& class_exists('finfo')) {
672
			$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
673
		} else {
674
			$tmpFileInfo = false;
675
		}
676
677
		if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
678
			$type = 'finfo';
679
			$this->finfo = finfo_open(FILEINFO_MIME);
680
		} elseif (($type == 'mime_content_type' || $type == 'auto')
681
		&& function_exists('mime_content_type')
682
		&& preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) {
0 ignored issues
show
Bug introduced by
explode(';', mime_content_type(__FILE__)) cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
683
			$type = 'mime_content_type';
684
		} else {
685
			$type = 'internal';
686
		}
687
		$this->mimeDetect = $type;
688
689
		// load mimes from external file for mimeDetect == 'internal'
690
		// based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
691
		// file must be in file directory or in parent one
692
		if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
693
			self::$mimetypesLoaded = true;
694
			$this->mimeDetect = 'internal';
695
			$file = false;
696
			if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
697
				$file = $this->options['mimefile'];
698
			} elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
699
				$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
700
			} elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
701
				$file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
702
			}
703
704
			if ($file && file_exists($file)) {
705
				$mimecf = file($file);
706
707
				foreach ($mimecf as $line_num => $line) {
708
					if (!preg_match('/^\s*#/', $line)) {
709
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
710
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
711
							if (!isset(self::$mimetypes[$mime[$i]])) {
712
								self::$mimetypes[$mime[$i]] = $mime[0];
713
							}
714
						}
715
					}
716
				}
717
			}
718
		}
719
720
		$this->rootName = empty($this->options['alias']) ? $this->_basename($this->root) : $this->options['alias'];
721
		$root = $this->stat($this->root);
722
723
		if (!$root) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $root 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...
724
			return $this->setError('Root folder does not exists.');
725
		}
726
		if (!$root['read'] && !$root['write']) {
727
			return $this->setError('Root folder has not read and write permissions.');
728
		}
729
730
		// debug($root);
731
732
		if ($root['read']) {
733
			// check startPath - path to open by default instead of root
734
			if ($this->options['startPath']) {
735
				$start = $this->stat($this->options['startPath']);
736
				if (!empty($start)
737
				&& $start['mime'] == 'directory'
738
				&& $start['read']
739
				&& empty($start['hidden'])
740
				&& $this->_inpath($this->options['startPath'], $this->root)) {
741
					$this->startPath = $this->options['startPath'];
742
					if (substr($this->startPath, -1, 1) == $this->options['separator']) {
743
						$this->startPath = substr($this->startPath, 0, -1);
744
					}
745
				}
746
			}
747
		} else {
748
			$this->options['URL']     = '';
749
			$this->options['tmbURL']  = '';
750
			$this->options['tmbPath'] = '';
751
			// read only volume
752
			array_unshift($this->attributes, array(
753
				'pattern' => '/.*/',
754
				'read'    => false
755
			));
756
		}
757
		$this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
758
		$this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
759
		$this->URL      = $this->options['URL'];
760
		if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
761
			$this->URL .= '/';
762
		}
763
764
		$this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
765
		if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
766
			$this->tmbURL .= '/';
767
		}
768
769
		$this->nameValidator = is_string($this->options['acceptedName']) && !empty($this->options['acceptedName'])
0 ignored issues
show
Bug introduced by
The property nameValidator 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...
770
			? $this->options['acceptedName']
771
			: '';
772
773
		$this->_checkArchivers();
774
		// manual control archive types to create
775 View Code Duplication
		if (!empty($this->options['archiveMimes']) && is_array($this->options['archiveMimes'])) {
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...
776
			foreach ($this->archivers['create'] as $mime => $v) {
777
				if (!in_array($mime, $this->options['archiveMimes'])) {
778
					unset($this->archivers['create'][$mime]);
779
				}
780
			}
781
		}
782
783
		// manualy add archivers
784 View Code Duplication
		if (!empty($this->options['archivers']['create']) && is_array($this->options['archivers']['create'])) {
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...
785
			foreach ($this->options['archivers']['create'] as $mime => $conf) {
786
				if (strpos($mime, 'application/') === 0
787
				&& !empty($conf['cmd'])
788
				&& isset($conf['argc'])
789
				&& !empty($conf['ext'])
790
				&& !isset($this->archivers['create'][$mime])) {
791
					$this->archivers['create'][$mime] = $conf;
792
				}
793
			}
794
		}
795
796 View Code Duplication
		if (!empty($this->options['archivers']['extract']) && is_array($this->options['archivers']['extract'])) {
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...
797
			foreach ($this->options['archivers']['extract'] as $mime => $conf) {
798
				if (strpos($mime, 'application/') === 0
799
				&& !empty($conf['cmd'])
800
				&& isset($conf['argc'])
801
				&& !empty($conf['ext'])
802
				&& !isset($this->archivers['extract'][$mime])) {
803
					$this->archivers['extract'][$mime] = $conf;
804
				}
805
			}
806
		}
807
808
		$this->configure();
809
		// echo $this->uploadMaxSize;
810
		// echo $this->options['uploadMaxSize'];
811
		return $this->mounted = true;
812
	}
813
814
	/**
815
	 * Some "unmount" stuffs - may be required by virtual fs
816
	 *
817
	 * @return void
818
	 * @author Dmitry (dio) Levashov
819
	 **/
820
	public function umount() {
821
	}
822
823
	/**
824
	 * Return error message from last failed action
825
	 *
826
	 * @return array
827
	 * @author Dmitry (dio) Levashov
828
	 **/
829
	public function error() {
830
		return $this->error;
831
	}
832
833
	/**
834
	 * Set mimetypes allowed to display to client
835
	 *
836
	 * @param  array  $mimes
837
	 * @return void
838
	 * @author Dmitry (dio) Levashov
839
	 **/
840
	public function setMimesFilter($mimes) {
841
		if (is_array($mimes)) {
842
			$this->onlyMimes = $mimes;
843
		}
844
	}
845
846
	/**
847
	 * Return root folder hash
848
	 *
849
	 * @return string
850
	 * @author Dmitry (dio) Levashov
851
	 **/
852
	public function root() {
853
		return $this->encode($this->root);
854
	}
855
856
	/**
857
	 * Return root or startPath hash
858
	 *
859
	 * @return string
860
	 * @author Dmitry (dio) Levashov
861
	 **/
862
	public function defaultPath() {
863
		return $this->encode($this->startPath ? $this->startPath : $this->root);
864
	}
865
866
	/**
867
	 * Return volume options required by client:
868
	 *
869
	 * @return array
870
	 * @author Dmitry (dio) Levashov
871
	 **/
872
	public function options($hash) {
873
		return array(
874
			'path'          => $this->_path($this->decode($hash)),
875
			'url'           => $this->URL,
876
			'tmbUrl'        => $this->tmbURL,
877
			'disabled'      => $this->disabled,
878
			'separator'     => $this->separator,
879
			'copyOverwrite' => intval($this->options['copyOverwrite']),
880
			'archivers'     => array(
881
				// 'create'  => array_keys($this->archivers['create']),
882
				// 'extract' => array_keys($this->archivers['extract']),
883
				'create'  => isset($this->archivers['create']) && is_array($this->archivers['create'])  ? array_keys($this->archivers['create'])  : array(),
884
				'extract' => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array()
885
			)
886
		);
887
	}
888
889
	/**
890
	 * Return true if command disabled in options
891
	 *
892
	 * @param  string  $cmd  command name
893
	 * @return bool
894
	 * @author Dmitry (dio) Levashov
895
	 **/
896
	public function commandDisabled($cmd) {
897
		return in_array($cmd, $this->disabled);
898
	}
899
900
	/**
901
	 * Return true if mime is required mimes list
902
	 *
903
	 * @param  string     $mime   mime type to check
904
	 * @param  array      $mimes  allowed mime types list or not set to use client mimes list
905
	 * @param  bool|null  $empty  what to return on empty list
906
	 * @return bool|null
907
	 * @author Dmitry (dio) Levashov
908
	 * @author Troex Nevelin
909
	 **/
910
	public function mimeAccepted($mime, $mimes = array(), $empty = true) {
911
		$mimes = !empty($mimes) ? $mimes : $this->onlyMimes;
912
		if (empty($mimes)) {
913
			return $empty;
914
		}
915
		return $mime == 'directory'
916
			|| in_array('all', $mimes)
917
			|| in_array('All', $mimes)
918
			|| in_array($mime, $mimes)
919
			|| in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
920
	}
921
922
	/**
923
	 * Return true if voume is readable.
924
	 *
925
	 * @return bool
926
	 * @author Dmitry (dio) Levashov
927
	 **/
928
	public function isReadable() {
929
		$stat = $this->stat($this->root);
930
		return $stat['read'];
931
	}
932
933
	/**
934
	 * Return true if copy from this volume allowed
935
	 *
936
	 * @return bool
937
	 * @author Dmitry (dio) Levashov
938
	 **/
939
	public function copyFromAllowed() {
940
		return !!$this->options['copyFrom'];
941
	}
942
943
	/**
944
	 * Return file path related to root
945
	 *
946
	 * @param  string   $hash  file hash
947
	 * @return string
948
	 * @author Dmitry (dio) Levashov
949
	 **/
950
	public function path($hash) {
951
		return $this->_path($this->decode($hash));
952
	}
953
954
	/**
955
	 * Return file real path if file exists
956
	 *
957
	 * @param  string  $hash  file hash
958
	 * @return string
959
	 * @author Dmitry (dio) Levashov
960
	 **/
961
	public function realpath($hash) {
962
		$path = $this->decode($hash);
963
		return $this->stat($path) ? $path : false;
964
	}
965
966
	/**
967
	 * Return list of moved/overwrited files
968
	 *
969
	 * @return array
970
	 * @author Dmitry (dio) Levashov
971
	 **/
972
	public function removed() {
973
		return $this->removed;
974
	}
975
976
	/**
977
	 * Clean removed files list
978
	 *
979
	 * @return void
980
	 * @author Dmitry (dio) Levashov
981
	 **/
982
	public function resetRemoved() {
983
		$this->removed = array();
984
	}
985
986
	/**
987
	 * Return file/dir hash or first founded child hash with required attr == $val
988
	 *
989
	 * @param  string   $hash  file hash
990
	 * @param  string   $attr  attribute name
991
	 * @param  bool     $val   attribute value
992
	 * @return string|false
993
	 * @author Dmitry (dio) Levashov
994
	 **/
995
	public function closest($hash, $attr, $val) {
996
		return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
997
	}
998
999
	/**
1000
	 * Return file info or false on error
1001
	 *
1002
	 * @param  string   $hash      file hash
1003
	 * @param  bool     $realpath  add realpath field to file info
0 ignored issues
show
Bug introduced by
There is no parameter named $realpath. 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...
1004
	 * @return array|false
1005
	 * @author Dmitry (dio) Levashov
1006
	 **/
1007
	public function file($hash) {
1008
		$path = $this->decode($hash);
1009
1010
		return ($file = $this->stat($path)) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1011
1012
		if (($file = $this->stat($path)) != false) {
0 ignored issues
show
Unused Code introduced by
if (($file = $this->stat... } return $file; } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1013
			if ($realpath) {
1014
				$file['realpath'] = $path;
1015
			}
1016
			return $file;
1017
		}
1018
		return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1019
	}
1020
1021
	/**
1022
	 * Return folder info
1023
	 *
1024
	 * @param  string   $hash  folder hash
1025
	 * @param  bool     $hidden  return hidden file info
0 ignored issues
show
Bug introduced by
There is no parameter named $hidden. 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...
1026
	 * @return array|false
1027
	 * @author Dmitry (dio) Levashov
1028
	 **/
1029
	public function dir($hash, $resolveLink=false) {
1030
		if (($dir = $this->file($hash)) == false) {
1031
			return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1032
		}
1033
1034
		if ($resolveLink && !empty($dir['thash'])) {
1035
			$dir = $this->file($dir['thash']);
1036
		}
1037
1038
		return $dir && $dir['mime'] == 'directory' && empty($dir['hidden'])
1039
			? $dir
1040
			: $this->setError(elFinder::ERROR_NOT_DIR);
1041
	}
1042
1043
	/**
1044
	 * Return directory content or false on error
1045
	 *
1046
	 * @param  string   $hash   file hash
1047
	 * @return array|false
1048
	 * @author Dmitry (dio) Levashov
1049
	 **/
1050
	public function scandir($hash) {
1051
		if (($dir = $this->dir($hash)) == false) {
1052
			return false;
1053
		}
1054
1055
		return $dir['read']
1056
			? $this->getScandir($this->decode($hash))
1057
			: $this->setError(elFinder::ERROR_PERM_DENIED);
1058
	}
1059
1060
	/**
1061
	 * Return dir files names list
1062
	 *
1063
	 * @param  string  $hash   file hash
1064
	 * @return array
1065
	 * @author Dmitry (dio) Levashov
1066
	 **/
1067
	public function ls($hash) {
1068
		if (($dir = $this->dir($hash)) == false || !$dir['read']) {
1069
			return false;
1070
		}
1071
1072
		$list = array();
1073
		$path = $this->decode($hash);
1074
1075
		foreach ($this->getScandir($path) as $stat) {
1076
			if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) {
1077
				$list[] = $stat['name'];
1078
			}
1079
		}
1080
1081
		return $list;
1082
	}
1083
1084
	/**
1085
	 * Return subfolders for required folder or false on error
1086
	 *
1087
	 * @param  string   $hash  folder hash or empty string to get tree from root folder
1088
	 * @param  int      $deep  subdir deep
1089
	 * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1090
	 * @return array|false
1091
	 * @author Dmitry (dio) Levashov
1092
	 **/
1093
	public function tree($hash='', $deep=0, $exclude='') {
1094
		$path = $hash ? $this->decode($hash) : $this->root;
1095
1096 View Code Duplication
		if (($dir = $this->stat($path)) == false || $dir['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...
1097
			return false;
1098
		}
1099
1100
		$dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1101
		array_unshift($dirs, $dir);
1102
		return $dirs;
1103
	}
1104
1105
	/**
1106
	 * Return part of dirs tree from required dir up to root dir
1107
	 *
1108
	 * @param  string    $hash   directory hash
1109
	 * @param  bool|null $lineal only lineal parents
1110
	 * @return array
1111
	 * @author Dmitry (dio) Levashov
1112
	 **/
1113
	public function parents($hash, $lineal = false) {
1114
		if (($current = $this->dir($hash)) == false) {
1115
			return false;
1116
		}
1117
1118
		$path = $this->decode($hash);
1119
		$tree = array();
1120
1121
		while ($path && $path != $this->root) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $path of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
1122
			$path = $this->_dirname($path);
1123
			$stat = $this->stat($path);
1124
			if (!empty($stat['hidden']) || !$stat['read']) {
1125
				return false;
1126
			}
1127
1128
			array_unshift($tree, $stat);
1129
			if (!$lineal && ($path != $this->root)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lineal 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...
1130
				foreach ($this->gettree($path, 0) as $dir) {
1131
					if (!in_array($dir, $tree)) {
1132
						$tree[] = $dir;
1133
					}
1134
				}
1135
			}
1136
		}
1137
1138
		return $tree ? $tree : array($current);
1139
	}
1140
1141
	/**
1142
	 * Create thumbnail for required file and return its name of false on failed
1143
	 *
1144
	 * @return string|false
1145
	 * @author Dmitry (dio) Levashov
1146
	 **/
1147
	public function tmb($hash) {
1148
		$path = $this->decode($hash);
1149
		$stat = $this->stat($path);
1150
1151
		if (isset($stat['tmb'])) {
1152
			return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1153
		}
1154
		return false;
1155
	}
1156
1157
	/**
1158
	 * Return file size / total directory size
1159
	 *
1160
	 * @param  string   file hash
1161
	 * @return int
1162
	 * @author Dmitry (dio) Levashov
1163
	 **/
1164
	public function size($hash) {
1165
		return $this->countSize($this->decode($hash));
1166
	}
1167
1168
	/**
1169
	 * Open file for reading and return file pointer
1170
	 *
1171
	 * @param  string   file hash
1172
	 * @return Resource
1173
	 * @author Dmitry (dio) Levashov
1174
	 **/
1175
	public function open($hash) {
1176 View Code Duplication
		if (($file = $this->file($hash)) == 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...
1177
		|| $file['mime'] == 'directory') {
1178
			return false;
1179
		}
1180
1181
		return $this->_fopen($this->decode($hash), 'rb');
1182
	}
1183
1184
	/**
1185
	 * Close file pointer
1186
	 *
1187
	 * @param  Resource  $fp   file pointer
1188
	 * @param  string    $hash file hash
1189
	 * @return void
1190
	 * @author Dmitry (dio) Levashov
1191
	 **/
1192
	public function close($fp, $hash) {
1193
		$this->_fclose($fp, $this->decode($hash));
1194
	}
1195
1196
	/**
1197
	 * Create directory and return dir info
1198
	 *
1199
	 * @param  string   $dst  destination directory
1200
	 * @param  string   $name directory name
1201
	 * @return array|false
1202
	 * @author Dmitry (dio) Levashov
1203
	 **/
1204
	public function mkdir($dst, $name) {
1205
		if ($this->commandDisabled('mkdir')) {
1206
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1207
		}
1208
1209
		if (!$this->nameAccepted($name)) {
1210
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1211
		}
1212
1213 View Code Duplication
		if (($dir = $this->dir($dst)) == 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...
1214
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1215
		}
1216
1217
		if (!$dir['write']) {
1218
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1219
		}
1220
1221
		$path = $this->decode($dst);
1222
		$dst  = $this->_joinPath($path, $name);
1223
		$stat = $this->stat($dst);
1224
		if (!empty($stat)) {
1225
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1226
		}
1227
		$this->clearcache();
1228
		return ($path = $this->_mkdir($path, $name)) ? $this->stat($path) : false;
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->_mkdir($path, $name) on line 1228 can also be of type boolean; however, elFinderVolumeDriver::stat() does only seem to accept string, 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...
1229
	}
1230
1231
	/**
1232
	 * Create empty file and return its info
1233
	 *
1234
	 * @param  string   $dst  destination directory
1235
	 * @param  string   $name file name
1236
	 * @return array|false
1237
	 * @author Dmitry (dio) Levashov
1238
	 **/
1239
	public function mkfile($dst, $name) {
1240
		if ($this->commandDisabled('mkfile')) {
1241
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1242
		}
1243
1244
		if (!$this->nameAccepted($name)) {
1245
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1246
		}
1247
1248 View Code Duplication
		if (($dir = $this->dir($dst)) == 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...
1249
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1250
		}
1251
1252
		$path = $this->decode($dst);
1253
1254
		if (!$dir['write'] || !$this->allowCreate($path, $name)) {
1255
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1256
		}
1257
1258
		if ($this->stat($this->_joinPath($path, $name))) {
1259
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1260
		}
1261
1262
		$this->clearcache();
1263
		return ($path = $this->_mkfile($path, $name)) ? $this->stat($path) : false;
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->_mkfile($path, $name) on line 1263 can also be of type boolean; however, elFinderVolumeDriver::stat() does only seem to accept string, 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...
1264
	}
1265
1266
	/**
1267
	 * Rename file and return file info
1268
	 *
1269
	 * @param  string  $hash  file hash
1270
	 * @param  string  $name  new file name
1271
	 * @return array|false
1272
	 * @author Dmitry (dio) Levashov
1273
	 **/
1274
	public function rename($hash, $name) {
1275
		if ($this->commandDisabled('rename')) {
1276
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1277
		}
1278
1279
		if (!$this->nameAccepted($name)) {
1280
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1281
		}
1282
1283
		if (!($file = $this->file($hash))) {
1284
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1285
		}
1286
1287
		if ($name == $file['name']) {
1288
			return $file;
1289
		}
1290
1291
		if (!empty($file['locked'])) {
1292
			return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1293
		}
1294
1295
		$path = $this->decode($hash);
1296
		$dir  = $this->_dirname($path);
1297
		$stat = $this->stat($this->_joinPath($dir, $name));
1298
		if ($stat) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1299
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1300
		}
1301
1302
		if (!$this->allowCreate($dir, $name)) {
1303
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1304
		}
1305
1306
		$this->rmTmb($file); // remove old name tmbs, we cannot do this after dir move
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...
1307
1308
1309
		if (($path = $this->_move($path, $dir, $name))) {
1310
			$this->clearcache();
1311
			return $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->_move($path, $dir, $name) on line 1309 can also be of type boolean; however, elFinderVolumeDriver::stat() does only seem to accept string, 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...
1312
		}
1313
		return false;
1314
	}
1315
1316
	/**
1317
	 * Create file copy with suffix "copy number" and return its info
1318
	 *
1319
	 * @param  string   $hash    file hash
1320
	 * @param  string   $suffix  suffix to add to file name
1321
	 * @return array|false
1322
	 * @author Dmitry (dio) Levashov
1323
	 **/
1324
	public function duplicate($hash, $suffix='copy') {
1325
		if ($this->commandDisabled('duplicate')) {
1326
			return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1327
		}
1328
1329 View Code Duplication
		if (($file = $this->file($hash)) == 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...
1330
			return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1331
		}
1332
1333
		$path = $this->decode($hash);
1334
		$dir  = $this->_dirname($path);
1335
		$name = $this->uniqueName($dir, $this->_basename($path), ' '.$suffix.' ');
1336
1337
		if (!$this->allowCreate($dir, $name)) {
1338
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1339
		}
1340
1341
		return ($path = $this->copy($path, $dir, $name)) == false
1342
			? false
1343
			: $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copy($path, $dir, $name) on line 1341 can also be of type boolean; however, elFinderVolumeDriver::stat() does only seem to accept string, 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...
1344
	}
1345
1346
	/**
1347
	 * Save uploaded file.
1348
	 * On success return array with new file stat and with removed file hash (if existed file was replaced)
1349
	 *
1350
	 * @param  Resource $fp      file pointer
1351
	 * @param  string   $dst     destination folder hash
1352
	 * @param  string   $src     file name
0 ignored issues
show
Bug introduced by
There is no parameter named $src. 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...
1353
	 * @param  string   $tmpname file tmp name - required to detect mime type
1354
	 * @return array|false
1355
	 * @author Dmitry (dio) Levashov
1356
	 **/
1357
	public function upload($fp, $dst, $name, $tmpname) {
1358
		if ($this->commandDisabled('upload')) {
1359
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1360
		}
1361
1362 View Code Duplication
		if (($dir = $this->dir($dst)) == 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...
1363
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1364
		}
1365
1366
		if (!$dir['write']) {
1367
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1368
		}
1369
1370
		if (!$this->nameAccepted($name)) {
1371
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1372
		}
1373
1374
		$mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name);
1375
		if ($mime == 'unknown' && $this->mimeDetect == 'internal') {
1376
			$mime = elFinderVolumeDriver::mimetypeInternalDetect($name);
1377
		}
1378
1379
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
1380
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
1381
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
1382
		$upload = true; // default to allow
0 ignored issues
show
Unused Code introduced by
$upload 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...
1383
		if (strtolower($this->uploadOrder[0]) == 'allow') { // array('allow', 'deny'), default is to 'deny'
1384
			$upload = false; // default is deny
1385
			if (!$deny && ($allow === true)) { // match only allow
0 ignored issues
show
Bug Best Practice introduced by
The expression $deny 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...
1386
				$upload = true;
1387
			}// else (both match | no match | match only deny) { deny }
1388
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
1389
			$upload = true; // default is allow
1390
			if (($deny === true) && !$allow) { // match only deny
0 ignored issues
show
Bug Best Practice introduced by
The expression $allow 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...
1391
				$upload = false;
1392
			} // else (both match | no match | match only allow) { allow }
1393
		}
1394
		if (!$upload) {
1395
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1396
		}
1397
1398
		if ($this->uploadMaxSize > 0 && filesize($tmpname) > $this->uploadMaxSize) {
1399
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1400
		}
1401
1402
		$dstpath = $this->decode($dst);
1403
		$test    = $this->_joinPath($dstpath, $name);
1404
1405
		$file = $this->stat($test);
1406
		$this->clearcache();
1407
1408
		if ($file) { // file exists
0 ignored issues
show
Bug Best Practice introduced by
The expression $file 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...
1409
			if ($this->options['uploadOverwrite']) {
1410
				if (!$file['write']) {
1411
					return $this->setError(elFinder::ERROR_PERM_DENIED);
1412
				} elseif ($file['mime'] == 'directory') {
1413
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1414
				}
1415
				$this->remove($test);
1416
			} else {
1417
				$name = $this->uniqueName($dstpath, $name, '-', false);
1418
			}
1419
		}
1420
1421
		$stat = array(
1422
			'mime'   => $mime,
1423
			'width'  => 0,
1424
			'height' => 0,
1425
			'size'   => filesize($tmpname));
1426
1427
		// $w = $h = 0;
1428
		if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
1429
			$stat['width'] = $s[0];
1430
			$stat['height'] = $s[1];
1431
		}
1432
		// $this->clearcache();
1433
		if (($path = $this->_save($fp, $dstpath, $name, $stat)) == false) {
1434
			return false;
1435
		}
1436
1437
1438
1439
		return $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->_save($fp, $dstpath, $name, $stat) on line 1433 can also be of type boolean; however, elFinderVolumeDriver::stat() does only seem to accept string, 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...
1440
	}
1441
1442
	/**
1443
	 * Paste files
1444
	 *
1445
	 * @param  Object  $volume  source volume
1446
	 * @param  string  $source  file hash
0 ignored issues
show
Bug introduced by
There is no parameter named $source. 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...
1447
	 * @param  string  $dst     destination dir hash
1448
	 * @param  bool    $rmSrc   remove source after copy?
1449
	 * @return array|false
1450
	 * @author Dmitry (dio) Levashov
1451
	 **/
1452
	public function paste($volume, $src, $dst, $rmSrc = false) {
1453
		$err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
1454
1455
		if ($this->commandDisabled('paste')) {
1456
			return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
1457
		}
1458
1459
		if (($file = $volume->file($src, $rmSrc)) == false) {
1460
			return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
1461
		}
1462
1463
		$name = $file['name'];
1464
		$errpath = $volume->path($src);
1465
1466 View Code Duplication
		if (($dir = $this->dir($dst)) == 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...
1467
			return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1468
		}
1469
1470
		if (!$dir['write'] || !$file['read']) {
1471
			return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1472
		}
1473
1474
		$destination = $this->decode($dst);
1475
1476
		if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
1477
			return $rmSrc
1478
				? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
1479
				: $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1480
		}
1481
1482
		$test = $this->_joinPath($destination, $name);
1483
		$stat = $this->stat($test);
1484
		$this->clearcache();
1485
		if ($stat) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1486
			if ($this->options['copyOverwrite']) {
1487
				// do not replace file with dir or dir with file
1488
				if (!$this->isSameType($file['mime'], $stat['mime'])) {
1489
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->_path($test));
1490
				}
1491
				// existed file is not writable
1492
				if (!$stat['write']) {
1493
					return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1494
				}
1495
				// existed file locked or has locked child
1496
				if (($locked = $this->closestByAttr($test, 'locked', true))) {
1497
					return $this->setError(elFinder::ERROR_LOCKED, $this->_path($locked));
1498
				}
1499
				// target is entity file of alias
1500
				if ($volume == $this && ($test == @$file['target'] || $test == $this->decode($src))) {
1501
					return $this->setError(elFinder::ERROR_REPLACE, $errpath);
1502
				}
1503
				// remove existed file
1504
				if (!$this->remove($test)) {
1505
					return $this->setError(elFinder::ERROR_REPLACE, $this->_path($test));
1506
				}
1507
			} else {
1508
				$name = $this->uniqueName($destination, $name, ' ', false);
1509
			}
1510
		}
1511
1512
		// copy/move inside current volume
1513
		if ($volume == $this) {
1514
			$source = $this->decode($src);
1515
			// do not copy into itself
1516
			if ($this->_inpath($destination, $source)) {
1517
				return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
1518
			}
1519
			$method = $rmSrc ? 'move' : 'copy';
1520
			return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false;
1521
		}
1522
1523
		// copy/move from another volume
1524
		if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
1525
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
1526
		}
1527
1528
		if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
1529
			return false;
1530
		}
1531
1532
		if ($rmSrc) {
1533
			if ($volume->rm($src)) {
1534
				$this->removed[] = $file;
1535
			} else {
1536
				return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
1537
			}
1538
		}
1539
		return $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copyFrom($volume,...c, $destination, $name) on line 1528 can also be of type boolean; however, elFinderVolumeDriver::stat() does only seem to accept string, 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...
1540
	}
1541
1542
	/**
1543
	 * Return file contents
1544
	 *
1545
	 * @param  string  $hash  file hash
1546
	 * @return string|false
1547
	 * @author Dmitry (dio) Levashov
1548
	 **/
1549
	public function getContents($hash) {
1550
		$file = $this->file($hash);
1551
1552
		if (!$file) {
1553
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1554
		}
1555
1556
		if ($file['mime'] == 'directory') {
1557
			return $this->setError(elFinder::ERROR_NOT_FILE);
1558
		}
1559
1560
		if (!$file['read']) {
1561
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1562
		}
1563
1564
		return $this->_getContents($this->decode($hash));
1565
	}
1566
1567
	/**
1568
	 * Put content in text file and return file info.
1569
	 *
1570
	 * @param  string  $hash     file hash
1571
	 * @param  string  $content  new file content
1572
	 * @return array
1573
	 * @author Dmitry (dio) Levashov
1574
	 **/
1575
	public function putContents($hash, $content) {
1576
		if ($this->commandDisabled('edit')) {
1577
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1578
		}
1579
1580
		$path = $this->decode($hash);
1581
1582
		if (!($file = $this->file($hash))) {
1583
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1584
		}
1585
1586
		if (!$file['write']) {
1587
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1588
		}
1589
		$this->clearcache();
1590
		return $this->_filePutContents($path, $content) ? $this->stat($path) : false;
1591
	}
1592
1593
	/**
1594
	 * Extract files from archive
1595
	 *
1596
	 * @param  string  $hash  archive hash
1597
	 * @return array|bool
1598
	 * @author Dmitry (dio) Levashov,
1599
	 * @author Alexey Sukhotin
1600
	 **/
1601
	public function extract($hash) {
1602
		if ($this->commandDisabled('extract')) {
1603
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1604
		}
1605
1606
		if (($file = $this->file($hash)) == false) {
1607
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1608
		}
1609
1610
		$archiver = isset($this->archivers['extract'][$file['mime']])
1611
			? $this->archivers['extract'][$file['mime']]
1612
			: false;
1613
1614
		if (!$archiver) {
1615
			return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
1616
		}
1617
1618
		$path   = $this->decode($hash);
1619
		$parent = $this->stat($this->_dirname($path));
1620
1621
		if (!$file['read'] || !$parent['write']) {
1622
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1623
		}
1624
		$this->clearcache();
1625
		return ($path = $this->_extract($path, $archiver)) ? $this->stat($path) : false;
0 ignored issues
show
Documentation introduced by
$path is of type 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...
1626
	}
1627
1628
	/**
1629
	 * Add files to archive
1630
	 *
1631
	 * @return void
1632
	 **/
1633
	public function archive($hashes, $mime) {
1634
		if ($this->commandDisabled('archive')) {
1635
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1636
		}
1637
1638
		$archiver = isset($this->archivers['create'][$mime])
1639
			? $this->archivers['create'][$mime]
1640
			: false;
1641
1642
		if (!$archiver) {
1643
			return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
1644
		}
1645
1646
		$files = array();
1647
1648
		foreach ($hashes as $hash) {
1649
			if (($file = $this->file($hash)) == false) {
1650
				return $this->error(elFinder::ERROR_FILE_NOT_FOUND, '#'+$hash);
0 ignored issues
show
Unused Code introduced by
The call to elFinderVolumeDriver::error() has too many arguments starting with \elFinder::ERROR_FILE_NOT_FOUND.

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

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

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

Loading history...
1651
			}
1652
			if (!$file['read']) {
1653
				return $this->error(elFinder::ERROR_PERM_DENIED);
0 ignored issues
show
Unused Code introduced by
The call to elFinderVolumeDriver::error() has too many arguments starting with \elFinder::ERROR_PERM_DENIED.

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

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

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

Loading history...
1654
			}
1655
			$path = $this->decode($hash);
1656
			if (!isset($dir)) {
1657
				$dir = $this->_dirname($path);
1658
				$stat = $this->stat($dir);
1659
				if (!$stat['write']) {
1660
					return $this->error(elFinder::ERROR_PERM_DENIED);
0 ignored issues
show
Unused Code introduced by
The call to elFinderVolumeDriver::error() has too many arguments starting with \elFinder::ERROR_PERM_DENIED.

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

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

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

Loading history...
1661
				}
1662
			}
1663
1664
			$files[] = $this->_basename($path);
1665
		}
1666
1667
		$name = (count($files) == 1 ? $files[0] : 'Archive').'.'.$archiver['ext'];
1668
		$name = $this->uniqueName($dir, $name, '');
0 ignored issues
show
Bug introduced by
The variable $dir does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1669
		$this->clearcache();
1670
		return ($path = $this->_archive($dir, $files, $name, $archiver)) ? $this->stat($path) : false;
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->_archive($dir, $files, $name, $archiver) on line 1670 can also be of type boolean; however, elFinderVolumeDriver::stat() does only seem to accept string, 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...
1671
	}
1672
1673
	/**
1674
	 * Resize image
1675
	 *
1676
	 * @param  string   $hash    image file
1677
	 * @param  int      $width   new width
1678
	 * @param  int      $height  new height
1679
	 * @param  int      $x       X start poistion for crop
1680
	 * @param  int      $y       Y start poistion for crop
1681
	 * @param  string   $mode    action how to mainpulate image
1682
	 * @return array|false
1683
	 * @author Dmitry (dio) Levashov
1684
	 * @author Alexey Sukhotin
1685
	 * @author nao-pon
1686
	 * @author Troex Nevelin
1687
	 **/
1688
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) {
1689
		if ($this->commandDisabled('resize')) {
1690
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1691
		}
1692
1693
		if (($file = $this->file($hash)) == false) {
1694
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1695
		}
1696
1697
		if (!$file['write'] || !$file['read']) {
1698
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1699
		}
1700
1701
		$path = $this->decode($hash);
1702
1703
		if (!$this->canResize($path, $file)) {
0 ignored issues
show
Bug introduced by
It seems like $file defined by $this->file($hash) on line 1693 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...
1704
			return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
1705
		}
1706
1707 View Code Duplication
		switch($mode) {
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...
1708
1709
			case 'propresize':
1710
				$result = $this->imgResize($path, $width, $height, true, true);
1711
				break;
1712
1713
			case 'crop':
1714
				$result = $this->imgCrop($path, $width, $height, $x, $y);
0 ignored issues
show
Documentation introduced by
$x is of type integer, but the function expects a boolean.

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...
Documentation introduced by
$y is of type integer, but the function expects a boolean.

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...
1715
				break;
1716
1717
			case 'fitsquare':
1718
				$result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']));
1719
				break;
1720
1721
			case 'rotate':
1722
				$result = $this->imgRotate($path, $degree, ($bg ? $bg : $this->options['tmbBgColor']));
1723
				break;
1724
1725
			default:
1726
				$result = $this->imgResize($path, $width, $height, false, true);
1727
				break;
1728
		}
1729
1730
		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...
1731
			$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...
1732
			$this->clearcache();
1733
			return $this->stat($path);
1734
		}
1735
1736
		return false;
1737
	}
1738
1739
	/**
1740
	 * Remove file/dir
1741
	 *
1742
	 * @param  string  $hash  file hash
1743
	 * @return bool
1744
	 * @author Dmitry (dio) Levashov
1745
	 **/
1746
	public function rm($hash) {
1747
		return $this->commandDisabled('rm')
1748
			? array(elFinder::ERROR_ACCESS_DENIED)
1749
			: $this->remove($this->decode($hash));
1750
	}
1751
1752
	/**
1753
	 * Search files
1754
	 *
1755
	 * @param  string  $q  search string
1756
	 * @param  array   $mimes
1757
	 * @return array
1758
	 * @author Dmitry (dio) Levashov
1759
	 **/
1760
	public function search($q, $mimes) {
1761
		return $this->doSearch($this->root, $q, $mimes);
1762
	}
1763
1764
	/**
1765
	 * Return image dimensions
1766
	 *
1767
	 * @param  string  $hash  file hash
1768
	 * @return array
1769
	 * @author Dmitry (dio) Levashov
1770
	 **/
1771
	public function dimensions($hash) {
1772
		if (($file = $this->file($hash)) == false) {
1773
			return false;
1774
		}
1775
1776
		return $this->_dimensions($this->decode($hash), $file['mime']);
1777
	}
1778
1779
	/**
1780
	 * Save error message
1781
	 *
1782
	 * @param  array  error
1783
	 * @return false
1784
	 * @author Dmitry(dio) Levashov
1785
	 **/
1786
	protected function setError($error) {
0 ignored issues
show
Unused Code introduced by
The parameter $error 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...
1787
1788
		$this->error = array();
1789
1790
		foreach (func_get_args() as $err) {
1791
			if (is_array($err)) {
1792
				$this->error = array_merge($this->error, $err);
1793
			} else {
1794
				$this->error[] = $err;
1795
			}
1796
		}
1797
1798
		// $this->error = is_array($error) ? $error : func_get_args();
1799
		return false;
1800
	}
1801
1802
	/*********************************************************************/
1803
	/*                               FS API                              */
1804
	/*********************************************************************/
1805
1806
	/***************** paths *******************/
1807
1808
	/**
1809
	 * Encode path into hash
1810
	 *
1811
	 * @param  string  file path
1812
	 * @return string
1813
	 * @author Dmitry (dio) Levashov
1814
	 * @author Troex Nevelin
1815
	 **/
1816
	protected function encode($path) {
1817
		if ($path !== '') {
1818
1819
			// cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
1820
			$p = $this->_relpath($path);
1821
			// if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
1822
			if ($p === '')	{
1823
				$p = DIRECTORY_SEPARATOR;
1824
			}
1825
1826
			// TODO crypt path and return hash
1827
			$hash = $this->crypt($p);
1828
			// hash is used as id in HTML that means it must contain vaild chars
1829
			// make base64 html safe and append prefix in begining
1830
			$hash = strtr(base64_encode($hash), '+/=', '-_.');
1831
			// remove dots '.' at the end, before it was '=' in base64
1832
			$hash = rtrim($hash, '.');
1833
			// append volume id to make hash unique
1834
			return $this->id.$hash;
1835
		}
1836
	}
1837
1838
	/**
1839
	 * Decode path from hash
1840
	 *
1841
	 * @param  string  file hash
1842
	 * @return string
1843
	 * @author Dmitry (dio) Levashov
1844
	 * @author Troex Nevelin
1845
	 **/
1846
	protected function decode($hash) {
1847
		if (strpos($hash, $this->id) === 0) {
1848
			// cut volume id after it was prepended in encode
1849
			$h = substr($hash, strlen($this->id));
1850
			// replace HTML safe base64 to normal
1851
			$h = base64_decode(strtr($h, '-_.', '+/='));
1852
			// TODO uncrypt hash and return path
1853
			$path = $this->uncrypt($h);
1854
			// append ROOT to path after it was cut in encode
1855
			return $this->_abspath($path);//$this->root.($path == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR.$path);
1856
		}
1857
	}
1858
1859
	/**
1860
	 * Return crypted path
1861
	 * Not implemented
1862
	 *
1863
	 * @param  string  path
1864
	 * @return mixed
1865
	 * @author Dmitry (dio) Levashov
1866
	 **/
1867
	protected function crypt($path) {
1868
		return $path;
1869
	}
1870
1871
	/**
1872
	 * Return uncrypted path
1873
	 * Not implemented
1874
	 *
1875
	 * @param  mixed  hash
1876
	 * @return mixed
1877
	 * @author Dmitry (dio) Levashov
1878
	 **/
1879
	protected function uncrypt($hash) {
1880
		return $hash;
1881
	}
1882
1883
	/**
1884
	 * Validate file name based on $this->options['acceptedName'] regexp
1885
	 *
1886
	 * @param  string  $name  file name
1887
	 * @return bool
1888
	 * @author Dmitry (dio) Levashov
1889
	 **/
1890
	protected function nameAccepted($name) {
1891
		if ($this->nameValidator) {
1892
			if (function_exists($this->nameValidator)) {
1893
				$f = $this->nameValidator;
1894
				return $f($name);
1895
			}
1896
1897
			return preg_match($this->nameValidator, $name);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return preg_match($this->nameValidator, $name); (integer) is incompatible with the return type documented by elFinderVolumeDriver::nameAccepted 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...
1898
		}
1899
		return true;
1900
	}
1901
1902
	/**
1903
	 * Return new unique name based on file name and suffix
1904
	 *
1905
	 * @param  string  $path    file path
0 ignored issues
show
Bug introduced by
There is no parameter named $path. 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...
1906
	 * @param  string  $suffix  suffix append to name
1907
	 * @return string
1908
	 * @author Dmitry (dio) Levashov
1909
	 **/
1910
	public function uniqueName($dir, $name, $suffix = ' copy', $checkNum=true) {
1911
		$ext  = '';
1912
1913
		if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
1914
			$ext  = '.'.$m[1];
1915
			$name = substr($name, 0,  strlen($name)-strlen($m[0]));
1916
		}
1917
1918
		if ($checkNum && preg_match('/('.$suffix.')(\d*)$/i', $name, $m)) {
1919
			$i    = (int)$m[2];
1920
			$name = substr($name, 0, strlen($name)-strlen($m[2]));
1921
		} else {
1922
			$i     = 1;
1923
			$name .= $suffix;
1924
		}
1925
		$max = $i+100000;
1926
1927
		while ($i <= $max) {
1928
			$n = $name.($i > 0 ? $i : '').$ext;
1929
1930
			if (!$this->stat($this->_joinPath($dir, $n))) {
1931
				$this->clearcache();
1932
				return $n;
1933
			}
1934
			$i++;
1935
		}
1936
		return $name.md5($dir).$ext;
1937
	}
1938
1939
	/*********************** file stat *********************/
1940
1941
	/**
1942
	 * Check file attribute
1943
	 *
1944
	 * @param  string  $path  file path
1945
	 * @param  string  $name  attribute name (read|write|locked|hidden)
1946
	 * @param  bool    $val   attribute value returned by file system
1947
	 * @return bool
1948
	 * @author Dmitry (dio) Levashov
1949
	 **/
1950
	protected function attr($path, $name, $val=null) {
1951
		if (!isset($this->defaults[$name])) {
1952
			return false;
1953
		}
1954
1955
1956
		$perm = null;
1957
1958 View Code Duplication
		if ($this->access) {
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...
1959
			$perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this);
1960
1961
			if ($perm !== null) {
1962
				return !!$perm;
1963
			}
1964
		}
1965
1966
		if ($this->separator != '/') {
1967
			$path = str_replace($this->separator, '/', $this->_relpath($path));
1968
		} else {
1969
			$path = $this->_relpath($path);
1970
		}
1971
1972
		$path = '/'.$path;
1973
1974 View Code Duplication
		for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
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...
1975
			$attrs = $this->attributes[$i];
1976
1977
			if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
1978
				$perm = $attrs[$name];
1979
			}
1980
		}
1981
1982
		return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
1983
	}
1984
1985
	/**
1986
	 * Return true if file with given name can be created in given folder.
1987
	 *
1988
	 * @param string $dir  parent dir path
1989
	 * @param string $name new file name
1990
	 * @return bool
1991
	 * @author Dmitry (dio) Levashov
1992
	 **/
1993
	protected function allowCreate($dir, $name) {
1994
		$path = $this->_joinPath($dir, $name);
1995
		$perm = null;
1996
1997 View Code Duplication
		if ($this->access) {
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...
1998
			$perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this);
1999
			if ($perm !== null) {
2000
				return !!$perm;
2001
			}
2002
		}
2003
2004
		$testPath = $this->separator.$this->_relpath($path);
2005
2006 View Code Duplication
		for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
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...
2007
			$attrs = $this->attributes[$i];
2008
2009
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2010
				$perm = $attrs['write'];
2011
			}
2012
		}
2013
2014
		return $perm === null ? true : $perm;
2015
	}
2016
2017
	/**
2018
	 * Return fileinfo
2019
	 *
2020
	 * @param  string  $path  file cache
2021
	 * @return array
2022
	 * @author Dmitry (dio) Levashov
2023
	 **/
2024
	protected function stat($path) {
2025
		if ($path === false) {
2026
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by elFinderVolumeDriver::stat of type array.

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...
2027
		}
2028
		return isset($this->cache[$path])
2029
			? $this->cache[$path]
2030
			: $this->updateCache($path, $this->_stat($path));
0 ignored issues
show
Security Bug introduced by
It seems like $this->_stat($path) targeting elFinderVolumeDriver::_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...
2031
	}
2032
2033
	/**
2034
	 * Put file stat in cache and return it
2035
	 *
2036
	 * @param  string  $path   file path
2037
	 * @param  array   $stat   file stat
2038
	 * @return array
2039
	 * @author Dmitry (dio) Levashov
2040
	 **/
2041
	protected function updateCache($path, $stat) {
2042
		if (empty($stat) || !is_array($stat)) {
2043
			return $this->cache[$path] = array();
2044
		}
2045
2046
		$stat['hash'] = $this->encode($path);
2047
2048
		$root = $path == $this->root;
2049
2050
		if ($root) {
2051
			$stat['volumeid'] = $this->id;
2052
			if ($this->rootName) {
2053
				$stat['name'] = $this->rootName;
2054
			}
2055
		} else {
2056
			if (!isset($stat['name']) || !strlen($stat['name'])) {
2057
				$stat['name'] = $this->_basename($path);
2058
			}
2059
			if (empty($stat['phash'])) {
2060
				$stat['phash'] = $this->encode($this->_dirname($path));
2061
			}
2062
		}
2063
2064
		// fix name if required
2065
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2066
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], json_encode($stat['name'])));
2067
		}
2068
2069
2070
		if (empty($stat['mime'])) {
2071
			$stat['mime'] = $this->mimetype($stat['name']);
2072
		}
2073
2074
		// @todo move dateformat to client
2075
		// $stat['date'] = isset($stat['ts'])
2076
		// 	? $this->formatDate($stat['ts'])
2077
		// 	: 'unknown';
2078
2079
		if (!isset($stat['size'])) {
2080
			$stat['size'] = 'unknown';
2081
		}
2082
2083
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null));
2084
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null));
2085
		if ($root) {
2086
			$stat['locked'] = 1;
2087
		} elseif ($this->attr($path, 'locked', !empty($stat['locked']))) {
2088
			$stat['locked'] = 1;
2089
		} else {
2090
			unset($stat['locked']);
2091
		}
2092
2093
		if ($root) {
2094
			unset($stat['hidden']);
2095
		} elseif ($this->attr($path, 'hidden', !empty($stat['hidden']))
2096
		|| !$this->mimeAccepted($stat['mime'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->mimeAccepted($stat['mime']) 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...
2097
			$stat['hidden'] = $root ? 0 : 1;
2098
		} else {
2099
			unset($stat['hidden']);
2100
		}
2101
2102
		if ($stat['read'] && empty($stat['hidden'])) {
2103
2104
			if ($stat['mime'] == 'directory') {
2105
				// for dir - check for subdirs
2106
2107
				if ($this->options['checkSubfolders']) {
2108
					if (isset($stat['dirs'])) {
2109
						if ($stat['dirs']) {
2110
							$stat['dirs'] = 1;
2111
						} else {
2112
							unset($stat['dirs']);
2113
						}
2114
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2115
						$stat['dirs'] = isset($this->cache[$stat['target']])
2116
							? intval(isset($this->cache[$stat['target']]['dirs']))
2117
							: $this->_subdirs($stat['target']);
2118
2119
					} elseif ($this->_subdirs($path)) {
2120
						$stat['dirs'] = 1;
2121
					}
2122
				} else {
2123
					$stat['dirs'] = 1;
2124
				}
2125
			} else {
2126
				// for files - check for thumbnails
2127
				$p = isset($stat['target']) ? $stat['target'] : $path;
2128
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
2129
					$tmb = $this->gettmb($p, $stat);
2130
					$stat['tmb'] = $tmb ? $tmb : 1;
2131
				}
2132
2133
			}
2134
		}
2135
2136
		if (!empty($stat['alias']) && !empty($stat['target'])) {
2137
			$stat['thash'] = $this->encode($stat['target']);
2138
			unset($stat['target']);
2139
		}
2140
2141
		return $this->cache[$path] = $stat;
2142
	}
2143
2144
	/**
2145
	 * Get stat for folder content and put in cache
2146
	 *
2147
	 * @param  string  $path
2148
	 * @return void
2149
	 * @author Dmitry (dio) Levashov
2150
	 **/
2151
	protected function cacheDir($path) {
2152
		$this->dirsCache[$path] = array();
2153
2154 View Code Duplication
		foreach ($this->_scandir($path) as $p) {
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...
2155
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
2156
				$this->dirsCache[$path][] = $p;
2157
			}
2158
		}
2159
	}
2160
2161
	/**
2162
	 * Clean cache
2163
	 *
2164
	 * @return void
2165
	 * @author Dmitry (dio) Levashov
2166
	 **/
2167
	protected function clearcache() {
2168
		$this->cache = $this->dirsCache = array();
2169
	}
2170
2171
	/**
2172
	 * Return file mimetype
2173
	 *
2174
	 * @param  string  $path  file path
2175
	 * @return string
2176
	 * @author Dmitry (dio) Levashov
2177
	 **/
2178
	protected function mimetype($path, $name = '') {
2179
		$type = '';
2180
2181
		if ($this->mimeDetect == 'finfo') {
2182
			if ($type = @finfo_file($this->finfo, $path)) {
2183
				if ($name === '') {
2184
					$name = $path;
2185
				}
2186
				$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
2187
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
2188
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
2189
				}
2190
			}
2191
		} elseif ($type == 'mime_content_type') {
2192
			$type = mime_content_type($path);
2193
		} else {
2194
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
2195
		}
2196
2197
		$type = explode(';', $type);
2198
		$type = trim($type[0]);
2199
2200
		if (in_array($type, array('application/x-empty', 'inode/x-empty'))) {
2201
			// finfo return this mime for empty files
2202
			$type = 'text/plain';
2203
		} elseif ($type == 'application/x-zip') {
2204
			// http://elrte.org/redmine/issues/163
2205
			$type = 'application/zip';
2206
		}
2207
2208
		return $type == 'unknown' && $this->mimeDetect != 'internal'
2209
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
2210
			: $type;
2211
2212
	}
2213
2214
	/**
2215
	 * Detect file mimetype using "internal" method
2216
	 *
2217
	 * @param  string  $path  file path
2218
	 * @return string
2219
	 * @author Dmitry (dio) Levashov
2220
	 **/
2221
	static protected function mimetypeInternalDetect($path) {
2222
		$pinfo = pathinfo($path);
2223
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
2224
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
2225
2226
	}
2227
2228
	/**
2229
	 * Return file/total directory size
2230
	 *
2231
	 * @param  string  $path  file path
2232
	 * @return int
2233
	 * @author Dmitry (dio) Levashov
2234
	 **/
2235
	protected function countSize($path) {
2236
		$stat = $this->stat($path);
2237
2238
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
2239
			return 'unknown';
2240
		}
2241
2242
		if ($stat['mime'] != 'directory') {
2243
			return $stat['size'];
2244
		}
2245
2246
		$subdirs = $this->options['checkSubfolders'];
2247
		$this->options['checkSubfolders'] = true;
2248
		$result = 0;
2249
		foreach ($this->getScandir($path) as $stat) {
2250
			$size = $stat['mime'] == 'directory' && $stat['read']
2251
				? $this->countSize($this->_joinPath($path, $stat['name']))
2252
				: (isset($stat['size']) ? intval($stat['size']) : 0);
2253
			if ($size > 0) {
2254
				$result += $size;
2255
			}
2256
		}
2257
		$this->options['checkSubfolders'] = $subdirs;
2258
		return $result;
2259
	}
2260
2261
	/**
2262
	 * Return true if all mimes is directory or files
2263
	 *
2264
	 * @param  string  $mime1  mimetype
2265
	 * @param  string  $mime2  mimetype
2266
	 * @return bool
2267
	 * @author Dmitry (dio) Levashov
2268
	 **/
2269
	protected function isSameType($mime1, $mime2) {
2270
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
2271
	}
2272
2273
	/**
2274
	 * If file has required attr == $val - return file path,
2275
	 * If dir has child with has required attr == $val - return child path
2276
	 *
2277
	 * @param  string   $path  file path
2278
	 * @param  string   $attr  attribute name
2279
	 * @param  bool     $val   attribute value
2280
	 * @return string|false
2281
	 * @author Dmitry (dio) Levashov
2282
	 **/
2283
	protected function closestByAttr($path, $attr, $val) {
2284
		$stat = $this->stat($path);
2285
2286
		if (empty($stat)) {
2287
			return false;
2288
		}
2289
2290
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
2291
2292
		if ($v == $val) {
2293
			return $path;
2294
		}
2295
2296
		return $stat['mime'] == 'directory'
2297
			? $this->childsByAttr($path, $attr, $val)
2298
			: false;
2299
	}
2300
2301
	/**
2302
	 * Return first found children with required attr == $val
2303
	 *
2304
	 * @param  string   $path  file path
2305
	 * @param  string   $attr  attribute name
2306
	 * @param  bool     $val   attribute value
2307
	 * @return string|false
2308
	 * @author Dmitry (dio) Levashov
2309
	 **/
2310
	protected function childsByAttr($path, $attr, $val) {
2311
		foreach ($this->_scandir($path) as $p) {
2312
			if (($_p = $this->closestByAttr($p, $attr, $val)) != false) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $_p = $this->closestByAttr($p, $attr, $val) of type false|string against false; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
2313
				return $_p;
2314
			}
2315
		}
2316
		return false;
2317
	}
2318
2319
	/*****************  get content *******************/
2320
2321
	/**
2322
	 * Return required dir's files info.
2323
	 * If onlyMimes is set - return only dirs and files of required mimes
2324
	 *
2325
	 * @param  string  $path  dir path
2326
	 * @return array
2327
	 * @author Dmitry (dio) Levashov
2328
	 **/
2329
	protected function getScandir($path) {
2330
		$files = array();
2331
2332
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
2333
2334 View Code Duplication
		foreach ($this->dirsCache[$path] as $p) {
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...
2335
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
2336
				$files[] = $stat;
2337
			}
2338
		}
2339
2340
		return $files;
2341
	}
2342
2343
2344
	/**
2345
	 * Return subdirs tree
2346
	 *
2347
	 * @param  string  $path  parent dir path
2348
	 * @param  int     $deep  tree deep
2349
	 * @return array
2350
	 * @author Dmitry (dio) Levashov
2351
	 **/
2352
	protected function gettree($path, $deep, $exclude='') {
2353
		$dirs = array();
2354
2355
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
2356
2357
		foreach ($this->dirsCache[$path] as $p) {
2358
			$stat = $this->stat($p);
2359
2360
			if ($stat && empty($stat['hidden']) && $p != $exclude && $stat['mime'] == 'directory') {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
2361
				$dirs[] = $stat;
2362
				if ($deep > 0 && !empty($stat['dirs'])) {
2363
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
2364
				}
2365
			}
2366
		}
2367
2368
		return $dirs;
2369
	}
2370
2371
	/**
2372
	 * Recursive files search
2373
	 *
2374
	 * @param  string  $path   dir path
2375
	 * @param  string  $q      search string
2376
	 * @param  array   $mimes
2377
	 * @return array
2378
	 * @author Dmitry (dio) Levashov
2379
	 **/
2380
	protected function doSearch($path, $q, $mimes) {
2381
		$result = array();
2382
2383
		foreach($this->_scandir($path) as $p) {
2384
			$stat = $this->stat($p);
2385
2386
			if (!$stat) { // invalid links
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
2387
				continue;
2388
			}
2389
2390
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'])) {
2391
				continue;
2392
			}
2393
2394
			$name = $stat['name'];
2395
2396
			if ($this->stripos($name, $q) !== false) {
2397
				$stat['path'] = $this->_path($p);
2398
				if ($this->URL && !isset($stat['url'])) {
2399
					$stat['url'] = $this->URL . str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
2400
				}
2401
2402
				$result[] = $stat;
2403
			}
2404
			if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) {
2405
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
2406
			}
2407
		}
2408
2409
		return $result;
2410
	}
2411
2412
	/**********************  manuipulations  ******************/
2413
2414
	/**
2415
	 * Copy file/recursive copy dir only in current volume.
2416
	 * Return new file path or false.
2417
	 *
2418
	 * @param  string  $src   source path
2419
	 * @param  string  $dst   destination dir path
2420
	 * @param  string  $name  new file name (optionaly)
2421
	 * @return string|false
2422
	 * @author Dmitry (dio) Levashov
2423
	 **/
2424
	protected function copy($src, $dst, $name) {
2425
		$srcStat = $this->stat($src);
2426
		$this->clearcache();
2427
2428
		if (!empty($srcStat['thash'])) {
2429
			$target = $this->decode($srcStat['thash']);
2430
			$stat   = $this->stat($target);
2431
			$this->clearcache();
2432
			return $stat && $this->_symlink($target, $dst, $name)
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
2433
				? $this->_joinPath($dst, $name)
2434
				: $this->setError(elFinder::ERROR_COPY, $this->_path($src));
2435
		}
2436
2437
		if ($srcStat['mime'] == 'directory') {
2438
			$test = $this->stat($this->_joinPath($dst, $name));
2439
2440 View Code Duplication
			if (($test && $test['mime'] != 'directory') || !$this->_mkdir($dst, $name)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $test of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2441
				return $this->setError(elFinder::ERROR_COPY, $this->_path($src));
2442
			}
2443
2444
			$dst = $this->_joinPath($dst, $name);
2445
2446
			foreach ($this->getScandir($src) as $stat) {
2447
				if (empty($stat['hidden'])) {
2448
					$name = $stat['name'];
2449
					if (!$this->copy($this->_joinPath($src, $name), $dst, $name)) {
2450
						$this->remove($dst, true); // fall back
2451
						return false;
2452
					}
2453
				}
2454
			}
2455
			$this->clearcache();
2456
			return $dst;
2457
		}
2458
2459
		return $this->_copy($src, $dst, $name)
2460
			? $this->_joinPath($dst, $name)
2461
			: $this->setError(elFinder::ERROR_COPY, $this->_path($src));
2462
	}
2463
2464
	/**
2465
	 * Move file
2466
	 * Return new file path or false.
2467
	 *
2468
	 * @param  string  $src   source path
2469
	 * @param  string  $dst   destination dir path
2470
	 * @param  string  $name  new file name
2471
	 * @return string|false
2472
	 * @author Dmitry (dio) Levashov
2473
	 **/
2474
	protected function move($src, $dst, $name) {
2475
		$stat = $this->stat($src);
2476
		$stat['realpath'] = $src;
2477
		$this->rmTmb($stat); // can not do rmTmb() after _move()
0 ignored issues
show
Documentation introduced by
$stat is of type array<string,string,{"realpath":"string"}>, 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...
2478
		$this->clearcache();
2479
2480
		if ($this->_move($src, $dst, $name)) {
2481
			$this->removed[] = $stat;
2482
2483
			return $this->_joinPath($dst, $name);
2484
		}
2485
2486
		return $this->setError(elFinder::ERROR_MOVE, $this->_path($src));
2487
	}
2488
2489
	/**
2490
	 * Copy file from another volume.
2491
	 * Return new file path or false.
2492
	 *
2493
	 * @param  Object  $volume       source volume
2494
	 * @param  string  $src          source file hash
2495
	 * @param  string  $destination  destination dir path
2496
	 * @param  string  $name         file name
2497
	 * @return string|false
2498
	 * @author Dmitry (dio) Levashov
2499
	 **/
2500
	protected function copyFrom($volume, $src, $destination, $name) {
2501
2502
		if (($source = $volume->file($src)) == false) {
2503
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
2504
		}
2505
2506
		$errpath = $volume->path($src);
2507
2508
		if (!$this->nameAccepted($source['name'])) {
2509
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
2510
		}
2511
2512
		if (!$source['read']) {
2513
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
2514
		}
2515
2516
		if ($source['mime'] == 'directory') {
2517
			$stat = $this->stat($this->_joinPath($destination, $name));
2518
			$this->clearcache();
2519 View Code Duplication
			if ((!$stat || $stat['mime'] != 'directory') && !$this->_mkdir($destination, $name)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
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...
2520
				return $this->setError(elFinder::ERROR_COPY, $errpath);
2521
			}
2522
2523
			$path = $this->_joinPath($destination, $name);
2524
2525
			foreach ($volume->scandir($src) as $entr) {
2526
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
2527
					return false;
2528
				}
2529
			}
2530
2531
		} else {
2532
			// $mime = $source['mime'];
2533
			// $w = $h = 0;
2534
			if (($dim = $volume->dimensions($src))) {
2535
				$s = explode('x', $dim);
2536
				$source['width']  = $s[0];
2537
				$source['height'] = $s[1];
2538
			}
2539
2540
			if (($fp = $volume->open($src)) == false
2541
			|| ($path = $this->_save($fp, $destination, $name, $source)) == false) {
2542
				$fp && $volume->close($fp, $src);
2543
				return $this->setError(elFinder::ERROR_COPY, $errpath);
2544
			}
2545
			$volume->close($fp, $src);
2546
		}
2547
2548
		return $path;
2549
	}
2550
2551
	/**
2552
	 * Remove file/ recursive remove dir
2553
	 *
2554
	 * @param  string  $path   file path
2555
	 * @param  bool    $force  try to remove even if file locked
2556
	 * @return bool
2557
	 * @author Dmitry (dio) Levashov
2558
	 **/
2559
	protected function remove($path, $force = false) {
2560
		$stat = $this->stat($path);
2561
2562
		if (empty($stat)) {
2563
			return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND);
2564
		}
2565
2566
		$stat['realpath'] = $path;
2567
		$this->rmTmb($stat);
0 ignored issues
show
Documentation introduced by
$stat is of type array<string,string,{"realpath":"string"}>, 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...
2568
		$this->clearcache();
2569
2570
		if (!$force && !empty($stat['locked'])) {
2571
			return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path));
2572
		}
2573
2574
		if ($stat['mime'] == 'directory') {
2575
			foreach ($this->_scandir($path) as $p) {
2576
				$name = $this->_basename($p);
2577
				if ($name != '.' && $name != '..' && !$this->remove($p)) {
2578
					return false;
2579
				}
2580
			}
2581
			if (!$this->_rmdir($path)) {
2582
				return $this->setError(elFinder::ERROR_RM, $this->_path($path));
2583
			}
2584
2585
		} else {
2586
			if (!$this->_unlink($path)) {
2587
				return $this->setError(elFinder::ERROR_RM, $this->_path($path));
2588
			}
2589
		}
2590
2591
		$this->removed[] = $stat;
2592
		return true;
2593
	}
2594
2595
2596
	/************************* thumbnails **************************/
2597
2598
	/**
2599
	 * Return thumbnail file name for required file
2600
	 *
2601
	 * @param  array  $stat  file stat
2602
	 * @return string
2603
	 * @author Dmitry (dio) Levashov
2604
	 **/
2605
	protected function tmbname($stat) {
2606
		return $stat['hash'].$stat['ts'].'.png';
2607
	}
2608
2609
	/**
2610
	 * Return thumnbnail name if exists
2611
	 *
2612
	 * @param  string  $path file path
2613
	 * @param  array   $stat file stat
2614
	 * @return string|false
2615
	 * @author Dmitry (dio) Levashov
2616
	 **/
2617
	protected function gettmb($path, $stat) {
2618
		if ($this->tmbURL && $this->tmbPath) {
2619
			// file itself thumnbnail
2620
			if (strpos($path, $this->tmbPath) === 0) {
2621
				return basename($path);
2622
			}
2623
2624
			$name = $this->tmbname($stat);
2625
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
2626
				return $name;
2627
			}
2628
		}
2629
		return false;
2630
	}
2631
2632
	/**
2633
	 * Return true if thumnbnail for required file can be created
2634
	 *
2635
	 * @param  string  $path  thumnbnail path
2636
	 * @param  array   $stat  file stat
2637
	 * @return string|bool
2638
	 * @author Dmitry (dio) Levashov
2639
	 **/
2640
	protected function canCreateTmb($path, $stat) {
2641
		return $this->tmbPathWritable
2642
			&& strpos($path, $this->tmbPath) === false // do not create thumnbnail for thumnbnail
2643
			&& $this->imgLib
2644
			&& strpos($stat['mime'], 'image') === 0
2645
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
2646
	}
2647
2648
	/**
2649
	 * Return true if required file can be resized.
2650
	 * By default - the same as canCreateTmb
2651
	 *
2652
	 * @param  string  $path  thumnbnail path
2653
	 * @param  array   $stat  file stat
2654
	 * @return string|bool
2655
	 * @author Dmitry (dio) Levashov
2656
	 **/
2657
	protected function canResize($path, $stat) {
2658
		return $this->canCreateTmb($path, $stat);
2659
	}
2660
2661
	/**
2662
	 * Create thumnbnail and return it's URL on success
2663
	 *
2664
	 * @param  string  $path  file path
2665
	 * @param  string  $mime  file mime type
0 ignored issues
show
Bug introduced by
There is no parameter named $mime. 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...
2666
	 * @return string|false
2667
	 * @author Dmitry (dio) Levashov
2668
	 **/
2669
	protected function createTmb($path, $stat) {
2670
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
2671
			return false;
2672
		}
2673
2674
		$name = $this->tmbname($stat);
2675
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
2676
2677
		// copy image into tmbPath so some drivers does not store files on local fs
2678
		if (($src = $this->_fopen($path, 'rb')) == false) {
2679
			return false;
2680
		}
2681
2682
		if (($trg = fopen($tmb, 'wb')) == false) {
2683
			$this->_fclose($src, $path);
2684
			return false;
2685
		}
2686
2687
		while (!feof($src)) {
2688
			fwrite($trg, fread($src, 8192));
2689
		}
2690
2691
		$this->_fclose($src, $path);
2692
		fclose($trg);
2693
2694
		$result = false;
0 ignored issues
show
Unused Code introduced by
$result 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...
2695
2696
		$tmbSize = $this->tmbSize;
2697
2698
  		if (($s = getimagesize($tmb)) == false) {
2699
			return false;
2700
		}
2701
2702
    		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
2703
    		if ($s[0] <= $tmbSize && $s[1]  <= $tmbSize) {
2704
     	   		$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
2705
	    	} else {
2706
2707
	    		if ($this->options['tmbCrop']) {
2708
2709
        			/* Resize and crop if image bigger than thumbnail */
2710
	        		if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
2711
    					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
0 ignored issues
show
Unused Code introduced by
$result 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...
2712
	        		}
2713
2714
				if (($s = getimagesize($tmb)) != false) {
2715
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
2716
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
2717
					$result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png');
0 ignored issues
show
Documentation introduced by
$x is of type integer, but the function expects a boolean.

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...
Documentation introduced by
$y is of type integer, but the function expects a boolean.

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...
Unused Code introduced by
$result 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...
2718
				}
2719
2720
    			} else {
2721
        			$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
0 ignored issues
show
Unused Code introduced by
$result 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...
2722
      			}
2723
2724
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
2725
		}
2726
2727
		if (!$result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result 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...
2728
			unlink($tmb);
2729
			return false;
2730
		}
2731
2732
		return $name;
2733
	}
2734
2735
	/**
2736
	 * Resize image
2737
	 *
2738
	 * @param  string   $path               image file
2739
	 * @param  int      $width              new width
2740
	 * @param  int      $height             new height
2741
	 * @param  bool	    $keepProportions    crop image
2742
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
2743
	 * @param  string   $destformat         image destination format
2744
	 * @return string|false
2745
	 * @author Dmitry (dio) Levashov
2746
	 * @author Alexey Sukhotin
2747
	 **/
2748
  	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) {
2749
		if (($s = @getimagesize($path)) == false) {
2750
			return false;
2751
		}
2752
2753
    	$result = false;
0 ignored issues
show
Unused Code introduced by
$result 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...
2754
2755
		list($size_w, $size_h) = array($width, $height);
2756
2757
    	if ($keepProportions == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2758
2759
      		list($orig_w, $orig_h, $new_w, $new_h) = array($s[0], $s[1], $width, $height);
2760
2761
      		/* Calculating image scale width and height */
2762
      		$xscale = $orig_w / $new_w;
0 ignored issues
show
Unused Code introduced by
$xscale 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...
2763
      		$yscale = $orig_h / $new_h;
0 ignored issues
show
Unused Code introduced by
$yscale 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...
2764
2765
      		/* Resizing by biggest side */
2766
2767
			if ($resizeByBiggerSide) {
2768
2769 View Code Duplication
		        if ($orig_w > $orig_h) {
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...
2770
					$size_h = $orig_h * $width / $orig_w;
2771
					$size_w = $width;
2772
        		} else {
2773
          			$size_w = $orig_w * $height / $orig_h;
2774
          			$size_h = $height;
2775
				}
2776
2777 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...
2778
        		if ($orig_w > $orig_h) {
2779
          			$size_w = $orig_w * $height / $orig_h;
2780
          			$size_h = $height;
2781
		        } else {
2782
					$size_h = $orig_h * $width / $orig_w;
2783
					$size_w = $width;
2784
				}
2785
			}
2786
    	}
2787
2788
		switch ($this->imgLib) {
2789
			case 'imagick':
2790
2791
				try {
2792
					$img = new imagick($path);
2793
				} catch (Exception $e) {
2794
2795
					return false;
2796
				}
2797
2798
				$img->resizeImage($size_w, $size_h, Imagick::FILTER_LANCZOS, true);
2799
2800
				$result = $img->writeImage($path);
2801
2802
				return $result ? $path : false;
2803
2804
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2805
2806
			case 'gd':
2807
				$img = self::gdImageCreate($path,$s['mime']);
2808
2809
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
2810
2811
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
2812
2813
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
2814
						return false;
2815
					}
2816
2817
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
2818
2819
					imagedestroy($img);
2820
					imagedestroy($tmp);
2821
2822
					return $result ? $path : false;
2823
2824
				}
2825
				break;
2826
		}
2827
2828
		return false;
2829
  	}
2830
2831
	/**
2832
	 * Crop image
2833
	 *
2834
	 * @param  string   $path               image file
2835
	 * @param  int      $width              crop width
2836
	 * @param  int      $height             crop height
2837
	 * @param  bool	    $x                  crop left offset
2838
	 * @param  bool	    $y                  crop top offset
2839
	 * @param  string   $destformat         image destination format
2840
	 * @return string|false
2841
	 * @author Dmitry (dio) Levashov
2842
	 * @author Alexey Sukhotin
2843
	 **/
2844
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) {
2845
		if (($s = @getimagesize($path)) == false) {
2846
			return false;
2847
		}
2848
2849
		$result = false;
0 ignored issues
show
Unused Code introduced by
$result 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...
2850
2851
		switch ($this->imgLib) {
2852 View Code Duplication
			case 'imagick':
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...
2853
2854
				try {
2855
					$img = new imagick($path);
2856
				} catch (Exception $e) {
2857
2858
					return false;
2859
				}
2860
2861
				$img->cropImage($width, $height, $x, $y);
2862
2863
				$result = $img->writeImage($path);
2864
2865
				return $result ? $path : false;
2866
2867
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2868
2869
			case 'gd':
2870
				$img = self::gdImageCreate($path,$s['mime']);
2871
2872
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
2873
2874
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
2875
2876
					$size_w = $width;
2877
					$size_h = $height;
2878
2879
					if ($s[0] < $width || $s[1] < $height) {
2880
						$size_w = $s[0];
2881
						$size_h = $s[1];
2882
					}
2883
2884
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
2885
						return false;
2886
					}
2887
2888
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
2889
2890
					imagedestroy($img);
2891
					imagedestroy($tmp);
2892
2893
					return $result ? $path : false;
2894
2895
				}
2896
				break;
2897
		}
2898
2899
		return false;
2900
	}
2901
2902
	/**
2903
	 * Put image to square
2904
	 *
2905
	 * @param  string   $path               image file
2906
	 * @param  int      $width              square width
2907
	 * @param  int      $height             square height
2908
	 * @param  int	    $align              reserved
2909
	 * @param  int 	    $valign             reserved
2910
	 * @param  string   $bgcolor            square background color in #rrggbb format
2911
	 * @param  string   $destformat         image destination format
2912
	 * @return string|false
2913
	 * @author Dmitry (dio) Levashov
2914
	 * @author Alexey Sukhotin
2915
	 **/
2916
	protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $align 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 $valign 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...
2917
		if (($s = @getimagesize($path)) == false) {
2918
			return false;
2919
		}
2920
2921
		$result = false;
0 ignored issues
show
Unused Code introduced by
$result 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...
2922
2923
		/* Coordinates for image over square aligning */
2924
		$y = ceil(abs($height - $s[1]) / 2);
2925
		$x = ceil(abs($width - $s[0]) / 2);
2926
2927
		switch ($this->imgLib) {
2928
			case 'imagick':
2929
				try {
2930
					$img = new imagick($path);
2931
				} catch (Exception $e) {
2932
					return false;
2933
				}
2934
2935
				$img1 = new Imagick();
2936
				$img1->newImage($width, $height, new ImagickPixel($bgcolor));
2937
				$img1->setImageColorspace($img->getImageColorspace());
2938
				$img1->setImageFormat($destformat != null ? $destformat : $img->getFormat());
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $destformat of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
2939
				$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
2940
				$result = $img1->writeImage($path);
2941
				return $result ? $path : false;
2942
2943
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2944
2945
			case 'gd':
2946
				$img = self::gdImageCreate($path,$s['mime']);
2947
2948
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
2949
2950
					self::gdImageBackground($tmp,$bgcolor);
2951
2952
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
2953
						return false;
2954
					}
2955
2956
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
2957
2958
					imagedestroy($img);
2959
					imagedestroy($tmp);
2960
2961
					return $result ? $path : false;
2962
				}
2963
				break;
2964
		}
2965
2966
		return false;
2967
	}
2968
2969
	/**
2970
	 * Rotate image
2971
	 *
2972
	 * @param  string   $path               image file
2973
	 * @param  int      $degree             rotete degrees
2974
	 * @param  string   $bgcolor            square background color in #rrggbb format
2975
	 * @param  string   $destformat         image destination format
2976
	 * @return string|false
2977
	 * @author nao-pon
2978
	 * @author Troex Nevelin
2979
	 **/
2980
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) {
2981
		if (($s = @getimagesize($path)) == false) {
2982
			return false;
2983
		}
2984
2985
		$result = false;
0 ignored issues
show
Unused Code introduced by
$result 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...
2986
2987
		switch ($this->imgLib) {
2988 View Code Duplication
			case 'imagick':
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...
2989
				try {
2990
					$img = new imagick($path);
2991
				} catch (Exception $e) {
2992
					return false;
2993
				}
2994
2995
				$img->rotateImage(new ImagickPixel($bgcolor), $degree);
2996
				$result = $img->writeImage($path);
2997
				return $result ? $path : false;
2998
2999
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3000
3001
			case 'gd':
3002
				$img = self::gdImageCreate($path,$s['mime']);
3003
3004
				$degree = 360 - $degree;
3005
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3006
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
3007
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
3008
3009
				$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3010
3011
				imageDestroy($img);
3012
				imageDestroy($tmp);
3013
3014
				return $result ? $path : false;
3015
3016
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3017
		}
3018
3019
		return false;
3020
	}
3021
3022
	/**
3023
	 * Execute shell command
3024
	 *
3025
	 * @param  string  $command       command line
3026
	 * @param  array   $output        stdout strings
3027
	 * @param  array   $return_var    process exit code
3028
	 * @param  array   $error_output  stderr strings
3029
	 * @return int     exit code
3030
	 * @author Alexey Sukhotin
3031
	 **/
3032
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
3033
3034
		$descriptorspec = array(
3035
			0 => array("pipe", "r"),  // stdin
3036
			1 => array("pipe", "w"),  // stdout
3037
			2 => array("pipe", "w")   // stderr
3038
		);
3039
3040
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
3041
3042
		if (is_resource($process)) {
3043
3044
			fclose($pipes[0]);
3045
3046
			$tmpout = '';
0 ignored issues
show
Unused Code introduced by
$tmpout 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...
3047
			$tmperr = '';
0 ignored issues
show
Unused Code introduced by
$tmperr 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...
3048
3049
			$output = stream_get_contents($pipes[1]);
3050
			$error_output = stream_get_contents($pipes[2]);
3051
3052
			fclose($pipes[1]);
3053
			fclose($pipes[2]);
3054
			$return_var = proc_close($process);
3055
3056
3057
		}
3058
3059
		return $return_var;
3060
3061
	}
3062
3063
	/**
3064
	 * Remove thumbnail, also remove recursively if stat is directory
3065
	 *
3066
	 * @param  string  $stat  file stat
3067
	 * @return void
3068
	 * @author Dmitry (dio) Levashov
3069
	 * @author Naoki Sawada
3070
	 * @author Troex Nevelin
3071
	 **/
3072
	protected function rmTmb($stat) {
3073
		if ($stat['mime'] === 'directory') {
3074
			foreach ($this->_scandir($this->decode($stat['hash'])) as $p) {
3075
				$name = $this->_basename($p);
3076
				$name != '.' && $name != '..' && $this->rmTmb($this->stat($p));
0 ignored issues
show
Documentation introduced by
$this->stat($p) is of type array, 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...
3077
			}
3078
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
3079
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
3080
			file_exists($tmb) && @unlink($tmb);
3081
			clearstatcache();
3082
		}
3083
	}
3084
3085
	/**
3086
	 * Create an gd image according to the specified mime type
3087
	 *
3088
	 * @param string $path image file
3089
	 * @param string $mime
3090
	 * @return gd image resource identifier
3091
	 */
3092
	protected function gdImageCreate($path,$mime){
3093
		switch($mime){
3094
			case 'image/jpeg':
3095
			return imagecreatefromjpeg($path);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return imagecreatefromjpeg($path); (resource) is incompatible with the return type documented by elFinderVolumeDriver::gdImageCreate of type gd.

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...
3096
3097
			case 'image/png':
3098
			return imagecreatefrompng($path);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return imagecreatefrompng($path); (resource) is incompatible with the return type documented by elFinderVolumeDriver::gdImageCreate of type gd.

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...
3099
3100
			case 'image/gif':
3101
			return imagecreatefromgif($path);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return imagecreatefromgif($path); (resource) is incompatible with the return type documented by elFinderVolumeDriver::gdImageCreate of type gd.

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...
3102
3103
			case 'image/xbm':
3104
			return imagecreatefromxbm($path);
3105
		}
3106
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by elFinderVolumeDriver::gdImageCreate of type gd.

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...
3107
	}
3108
3109
	/**
3110
	 * Output gd image to file
3111
	 *
3112
	 * @param resource $image gd image resource
3113
	 * @param string $filename The path to save the file to.
3114
	 * @param string $destformat The Image type to use for $filename
3115
	 * @param string $mime The original image mime type
3116
	 */
3117
	protected function gdImage($image, $filename, $destformat, $mime ){
3118
3119
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
3120
			return imagejpeg($image, $filename, 100);
3121
		}
3122
3123
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
3124
			return imagegif($image, $filename, 7);
3125
		}
3126
3127
		return imagepng($image, $filename, 7);
3128
	}
3129
3130
	/**
3131
	 * Assign the proper background to a gd image
3132
	 *
3133
	 * @param resource $image gd image resource
3134
	 * @param string $bgcolor background color in #rrggbb format
3135
	 */
3136
	protected function gdImageBackground($image, $bgcolor){
3137
3138
		if( $bgcolor == 'transparent' ){
3139
			imagesavealpha($image,true);
3140
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
3141
3142
		}else{
3143
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3144
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
3145
		}
3146
3147
		imagefill($image, 0, 0, $bgcolor1);
3148
	}
3149
3150
	/*********************** misc *************************/
3151
3152
	/**
3153
	 * Return smart formatted date
3154
	 *
3155
	 * @param  int     $ts  file timestamp
3156
	 * @return string
3157
	 * @author Dmitry (dio) Levashov
3158
	 **/
3159
	// protected function formatDate($ts) {
3160
	// 	if ($ts > $this->today) {
3161
	// 		return 'Today '.date($this->options['timeFormat'], $ts);
3162
	// 	}
3163
	//
3164
	// 	if ($ts > $this->yesterday) {
3165
	// 		return 'Yesterday '.date($this->options['timeFormat'], $ts);
3166
	// 	}
3167
	//
3168
	// 	return date($this->options['dateFormat'], $ts);
3169
	// }
3170
3171
	/**
3172
	* Find position of first occurrence of string in a string with multibyte support
3173
	*
3174
	* @param  string  $haystack  The string being checked.
3175
	* @param  string  $needle    The string to find in haystack.
3176
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
3177
	* @return int|bool
3178
	* @author Alexey Sukhotin
3179
	**/
3180
	protected function stripos($haystack , $needle , $offset = 0) {
3181
		if (function_exists('mb_stripos')) {
3182
			return mb_stripos($haystack , $needle , $offset);
3183
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
3184
			return mb_strpos(mb_strtolower($haystack), mb_strtolower($needle), $offset);
3185
		}
3186
		return stripos($haystack , $needle , $offset);
3187
	}
3188
3189
	/**
3190
	 * Get server side available archivers
3191
	 *
3192
	 * @param bool $use_cache
3193
	 * @return array
3194
	 */
3195
	protected function getArchivers($use_cache = true) {
3196
		if (!function_exists('proc_open')) {
3197
			return array();
3198
		}
3199
3200
		if ($use_cache && isset($_SESSION['ELFINDER_ARCHIVERS_CACHE']) && is_array($_SESSION['ELFINDER_ARCHIVERS_CACHE'])) {
3201
			return $_SESSION['ELFINDER_ARCHIVERS_CACHE'];
3202
		}
3203
3204
		$arcs = array(
3205
				'create'  => array(),
3206
				'extract' => array()
3207
		);
3208
3209
		$this->procExec('tar --version', $o, $ctar);
3210
3211
		if ($ctar == 0) {
3212
			$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
3213
			$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
3214
			unset($o);
3215
			$test = $this->procExec('gzip --version', $o, $c);
0 ignored issues
show
Unused Code introduced by
$test 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...
3216
3217 View Code Duplication
			if ($c == 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...
3218
				$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
3219
				$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
3220
			}
3221
			unset($o);
3222
			$test = $this->procExec('bzip2 --version', $o, $c);
0 ignored issues
show
Unused Code introduced by
$test 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...
3223 View Code Duplication
			if ($c == 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...
3224
				$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
3225
				$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
3226
			}
3227
		}
3228
		unset($o);
3229
		$this->procExec('zip -v', $o, $c);
3230
		if ($c == 0) {
3231
			$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
3232
		}
3233
		unset($o);
3234
		$this->procExec('unzip --help', $o, $c);
3235
		if ($c == 0) {
3236
			$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
3237
		}
3238
		unset($o);
3239
		$this->procExec('rar --version', $o, $c);
3240
		if ($c == 0 || $c == 7) {
3241
			$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
3242
			$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
3243
		} else {
3244
			unset($o);
3245
			$test = $this->procExec('unrar', $o, $c);
0 ignored issues
show
Unused Code introduced by
$test 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...
3246
			if ($c==0 || $c == 7) {
3247
				$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
3248
			}
3249
		}
3250
		unset($o);
3251
		$this->procExec('7za --help', $o, $c);
3252
		if ($c == 0) {
3253
			$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
3254
			$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'e -y', 'ext' => '7z');
3255
3256
			if (empty($arcs['create']['application/x-gzip'])) {
3257
				$arcs['create']['application/x-gzip'] = array('cmd' => '7za', 'argc' => 'a -tgzip', 'ext' => 'tar.gz');
3258
			}
3259
			if (empty($arcs['extract']['application/x-gzip'])) {
3260
				$arcs['extract']['application/x-gzip'] = array('cmd' => '7za', 'argc' => 'e -tgzip -y', 'ext' => 'tar.gz');
3261
			}
3262
			if (empty($arcs['create']['application/x-bzip2'])) {
3263
				$arcs['create']['application/x-bzip2'] = array('cmd' => '7za', 'argc' => 'a -tbzip2', 'ext' => 'tar.bz');
3264
			}
3265
			if (empty($arcs['extract']['application/x-bzip2'])) {
3266
				$arcs['extract']['application/x-bzip2'] = array('cmd' => '7za', 'argc' => 'a -tbzip2 -y', 'ext' => 'tar.bz');
3267
			}
3268 View Code Duplication
			if (empty($arcs['create']['application/zip'])) {
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...
3269
				$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip -l', 'ext' => 'zip');
3270
			}
3271
			if (empty($arcs['extract']['application/zip'])) {
3272
				$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'e -tzip -y', 'ext' => 'zip');
3273
			}
3274 View Code Duplication
			if (empty($arcs['create']['application/x-tar'])) {
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...
3275
				$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar -l', 'ext' => 'tar');
3276
			}
3277
			if (empty($arcs['extract']['application/x-tar'])) {
3278
				$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'e -ttar -y', 'ext' => 'tar');
3279
			}
3280
		} else if (substr(PHP_OS,0,3) === 'WIN') {
3281
			// check `7z` for Windows server.
3282
			unset($o);
3283
			$this->procExec('7z', $o, $c);
3284
			if ($c == 0) {
3285
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a -mx0', 'ext' => '7z');
3286
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
3287
3288 View Code Duplication
				if (empty($arcs['create']['application/x-gzip'])) {
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...
3289
					$arcs['create']['application/x-gzip'] = array('cmd' => '7z', 'argc' => 'a -tgzip -mx0', 'ext' => 'tar.gz');
3290
				}
3291 View Code Duplication
				if (empty($arcs['extract']['application/x-gzip'])) {
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...
3292
					$arcs['extract']['application/x-gzip'] = array('cmd' => '7z', 'argc' => 'x -tgzip -y', 'ext' => 'tar.gz');
3293
				}
3294 View Code Duplication
				if (empty($arcs['create']['application/x-bzip2'])) {
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...
3295
					$arcs['create']['application/x-bzip2'] = array('cmd' => '7z', 'argc' => 'a -tbzip2 -mx0', 'ext' => 'tar.bz');
3296
				}
3297 View Code Duplication
				if (empty($arcs['extract']['application/x-bzip2'])) {
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...
3298
					$arcs['extract']['application/x-bzip2'] = array('cmd' => '7z', 'argc' => 'x -tbzip2 -y', 'ext' => 'tar.bz');
3299
				}
3300 View Code Duplication
				if (empty($arcs['create']['application/zip'])) {
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...
3301
					$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip -l -mx0', 'ext' => 'zip');
3302
				}
3303 View Code Duplication
				if (empty($arcs['extract']['application/zip'])) {
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...
3304
					$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
3305
				}
3306 View Code Duplication
				if (empty($arcs['create']['application/x-tar'])) {
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...
3307
					$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar -l -mx0', 'ext' => 'tar');
3308
				}
3309 View Code Duplication
				if (empty($arcs['extract']['application/x-tar'])) {
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...
3310
					$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
3311
				}
3312 View Code Duplication
				if (empty($arcs['create']['application/x-rar'])) {
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...
3313
					$arcs['create']['application/x-rar']  = array('cmd' => '7z', 'argc' => 'a -trar -l -mx0', 'ext' => 'rar');
3314
				}
3315 View Code Duplication
				if (empty($arcs['extract']['application/x-rar'])) {
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...
3316
					$arcs['extract']['application/x-rar'] = array('cmd' => '7z', 'argc' => 'x -trar -y', 'ext' => 'rar');
3317
				}
3318
			}
3319
		}
3320
3321
		$_SESSION['ELFINDER_ARCHIVERS_CACHE'] = $arcs;
3322
		return $arcs;
3323
	}
3324
3325
	/**==================================* abstract methods *====================================**/
3326
3327
	/*********************** paths/urls *************************/
3328
3329
	/**
3330
	 * Return parent directory path
3331
	 *
3332
	 * @param  string  $path  file path
3333
	 * @return string
3334
	 * @author Dmitry (dio) Levashov
3335
	 **/
3336
	abstract protected function _dirname($path);
3337
3338
	/**
3339
	 * Return file name
3340
	 *
3341
	 * @param  string  $path  file path
3342
	 * @return string
3343
	 * @author Dmitry (dio) Levashov
3344
	 **/
3345
	abstract protected function _basename($path);
3346
3347
	/**
3348
	 * Join dir name and file name and return full path.
3349
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
3350
	 *
3351
	 * @param  string  $dir   dir path
3352
	 * @param  string  $name  file name
3353
	 * @return string
3354
	 * @author Dmitry (dio) Levashov
3355
	 **/
3356
	abstract protected function _joinPath($dir, $name);
3357
3358
	/**
3359
	 * Return normalized path
3360
	 *
3361
	 * @param  string  $path  file path
3362
	 * @return string
3363
	 * @author Dmitry (dio) Levashov
3364
	 **/
3365
	abstract protected function _normpath($path);
3366
3367
	/**
3368
	 * Return file path related to root dir
3369
	 *
3370
	 * @param  string  $path  file path
3371
	 * @return string
3372
	 * @author Dmitry (dio) Levashov
3373
	 **/
3374
	abstract protected function _relpath($path);
3375
3376
	/**
3377
	 * Convert path related to root dir into real path
3378
	 *
3379
	 * @param  string  $path  rel file path
3380
	 * @return string
3381
	 * @author Dmitry (dio) Levashov
3382
	 **/
3383
	abstract protected function _abspath($path);
3384
3385
	/**
3386
	 * Return fake path started from root dir.
3387
	 * Required to show path on client side.
3388
	 *
3389
	 * @param  string  $path  file path
3390
	 * @return string
3391
	 * @author Dmitry (dio) Levashov
3392
	 **/
3393
	abstract protected function _path($path);
3394
3395
	/**
3396
	 * Return true if $path is children of $parent
3397
	 *
3398
	 * @param  string  $path    path to check
3399
	 * @param  string  $parent  parent path
3400
	 * @return bool
3401
	 * @author Dmitry (dio) Levashov
3402
	 **/
3403
	abstract protected function _inpath($path, $parent);
3404
3405
	/**
3406
	 * Return stat for given path.
3407
	 * Stat contains following fields:
3408
	 * - (int)    size    file size in b. required
3409
	 * - (int)    ts      file modification time in unix time. required
3410
	 * - (string) mime    mimetype. required for folders, others - optionally
3411
	 * - (bool)   read    read permissions. required
3412
	 * - (bool)   write   write permissions. required
3413
	 * - (bool)   locked  is object locked. optionally
3414
	 * - (bool)   hidden  is object hidden. optionally
3415
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
3416
	 * - (string) target  for symlinks - link target path. optionally
3417
	 *
3418
	 * If file does not exists - returns empty array or false.
3419
	 *
3420
	 * @param  string  $path    file path
3421
	 * @return array|false
3422
	 * @author Dmitry (dio) Levashov
3423
	 **/
3424
	abstract protected function _stat($path);
3425
3426
3427
	/***************** file stat ********************/
3428
3429
3430
	/**
3431
	 * Return true if path is dir and has at least one childs directory
3432
	 *
3433
	 * @param  string  $path  dir path
3434
	 * @return bool
3435
	 * @author Dmitry (dio) Levashov
3436
	 **/
3437
	abstract protected function _subdirs($path);
3438
3439
	/**
3440
	 * Return object width and height
3441
	 * Ususaly used for images, but can be realize for video etc...
3442
	 *
3443
	 * @param  string  $path  file path
3444
	 * @param  string  $mime  file mime type
3445
	 * @return string
3446
	 * @author Dmitry (dio) Levashov
3447
	 **/
3448
	abstract protected function _dimensions($path, $mime);
3449
3450
	/******************** file/dir content *********************/
3451
3452
	/**
3453
	 * Return files list in directory
3454
	 *
3455
	 * @param  string  $path  dir path
3456
	 * @return array
3457
	 * @author Dmitry (dio) Levashov
3458
	 **/
3459
	abstract protected function _scandir($path);
3460
3461
	/**
3462
	 * Open file and return file pointer
3463
	 *
3464
	 * @param  string  $path  file path
3465
	 * @param  bool    $write open file for writing
0 ignored issues
show
Bug introduced by
There is no parameter named $write. 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...
3466
	 * @return resource|false
3467
	 * @author Dmitry (dio) Levashov
3468
	 **/
3469
	abstract protected function _fopen($path, $mode="rb");
3470
3471
	/**
3472
	 * Close opened file
3473
	 *
3474
	 * @param  resource  $fp    file pointer
3475
	 * @param  string    $path  file path
3476
	 * @return bool
3477
	 * @author Dmitry (dio) Levashov
3478
	 **/
3479
	abstract protected function _fclose($fp, $path='');
3480
3481
	/********************  file/dir manipulations *************************/
3482
3483
	/**
3484
	 * Create dir and return created dir path or false on failed
3485
	 *
3486
	 * @param  string  $path  parent dir path
3487
	 * @param string  $name  new directory name
3488
	 * @return string|bool
3489
	 * @author Dmitry (dio) Levashov
3490
	 **/
3491
	abstract protected function _mkdir($path, $name);
3492
3493
	/**
3494
	 * Create file and return it's path or false on failed
3495
	 *
3496
	 * @param  string  $path  parent dir path
3497
	 * @param string  $name  new file name
3498
	 * @return string|bool
3499
	 * @author Dmitry (dio) Levashov
3500
	 **/
3501
	abstract protected function _mkfile($path, $name);
3502
3503
	/**
3504
	 * Create symlink
3505
	 *
3506
	 * @param  string  $source     file to link to
3507
	 * @param  string  $targetDir  folder to create link in
3508
	 * @param  string  $name       symlink name
3509
	 * @return bool
3510
	 * @author Dmitry (dio) Levashov
3511
	 **/
3512
	abstract protected function _symlink($source, $targetDir, $name);
3513
3514
	/**
3515
	 * Copy file into another file (only inside one volume)
3516
	 *
3517
	 * @param  string  $source  source file path
3518
	 * @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...
3519
	 * @param  string  $name    file name
3520
	 * @return bool
3521
	 * @author Dmitry (dio) Levashov
3522
	 **/
3523
	abstract protected function _copy($source, $targetDir, $name);
3524
3525
	/**
3526
	 * Move file into another parent dir.
3527
	 * Return new file path or false.
3528
	 *
3529
	 * @param  string  $source  source file path
3530
	 * @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...
3531
	 * @param  string  $name    file name
3532
	 * @return string|bool
3533
	 * @author Dmitry (dio) Levashov
3534
	 **/
3535
	abstract protected function _move($source, $targetDir, $name);
3536
3537
	/**
3538
	 * Remove file
3539
	 *
3540
	 * @param  string  $path  file path
3541
	 * @return bool
3542
	 * @author Dmitry (dio) Levashov
3543
	 **/
3544
	abstract protected function _unlink($path);
3545
3546
	/**
3547
	 * Remove dir
3548
	 *
3549
	 * @param  string  $path  dir path
3550
	 * @return bool
3551
	 * @author Dmitry (dio) Levashov
3552
	 **/
3553
	abstract protected function _rmdir($path);
3554
3555
	/**
3556
	 * Create new file and write into it from file pointer.
3557
	 * Return new file path or false on error.
3558
	 *
3559
	 * @param  resource  $fp   file pointer
3560
	 * @param  string    $dir  target dir path
3561
	 * @param  string    $name file name
3562
	 * @param  array     $stat file stat (required by some virtual fs)
3563
	 * @return bool|string
3564
	 * @author Dmitry (dio) Levashov
3565
	 **/
3566
	abstract protected function _save($fp, $dir, $name, $stat);
3567
3568
	/**
3569
	 * Get file contents
3570
	 *
3571
	 * @param  string  $path  file path
3572
	 * @return string|false
3573
	 * @author Dmitry (dio) Levashov
3574
	 **/
3575
	abstract protected function _getContents($path);
3576
3577
	/**
3578
	 * Write a string to a file
3579
	 *
3580
	 * @param  string  $path     file path
3581
	 * @param  string  $content  new file content
3582
	 * @return bool
3583
	 * @author Dmitry (dio) Levashov
3584
	 **/
3585
	abstract protected function _filePutContents($path, $content);
3586
3587
	/**
3588
	 * Extract files from archive
3589
	 *
3590
	 * @param  string  $path file path
3591
	 * @param  array   $arc  archiver options
3592
	 * @return bool
3593
	 * @author Dmitry (dio) Levashov,
3594
	 * @author Alexey Sukhotin
3595
	 **/
3596
	abstract protected function _extract($path, $arc);
3597
3598
	/**
3599
	 * Create archive and return its path
3600
	 *
3601
	 * @param  string  $dir    target dir
3602
	 * @param  array   $files  files names list
3603
	 * @param  string  $name   archive name
3604
	 * @param  array   $arc    archiver options
3605
	 * @return string|bool
3606
	 * @author Dmitry (dio) Levashov,
3607
	 * @author Alexey Sukhotin
3608
	 **/
3609
	abstract protected function _archive($dir, $files, $name, $arc);
3610
3611
	/**
3612
	 * Detect available archivers
3613
	 *
3614
	 * @return void
3615
	 * @author Dmitry (dio) Levashov,
3616
	 * @author Alexey Sukhotin
3617
	 **/
3618
	abstract protected function _checkArchivers();
3619
3620
} // END class
3621