Completed
Push — 2.x ( 1fe29b...a54408 )
by Naoki
03:25
created

elFinderVolumeDriver::putContents()   C

Complexity

Conditions 11
Paths 12

Size

Total Lines 35
Code Lines 22

Duplication

Lines 3
Ratio 8.57 %
Metric Value
dl 3
loc 35
rs 5.2653
cc 11
eloc 22
nc 12
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

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

namespace YourVendor;

class YourClass { }

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

Loading history...
Coding Style introduced by
As per PSR2, the opening brace for this class should be on a new line.
Loading history...
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
	 * Server character encoding
122
	 *
123
	 * @var string or null
124
	 **/
125
	protected $encoding = null;
126
	
127
	/**
128
	 * How many subdirs levels return for tree
129
	 *
130
	 * @var int
131
	 **/
132
	protected $treeDeep = 1;
133
	
134
	/**
135
	 * Errors from last failed action
136
	 *
137
	 * @var array
138
	 **/
139
	protected $error = array();
140
	
141
	/**
142
	 * Today 24:00 timestamp
143
	 *
144
	 * @var int
145
	 **/
146
	protected $today = 0;
147
	
148
	/**
149
	 * Yesterday 24:00 timestamp
150
	 *
151
	 * @var int
152
	 **/
153
	protected $yesterday = 0;
154
	
155
	/**
156
	 * Force make dirctory on extract
157
	 *
158
	 * @var int
159
	 **/
160
	protected $extractToNewdir = 'auto';
161
	
162
	/**
163
	 * Object configuration
164
	 *
165
	 * @var array
166
	 **/
167
	protected $options = array(
168
		'id'              => '',
169
		// root directory path
170
		'path'            => '',
171
		// open this path on initial request instead of root path
172
		'startPath'       => '',
173
		// how many subdirs levels return per request
174
		'treeDeep'        => 1,
175
		// root url, not set to disable sending URL to client (replacement for old "fileURL" option)
176
		'URL'             => '',
177
		// directory separator. required by client to show paths correctly
178
		'separator'       => DIRECTORY_SEPARATOR,
179
		// Server character encoding (default is '': UTF-8)
180
		'encoding'        => '',
181
		// for convert character encoding (default is '': Not change locale)
182
		'locale'          => '',
183
		// URL of volume icon (16x16 pixel image file)
184
		'icon'            => '',
185
		// CSS Class of volume root in tree
186
		'rootCssClass'    => '',
187
		// library to crypt/uncrypt files names (not implemented)
188
		'cryptLib'        => '',
189
		// how to detect files mimetypes. (auto/internal/finfo/mime_content_type)
190
		'mimeDetect'      => 'auto',
191
		// mime.types file path (for mimeDetect==internal)
192
		'mimefile'        => '',
193
		// mime type normalize map : Array '[ext]:[detected mime type]' => '[normalized mime]'
194
		'mimeMap'         => array(
195
		                     'md:application/x-genesis-rom' => 'text/x-markdown',
196
		                     'md:text/plain'                => 'text/x-markdown',
197
		                     'markdown:text/plain'          => 'text/x-markdown',
198
		                     'css:text/x-asm'               => 'text/css'
199
		                    ),
200
		// directory for thumbnails
201
		'tmbPath'         => '.tmb',
202
		// mode to create thumbnails dir
203
		'tmbPathMode'     => 0777,
204
		// thumbnails dir URL. Set it if store thumbnails outside root directory
205
		'tmbURL'          => '',
206
		// thumbnails size (px)
207
		'tmbSize'         => 48,
208
		// thumbnails crop (true - crop, false - scale image to fit thumbnail size)
209
		'tmbCrop'         => true,
210
		// thumbnails background color (hex #rrggbb or 'transparent')
211
		'tmbBgColor'      => '#ffffff',
212
		// image manipulations library
213
		'imgLib'          => 'auto',
214
		// on paste file -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
215
		'copyOverwrite'   => true,
216
		// if true - join new and old directories content on paste
217
		'copyJoin'        => true,
218
		// on upload -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
219
		'uploadOverwrite' => true,
220
		// mimetypes allowed to upload
221
		'uploadAllow'     => array(),
222
		// mimetypes not allowed to upload
223
		'uploadDeny'      => array(),
224
		// order to proccess uploadAllow and uploadDeny options
225
		'uploadOrder'     => array('deny', 'allow'),
226
		// maximum upload file size. NOTE - this is size for every uploaded files
227
		'uploadMaxSize'   => 0,
228
		// files dates format
229
		'dateFormat'      => 'j M Y H:i',
230
		// files time format
231
		'timeFormat'      => 'H:i',
232
		// if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders
233
		'checkSubfolders' => true,
234
		// allow to copy from this volume to other ones?
235
		'copyFrom'        => true,
236
		// allow to copy from other volumes to this one?
237
		'copyTo'          => true,
238
		// list of commands disabled on this root
239
		'disabled'        => array(),
240
		// enable file owner, group & mode info, `false` to inactivate "chmod" command.
241
		'statOwner'       => false,
242
		// allow exec chmod of read-only files
243
		'allowChmodReadOnly' => false,
244
		// regexp or function name to validate new file name
245
		'acceptedName'    => '/^[^\.].*/', //<-- DONT touch this! Use constructor options to overwrite it!
246
		// function/class method to control files permissions
247
		'accessControl'   => null,
248
		// some data required by access control
249
		'accessControlData' => null,
250
		// default permissions.
251
		'defaults'     => array(
252
			'read'   => true,
253
			'write'  => true,
254
			'locked' => false,
255
			'hidden' => false
256
		),
257
		// files attributes
258
		'attributes'   => array(),
259
		// Allowed archive's mimetypes to create. Leave empty for all available types.
260
		'archiveMimes' => array(),
261
		// Manual config for archivers. See example below. Leave empty for auto detect
262
		'archivers'    => array(),
263
		// plugin settings
264
		'plugin'       => array(),
265
		// required to fix bug on macos
266
		'utf8fix'      => false,
267
		 //                           й                 ё              Й               Ё              Ø         Å
268
		'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"),
269
		'utf8replace'  => array("\u0439",        "\u0451",       "\u0419",       "\u0401",       "\u00d8", "\u00c5")
270
	);
271
272
	/**
273
	 * Defaults permissions
274
	 *
275
	 * @var array
276
	 **/
277
	protected $defaults = array(
278
		'read'   => true,
279
		'write'  => true,
280
		'locked' => false,
281
		'hidden' => false
282
	);
283
	
284
	/**
285
	 * Access control function/class
286
	 *
287
	 * @var mixed
288
	 **/
289
	protected $attributes = array();
290
	
291
	/**
292
	 * Access control function/class
293
	 *
294
	 * @var mixed
295
	 **/
296
	protected $access = null;
297
	
298
	/**
299
	 * Mime types allowed to upload
300
	 *
301
	 * @var array
302
	 **/
303
	protected $uploadAllow = array();
304
	
305
	/**
306
	 * Mime types denied to upload
307
	 *
308
	 * @var array
309
	 **/
310
	protected $uploadDeny = array();
311
	
312
	/**
313
	 * Order to validate uploadAllow and uploadDeny
314
	 *
315
	 * @var array
316
	 **/
317
	protected $uploadOrder = array();
318
	
319
	/**
320
	 * Maximum allowed upload file size.
321
	 * Set as number or string with unit - "10M", "500K", "1G"
322
	 *
323
	 * @var int|string
324
	 **/
325
	protected $uploadMaxSize = 0;
326
	
327
	/**
328
	 * Mimetype detect method
329
	 *
330
	 * @var string
331
	 **/
332
	protected $mimeDetect = 'auto';
333
	
334
	/**
335
	 * Flag - mimetypes from externail file was loaded
336
	 *
337
	 * @var bool
338
	 **/
339
	private static $mimetypesLoaded = false;
340
	
341
	/**
342
	 * Finfo object for mimeDetect == 'finfo'
343
	 *
344
	 * @var object
345
	 **/
346
	protected $finfo = null;
347
	
348
	/**
349
	 * List of disabled client's commands
350
	 *
351
	 * @var array
352
	 **/
353
	protected $disabled = array();
354
	
355
	/**
356
	 * default extensions/mimetypes for mimeDetect == 'internal' 
357
	 *
358
	 * @var array
359
	 **/
360
	protected static $mimetypes = array(
361
		// applications
362
		'ai'    => 'application/postscript',
363
		'eps'   => 'application/postscript',
364
		'exe'   => 'application/x-executable',
365
		'doc'   => 'application/vnd.ms-word',
366
		'xls'   => 'application/vnd.ms-excel',
367
		'ppt'   => 'application/vnd.ms-powerpoint',
368
		'pps'   => 'application/vnd.ms-powerpoint',
369
		'pdf'   => 'application/pdf',
370
		'xml'   => 'application/xml',
371
		'swf'   => 'application/x-shockwave-flash',
372
		'torrent' => 'application/x-bittorrent',
373
		'jar'   => 'application/x-jar',
374
		// open office (finfo detect as application/zip)
375
		'odt'   => 'application/vnd.oasis.opendocument.text',
376
		'ott'   => 'application/vnd.oasis.opendocument.text-template',
377
		'oth'   => 'application/vnd.oasis.opendocument.text-web',
378
		'odm'   => 'application/vnd.oasis.opendocument.text-master',
379
		'odg'   => 'application/vnd.oasis.opendocument.graphics',
380
		'otg'   => 'application/vnd.oasis.opendocument.graphics-template',
381
		'odp'   => 'application/vnd.oasis.opendocument.presentation',
382
		'otp'   => 'application/vnd.oasis.opendocument.presentation-template',
383
		'ods'   => 'application/vnd.oasis.opendocument.spreadsheet',
384
		'ots'   => 'application/vnd.oasis.opendocument.spreadsheet-template',
385
		'odc'   => 'application/vnd.oasis.opendocument.chart',
386
		'odf'   => 'application/vnd.oasis.opendocument.formula',
387
		'odb'   => 'application/vnd.oasis.opendocument.database',
388
		'odi'   => 'application/vnd.oasis.opendocument.image',
389
		'oxt'   => 'application/vnd.openofficeorg.extension',
390
		// MS office 2007 (finfo detect as application/zip)
391
		'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
392
		'docm'  => 'application/vnd.ms-word.document.macroEnabled.12',
393
		'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
394
		'dotm'  => 'application/vnd.ms-word.template.macroEnabled.12',
395
		'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
396
		'xlsm'  => 'application/vnd.ms-excel.sheet.macroEnabled.12',
397
		'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
398
		'xltm'  => 'application/vnd.ms-excel.template.macroEnabled.12',
399
		'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
400
		'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
401
		'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
402
		'pptm'  => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
403
		'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
404
		'ppsm'  => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
405
		'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
406
		'potm'  => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
407
		'ppam'  => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
408
		'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
409
		'sldm'  => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
410
		// archives
411
		'gz'    => 'application/x-gzip',
412
		'tgz'   => 'application/x-gzip',
413
		'bz'    => 'application/x-bzip2',
414
		'bz2'   => 'application/x-bzip2',
415
		'tbz'   => 'application/x-bzip2',
416
		'xz'    => 'application/x-xz',
417
		'zip'   => 'application/zip',
418
		'rar'   => 'application/x-rar',
419
		'tar'   => 'application/x-tar',
420
		'7z'    => 'application/x-7z-compressed',
421
		// texts
422
		'txt'   => 'text/plain',
423
		'php'   => 'text/x-php',
424
		'html'  => 'text/html',
425
		'htm'   => 'text/html',
426
		'js'    => 'text/javascript',
427
		'css'   => 'text/css',
428
		'rtf'   => 'text/rtf',
429
		'rtfd'  => 'text/rtfd',
430
		'py'    => 'text/x-python',
431
		'java'  => 'text/x-java-source',
432
		'rb'    => 'text/x-ruby',
433
		'sh'    => 'text/x-shellscript',
434
		'pl'    => 'text/x-perl',
435
		'xml'   => 'text/xml',
436
		'sql'   => 'text/x-sql',
437
		'c'     => 'text/x-csrc',
438
		'h'     => 'text/x-chdr',
439
		'cpp'   => 'text/x-c++src',
440
		'hh'    => 'text/x-c++hdr',
441
		'log'   => 'text/plain',
442
		'csv'   => 'text/x-comma-separated-values',
443
		'md'    => 'text/x-markdown',
444
		'markdown' => 'text/x-markdown',
445
		// images
446
		'bmp'   => 'image/x-ms-bmp',
447
		'jpg'   => 'image/jpeg',
448
		'jpeg'  => 'image/jpeg',
449
		'gif'   => 'image/gif',
450
		'png'   => 'image/png',
451
		'tif'   => 'image/tiff',
452
		'tiff'  => 'image/tiff',
453
		'tga'   => 'image/x-targa',
454
		'psd'   => 'image/vnd.adobe.photoshop',
455
		'ai'    => 'image/vnd.adobe.photoshop',
456
		'xbm'   => 'image/xbm',
457
		'pxm'   => 'image/pxm',
458
		//audio
459
		'mp3'   => 'audio/mpeg',
460
		'mid'   => 'audio/midi',
461
		'ogg'   => 'audio/ogg',
462
		'oga'   => 'audio/ogg',
463
		'm4a'   => 'audio/x-m4a',
464
		'wav'   => 'audio/wav',
465
		'wma'   => 'audio/x-ms-wma',
466
		// video
467
		'avi'   => 'video/x-msvideo',
468
		'dv'    => 'video/x-dv',
469
		'mp4'   => 'video/mp4',
470
		'mpeg'  => 'video/mpeg',
471
		'mpg'   => 'video/mpeg',
472
		'mov'   => 'video/quicktime',
473
		'wm'    => 'video/x-ms-wmv',
474
		'flv'   => 'video/x-flv',
475
		'mkv'   => 'video/x-matroska',
476
		'webm'  => 'video/webm',
477
		'ogv'   => 'video/ogg',
478
		'ogm'   => 'video/ogg'
479
		);
480
	
481
	/**
482
	 * Directory separator - required by client
483
	 *
484
	 * @var string
485
	 **/
486
	protected $separator = DIRECTORY_SEPARATOR;
487
	
488
	/**
489
	 * System Root path (Unix like: '/', Windows: '\', 'C:\' or 'D:\'...)
490
	 *
491
	 * @var string
492
	 **/
493
	protected $systemRoot = DIRECTORY_SEPARATOR;
494
	
495
	/**
496
	 * Mimetypes allowed to display
497
	 *
498
	 * @var array
499
	 **/
500
	protected $onlyMimes = array();
501
	
502
	/**
503
	 * Store files moved or overwrited files info
504
	 *
505
	 * @var array
506
	 **/
507
	protected $removed = array();
508
	
509
	/**
510
	 * Cache storage
511
	 *
512
	 * @var array
513
	 **/
514
	protected $cache = array();
515
	
516
	/**
517
	 * Cache by folders
518
	 *
519
	 * @var array
520
	 **/
521
	protected $dirsCache = array();
522
	
523
	/**
524
	 * Cache for subdirsCE()
525
	 * 
526
	 * @var array
527
	 */
528
	protected $subdirsCache = array();
529
	
530
	/**
531
	 * Reference of $_SESSION[elFinder::$sessionCacheKey][$this->id]
532
	 * 
533
	 * @var array
534
	 */
535
	protected $sessionCache;
536
	
537
	/*********************************************************************/
538
	/*                            INITIALIZATION                         */
539
	/*********************************************************************/
540
	
541
	/**
542
	 * Prepare driver before mount volume.
543
	 * Return true if volume is ready.
544
	 *
545
	 * @return bool
546
	 * @author Dmitry (dio) Levashov
547
	 **/
548
	protected function init() {
549
		return true;
550
	}	
551
		
552
	/**
553
	 * Configure after successfull mount.
554
	 * By default set thumbnails path and image manipulation library.
555
	 *
556
	 * @return void
557
	 * @author Dmitry (dio) Levashov
558
	 **/
559
	protected function configure() {
560
		// set thumbnails path
561
		$path = $this->options['tmbPath'];
562
		if ($path) {
563 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...
564
				if (@mkdir($path)) {
565
					chmod($path, $this->options['tmbPathMode']);
566
				} else {
567
					$path = '';
568
				}
569
			} 
570
			
571
			if (is_dir($path) && is_readable($path)) {
572
				$this->tmbPath = $path;
573
				$this->tmbPathWritable = is_writable($path);
574
			}
575
		}
576
577
		// set image manipulation library
578
		$type = preg_match('/^(imagick|gd|auto)$/i', $this->options['imgLib'])
579
			? strtolower($this->options['imgLib'])
580
			: 'auto';
581
582
		if (($type == 'imagick' || $type == 'auto') && extension_loaded('imagick')) {
583
			$this->imgLib = 'imagick';
584
		} else {
585
			$this->imgLib = function_exists('gd_info') ? 'gd' : '';
586
		}
587
		
588
		// check 'statOwner' for command `chmod`
589
		if (empty($this->options['statOwner'])) {
590
			$this->disabled[] ='chmod';
591
		}
592
		
593
		// check 'mimeMap'
594
		if (!is_array($this->options['mimeMap'])) {
595
			$this->options['mimeMap'] = array();
596
		}
597
	}
598
	
599
	
600
	/*********************************************************************/
601
	/*                              PUBLIC API                           */
602
	/*********************************************************************/
603
	
604
	/**
605
	 * Return driver id. Used as a part of volume id.
606
	 *
607
	 * @return string
608
	 * @author Dmitry (dio) Levashov
609
	 **/
610
	public function driverId() {
611
		return $this->driverId;
612
	}
613
	
614
	/**
615
	 * Return volume id
616
	 *
617
	 * @return string
618
	 * @author Dmitry (dio) Levashov
619
	 **/
620
	public function id() {
621
		return $this->id;
622
	}
623
		
624
	/**
625
	 * Return debug info for client
626
	 *
627
	 * @return array
628
	 * @author Dmitry (dio) Levashov
629
	 **/
630
	public function debug() {
631
		return array(
632
			'id'         => $this->id(),
633
			'name'       => strtolower(substr(get_class($this), strlen('elfinderdriver'))),
634
			'mimeDetect' => $this->mimeDetect,
635
			'imgLib'     => $this->imgLib
636
		);
637
	}
638
639
	/**
640
	 * chmod a file or folder
641
	 *
642
	 * @param  string   $hash    file or folder hash to chmod
643
	 * @param  string   $mode    octal string representing new permissions
644
	 * @return array|false
645
	 * @author David Bartle
646
	 **/
647
	public function chmod($hash, $mode) {
648
		if ($this->commandDisabled('chmod')) {
649
			return $this->setError(elFinder::ERROR_PERM_DENIED);
650
		}
651
652
		if (!($file = $this->file($hash))) {
653
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
654
		}
655
656
		$write = $file['write'];
657
		if (!$write && !$this->options['allowChmodReadOnly']) {
658
			return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
659
		}
660
661
		$path = $this->decode($hash);
662
663 View Code Duplication
		if ($this->convEncOut(!$this->_chmod($this->convEncIn($path), $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...
664
			return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
665
		}
666
667
		$this->clearcache();
668
669
		if ($file = $this->stat($path)) {
670
			$files = array($file);
671
			if ($file['mime'] === 'directory' && $write !== $file['write']) {
672
				foreach ($this->getScandir($path) as $stat) {
673
					if ($this->mimeAccepted($stat['mime'])) {
674
						$files[] = $stat;
675
					}
676
				}
677
			}
678
			return $files;
679
		} else {
680
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
681
		}
682
	}
683
	
684
	/**
685
	 * "Mount" volume.
686
	 * Return true if volume available for read or write, 
687
	 * false - otherwise
688
	 *
689
	 * @return bool
690
	 * @author Dmitry (dio) Levashov
691
	 * @author Alexey Sukhotin
692
	 **/
693
	public function mount(array $opts) {
0 ignored issues
show
Coding Style introduced by
mount uses the super-global variable $_SERVER which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
mount uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
mount uses the super-global variable $_GET which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
mount uses the super-global variable $_SESSION which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
694
		if (!isset($opts['path']) || $opts['path'] === '') {
695
			return $this->setError('Path undefined.');;
696
		}
697
		
698
		$this->options = array_merge($this->options, $opts);
699
		$this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
700
		$this->root = $this->normpathCE($this->options['path']);
701
		$this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
702
		$this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : DIRECTORY_SEPARATOR;
703
		
704
		// set server encoding
705
		if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') {
706
			$this->encoding = $this->options['encoding'];
707
		} else {
708
			$this->encoding = null;
709
		}
710
		
711
		$argInit = ($_SERVER['REQUEST_METHOD'] === 'POST')? !empty($_POST['init']) : !empty($_GET['init']);
712
		
713
		// session cache
714
		if ($argInit || ! isset($_SESSION[elFinder::$sessionCacheKey][$this->id])) {
715
			$_SESSION[elFinder::$sessionCacheKey][$this->id] = array();
716
		}
717
		$this->sessionCache = &$_SESSION[elFinder::$sessionCacheKey][$this->id];
718
		
719
		// default file attribute
720
		$this->defaults = array(
721
			'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
722
			'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
723
			'locked'  => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false,
724
			'hidden'  => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false
725
		);
726
727
		// root attributes
728
		$this->attributes[] = array(
729
			'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
730
			'locked'  => true,
731
			'hidden'  => false
732
		);
733
		// set files attributes
734 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...
735
			
736
			foreach ($this->options['attributes'] as $a) {
737
				// attributes must contain pattern and at least one rule
738
				if (!empty($a['pattern']) || count($a) > 1) {
739
					$this->attributes[] = $a;
740
				}
741
			}
742
		}
743
744
		if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
745
			$this->access = $this->options['accessControl'];
746
		}
747
		
748
		$this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
749
		$this->yesterday = $this->today-86400;
750
		
751
		// debug($this->attributes);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
752
		if (!$this->init()) {
753
			return false;
754
		}
755
		
756
		// check some options is arrays
757
		$this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
758
			? $this->options['uploadAllow']
759
			: array();
760
			
761
		$this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
762
			? $this->options['uploadDeny']
763
			: array();
764
765
		if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
766
			$parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
767
			$this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
768
		} else { // telephat_mode off
769
			$this->uploadOrder = $this->options['uploadOrder'];
770
		}
771
			
772
		if (!empty($this->options['uploadMaxSize'])) {
773
			$size = ''.$this->options['uploadMaxSize'];
774
			$unit = strtolower(substr($size, strlen($size) - 1));
775
			$n = 1;
776
			switch ($unit) {
777
				case 'k':
778
					$n = 1024;
779
					break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
780
				case 'm':
781
					$n = 1048576;
782
					break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
783
				case 'g':
784
					$n = 1073741824;
785
			}
786
			$this->uploadMaxSize = intval($size)*$n;
787
		}
788
		// Set maximum to PHP_INT_MAX
789
		if (!defined('PHP_INT_MAX')) {
790
			define('PHP_INT_MAX', 2147483647);
791
		}
792
		if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) {
793
			$this->uploadMaxSize = PHP_INT_MAX;
794
		}
795
		
796
		$this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
797
			? $this->options['disabled']
798
			: array();
799
		
800
		$this->cryptLib   = $this->options['cryptLib'];
801
		$this->mimeDetect = $this->options['mimeDetect'];
802
803
		// find available mimetype detect method
804
		$type = strtolower($this->options['mimeDetect']);
805
		$type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
806
		$regexp = '/text\/x\-(php|c\+\+)/';
807
	
808
		if (($type == 'finfo' || $type == 'auto') 
809
		&& class_exists('finfo')) {
810
			$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
811
		} else {
812
			$tmpFileInfo = false;
813
		}
814
	
815
		if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
816
			$type = 'finfo';
817
			$this->finfo = finfo_open(FILEINFO_MIME);
818
		} elseif (($type == 'mime_content_type' || $type == 'auto') 
819
		&& function_exists('mime_content_type')
820
		&& 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...
821
			$type = 'mime_content_type';
822
		} else {
823
			$type = 'internal';
824
		}
825
		$this->mimeDetect = $type;
826
827
		// load mimes from external file for mimeDetect == 'internal'
828
		// based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
829
		// file must be in file directory or in parent one 
830
		if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
831
			self::$mimetypesLoaded = true;
832
			$this->mimeDetect = 'internal';
833
			$file = false;
834
			if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
835
				$file = $this->options['mimefile'];
836
			} elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
837
				$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
838
			} elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
839
				$file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
840
			}
841
842
			if ($file && file_exists($file)) {
843
				$mimecf = file($file);
844
845
				foreach ($mimecf as $line_num => $line) {
846
					if (!preg_match('/^\s*#/', $line)) {
847
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
848
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
849
							if (!isset(self::$mimetypes[$mime[$i]])) {
850
								self::$mimetypes[$mime[$i]] = $mime[0];
851
							}
852
						}
853
					}
854
				}
855
			}
856
		}
857
858
		$this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias'];
859
860
		// This get's triggered if $this->root == '/' and alias is empty.
861
		// Maybe modify _basename instead?
862
		if ($this->rootName === '') $this->rootName = $this->separator;
863
864
		$root = $this->stat($this->root);
865
		
866
		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...
867
			return $this->setError('Root folder does not exists.');
868
		}
869
		if (!$root['read'] && !$root['write']) {
870
			return $this->setError('Root folder has not read and write permissions.');
871
		}
872
		
873
		// debug($root);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
874
		
875
		if ($root['read']) {
876
			// check startPath - path to open by default instead of root
877
			if ($this->options['startPath']) {
878
				$start = $this->stat($this->options['startPath']);
879
				if (!empty($start)
880
				&& $start['mime'] == 'directory'
881
				&& $start['read']
882
				&& empty($start['hidden'])
883
				&& $this->inpathCE($this->options['startPath'], $this->root)) {
884
					$this->startPath = $this->options['startPath'];
885
					if (substr($this->startPath, -1, 1) == $this->options['separator']) {
886
						$this->startPath = substr($this->startPath, 0, -1);
887
					}
888
				}
889
			}
890
		} else {
891
			$this->options['URL']     = '';
892
			$this->options['tmbURL']  = '';
893
			$this->options['tmbPath'] = '';
894
			// read only volume
895
			array_unshift($this->attributes, array(
896
				'pattern' => '/.*/',
897
				'read'    => false
898
			));
899
		}
900
		$this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
901
		$this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
902
		$this->URL      = $this->options['URL'];
903
		if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
904
			$this->URL .= '/';
905
		}
906
907
		$this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
908
		if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
909
			$this->tmbURL .= '/';
910
		}
911
		
912
		$this->nameValidator = !empty($this->options['acceptedName']) && (is_string($this->options['acceptedName']) || is_callable($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...
913
			? $this->options['acceptedName']
914
			: '';
915
916
		$this->_checkArchivers();
917
		// manual control archive types to create
918 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...
919
			foreach ($this->archivers['create'] as $mime => $v) {
920
				if (!in_array($mime, $this->options['archiveMimes'])) {
921
					unset($this->archivers['create'][$mime]);
922
				}
923
			}
924
		}
925
		
926
		// manualy add archivers
927 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...
928
			foreach ($this->options['archivers']['create'] as $mime => $conf) {
929
				if (strpos($mime, 'application/') === 0 
930
				&& !empty($conf['cmd']) 
931
				&& isset($conf['argc']) 
932
				&& !empty($conf['ext'])
933
				&& !isset($this->archivers['create'][$mime])) {
934
					$this->archivers['create'][$mime] = $conf;
935
				}
936
			}
937
		}
938
		
939 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...
940
			foreach ($this->options['archivers']['extract'] as $mime => $conf) {
941
				if (strpos($mime, 'application/') === 0
942
				&& !empty($conf['cmd']) 
943
				&& isset($conf['argc']) 
944
				&& !empty($conf['ext'])
945
				&& !isset($this->archivers['extract'][$mime])) {
946
					$this->archivers['extract'][$mime] = $conf;
947
				}
948
			}
949
		}
950
951
		$this->configure();
952
		// echo $this->uploadMaxSize;
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
953
		// echo $this->options['uploadMaxSize'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
954
		return $this->mounted = true;
955
	}
956
	
957
	/**
958
	 * Some "unmount" stuffs - may be required by virtual fs
959
	 *
960
	 * @return void
961
	 * @author Dmitry (dio) Levashov
962
	 **/
963
	public function umount() {
964
	}
965
	
966
	/**
967
	 * Return error message from last failed action
968
	 *
969
	 * @return array
970
	 * @author Dmitry (dio) Levashov
971
	 **/
972
	public function error() {
973
		return $this->error;
974
	}
975
	
976
	/**
977
	 * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes)
978
	 * 
979
	 * @return array
980
	 * @author Naoki Sawada
981
	 */
982
	public function getMimeTable() {
983
		return elFinderVolumeDriver::$mimetypes;
984
	}
985
	
986
	/**
987
	 * Set mimetypes allowed to display to client
988
	 *
989
	 * @param  array  $mimes
990
	 * @return void
991
	 * @author Dmitry (dio) Levashov
992
	 **/
993
	public function setMimesFilter($mimes) {
994
		if (is_array($mimes)) {
995
			$this->onlyMimes = $mimes;
996
		}
997
	}
998
	
999
	/**
1000
	 * Return root folder hash
1001
	 *
1002
	 * @return string
1003
	 * @author Dmitry (dio) Levashov
1004
	 **/
1005
	public function root() {
1006
		return $this->encode($this->root);
1007
	}
1008
	
1009
	/**
1010
	 * Return root or startPath hash
1011
	 *
1012
	 * @return string
1013
	 * @author Dmitry (dio) Levashov
1014
	 **/
1015
	public function defaultPath() {
1016
		return $this->encode($this->startPath ? $this->startPath : $this->root);
1017
	}
1018
		
1019
	/**
1020
	 * Return volume options required by client:
1021
	 *
1022
	 * @return array
1023
	 * @author Dmitry (dio) Levashov
1024
	 **/
1025
	public function options($hash) {
1026
		$create = $createext = array();
1027
		if (isset($this->archivers['create']) && is_array($this->archivers['create'])) {
1028
			foreach($this->archivers['create'] as $m => $v) {
1029
				$create[] = $m;
1030
				$createext[$m] = $v['ext'];
1031
			}
1032
		}
1033
		return array(
1034
			'path'          => $this->path($hash),
1035
			'url'           => $this->URL,
1036
			'tmbUrl'        => $this->tmbURL,
1037
			'disabled'      => array_merge(array_unique($this->disabled)), // `array_merge` for type array of JSON
1038
			'separator'     => $this->separator,
1039
			'copyOverwrite' => intval($this->options['copyOverwrite']),
1040
			'uploadMaxSize' => intval($this->uploadMaxSize),
1041
			'archivers'     => array(
1042
				'create'  => $create,
1043
				'extract' => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(),
1044
				'createext' => $createext
1045
			),
1046
			'uiCmdMap' => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array()
1047
		);
1048
	}
1049
	
1050
	/**
1051
	 * Get plugin values of this options
1052
	 * 
1053
	 * @param string $name  Plugin name
1054
	 * @return NULL|array   Plugin values
1055
	 * @author Naoki Sawada
1056
	 */
1057
	public function getOptionsPlugin($name = '') {
1058
		if ($name) {
1059
			return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array();
1060
		} else {
1061
			return $this->options['plugin'];
1062
		}
1063
	}
1064
	
1065
	/**
1066
	 * Return true if command disabled in options
1067
	 *
1068
	 * @param  string  $cmd  command name
1069
	 * @return bool
1070
	 * @author Dmitry (dio) Levashov
1071
	 **/
1072
	public function commandDisabled($cmd) {
1073
		return in_array($cmd, $this->disabled);
1074
	}
1075
	
1076
	/**
1077
	 * Return true if mime is required mimes list
1078
	 *
1079
	 * @param  string     $mime   mime type to check
1080
	 * @param  array      $mimes  allowed mime types list or not set to use client mimes list
1081
	 * @param  bool|null  $empty  what to return on empty list
1082
	 * @return bool|null
1083
	 * @author Dmitry (dio) Levashov
1084
	 * @author Troex Nevelin
1085
	 **/
1086
	public function mimeAccepted($mime, $mimes = null, $empty = true) {
1087
		$mimes = is_array($mimes) ? $mimes : $this->onlyMimes;
1088
		if (empty($mimes)) {
1089
			return $empty;
1090
		}
1091
		return $mime == 'directory'
1092
			|| in_array('all', $mimes)
1093
			|| in_array('All', $mimes)
1094
			|| in_array($mime, $mimes)
1095
			|| in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
1096
	}
1097
	
1098
	/**
1099
	 * Return true if voume is readable.
1100
	 *
1101
	 * @return bool
1102
	 * @author Dmitry (dio) Levashov
1103
	 **/
1104
	public function isReadable() {
1105
		$stat = $this->stat($this->root);
1106
		return $stat['read'];
1107
	}
1108
	
1109
	/**
1110
	 * Return true if copy from this volume allowed
1111
	 *
1112
	 * @return bool
1113
	 * @author Dmitry (dio) Levashov
1114
	 **/
1115
	public function copyFromAllowed() {
1116
		return !!$this->options['copyFrom'];
1117
	}
1118
	
1119
	/**
1120
	 * Return file path related to root with convert encoging
1121
	 *
1122
	 * @param  string   $hash  file hash
1123
	 * @return string
1124
	 * @author Dmitry (dio) Levashov
1125
	 **/
1126
	public function path($hash) {
1127
		return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash))));
1128
	}
1129
	
1130
	/**
1131
	 * Return file real path if file exists
1132
	 *
1133
	 * @param  string  $hash  file hash
1134
	 * @return string
1135
	 * @author Dmitry (dio) Levashov
1136
	 **/
1137
	public function realpath($hash) {
1138
		$path = $this->decode($hash);
1139
		return $this->stat($path) ? $path : false;
1140
	}
1141
	
1142
	/**
1143
	 * Return list of moved/overwrited files
1144
	 *
1145
	 * @return array
1146
	 * @author Dmitry (dio) Levashov
1147
	 **/
1148
	public function removed() {
1149
		return $this->removed;
1150
	}
1151
	
1152
	/**
1153
	 * Clean removed files list
1154
	 *
1155
	 * @return void
1156
	 * @author Dmitry (dio) Levashov
1157
	 **/
1158
	public function resetRemoved() {
1159
		$this->removed = array();
1160
	}
1161
	
1162
	/**
1163
	 * Return file/dir hash or first founded child hash with required attr == $val
1164
	 *
1165
	 * @param  string   $hash  file hash
1166
	 * @param  string   $attr  attribute name
1167
	 * @param  bool     $val   attribute value
1168
	 * @return string|false
1169
	 * @author Dmitry (dio) Levashov
1170
	 **/
1171
	public function closest($hash, $attr, $val) {
1172
		return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1173
	}
1174
	
1175
	/**
1176
	 * Return file info or false on error
1177
	 *
1178
	 * @param  string   $hash      file hash
1179
	 * @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...
1180
	 * @return array|false
1181
	 * @author Dmitry (dio) Levashov
1182
	 **/
1183
	public function file($hash) {
1184
		$path = $this->decode($hash);
1185
		
1186
		$file = $this->stat($path);
1187
		
1188
		if ($hash === $this->root()) {
1189
			$file['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array();
1190
			$file['disabled'] = array_merge(array_unique($this->disabled)); // `array_merge` for type array of JSON
1191
		}
1192
		
1193
		return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1194
	}
1195
	
1196
	/**
1197
	 * Return folder info
1198
	 *
1199
	 * @param  string   $hash  folder hash
1200
	 * @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...
1201
	 * @return array|false
1202
	 * @author Dmitry (dio) Levashov
1203
	 **/
1204
	public function dir($hash, $resolveLink=false) {
1205
		if (($dir = $this->file($hash)) == false) {
1206
			return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1207
		}
1208
1209
		if ($resolveLink && !empty($dir['thash'])) {
1210
			$dir = $this->file($dir['thash']);
1211
		}
1212
		
1213
		return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) 
1214
			? $dir 
1215
			: $this->setError(elFinder::ERROR_NOT_DIR);
1216
	}
1217
	
1218
	/**
1219
	 * Return directory content or false on error
1220
	 *
1221
	 * @param  string   $hash   file hash
1222
	 * @return array|false
1223
	 * @author Dmitry (dio) Levashov
1224
	 **/
1225
	public function scandir($hash) {
1226
		if (($dir = $this->dir($hash)) == false) {
1227
			return false;
1228
		}
1229
		
1230
		return $dir['read']
1231
			? $this->getScandir($this->decode($hash))
1232
			: $this->setError(elFinder::ERROR_PERM_DENIED);
1233
	}
1234
1235
	/**
1236
	 * Return dir files names list
1237
	 * 
1238
	 * @param  string  $hash   file hash
1239
	 * @return array
1240
	 * @author Dmitry (dio) Levashov
1241
	 **/
1242
	public function ls($hash) {
1243 View Code Duplication
		if (($dir = $this->dir($hash)) == false || !$dir['read']) {
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...
1244
			return false;
1245
		}
1246
		
1247
		$list = array();
1248
		$path = $this->decode($hash);
1249
		
1250
		foreach ($this->getScandir($path) as $stat) {
1251
			if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) {
1252
				$list[] = $stat['name'];
1253
			}
1254
		}
1255
1256
		return $list;
1257
	}
1258
1259
	/**
1260
	 * Return subfolders for required folder or false on error
1261
	 *
1262
	 * @param  string   $hash  folder hash or empty string to get tree from root folder
1263
	 * @param  int      $deep  subdir deep
1264
	 * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1265
	 * @return array|false
1266
	 * @author Dmitry (dio) Levashov
1267
	 **/
1268
	public function tree($hash='', $deep=0, $exclude='') {
1269
		$path = $hash ? $this->decode($hash) : $this->root;
1270
		
1271 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...
1272
			return false;
1273
		}
1274
		
1275
		$dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1276
		array_unshift($dirs, $dir);
1277
		return $dirs;
1278
	}
1279
	
1280
	/**
1281
	 * Return part of dirs tree from required dir up to root dir
1282
	 *
1283
	 * @param  string    $hash   directory hash
1284
	 * @param  bool|null $lineal only lineal parents
1285
	 * @return array
1286
	 * @author Dmitry (dio) Levashov
1287
	 **/
1288
	public function parents($hash, $lineal = false) {
1289
		if (($current = $this->dir($hash)) == false) {
1290
			return false;
1291
		}
1292
1293
		$path = $this->decode($hash);
1294
		$tree = array();
1295
		
1296
		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...
1297
			$path = $this->dirnameCE($path);
1298
			$stat = $this->stat($path);
1299
			if (!empty($stat['hidden']) || !$stat['read']) {
1300
				return false;
1301
			}
1302
			
1303
			array_unshift($tree, $stat);
1304
			if (!$lineal) {
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...
1305
				foreach ($this->gettree($path, 0) as $dir) {
1306
					if (!in_array($dir, $tree)) {
1307
						$tree[] = $dir;
1308
					}
1309
				}
1310
			}
1311
		}
1312
1313
		return $tree ? $tree : array($current);
1314
	}
1315
	
1316
	/**
1317
	 * Create thumbnail for required file and return its name of false on failed
1318
	 *
1319
	 * @return string|false
1320
	 * @author Dmitry (dio) Levashov
1321
	 **/
1322
	public function tmb($hash) {
1323
		$path = $this->decode($hash);
1324
		$stat = $this->stat($path);
1325
		
1326
		if (isset($stat['tmb'])) {
1327
			return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1328
		}
1329
		return false;
1330
	}
1331
	
1332
	/**
1333
	 * Return file size / total directory size
1334
	 *
1335
	 * @param  string   file hash
1336
	 * @return int
1337
	 * @author Dmitry (dio) Levashov
1338
	 **/
1339
	public function size($hash) {
1340
		return $this->countSize($this->decode($hash));
1341
	}
1342
	
1343
	/**
1344
	 * Open file for reading and return file pointer
1345
	 *
1346
	 * @param  string   file hash
1347
	 * @return Resource
1348
	 * @author Dmitry (dio) Levashov
1349
	 **/
1350
	public function open($hash) {
1351 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...
1352
		|| $file['mime'] == 'directory') {
1353
			return false;
1354
		}
1355
		
1356
		return $this->fopenCE($this->decode($hash), 'rb');
1357
	}
1358
	
1359
	/**
1360
	 * Close file pointer
1361
	 *
1362
	 * @param  Resource  $fp   file pointer
1363
	 * @param  string    $hash file hash
1364
	 * @return void
1365
	 * @author Dmitry (dio) Levashov
1366
	 **/
1367
	public function close($fp, $hash) {
1368
		$this->fcloseCE($fp, $this->decode($hash));
1369
	}
1370
	
1371
	/**
1372
	 * Create directory and return dir info
1373
	 *
1374
	 * @param  string   $dsthash  destination directory hash
1375
	 * @param  string   $name directory name
1376
	 * @return array|false
1377
	 * @author Dmitry (dio) Levashov
1378
	 **/
1379
	public function mkdir($dsthash, $name) {
1380
		if ($this->commandDisabled('mkdir')) {
1381
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1382
		}
1383
		
1384
		if (!$this->nameAccepted($name)) {
1385
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1386
		}
1387
		
1388
		if (($dir = $this->dir($dsthash)) == false) {
1389
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash);
1390
		}
1391
		
1392
		$path = $this->decode($dsthash);
1393
		
1394
		if (!$dir['write'] || !$this->allowCreate($path, $name, true)) {
1395
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1396
		}
1397
		
1398
		$dst  = $this->joinPathCE($path, $name);
1399
		$stat = $this->stat($dst); 
1400
		if (!empty($stat)) { 
1401
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1402
		}
1403
		$this->clearcache();
1404
		return ($path = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1405
	}
1406
	
1407
	/**
1408
	 * Create empty file and return its info
1409
	 *
1410
	 * @param  string   $dst  destination directory
1411
	 * @param  string   $name file name
1412
	 * @return array|false
1413
	 * @author Dmitry (dio) Levashov
1414
	 **/
1415
	public function mkfile($dst, $name) {
1416
		if ($this->commandDisabled('mkfile')) {
1417
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1418
		}
1419
		
1420
		if (!$this->nameAccepted($name)) {
1421
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1422
		}
1423
		
1424 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...
1425
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1426
		}
1427
		
1428
		$path = $this->decode($dst);
1429
		
1430
		if (!$dir['write'] || !$this->allowCreate($path, $name, false)) {
1431
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1432
		}
1433
		
1434
		if ($this->stat($this->joinPathCE($path, $name))) {
1435
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1436
		}
1437
		
1438
		$this->clearcache();
1439
		return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1440
	}
1441
	
1442
	/**
1443
	 * Rename file and return file info
1444
	 *
1445
	 * @param  string  $hash  file hash
1446
	 * @param  string  $name  new file name
1447
	 * @return array|false
1448
	 * @author Dmitry (dio) Levashov
1449
	 **/
1450
	public function rename($hash, $name) {
1451
		if ($this->commandDisabled('rename')) {
1452
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1453
		}
1454
		
1455
		if (!$this->nameAccepted($name)) {
1456
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1457
		}
1458
		
1459
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1460 View Code Duplication
		if ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName)) {
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...
1461
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1462
		}
1463
		
1464
		if (!($file = $this->file($hash))) {
1465
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1466
		}
1467
		
1468
		if ($name == $file['name']) {
1469
			return $file;
1470
		}
1471
		
1472
		if (!empty($file['locked'])) {
1473
			return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1474
		}
1475
		
1476
		$path = $this->decode($hash);
1477
		$dir  = $this->dirnameCE($path);
1478
		$stat = $this->stat($this->joinPathCE($dir, $name));
1479
		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...
1480
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1481
		}
1482
		
1483 View Code Duplication
		if (!$this->allowCreate($dir, $name, ($file['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...
1484
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1485
		}
1486
1487
		$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...
1488
1489
1490 View Code Duplication
		if ($path = $this->convEncOut($this->_move($this->convEncIn($path), $this->convEncIn($dir), $this->convEncIn($name)))) {
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...
1491
			$this->clearcache();
1492
			return $this->stat($path);
1493
		}
1494
		return false;
1495
	}
1496
	
1497
	/**
1498
	 * Create file copy with suffix "copy number" and return its info
1499
	 *
1500
	 * @param  string   $hash    file hash
1501
	 * @param  string   $suffix  suffix to add to file name
1502
	 * @return array|false
1503
	 * @author Dmitry (dio) Levashov
1504
	 **/
1505
	public function duplicate($hash, $suffix='copy') {
1506
		if ($this->commandDisabled('duplicate')) {
1507
			return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1508
		}
1509
		
1510
		if (($file = $this->file($hash)) == false) {
1511
			return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1512
		}
1513
1514
		$path = $this->decode($hash);
1515
		$dir  = $this->dirnameCE($path);
1516
		$name = $this->uniqueName($dir, $this->basenameCE($path), ' '.$suffix.' ');
1517
1518 View Code Duplication
		if (!$this->allowCreate($dir, $name, ($file['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...
1519
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1520
		}
1521
1522
		return ($path = $this->copy($path, $dir, $name)) == false
1523
			? false
1524
			: $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copy($path, $dir, $name) on line 1522 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...
1525
	}
1526
	
1527
	/**
1528
	 * Save uploaded file. 
1529
	 * On success return array with new file stat and with removed file hash (if existed file was replaced)
1530
	 *
1531
	 * @param  Resource $fp      file pointer
1532
	 * @param  string   $dst     destination folder hash
1533
	 * @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...
1534
	 * @param  string   $tmpname file tmp name - required to detect mime type
1535
	 * @return array|false
1536
	 * @author Dmitry (dio) Levashov
1537
	 **/
1538
	public function upload($fp, $dst, $name, $tmpname) {
0 ignored issues
show
Coding Style introduced by
upload uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1539
		if ($this->commandDisabled('upload')) {
1540
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1541
		}
1542
		
1543 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...
1544
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1545
		}
1546
1547
		if (!$dir['write']) {
1548
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1549
		}
1550
		
1551
		if (!$this->nameAccepted($name)) {
1552
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1553
		}
1554
		
1555
		$mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name);
1556
		$mimeByName = '';
1557
		if ($this->mimeDetect !== 'internal') {
1558
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1559
			if ($mime == 'unknown') {
1560
				$mime = $mimeByName;
1561
			}
1562
		}
1563
1564 View Code Duplication
		if (!$this->allowPutMime($mime) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
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...
1565
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1566
		}
1567
1568
		$tmpsize = sprintf('%u', filesize($tmpname));
1569
		if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) {
1570
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1571
		}
1572
1573
		$dstpath = $this->decode($dst);
1574
		$test    = $this->joinPathCE($dstpath, $name);
1575
		
1576
		$file = $this->stat($test);
1577
		$this->clearcache();
1578
		
1579
		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...
1580
			// check POST data `overwrite` for 3rd party uploader
1581
			$overwrite = isset($_POST['overwrite'])? (bool)$_POST['overwrite'] : $this->options['uploadOverwrite'];
1582
			if ($overwrite) {
1583 View Code Duplication
				if (!$file['write']) {
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...
1584
					return $this->setError(elFinder::ERROR_PERM_DENIED);
1585
				} elseif ($file['mime'] == 'directory') {
1586
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1587
				} 
1588
				$this->remove($test);
1589
			} else {
1590
				$name = $this->uniqueName($dstpath, $name, '-', false);
1591
			}
1592
		}
1593
		
1594
		$stat = array(
1595
			'mime'   => $mime, 
1596
			'width'  => 0, 
1597
			'height' => 0, 
1598
			'size'   => $tmpsize);
1599
		
1600
		// $w = $h = 0;
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1601
		if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
1602
			$stat['width'] = $s[0];
1603
			$stat['height'] = $s[1];
1604
		}
1605
		// $this->clearcache();
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1606
		if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) {
1607
			return false;
1608
		}
1609
		
1610
		
1611
1612
		return $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->saveCE($fp, $dstpath, $name, $stat) on line 1606 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...
1613
	}
1614
	
1615
	/**
1616
	 * Paste files
1617
	 *
1618
	 * @param  Object  $volume  source volume
1619
	 * @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...
1620
	 * @param  string  $dst     destination dir hash
1621
	 * @param  bool    $rmSrc   remove source after copy?
1622
	 * @return array|false
1623
	 * @author Dmitry (dio) Levashov
1624
	 **/
1625
	public function paste($volume, $src, $dst, $rmSrc = false) {
1626
		$err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
1627
		
1628
		if ($this->commandDisabled('paste')) {
1629
			return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
1630
		}
1631
1632
		if (($file = $volume->file($src, $rmSrc)) == false) {
1633
			return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
1634
		}
1635
1636
		$name = $file['name'];
1637
		$errpath = $volume->path($file['hash']);
1638
		
1639 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...
1640
			return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1641
		}
1642
		
1643
		if (!$dir['write'] || !$file['read']) {
1644
			return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1645
		}
1646
1647
		$destination = $this->decode($dst);
1648
1649
		if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
1650
			return $rmSrc
1651
				? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
1652
				: $this->setError($err, $errpath, !empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK);
1653
		}
1654
1655
		$test = $this->joinPathCE($destination, $name);
1656
		$stat = $this->stat($test);
1657
		$this->clearcache();
1658
		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...
1659
			if ($this->options['copyOverwrite']) {
1660
				// do not replace file with dir or dir with file
1661
				if (!$this->isSameType($file['mime'], $stat['mime'])) {
1662
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash']));
1663
				}
1664
				// existed file is not writable
1665
				if (!$stat['write']) {
1666
					return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1667
				}
1668
				// existed file locked or has locked child
1669
				if (($locked = $this->closestByAttr($test, 'locked', true))) {
1670
					$stat = $this->stat($locked);
1671
					return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
1672
				}
1673
				// target is entity file of alias
1674
				if ($volume == $this && ($test == @$file['target'] || $test == $this->decode($src))) {
1675
					return $this->setError(elFinder::ERROR_REPLACE, $errpath);
1676
				}
1677
				// remove existed file
1678
				if (!$this->remove($test)) {
1679
					return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash']));
1680
				}
1681
			} else {
1682
				$name = $this->uniqueName($destination, $name, ' ', false);
1683
			}
1684
		}
1685
		
1686
		// copy/move inside current volume
1687
		if ($volume == $this) {
1688
			$source = $this->decode($src);
1689
			// do not copy into itself
1690
			if ($this->inpathCE($destination, $source)) {
1691
				return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
1692
			}
1693
			$method = $rmSrc ? 'move' : 'copy';
1694
			return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false;
1695
		}
1696
		
1697
		// copy/move from another volume
1698
		if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
1699
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
1700
		}
1701
		
1702
		if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
1703
			return false;
1704
		}
1705
		
1706
		if ($rmSrc) {
1707
			if (!$volume->rm($src)) {
1708
				return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
1709
			}
1710
		}
1711
		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 1702 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...
1712
	}
1713
	
1714
	/**
1715
	 * Return file contents
1716
	 *
1717
	 * @param  string  $hash  file hash
1718
	 * @return string|false
1719
	 * @author Dmitry (dio) Levashov
1720
	 **/
1721
	public function getContents($hash) {
1722
		$file = $this->file($hash);
1723
		
1724
		if (!$file) {
1725
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->setError(\...:ERROR_FILE_NOT_FOUND); (boolean) is incompatible with the return type documented by elFinderVolumeDriver::getContents of type string|false.

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...
1726
		}
1727
		
1728
		if ($file['mime'] == 'directory') {
1729
			return $this->setError(elFinder::ERROR_NOT_FILE);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->setError(\...inder::ERROR_NOT_FILE); (boolean) is incompatible with the return type documented by elFinderVolumeDriver::getContents of type string|false.

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...
1730
		}
1731
		
1732
		if (!$file['read']) {
1733
			return $this->setError(elFinder::ERROR_PERM_DENIED);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->setError(\...er::ERROR_PERM_DENIED); (boolean) is incompatible with the return type documented by elFinderVolumeDriver::getContents of type string|false.

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...
1734
		}
1735
		
1736
		return $this->convEncOut($this->_getContents($this->convEncIn($this->decode($hash))));
1737
	}
1738
	
1739
	/**
1740
	 * Put content in text file and return file info.
1741
	 *
1742
	 * @param  string  $hash     file hash
1743
	 * @param  string  $content  new file content
1744
	 * @return array
1745
	 * @author Dmitry (dio) Levashov
1746
	 **/
1747
	public function putContents($hash, $content) {
1748
		if ($this->commandDisabled('edit')) {
1749
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1750
		}
1751
		
1752
		$path = $this->decode($hash);
1753
		
1754
		if (!($file = $this->file($hash))) {
1755
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1756
		}
1757
		
1758
		if (!$file['write']) {
1759
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1760
		}
1761
		
1762
		// check MIME
1763
		$name = $this->basenameCE($path);
1764
		$mime = '';
1765
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1766
		if ($this->mimeDetect !== 'internal') {
1767
			if ($tp = tmpfile()) {
1768
				fwrite($tp, $content);
1769
				$info = stream_get_meta_data($tp);
1770
				$filepath = $info['uri'];
1771
				$mime = $this->mimetype($filepath, $name);
1772
				fclose($tp);
1773
			}
1774
		}
1775 View Code Duplication
		if (!$this->allowPutMime($mimeByName) || ($mime && $mime !== 'unknown' && !$this->allowPutMime($mime))) {
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...
1776
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1777
		}
1778
		
1779
		$this->clearcache();
1780
		return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false;
1781
	}
1782
	
1783
	/**
1784
	 * Extract files from archive
1785
	 *
1786
	 * @param  string  $hash  archive hash
1787
	 * @return array|bool
1788
	 * @author Dmitry (dio) Levashov, 
1789
	 * @author Alexey Sukhotin
1790
	 **/
1791
	public function extract($hash, $makedir = null) {
1792
		if ($this->commandDisabled('extract')) {
1793
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1794
		}
1795
		
1796
		if (($file = $this->file($hash)) == false) {
1797
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1798
		}
1799
		
1800
		$archiver = isset($this->archivers['extract'][$file['mime']])
1801
			? $this->archivers['extract'][$file['mime']]
1802
			: false;
1803
			
1804
		if (!$archiver) {
1805
			return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
1806
		}
1807
		
1808
		$path   = $this->decode($hash);
1809
		$parent = $this->stat($this->dirnameCE($path));
1810
1811
		if (!$file['read'] || !$parent['write']) {
1812
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1813
		}
1814
		$this->clearcache();
1815
		$this->extractToNewdir = is_null($makedir)? 'auto' : (bool)$makedir;
1 ignored issue
show
Documentation Bug introduced by
It seems like is_null($makedir) ? 'auto' : (bool) $makedir of type string or boolean is incompatible with the declared type integer of property $extractToNewdir.

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

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

Loading history...
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
1816
		
1817
		if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) {
1818
			if (is_array($path)) {
1819
				foreach ($path as $_k => $_p) {
1820
					$path[$_k] = $this->stat($_p);
1821
				}
1822
			} else {
1823
				$path = $this->stat($path);
1824
			}
1825
			return $path;
1826
		} else {
1827
			return false;
1828
		}
1829
	}
1830
1831
	/**
1832
	 * Add files to archive
1833
	 *
1834
	 * @return void
1835
	 **/
1836
	public function archive($hashes, $mime, $name = '') {
1837
		if ($this->commandDisabled('archive')) {
1838
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1839
		}
1840
1841
		$archiver = isset($this->archivers['create'][$mime])
1842
			? $this->archivers['create'][$mime]
1843
			: false;
1844
			
1845
		if (!$archiver) {
1846
			return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
1847
		}
1848
		
1849
		$files = array();
1850
		
1851
		foreach ($hashes as $hash) {
1852
			if (($file = $this->file($hash)) == false) {
1853
				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...
1854
			}
1855
			if (!$file['read']) {
1856
				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...
1857
			}
1858
			$path = $this->decode($hash);
1859
			if (!isset($dir)) {
1860
				$dir = $this->dirnameCE($path);
1861
				$stat = $this->stat($dir);
1862
				if (!$stat['write']) {
1863
					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...
1864
				}
1865
			}
1866
			
1867
			$files[] = $this->basenameCE($path);
1868
		}
1869
		
1870
		if ($name === '') {
1871
			$name = count($files) == 1 ? $files[0] : 'Archive';
1872
		} else {
1873
			$name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name));
1874
		}
1875
		$name .='.' . $archiver['ext'];
1876
		$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...
1877
		$this->clearcache();
1878
		return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false;
1879
	}
1880
	
1881
	/**
1882
	 * Resize image
1883
	 *
1884
	 * @param  string   $hash    image file
1885
	 * @param  int      $width   new width
1886
	 * @param  int      $height  new height
1887
	 * @param  int      $x       X start poistion for crop
1888
	 * @param  int      $y       Y start poistion for crop
1889
	 * @param  string   $mode    action how to mainpulate image
1890
	 * @return array|false
1891
	 * @author Dmitry (dio) Levashov
1892
	 * @author Alexey Sukhotin
1893
	 * @author nao-pon
1894
	 * @author Troex Nevelin
1895
	 **/
1896
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) {
1897
		if ($this->commandDisabled('resize')) {
1898
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1899
		}
1900
		
1901
		if (($file = $this->file($hash)) == false) {
1902
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1903
		}
1904
		
1905
		if (!$file['write'] || !$file['read']) {
1906
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1907
		}
1908
		
1909
		$path = $this->decode($hash);
1910
		
1911
		$work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path);
1912
1913
		if (!$work_path || !is_writable($work_path)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $work_path of type string|false 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...
1914
			if ($work_path && $path !== $work_path && is_file($work_path)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $work_path of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1915
				@unlink($work_path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1916
			}
1917
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1918
		}
1919
1920
		if ($this->imgLib != 'imagick') {
1921
			if (elFinder::isAnimationGif($work_path)) {
1922
				return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
1923
			}
1924
		}
1925
1926
		switch($mode) {
1927
			
1928
			case 'propresize':
1929
				$result = $this->imgResize($work_path, $width, $height, true, true);
1930
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
1931
1932
			case 'crop':
1933
				$result = $this->imgCrop($work_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...
1934
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
1935
1936
			case 'fitsquare':
1937
				$result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']));
1938
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
1939
1940
			case 'rotate':
1941
				$result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['tmbBgColor']));
1942
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
1943
1944
			default:
1945
				$result = $this->imgResize($work_path, $width, $height, false, true);
1946
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
1947
		}
1948
		
1949
		$ret = false;
1950
		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...
1951
			$stat = $this->stat($path);
1952
			clearstatcache();
1953
			$fstat = stat($work_path);
1954
			$stat['size'] = $fstat['size'];
1955
			$stat['ts'] = $fstat['mtime'];
1956
			if ($imgsize = @getimagesize($work_path)) {
1957
				$stat['width'] = $imgsize[0];
1958
				$stat['height'] = $imgsize[1];
1959
				$stat['mime'] = $imgsize['mime'];
1960
			}
1961
			if ($path !== $work_path) {
1962
				if ($fp = @fopen($work_path, 'rb')) {
1963
					$ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat);
1964
					@fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1965
				}
1966
			} else {
1967
				$ret = true;
1968
			}
1969
			if ($ret) {
1970
				$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...
1971
				$this->clearcache();
1972
				$ret = $this->stat($path);
1973
				$ret['width'] = $stat['width'];
1974
				$ret['height'] = $stat['height'];
1975
			}
1976
		}
1977
		if ($path !== $work_path) {
1978
			is_file($work_path) && @unlink($work_path);
1979
		}
1980
		
1981
		return $ret;
1982
	}
1983
	
1984
	/**
1985
	 * Remove file/dir
1986
	 *
1987
	 * @param  string  $hash  file hash
1988
	 * @return bool
1989
	 * @author Dmitry (dio) Levashov
1990
	 **/
1991
	public function rm($hash) {
1992
		return $this->commandDisabled('rm')
1993
			? $this->setError(elFinder::ERROR_PERM_DENIED)
1994
			: $this->remove($this->decode($hash));
1995
	}
1996
	
1997
	/**
1998
	 * Search files
1999
	 *
2000
	 * @param  string  $q  search string
2001
	 * @param  array   $mimes
2002
	 * @return array
2003
	 * @author Dmitry (dio) Levashov
2004
	 **/
2005
	public function search($q, $mimes, $hash = null) {
2006
		$dir = null;
2007
		if ($hash) {
2008
			$dir = $this->decode($hash);
2009
			$stat = $this->stat($dir);
2010
			if (!$stat || $stat['mime'] !== 'directory' || !$stat['read']) {
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...
2011
				$q = '';
2012
			}
2013
		}
2014
		if ($mimes && $this->onlyMimes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mimes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
Bug Best Practice introduced by
The expression $this->onlyMimes 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...
2015
			$mimes = array_intersect($mimes, $this->onlyMimes);
2016
			if (!$mimes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mimes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
2017
				$q = '';
2018
			}
2019
		}
2020
		return ($q === '' || $this->commandDisabled('search'))
2021
			? array()
2022
			: $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes);
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
2023
	}
2024
	
2025
	/**
2026
	 * Return image dimensions
2027
	 *
2028
	 * @param  string  $hash  file hash
2029
	 * @return array
2030
	 * @author Dmitry (dio) Levashov
2031
	 **/
2032
	public function dimensions($hash) {
2033
		if (($file = $this->file($hash)) == false) {
2034
			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::dimensions 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...
2035
		}
2036
		
2037
		return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime']));
2038
	}
2039
	
2040
	/**
2041
	 * Return content URL (for netmout volume driver)
2042
	 * If file.url == 1 requests from JavaScript client with XHR
2043
	 * 
2044
	 * @param string $hash  file hash
2045
	 * @param array $options  options array
2046
	 * @return boolean|string
2047
	 * @author Naoki Sawada
2048
	 */
2049
	public function getContentUrl($hash, $options = array()) {
0 ignored issues
show
Unused Code introduced by
The parameter $options 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...
2050 View Code Duplication
		if (($file = $this->file($hash)) == false || !$file['url'] || $file['url'] == 1) {
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...
2051
			return false;
2052
		}
2053
		return $file['url'];
2054
	}
2055
	
2056
	/**
2057
	 * Return temp path
2058
	 * 
2059
	 * @return string
2060
	 * @author Naoki Sawada
2061
	 */
2062
	public function getTempPath() {
2063
		if (@ $this->tmpPath) {
2064
			return $this->tmpPath;
0 ignored issues
show
Bug introduced by
The property tmpPath 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...
2065
		} else if (@ $this->tmp) {
2066
			return $this->tmp;
0 ignored issues
show
Bug introduced by
The property tmp does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
2067
		} else if (function_exists('sys_get_temp_dir')) {
2068
			return sys_get_temp_dir();
2069
		} else if (@ $this->tmbPath) {
2070
			return $this->tmbPath;
2071
		} else {
2072
			return null;
2073
		}
2074
	}
2075
	
2076
	/**
2077
	 * (Make &) Get upload taget dirctory hash
2078
	 * 
2079
	 * @param string $baseTargetHash
2080
	 * @param string $path
2081
	 * @param array  $result
2082
	 * @return boolean|string
2083
	 * @author Naoki Sawada
2084
	 */
2085
	public function getUploadTaget($baseTargetHash, $path, & $result) {
2086
		$base = $this->decode($baseTargetHash);
2087
		$targetHash = $baseTargetHash;
2088
		$path = ltrim($path, $this->separator);
2089
		$dirs = explode($this->separator, $path);
2090
		array_pop($dirs);
2091
		foreach($dirs as $dir) {
2092
			$targetPath = $this->joinPathCE($base, $dir);
2093
			if (! $_realpath = $this->realpath($this->encode($targetPath))) {
2094
				if ($stat = $this->mkdir($targetHash, $dir)) {
2095
					$result['added'][] = $stat;
2096
					$targetHash = $stat['hash'];
2097
					$base = $this->decode($targetHash);
2098
				} else {
2099
					return false;
2100
				}
2101
			} else {
2102
				$targetHash = $this->encode($_realpath);
2103
				if ($this->dir($targetHash)) {
2104
					$base = $this->decode($targetHash);
2105
				} else {
2106
					return false;
2107
				}
2108
			}
2109
		}
2110
		return $targetHash;
2111
	}
2112
	
2113
	/**
2114
	 * Return this uploadMaxSize value
2115
	 * 
2116
	 * @return integer
2117
	 * @author Naoki Sawada
2118
	 */
2119
	public function getUploadMaxSize() {
2120
		return $this->uploadMaxSize;
2121
	}
2122
	
2123
	/**
2124
	 * Save error message
2125
	 *
2126
	 * @param  array  error 
2127
	 * @return false
2128
	 * @author Dmitry(dio) Levashov
2129
	 **/
2130
	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...
2131
		
2132
		$this->error = array();
2133
		
2134
		foreach (func_get_args() as $err) {
2135
			if (is_array($err)) {
2136
				$this->error = array_merge($this->error, $err);
2137
			} else {
2138
				$this->error[] = $err;
2139
			}
2140
		}
2141
		
2142
		// $this->error = is_array($error) ? $error : func_get_args();
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2143
		return false;
2144
	}
2145
	
2146
	/*********************************************************************/
2147
	/*                               FS API                              */
2148
	/*********************************************************************/
2149
	
2150
	/***************** server encoding support *******************/
2151
	
2152
	/**
2153
	 * Return parent directory path (with convert encording)
2154
	 *
2155
	 * @param  string  $path  file path
2156
	 * @return string
2157
	 * @author Naoki Sawada
2158
	 **/
2159
	protected function dirnameCE($path) {
2160
		return (!$this->encoding)? $this->_dirname($path) :	$this->convEncOut($this->_dirname($this->convEncIn($path)));
2161
	}
2162
	
2163
	/**
2164
	 * Return file name (with convert encording)
2165
	 *
2166
	 * @param  string  $path  file path
2167
	 * @return string
2168
	 * @author Naoki Sawada
2169
	 **/
2170
	protected function basenameCE($path) {
2171
		return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path)));
2172
	}
2173
	
2174
	/**
2175
	 * Join dir name and file name and return full path. (with convert encording)
2176
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
2177
	 *
2178
	 * @param  string  $dir   dir path
2179
	 * @param  string  $name  file name
2180
	 * @return string
2181
	 * @author Naoki Sawada
2182
	 **/
2183
	protected function joinPathCE($dir, $name) {
2184
		return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name)));
2185
	}
2186
	
2187
	/**
2188
	 * Return normalized path (with convert encording)
2189
	 *
2190
	 * @param  string  $path  file path
2191
	 * @return string
2192
	 * @author Naoki Sawada
2193
	 **/
2194
	protected function normpathCE($path) {
2195
		return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path)));
2196
	}
2197
	
2198
	/**
2199
	 * Return file path related to root dir (with convert encording)
2200
	 *
2201
	 * @param  string  $path  file path
2202
	 * @return string
2203
	 * @author Naoki Sawada
2204
	 **/
2205
	protected function relpathCE($path) {
2206
		return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path)));
2207
	}
2208
	
2209
	/**
2210
	 * Convert path related to root dir into real path (with convert encording)
2211
	 *
2212
	 * @param  string  $path  rel file path
2213
	 * @return string
2214
	 * @author Naoki Sawada
2215
	 **/
2216
	protected function abspathCE($path) {
2217
		return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path)));
2218
	}
2219
	
2220
	/**
2221
	 * Return true if $path is children of $parent (with convert encording)
2222
	 *
2223
	 * @param  string  $path    path to check
2224
	 * @param  string  $parent  parent path
2225
	 * @return bool
2226
	 * @author Naoki Sawada
2227
	 **/
2228
	protected function inpathCE($path, $parent) {
2229
		return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent)));
2230
	}
2231
	
2232
	/**
2233
	 * Open file and return file pointer (with convert encording)
2234
	 *
2235
	 * @param  string  $path  file path
2236
	 * @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...
2237
	 * @return resource|false
2238
	 * @author Naoki Sawada
2239
	 **/
2240
	protected function fopenCE($path, $mode='rb') {
2241
		return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode));
2242
	}
2243
	
2244
	/**
2245
	 * Close opened file (with convert encording)
2246
	 * 
2247
	 * @param  resource  $fp    file pointer
2248
	 * @param  string    $path  file path
2249
	 * @return bool
2250
	 * @author Naoki Sawada
2251
	 **/
2252
	protected function fcloseCE($fp, $path='') {
2253
		return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path)));
2254
	}
2255
	
2256
	/**
2257
	 * Create new file and write into it from file pointer. (with convert encording)
2258
	 * Return new file path or false on error.
2259
	 *
2260
	 * @param  resource  $fp   file pointer
2261
	 * @param  string    $dir  target dir path
2262
	 * @param  string    $name file name
2263
	 * @param  array     $stat file stat (required by some virtual fs)
2264
	 * @return bool|string
2265
	 * @author Naoki Sawada
2266
	 **/
2267
	protected function saveCE($fp, $dir, $name, $stat) {
2268
		return (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat)));
2269
	}
2270
	
2271
	/**
2272
	 * Return true if path is dir and has at least one childs directory (with convert encording)
2273
	 *
2274
	 * @param  string  $path  dir path
2275
	 * @return bool
2276
	 * @author Naoki Sawada
2277
	 **/
2278
	protected function subdirsCE($path) {
2279
		if (!isset($this->subdirsCache[$path])) {
2280
			$this->subdirsCache[$path] = (!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)));
2281
		}
2282
		return $this->subdirsCache[$path];
2283
	}
2284
	
2285
	/**
2286
	 * Return files list in directory (with convert encording)
2287
	 *
2288
	 * @param  string  $path  dir path
2289
	 * @return array
2290
	 * @author Naoki Sawada
2291
	 **/
2292
	protected function scandirCE($path) {
2293
		return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path)));
2294
	}
2295
	
2296
	/**
2297
	 * Create symlink (with convert encording)
2298
	 *
2299
	 * @param  string  $source     file to link to
2300
	 * @param  string  $targetDir  folder to create link in
2301
	 * @param  string  $name       symlink name
2302
	 * @return bool
2303
	 * @author Naoki Sawada
2304
	 **/
2305
	protected function symlinkCE($source, $targetDir, $name) {
2306
		return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name)));
2307
	}
2308
	
2309
	/***************** paths *******************/
2310
	
2311
	/**
2312
	 * Encode path into hash
2313
	 *
2314
	 * @param  string  file path
2315
	 * @return string
2316
	 * @author Dmitry (dio) Levashov
2317
	 * @author Troex Nevelin
2318
	 **/
2319
	protected function encode($path) {
2320
		if ($path !== '') {
2321
2322
			// cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
2323
			$p = $this->relpathCE($path);
2324
			// if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
2325
			if ($p === '')	{
2326
				$p = DIRECTORY_SEPARATOR;
2327
			}
2328
2329
			// TODO crypt path and return hash
2330
			$hash = $this->crypt($p);
2331
			// hash is used as id in HTML that means it must contain vaild chars
2332
			// make base64 html safe and append prefix in begining
2333
			$hash = strtr(base64_encode($hash), '+/=', '-_.');
2334
			// remove dots '.' at the end, before it was '=' in base64
2335
			$hash = rtrim($hash, '.'); 
2336
			// append volume id to make hash unique
2337
			return $this->id.$hash;
2338
		}
2339
	}
2340
	
2341
	/**
2342
	 * Decode path from hash
2343
	 *
2344
	 * @param  string  file hash
2345
	 * @return string
2346
	 * @author Dmitry (dio) Levashov
2347
	 * @author Troex Nevelin
2348
	 **/
2349
	protected function decode($hash) {
2350
		if (strpos($hash, $this->id) === 0) {
2351
			// cut volume id after it was prepended in encode
2352
			$h = substr($hash, strlen($this->id));
2353
			// replace HTML safe base64 to normal
2354
			$h = base64_decode(strtr($h, '-_.', '+/='));
2355
			// TODO uncrypt hash and return path
2356
			$path = $this->uncrypt($h); 
2357
			// append ROOT to path after it was cut in encode
2358
			return $this->abspathCE($path);//$this->root.($path == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR.$path); 
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2359
		}
2360
	}
2361
	
2362
	/**
2363
	 * Return crypted path 
2364
	 * Not implemented
2365
	 *
2366
	 * @param  string  path
2367
	 * @return mixed
2368
	 * @author Dmitry (dio) Levashov
2369
	 **/
2370
	protected function crypt($path) {
2371
		return $path;
2372
	}
2373
	
2374
	/**
2375
	 * Return uncrypted path 
2376
	 * Not implemented
2377
	 *
2378
	 * @param  mixed  hash
2379
	 * @return mixed
2380
	 * @author Dmitry (dio) Levashov
2381
	 **/
2382
	protected function uncrypt($hash) {
2383
		return $hash;
2384
	}
2385
	
2386
	/**
2387
	 * Validate file name based on $this->options['acceptedName'] regexp or function
2388
	 *
2389
	 * @param  string  $name  file name
2390
	 * @return bool
2391
	 * @author Dmitry (dio) Levashov
2392
	 **/
2393
	protected function nameAccepted($name) {
2394
		if ($this->nameValidator) {
2395
			if (is_callable($this->nameValidator)) {
2396
				$res = call_user_func($this->nameValidator, $name);
2397
				return $res;
2398
			}
2399
			if (preg_match($this->nameValidator, '') !== false) {
2400
				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...
2401
			}
2402
		}
2403
		return true;
2404
	}
2405
	
2406
	/**
2407
	 * Return new unique name based on file name and suffix
2408
	 *
2409
	 * @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...
2410
	 * @param  string  $suffix  suffix append to name
2411
	 * @return string
2412
	 * @author Dmitry (dio) Levashov
2413
	 **/
2414
	public function uniqueName($dir, $name, $suffix = ' copy', $checkNum=true) {
2415
		$ext  = '';
2416
2417
		if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
2418
			$ext  = '.'.$m[1];
2419
			$name = substr($name, 0,  strlen($name)-strlen($m[0]));
2420
		} 
2421
		
2422
		if ($checkNum && preg_match('/('.$suffix.')(\d*)$/i', $name, $m)) {
2423
			$i    = (int)$m[2];
2424
			$name = substr($name, 0, strlen($name)-strlen($m[2]));
2425
		} else {
2426
			$i     = 1;
2427
			$name .= $suffix;
2428
		}
2429
		$max = $i+100000;
2430
2431
		while ($i <= $max) {
2432
			$n = $name.($i > 0 ? $i : '').$ext;
2433
2434
			if (!$this->stat($this->joinPathCE($dir, $n))) {
2435
				$this->clearcache();
2436
				return $n;
2437
			}
2438
			$i++;
2439
		}
2440
		return $name.md5($dir).$ext;
2441
	}
2442
	
2443
	/**
2444
	 * Converts character encoding from UTF-8 to server's one
2445
	 * 
2446
	 * @param  mixed  $var           target string or array var
2447
	 * @param  bool   $restoreLocale do retore global locale, default is false
2448
	 * @param  string $unknown       replaces character for unknown
2449
	 * @return mixed
2450
	 * @author Naoki Sawada
2451
	 */
2452
	public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') {
2453
		return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown);
2454
	}
2455
	
2456
	/**
2457
	 * Converts character encoding from server's one to UTF-8
2458
	 * 
2459
	 * @param  mixed  $var           target string or array var
2460
	 * @param  bool   $restoreLocale do retore global locale, default is true
2461
	 * @param  string $unknown       replaces character for unknown
2462
	 * @return mixed
2463
	 * @author Naoki Sawada
2464
	 */
2465
	public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') {
2466
		return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown);
2467
	}
2468
	
2469
	/**
2470
	 * Converts character encoding (base function)
2471
	 * 
2472
	 * @param  mixed  $var     target string or array var
2473
	 * @param  string $from    from character encoding
2474
	 * @param  string $to      to character encoding
2475
	 * @param  string $locale  local locale
2476
	 * @param  string $unknown replaces character for unknown
2477
	 * @return mixed
2478
	 */
2479
	protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') {
2480
		if (strtoupper($from) !== strtoupper($to)) {
2481
			if ($locale) {
2482
				@setlocale(LC_ALL, $locale);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2483
			}
2484
			if (is_array($var)) {
2485
				$_ret = array();
2486
				foreach($var as $_k => $_v) {
2487
					$_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_');
2488
				}
2489
				$var = $_ret;
2490
			} else {
2491
				$_var = false;
2492
				if (is_string($var)) {
2493
					$_var = $var;
2494
					if (false !== ($_var = @iconv($from, $to.'//TRANSLIT', $_var))) {
2495
						$_var = str_replace('?', $unknown, $_var);
2496
					}
2497
				}
2498
				if  ($_var !== false) {
2499
					$var = $_var;
2500
				}
2501
			}
2502
			if ($restoreLocale) {
2503
				setlocale(LC_ALL, elFinder::$locale);
2504
			}
2505
		}
2506
		return $var;
2507
	}
2508
	
2509
	/*********************** util mainly for inheritance class *********************/
2510
	
2511
	/**
2512
	 * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called.
2513
	 * When needing the unique file to a path, give $path to parameter.
2514
	 * 
2515
	 * @param  string       $path for get unique file to a path
2516
	 * @return string|false
2517
	 * @author Naoki Sawada
2518
	 */
2519
	protected function getTempFile($path = '') {
2520
		static $cache = array();
2521
		static $rmfunc;
2522
		
2523
		$key = '';
2524
		if ($path !== '') {
2525
			$key = $this->id . '#' . $path;
2526
			if (isset($cache[$key])) {
2527
				return $cache[$key];
2528
			}
2529
		}
2530
		
2531
		if ($tmpdir = $this->getTempPath()) {
2532
			if (!$rmfunc) {
2533
				$rmfunc = create_function('$f', '@unlink($f);');
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
2534
			}
2535
			$name = tempnam($tmpdir, 'ELF');
2536
			if ($key) {
2537
				$cache[$key] = $name;
2538
			}
2539
			register_shutdown_function($rmfunc, $name);
2540
			return $name;
2541
		}
2542
		
2543
		return false;
2544
	}
2545
	
2546
	/**
2547
	 * File path of local server side work file path
2548
	 * 
2549
	 * @param  string $path path need convert encoding to server encoding
2550
	 * @return string
2551
	 * @author Naoki Sawada
2552
	 */
2553
	protected function getWorkFile($path) {
2554
		if ($work = $this->getTempFile()) {
2555
			if ($wfp = fopen($work, 'wb')) {
2556
				if ($fp = $this->_fopen($path)) {
2557
					while(!feof($fp)) {
2558
						fwrite($wfp, fread($fp, 8192));
2559
					}
2560
					$this->_fclose($fp, $path);
2561
					fclose($wfp);
2562
					return $work;
2563
				}
2564
			}
2565
		}
2566
		return false;
2567
	}
2568
	
2569
	/**
2570
	 * Get image size array with `dimensions`
2571
	 *
2572
	 * @param string $path path need convert encoding to server encoding
2573
	 * @param string $mime file mime type
2574
	 * @return array|false
2575
	 */
2576
	public function getImageSize($path, $mime = '') {
2577
		$size = false;
2578
		if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') {
2579
			if ($work = $this->getWorkFile($path)) {
2580
				if ($size = @getimagesize($work)) {
2581
					$size['dimensions'] = $size[0].'x'.$size[1];
2582
				}
2583
			}
2584
			is_file($work) && @unlink($work);
2585
		}
2586
		return $size;
2587
	}
2588
	
2589
	/**
2590
	 * Delete dirctory trees
2591
	 *
2592
	 * @param string $localpath path need convert encoding to server encoding
2593
	 * @return boolean
2594
	 * @author Naoki Sawada
2595
	 */
2596
	protected function delTree($localpath) {
2597
		foreach ($this->_scandir($localpath) as $p) {
2598
			@set_time_limit(30);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2599
			$stat = $this->stat($this->convEncOut($p));
2600
			$this->convEncIn();
2601
			($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p);
2602
		}
2603
		return $this->_rmdir($localpath);
2604
	}
2605
	
2606
	/*********************** file stat *********************/
2607
	
2608
	/**
2609
	 * Check file attribute
2610
	 *
2611
	 * @param  string  $path  file path
2612
	 * @param  string  $name  attribute name (read|write|locked|hidden)
2613
	 * @param  bool    $val   attribute value returned by file system
2614
	 * @param  bool    $isDir path is directory (true: directory, false: file)
2615
	 * @return bool
2616
	 * @author Dmitry (dio) Levashov
2617
	 **/
2618
	protected function attr($path, $name, $val=null, $isDir=null) {
2619
		if (!isset($this->defaults[$name])) {
2620
			return false;
2621
		}
2622
		
2623
		
2624
		$perm = null;
2625
		
2626 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...
2627
			$perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir);
2628
2629
			if ($perm !== null) {
2630
				return !!$perm;
2631
			}
2632
		}
2633
		
2634
		if ($this->separator != '/') {
2635
			$path = str_replace($this->separator, '/', $this->relpathCE($path));
2636
		} else {
2637
			$path = $this->relpathCE($path);
2638
		}
2639
2640
		$path = '/'.$path;
2641
2642 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...
2643
			$attrs = $this->attributes[$i];
2644
			
2645
			if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
2646
				$perm = $attrs[$name];
2647
			} 
2648
		}
2649
		
2650
		return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
2651
	}
2652
	
2653
	/**
2654
	 * Return true if file with given name can be created in given folder.
2655
	 *
2656
	 * @param string $dir  parent dir path
2657
	 * @param string $name new file name
2658
	 * @return bool
2659
	 * @author Dmitry (dio) Levashov
2660
	 **/
2661
	protected function allowCreate($dir, $name, $isDir = null) {
2662
		$path = $this->joinPathCE($dir, $name);
2663
		$perm = null;
2664
		
2665 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...
2666
			$perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this, $isDir);			
2667
			if ($perm !== null) {
2668
				return !!$perm;
2669
			}
2670
		}
2671
		
2672
		$testPath = $this->separator.$this->relpathCE($path);
2673
		
2674 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...
2675
			$attrs = $this->attributes[$i];
2676
			
2677
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2678
				$perm = $attrs['write'];
2679
			} 
2680
		}
2681
		
2682
		return $perm === null ? true : $perm;
2683
	}
2684
	
2685
	/**
2686
	 * Return true if file MIME type can save with check uploadOrder config.
2687
	 * 
2688
	 * @param string $mime
2689
	 * @return boolean
2690
	 */
2691
	protected function allowPutMime($mime) {
2692
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
2693
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
2694
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
2695
		$res = true; // default to allow
0 ignored issues
show
Unused Code introduced by
$res is not used, you could remove the assignment.

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

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

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

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

Loading history...
2696
		if (strtolower($this->uploadOrder[0]) == 'allow') { // array('allow', 'deny'), default is to 'deny'
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2697
			$res = false; // default is deny
2698
			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...
2699
				$res = true;
2700
			}// else (both match | no match | match only deny) { deny }
2701
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
2702
			$res = true; // default is allow
2703
			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...
2704
				$res = false;
2705
			} // else (both match | no match | match only allow) { allow }
2706
		}
2707
		return $res;
2708
	}
2709
	
2710
	/**
2711
	 * Return fileinfo 
2712
	 *
2713
	 * @param  string  $path  file cache
2714
	 * @return array
2715
	 * @author Dmitry (dio) Levashov
2716
	 **/
2717
	protected function stat($path) {
2718
		if ($path === false || is_null($path)) {
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
2719
			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...
2720
		}
2721
		$is_root = ($path === $this->root);
2722
		if ($is_root) {
2723
			$rootKey = md5($path);
2724
			if (!isset($this->sessionCache['rootstat'])) {
2725
				$this->sessionCache['rootstat'] = array();
2726
			}
2727
			// need $path as key for netmount/netunmount
2728
			if (isset($this->sessionCache['rootstat'][$rootKey])) {
2729
				if ($ret = elFinder::sessionDataDecode($this->sessionCache['rootstat'][$rootKey], 'array')) {
0 ignored issues
show
Documentation introduced by
'array' is of type string, but the function expects a boolean|null.

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...
2730
					return $ret;
2731
				}
2732
			}
2733
		}
2734
		$ret = isset($this->cache[$path])
2735
			? $this->cache[$path]
2736
			: $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
2737
		if ($is_root) {
2738
			$this->sessionCache['rootstat'][$rootKey] = elFinder::sessionDataEncode($ret);
0 ignored issues
show
Bug introduced by
The variable $rootKey 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...
2739
		}
2740
		return $ret;
2741
	}
2742
	
2743
	/**
2744
	 * Put file stat in cache and return it
2745
	 *
2746
	 * @param  string  $path   file path
2747
	 * @param  array   $stat   file stat
2748
	 * @return array
2749
	 * @author Dmitry (dio) Levashov
2750
	 **/
2751
	protected function updateCache($path, $stat) {
2752
		if (empty($stat) || !is_array($stat)) {
2753
			return $this->cache[$path] = array();
2754
		}
2755
2756
		$stat['hash'] = $this->encode($path);
2757
2758
		$root = $path == $this->root;
2759
		$parent = '';
2760
		
2761
		if ($root) {
2762
			if ($this->rootName) {
2763
				$stat['name'] = $this->rootName;
2764
			}
2765
			if (! empty($this->options['icon'])) {
2766
				$stat['icon'] = $this->options['icon'];
2767
			}
2768
			if (! empty($this->options['rootCssClass'])) {
2769
				$stat['csscls'] = $this->options['rootCssClass'];
2770
			}
2771
		} else {
2772
			if (!isset($stat['name']) || $stat['name'] === '') {
2773
				$stat['name'] = $this->basenameCE($path);
2774
			}
2775
			if (empty($stat['phash'])) {
2776
				$parent = $this->dirnameCE($path);
2777
				$stat['phash'] = $this->encode($parent);
2778
			}
2779
		}
2780
		
2781
		// fix name if required
2782
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2783
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], json_encode($stat['name'])));
2784
		}
2785
		
2786
		
2787
		if (empty($stat['mime'])) {
2788
			$stat['mime'] = $this->mimetype($stat['name']);
2789
		}
2790
		
2791
		// @todo move dateformat to client
2792
		// $stat['date'] = isset($stat['ts'])
0 ignored issues
show
Unused Code Comprehensibility introduced by
74% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2793
		// 	? $this->formatDate($stat['ts'])
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2794
		// 	: 'unknown';
2795
			
2796
		if (!isset($stat['size'])) {
2797
			$stat['size'] = 'unknown';
2798
		}	
2799
2800
		if ($isDir = ($stat['mime'] === 'directory')) {
2801
			$stat['volumeid'] = $this->id;
2802
		}
2803
		
2804
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir));
2805
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir));
2806
		if ($root) {
2807
			$stat['locked'] = 1;
2808
		} else {
2809
			// lock when parent directory is not writable
2810
			if (!isset($stat['locked'])) {
2811
				$parent = $this->dirnameCE($path);
2812
				$pstat = isset($this->cache[$parent])? $this->cache[$parent] : array();
2813
				if (isset($pstat['write']) && !$pstat['write']) {
2814
					$stat['locked'] = true;
2815
				}
2816
			}
2817
			if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) {
2818
				$stat['locked'] = 1;
2819
			} else {
2820
				unset($stat['locked']);
2821
			}
2822
		}
2823
2824
		if ($root) {
2825
			unset($stat['hidden']);
2826
		} elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) 
2827
		|| !$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...
2828
			$stat['hidden'] = 1;
2829
		} else {
2830
			unset($stat['hidden']);
2831
		}
2832
		
2833
		if ($stat['read'] && empty($stat['hidden'])) {
2834
			
2835
			if ($isDir) {
2836
				// caching parent's subdirs
2837
				if ($parent) {
2838
					$this->subdirsCache[$parent] = true;
2839
				}
2840
				// for dir - check for subdirs
2841
				if ($this->options['checkSubfolders']) {
2842
					if (isset($stat['dirs'])) {
2843
						if ($stat['dirs']) {
2844
							$stat['dirs'] = 1;
2845
						} else {
2846
							unset($stat['dirs']);
2847
						}
2848
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2849
						$stat['dirs'] = isset($this->cache[$stat['target']])
2850
							? intval(isset($this->cache[$stat['target']]['dirs']))
2851
							: $this->subdirsCE($stat['target']);
2852
						
2853
					} elseif ($this->subdirsCE($path)) {
2854
						$stat['dirs'] = 1;
2855
					}
2856
				} else {
2857
					$stat['dirs'] = 1;
2858
				}
2859
			} else {
2860
				// for files - check for thumbnails
2861
				$p = isset($stat['target']) ? $stat['target'] : $path;
2862
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
2863
					$tmb = $this->gettmb($p, $stat);
2864
					$stat['tmb'] = $tmb ? $tmb : 1;
2865
				}
2866
				
2867
			}
2868
			if (!isset($stat['url']) && $this->URL && $this->encoding) {
2869
				$_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
2870
				$stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode($this->convEncIn($_path, true)));
2871
			}
2872
		} else {
2873
			if ($isDir) {
2874
				unset($stat['dirs']);
2875
			}
2876
		}
2877
		
2878
		if (!empty($stat['alias']) && !empty($stat['target'])) {
2879
			$stat['thash'] = $this->encode($stat['target']);
2880
			//$this->cache[$stat['target']] = $stat;
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2881
			unset($stat['target']);
2882
		}
2883
		
2884
		if (isset($this->options['netkey']) && $path === $this->root) {
2885
			$stat['netkey'] = $this->options['netkey'];
2886
		}
2887
		
2888
		return $this->cache[$path] = $stat;
2889
	}
2890
	
2891
	/**
2892
	 * Get stat for folder content and put in cache
2893
	 *
2894
	 * @param  string  $path
2895
	 * @return void
2896
	 * @author Dmitry (dio) Levashov
2897
	 **/
2898
	protected function cacheDir($path) {
2899
		$this->dirsCache[$path] = array();
2900
		$this->subdirsCache[$path] = false;
2901
2902
		foreach ($this->scandirCE($path) as $p) {
2903 View Code Duplication
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
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...
2904
				if ($stat['mime'] === 'directory') {
2905
					$this->subdirsCache[$path] = true;
2906
				}
2907
				$this->dirsCache[$path][] = $p;
2908
			}
2909
		}
2910
	}
2911
	
2912
	/**
2913
	 * Clean cache
2914
	 *
2915
	 * @return void
2916
	 * @author Dmitry (dio) Levashov
2917
	 **/
2918
	protected function clearcache() {
2919
		$this->cache = $this->dirsCache = array();
2920
	}
2921
	
2922
	/**
2923
	 * Return file mimetype
2924
	 *
2925
	 * @param  string  $path  file path
2926
	 * @return string
2927
	 * @author Dmitry (dio) Levashov
2928
	 **/
2929
	protected function mimetype($path, $name = '') {
2930
		$type = '';
0 ignored issues
show
Unused Code introduced by
$type 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...
2931
		
2932
		if ($name === '') {
2933
			$name = $path;
2934
		}
2935
		$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
2936
		if ($this->mimeDetect == 'finfo') {
2937
			if ($type = @finfo_file($this->finfo, $path)) {
2938
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
2939
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
2940
				} else if ($ext === 'js' && preg_match('~^text/~', $type)) {
2941
					$type = 'text/javascript';
2942
				}
2943
			} else {
2944
				$type = 'unknown';
2945
			}
2946
		} elseif ($this->mimeDetect == 'mime_content_type') {
2947
			$type = mime_content_type($path);
2948
		} else {
2949
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
2950
		}
2951
		
2952
		$type = explode(';', $type);
2953
		$type = trim($type[0]);
2954
2955 View Code Duplication
		if (in_array($type, array('application/x-empty', 'inode/x-empty'))) {
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...
2956
			// finfo return this mime for empty files
2957
			$type = 'text/plain';
2958
		} elseif ($type == 'application/x-zip') {
2959
			// http://elrte.org/redmine/issues/163
2960
			$type = 'application/zip';
2961
		}
2962
		
2963
		// mime type normalization
2964
		$_checkKey = strtolower($ext.':'.$type);
2965
		if (isset($this->options['mimeMap'][$_checkKey])) {
2966
			$type = $this->options['mimeMap'][$_checkKey];
2967
		}
2968
		
2969
		return $type == 'unknown' && $this->mimeDetect != 'internal'
2970
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
2971
			: $type;
2972
		
2973
	}
2974
	
2975
	/**
2976
	 * Detect file mimetype using "internal" method
2977
	 *
2978
	 * @param  string  $path  file path
2979
	 * @return string
2980
	 * @author Dmitry (dio) Levashov
2981
	 **/
2982
	static protected function mimetypeInternalDetect($path) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
2983
		$pinfo = pathinfo($path); 
2984
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
2985
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
2986
		
2987
	}
2988
	
2989
	/**
2990
	 * Return file/total directory size
2991
	 *
2992
	 * @param  string  $path  file path
2993
	 * @return int
2994
	 * @author Dmitry (dio) Levashov
2995
	 **/
2996
	protected function countSize($path) {
2997
		$stat = $this->stat($path);
2998
2999
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3000
			return 'unknown';
3001
		}
3002
		
3003
		if ($stat['mime'] != 'directory') {
3004
			return $stat['size'];
3005
		}
3006
		
3007
		$subdirs = $this->options['checkSubfolders'];
3008
		$this->options['checkSubfolders'] = true;
3009
		$result = 0;
3010
		foreach ($this->getScandir($path) as $stat) {
3011
			$size = $stat['mime'] == 'directory' && $stat['read'] 
3012
				? $this->countSize($this->joinPathCE($path, $stat['name'])) 
3013
				: (isset($stat['size']) ? intval($stat['size']) : 0);
3014
			if ($size > 0) {
3015
				$result += $size;
3016
			}
3017
		}
3018
		$this->options['checkSubfolders'] = $subdirs;
3019
		return $result;
3020
	}
3021
	
3022
	/**
3023
	 * Return true if all mimes is directory or files
3024
	 *
3025
	 * @param  string  $mime1  mimetype
3026
	 * @param  string  $mime2  mimetype
3027
	 * @return bool
3028
	 * @author Dmitry (dio) Levashov
3029
	 **/
3030
	protected function isSameType($mime1, $mime2) {
3031
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3032
	}
3033
	
3034
	/**
3035
	 * If file has required attr == $val - return file path,
3036
	 * If dir has child with has required attr == $val - return child path
3037
	 *
3038
	 * @param  string   $path  file path
3039
	 * @param  string   $attr  attribute name
3040
	 * @param  bool     $val   attribute value
3041
	 * @return string|false
3042
	 * @author Dmitry (dio) Levashov
3043
	 **/
3044
	protected function closestByAttr($path, $attr, $val) {
3045
		$stat = $this->stat($path);
3046
		
3047
		if (empty($stat)) {
3048
			return false;
3049
		}
3050
		
3051
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
3052
		
3053
		if ($v == $val) {
3054
			return $path;
3055
		}
3056
3057
		return $stat['mime'] == 'directory'
3058
			? $this->childsByAttr($path, $attr, $val) 
3059
			: false;
3060
	}
3061
	
3062
	/**
3063
	 * Return first found children with required attr == $val
3064
	 *
3065
	 * @param  string   $path  file path
3066
	 * @param  string   $attr  attribute name
3067
	 * @param  bool     $val   attribute value
3068
	 * @return string|false
3069
	 * @author Dmitry (dio) Levashov
3070
	 **/
3071
	protected function childsByAttr($path, $attr, $val) {
3072
		foreach ($this->scandirCE($path) as $p) {
3073
			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 string|false against false; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
3074
				return $_p;
3075
			}
3076
		}
3077
		return false;
3078
	}
3079
	
3080
	/*****************  get content *******************/
3081
	
3082
	/**
3083
	 * Return required dir's files info.
3084
	 * If onlyMimes is set - return only dirs and files of required mimes
3085
	 *
3086
	 * @param  string  $path  dir path
3087
	 * @return array
3088
	 * @author Dmitry (dio) Levashov
3089
	 **/
3090
	protected function getScandir($path) {
3091
		$files = array();
3092
		
3093
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3094
3095
		foreach ($this->dirsCache[$path] as $p) {
3096
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3097
				$files[] = $stat;
3098
			}
3099
		}
3100
3101
		return $files;
3102
	}
3103
	
3104
	
3105
	/**
3106
	 * Return subdirs tree
3107
	 *
3108
	 * @param  string  $path  parent dir path
3109
	 * @param  int     $deep  tree deep
3110
	 * @return array
3111
	 * @author Dmitry (dio) Levashov
3112
	 **/
3113
	protected function gettree($path, $deep, $exclude='') {
3114
		$dirs = array();
3115
		
3116
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3117
3118
		foreach ($this->dirsCache[$path] as $p) {
3119
			$stat = $this->stat($p);
3120
			
3121
			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...
3122
				$dirs[] = $stat;
3123
				if ($deep > 0 && !empty($stat['dirs'])) {
3124
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3125
				}
3126
			}
3127
		}
3128
3129
		return $dirs;
3130
	}	
3131
		
3132
	/**
3133
	 * Recursive files search
3134
	 *
3135
	 * @param  string  $path   dir path
3136
	 * @param  string  $q      search string
3137
	 * @param  array   $mimes
3138
	 * @return array
3139
	 * @author Dmitry (dio) Levashov
3140
	 **/
3141
	protected function doSearch($path, $q, $mimes) {
3142
		$result = array();
3143
3144
		foreach($this->scandirCE($path) as $p) {
3145
			@set_time_limit(30);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
3146
			$stat = $this->stat($p);
3147
3148
			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...
3149
				continue;
3150
			}
3151
3152
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3153
				continue;
3154
			}
3155
			
3156
			$name = $stat['name'];
3157
3158
			if ((!$mimes || $stat['mime'] !== 'directory') && $this->stripos($name, $q) !== false) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mimes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
3159
				$stat['path'] = $this->path($stat['hash']);
3160 View Code Duplication
				if ($this->URL && !isset($stat['url'])) {
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...
3161
					$path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
3162
					if ($this->encoding) {
3163
						$path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
3164
					}
3165
					$stat['url'] = $this->URL . $path;
3166
				}
3167
				
3168
				$result[] = $stat;
3169
			}
3170 View Code Duplication
			if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3171
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
3172
			}
3173
		}
3174
		
3175
		return $result;
3176
	}
3177
		
3178
	/**********************  manuipulations  ******************/
3179
		
3180
	/**
3181
	 * Copy file/recursive copy dir only in current volume.
3182
	 * Return new file path or false.
3183
	 *
3184
	 * @param  string  $src   source path
3185
	 * @param  string  $dst   destination dir path
3186
	 * @param  string  $name  new file name (optionaly)
3187
	 * @return string|false
3188
	 * @author Dmitry (dio) Levashov
3189
	 **/
3190
	protected function copy($src, $dst, $name) {
3191
		$srcStat = $this->stat($src);
3192
		$this->clearcache();
3193
		
3194
		if (!empty($srcStat['thash'])) {
3195
			$target = $this->decode($srcStat['thash']);
3196
			if (!$this->inpathCE($target, $this->root)) {
3197
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
3198
			}
3199
			$stat   = $this->stat($target);
3200
			$this->clearcache();
3201
			return $stat && $this->symlinkCE($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...
3202
				? $this->joinPathCE($dst, $name)
3203
				: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3204
		} 
3205
		
3206
		if ($srcStat['mime'] == 'directory') {
3207
			$test = $this->stat($this->joinPathCE($dst, $name));
3208
			
3209 View Code Duplication
			if (($test && $test['mime'] != 'directory') || $this->convEncOut(!$this->_mkdir($this->convEncIn($dst), $this->convEncIn($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...
3210
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3211
			}
3212
			
3213
			$dst = $this->joinPathCE($dst, $name);
3214
			
3215
			foreach ($this->getScandir($src) as $stat) {
3216
				if (empty($stat['hidden'])) {
3217
					$name = $stat['name'];
3218
					if (!$this->copy($this->joinPathCE($src, $name), $dst, $name)) {
3219
						$this->remove($dst, true); // fall back
3220
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
3221
					}
3222
				}
3223
			}
3224
			$this->clearcache();
3225
			return $dst;
3226
		} 
3227
3228
		return $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))
3229
			? $this->joinPathCE($dst, $name) 
3230
			: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3231
	}
3232
3233
	/**
3234
	 * Move file
3235
	 * Return new file path or false.
3236
	 *
3237
	 * @param  string  $src   source path
3238
	 * @param  string  $dst   destination dir path
3239
	 * @param  string  $name  new file name 
3240
	 * @return string|false
3241
	 * @author Dmitry (dio) Levashov
3242
	 **/
3243
	protected function move($src, $dst, $name) {
3244
		$stat = $this->stat($src);
3245
		$stat['realpath'] = $src;
3246
		$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...
3247
		$this->clearcache();
3248
		
3249 View Code Duplication
		if ($this->convEncOut($this->_move($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) {
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...
3250
			$this->removed[] = $stat;
3251
3252
			return $this->joinPathCE($dst, $name);
3253
		}
3254
3255
		return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
3256
	}
3257
3258
	/**
3259
	 * Copy file from another volume.
3260
	 * Return new file path or false.
3261
	 *
3262
	 * @param  Object  $volume       source volume
3263
	 * @param  string  $src          source file hash
3264
	 * @param  string  $destination  destination dir path
3265
	 * @param  string  $name         file name
3266
	 * @return string|false
3267
	 * @author Dmitry (dio) Levashov
3268
	 **/
3269
	protected function copyFrom($volume, $src, $destination, $name) {
3270
		
3271
		if (($source = $volume->file($src)) == false) {
3272
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
3273
		}
3274
		
3275
		$errpath = $volume->path($source['hash']);
3276
		
3277
		if (!$this->nameAccepted($source['name'])) {
3278
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
3279
		}
3280
				
3281
		if (!$source['read']) {
3282
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
3283
		}
3284
		
3285
		if ($source['mime'] == 'directory') {
3286
			$stat = $this->stat($this->joinPathCE($destination, $name));
3287
			$this->clearcache();
3288 View Code Duplication
			if ((!$stat || $stat['mime'] != 'directory') && $this->convEncOut(!$this->_mkdir($this->convEncIn($destination), $this->convEncIn($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...
3289
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3290
			}
3291
			
3292
			$path = $this->joinPathCE($destination, $name);
3293
			
3294
			foreach ($volume->scandir($src) as $entr) {
3295
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
3296
					$this->remove($path, true); // fall back
3297
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
3298
				}
3299
			}
3300
			
3301
		} else {
3302
			// $mime = $source['mime'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3303
			// $w = $h = 0;
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3304
			if (($dim = $volume->dimensions($src))) {
3305
				$s = explode('x', $dim);
3306
				$source['width']  = $s[0];
3307
				$source['height'] = $s[1];
3308
			}
3309
			
3310
			if (($fp = $volume->open($src)) == false
3311
			|| ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
3312
				$fp && $volume->close($fp, $src);
3313
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3314
			}
3315
			$volume->close($fp, $src);
3316
			
3317
			// MIME check
3318
			$stat = $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->saveCE($fp, $destination, $name, $source) on line 3311 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...
3319
			if (!$this->allowPutMime($stat['mime'])) {
3320
				$this->remove($path, true);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->saveCE($fp, $destination, $name, $source) on line 3311 can also be of type boolean; however, elFinderVolumeDriver::remove() 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...
3321
				return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
3322
			}
3323
		}
3324
		
3325
		return $path;
3326
	}
3327
		
3328
	/**
3329
	 * Remove file/ recursive remove dir
3330
	 *
3331
	 * @param  string  $path   file path
3332
	 * @param  bool    $force  try to remove even if file locked
3333
	 * @return bool
3334
	 * @author Dmitry (dio) Levashov
3335
	 **/
3336
	protected function remove($path, $force = false) {
3337
		$stat = $this->stat($path);
3338
		
3339
		if (empty($stat)) {
3340
			return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']), elFinder::ERROR_FILE_NOT_FOUND);
3341
		}
3342
		
3343
		$stat['realpath'] = $path;
3344
		$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...
3345
		$this->clearcache();
3346
		
3347 View Code Duplication
		if (!$force && !empty($stat['locked'])) {
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...
3348
			return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
3349
		}
3350
		
3351
		if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
3352
			$ret = $this->delTree($this->convEncIn($path));
3353
			$this->convEncOut();
3354
			if (!$ret) {
3355
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3356
			}
3357 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...
3358
			if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
3359
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3360
			}
3361
		}
3362
3363
		$this->removed[] = $stat;
3364
		return true;
3365
	}
3366
	
3367
3368
	/************************* thumbnails **************************/
3369
		
3370
	/**
3371
	 * Return thumbnail file name for required file
3372
	 *
3373
	 * @param  array  $stat  file stat
3374
	 * @return string
3375
	 * @author Dmitry (dio) Levashov
3376
	 **/
3377
	protected function tmbname($stat) {
3378
		return $stat['hash'].$stat['ts'].'.png';
3379
	}
3380
	
3381
	/**
3382
	 * Return thumnbnail name if exists
3383
	 *
3384
	 * @param  string  $path file path
3385
	 * @param  array   $stat file stat
3386
	 * @return string|false
3387
	 * @author Dmitry (dio) Levashov
3388
	 **/
3389
	protected function gettmb($path, $stat) {
3390
		if ($this->tmbURL && $this->tmbPath) {
3391
			// file itself thumnbnail
3392
			if (strpos($path, $this->tmbPath) === 0) {
3393
				return basename($path);
3394
			}
3395
3396
			$name = $this->tmbname($stat);
3397
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
3398
				return $name;
3399
			}
3400
		}
3401
		return false;
3402
	}
3403
	
3404
	/**
3405
	 * Return true if thumnbnail for required file can be created
3406
	 *
3407
	 * @param  string  $path  thumnbnail path 
3408
	 * @param  array   $stat  file stat
3409
	 * @param  bool    $checkTmbPath
3410
	 * @return string|bool
3411
	 * @author Dmitry (dio) Levashov
3412
	 **/
3413
	protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
3414
		return (!$checkTmbPath || $this->tmbPathWritable) 
3415
			&& (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
3416
			&& $this->imgLib 
3417
			&& strpos($stat['mime'], 'image') === 0 
3418
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
3419
	}
3420
	
3421
	/**
3422
	 * Return true if required file can be resized.
3423
	 * By default - the same as canCreateTmb
3424
	 *
3425
	 * @param  string  $path  thumnbnail path 
3426
	 * @param  array   $stat  file stat
3427
	 * @return string|bool
3428
	 * @author Dmitry (dio) Levashov
3429
	 **/
3430
	protected function canResize($path, $stat) {
3431
		return $this->canCreateTmb($path, $stat, false);
3432
	}
3433
	
3434
	/**
3435
	 * Create thumnbnail and return it's URL on success
3436
	 *
3437
	 * @param  string  $path  file path
3438
	 * @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...
3439
	 * @return string|false
3440
	 * @author Dmitry (dio) Levashov
3441
	 **/
3442
	protected function createTmb($path, $stat) {
3443
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
3444
			return false;
3445
		}
3446
3447
		$name = $this->tmbname($stat);
3448
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
3449
3450
		// copy image into tmbPath so some drivers does not store files on local fs
3451
		if (($src = $this->fopenCE($path, 'rb')) == false) {
3452
			return false;
3453
		}
3454
3455
		if (($trg = fopen($tmb, 'wb')) == false) {
3456
			$this->fcloseCE($src, $path);
3457
			return false;
3458
		}
3459
3460
		while (!feof($src)) {
3461
			fwrite($trg, fread($src, 8192));
3462
		}
3463
3464
		$this->fcloseCE($src, $path);
3465
		fclose($trg);
3466
3467
		$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...
3468
		
3469
		$tmbSize = $this->tmbSize;
3470
		
3471
		if (($s = getimagesize($tmb)) == false) {
3472
			return false;
3473
		}
3474
3475
		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
3476
		if ($s[0] <= $tmbSize && $s[1]	<= $tmbSize) {
3477
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3478
		} else {
3479
		
3480
			if ($this->options['tmbCrop']) {
3481
		
3482
				$result = $tmb;
3483
				/* Resize and crop if image bigger than thumbnail */
3484 View Code Duplication
				if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
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...
3485
					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
3486
				}
3487
		
3488 View Code Duplication
				if ($result && ($s = getimagesize($tmb)) != false) {
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...
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...
3489
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
3490
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
3491
					$result = $this->imgCrop($result, $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...
3492
				} else {
3493
					$result = false;
3494
				}
3495
		
3496
			} else {
3497
				$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
3498
			}
3499
		
3500
			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...
3501
				$result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3502
			}
3503
		}
3504
		
3505
		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...
3506
			unlink($tmb);
3507
			return false;
3508
		}
3509
		
3510
		return $name;
3511
	}
3512
3513
	/**
3514
	 * Resize image
3515
	 *
3516
	 * @param  string   $path               image file
3517
	 * @param  int      $width              new width
3518
	 * @param  int      $height             new height
3519
	 * @param  bool	    $keepProportions    crop image
3520
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
3521
	 * @param  string   $destformat         image destination format
3522
	 * @return string|false
3523
	 * @author Dmitry (dio) Levashov
3524
	 * @author Alexey Sukhotin
3525
	 **/
3526
	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) {
3527
		if (($s = @getimagesize($path)) == false) {
3528
			return false;
3529
		}
3530
3531
		$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...
3532
		
3533
		list($size_w, $size_h) = array($width, $height);
3534
	
3535
		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...
3536
		
3537
			list($orig_w, $orig_h) = array($s[0], $s[1]);
3538
		
3539
			/* Resizing by biggest side */
3540
			if ($resizeByBiggerSide) {
3541 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...
3542
					$size_h = round($orig_h * $width / $orig_w);
3543
					$size_w = $width;
3544
				} else {
3545
					$size_w = round($orig_w * $height / $orig_h);
3546
					$size_h = $height;
3547
				}
3548 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...
3549
				if ($orig_w > $orig_h) {
3550
					$size_w = round($orig_w * $height / $orig_h);
3551
					$size_h = $height;
3552
				} else {
3553
					$size_h = round($orig_h * $width / $orig_w);
3554
					$size_w = $width;
3555
				}
3556
			}
3557
		}
3558
3559
		switch ($this->imgLib) {
3560
			case 'imagick':
3561
				
3562
				try {
3563
					$img = new imagick($path);
3564
				} catch (Exception $e) {
3565
					return false;
3566
				}
3567
3568
				// Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
3569
				// resize bench: http://app-mgng.rhcloud.com/9
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3570
				// resize sample: http://www.dylanbeattie.net/magick/filters/result.html
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3571
				$filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
3572
				
3573
				$ani = ($img->getNumberImages() > 1);
3574
				if ($ani && is_null($destformat)) {
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
3575
					$img = $img->coalesceImages();
3576
					do {
3577
						$img->resizeImage($size_w, $size_h, $filter, 1);
3578
					} while ($img->nextImage());
3579
					$img = $img->optimizeImageLayers();
3580
					$result = $img->writeImages($path, true);
3581
				} else {
3582
					if ($ani) {
3583
						$img->setFirstIterator();
3584
					}
3585
					$img->resizeImage($size_w, $size_h, $filter, 1);
3586
					$result = $img->writeImage($path);
3587
				}
3588
				
3589
				$img->destroy();
3590
3591
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3592
3593
				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...
3594
3595
			case 'gd':
3596
				$img = self::gdImageCreate($path,$s['mime']);
3597
3598
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
3599
				
3600
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3601
					
3602
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
3603
						return false;
3604
					}
3605
		
3606
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3607
3608
					imagedestroy($img);
3609
					imagedestroy($tmp);
3610
3611
					return $result ? $path : false;
3612
3613
				}
3614
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3615
		}
3616
		
3617
		return false;
3618
  	}
3619
  
3620
	/**
3621
	 * Crop image
3622
	 *
3623
	 * @param  string   $path               image file
3624
	 * @param  int      $width              crop width
3625
	 * @param  int      $height             crop height
3626
	 * @param  bool	    $x                  crop left offset
3627
	 * @param  bool	    $y                  crop top offset
3628
	 * @param  string   $destformat         image destination format
3629
	 * @return string|false
3630
	 * @author Dmitry (dio) Levashov
3631
	 * @author Alexey Sukhotin
3632
	 **/
3633
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) {
3634
		if (($s = @getimagesize($path)) == false) {
3635
			return false;
3636
		}
3637
3638
		$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...
3639
		
3640
		switch ($this->imgLib) {
3641
			case 'imagick':
3642
				
3643
				try {
3644
					$img = new imagick($path);
3645
				} catch (Exception $e) {
3646
					return false;
3647
				}
3648
				
3649
				$ani = ($img->getNumberImages() > 1);
3650
				if ($ani && is_null($destformat)) {
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
3651
					$img = $img->coalesceImages();
3652
					do {
3653
						$img->setImagePage($s[0], $s[1], 0, 0);
3654
						$img->cropImage($width, $height, $x, $y);
3655
						$img->setImagePage($width, $height, 0, 0);
3656
					} while ($img->nextImage());
3657
					$img = $img->optimizeImageLayers();
3658
					$result = $img->writeImages($path, true);
3659
				} else {
3660
					if ($ani) {
3661
						$img->setFirstIterator();
3662
					}
3663
					$img->setImagePage($s[0], $s[1], 0, 0);
3664
					$img->cropImage($width, $height, $x, $y);
3665
					$img->setImagePage($width, $height, 0, 0);
3666
					$result = $img->writeImage($path);
3667
				}
3668
				
3669
				$img->destroy();
3670
3671
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3672
3673
				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...
3674
3675
			case 'gd':
3676
				$img = self::gdImageCreate($path,$s['mime']);
3677
3678
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3679
					
3680
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3681
3682
					$size_w = $width;
3683
					$size_h = $height;
3684
3685
					if ($s[0] < $width || $s[1] < $height) {
3686
						$size_w = $s[0];
3687
						$size_h = $s[1];
3688
					}
3689
3690
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
3691
						return false;
3692
					}
3693
					
3694
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3695
3696
					imagedestroy($img);
3697
					imagedestroy($tmp);
3698
3699
					return $result ? $path : false;
3700
3701
				}
3702
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3703
		}
3704
3705
		return false;
3706
	}
3707
3708
	/**
3709
	 * Put image to square
3710
	 *
3711
	 * @param  string   $path               image file
3712
	 * @param  int      $width              square width
3713
	 * @param  int      $height             square height
3714
	 * @param  int	    $align              reserved
3715
	 * @param  int 	    $valign             reserved
3716
	 * @param  string   $bgcolor            square background color in #rrggbb format
3717
	 * @param  string   $destformat         image destination format
3718
	 * @return string|false
3719
	 * @author Dmitry (dio) Levashov
3720
	 * @author Alexey Sukhotin
3721
	 **/
3722
	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...
3723
		if (($s = @getimagesize($path)) == false) {
3724
			return false;
3725
		}
3726
3727
		$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...
3728
3729
		/* Coordinates for image over square aligning */
3730
		$y = ceil(abs($height - $s[1]) / 2); 
3731
		$x = ceil(abs($width - $s[0]) / 2);
3732
3733
		switch ($this->imgLib) {
3734
			case 'imagick':
3735
				try {
3736
					$img = new imagick($path);
3737
				} catch (Exception $e) {
3738
					return false;
3739
				}
3740
				
3741
				$ani = ($img->getNumberImages() > 1);
3742
				if ($ani && is_null($destformat)) {
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
3743
					$img1 = new Imagick();
3744
					$img1->setFormat('gif');
3745
					$img = $img->coalesceImages();
3746
					do {
3747
						$gif = new Imagick();
3748
						$gif->newImage($width, $height, new ImagickPixel($bgcolor));
3749
						$gif->setImageColorspace($img->getImageColorspace());
3750
						$gif->setImageFormat('gif');
3751
						$gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3752
						$gif->setImageDelay($img->getImageDelay());
3753
						$gif->setImageIterations($img->getImageIterations());
3754
						$img1->addImage($gif);
3755
						$gif->destroy();
3756
					} while ($img->nextImage());
3757
					$img1 = $img1->optimizeImageLayers();
3758
					$result = $img1->writeImages($path, true);
3759
				} else {
3760
					if ($ani) {
3761
						$img->setFirstIterator();
3762
					}
3763
					$img1 = new Imagick();
3764
					$img1->newImage($width, $height, new ImagickPixel($bgcolor));
3765
					$img1->setImageColorspace($img->getImageColorspace());
3766
					$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...
3767
					$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3768
					$result = $img1->writeImage($path);
3769
				}
3770
				
3771
				$img1->destroy();
3772
				$img->destroy();
3773
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3774
3775
				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...
3776
3777
			case 'gd':
3778
				$img = self::gdImageCreate($path,$s['mime']);
3779
3780
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3781
3782
					self::gdImageBackground($tmp,$bgcolor);
3783
3784
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
3785
						return false;
3786
					}
3787
3788
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3789
3790
					imagedestroy($img);
3791
					imagedestroy($tmp);
3792
3793
					return $result ? $path : false;
3794
				}
3795
				break;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3796
		}
3797
3798
		return false;
3799
	}
3800
3801
	/**
3802
	 * Rotate image
3803
	 *
3804
	 * @param  string   $path               image file
3805
	 * @param  int      $degree             rotete degrees
3806
	 * @param  string   $bgcolor            square background color in #rrggbb format
3807
	 * @param  string   $destformat         image destination format
3808
	 * @return string|false
3809
	 * @author nao-pon
3810
	 * @author Troex Nevelin
3811
	 **/
3812
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) {
3813
		if (($s = @getimagesize($path)) == false || $degree % 360 === 0) {
3814
			return false;
3815
		}
3816
3817
		$result = false;
3818
3819
		// try lossless rotate
3820
		if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
3821
			$count = ($degree / 90) % 4;
3822
			$exiftran = array(
3823
				1 => '-9',
3824
				2 => '-1',
3825
				3 => '-2'
3826
			);
3827
			$jpegtran = array(
3828
				1 => '90',
3829
				2 => '180',
3830
				3 => '270'
3831
			);
3832
			$quotedPath = escapeshellarg($path);
3833
			$cmds = array(
3834
				'exiftran -i '.$exiftran[$count].' '.$path,
3835
				'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
3836
			);
3837
			foreach($cmds as $cmd) {
3838
				if ($this->procExec($cmd) === 0) {
3839
					$result = true;
3840
					break;
3841
				}
3842
			}
3843
			if ($result) {
3844
				return $path;
3845
			}
3846
		}
3847
3848
		switch ($this->imgLib) {
3849
			case 'imagick':
3850
				try {
3851
					$img = new imagick($path);
3852
				} catch (Exception $e) {
3853
					return false;
3854
				}
3855
3856
				if ($img->getNumberImages() > 1) {
3857
					$img = $img->coalesceImages();
3858
					do {
3859
						$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3860
					} while ($img->nextImage());
3861
					$img = $img->optimizeImageLayers();
3862
					$result = $img->writeImages($path, true);
3863
				} else {
3864
					$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3865
					$result = $img->writeImage($path);
3866
				}
3867
				$img->destroy();
3868
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3869
3870
				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...
3871
3872
			case 'gd':
3873
				$img = self::gdImageCreate($path,$s['mime']);
3874
3875
				$degree = 360 - $degree;
3876
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3877
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
3878
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
3879
3880
				$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3881
3882
				imageDestroy($img);
3883
				imageDestroy($tmp);
3884
3885
				return $result ? $path : false;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3886
3887
				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...
3888
		}
3889
3890
		return false;
3891
	}
3892
3893
	/**
3894
	 * Execute shell command
3895
	 *
3896
	 * @param  string  $command       command line
3897
	 * @param  array   $output        stdout strings
3898
	 * @param  array   $return_var    process exit code
3899
	 * @param  array   $error_output  stderr strings
3900
	 * @return int     exit code
3901
	 * @author Alexey Sukhotin
3902
	 **/
3903
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
3904
3905
		$descriptorspec = array(
3906
			0 => array("pipe", "r"),  // stdin
3907
			1 => array("pipe", "w"),  // stdout
3908
			2 => array("pipe", "w")   // stderr
3909
		);
3910
3911
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
3912
3913
		if (is_resource($process)) {
3914
3915
			fclose($pipes[0]);
3916
3917
			$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...
3918
			$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...
3919
3920
			$output = stream_get_contents($pipes[1]);
3921
			$error_output = stream_get_contents($pipes[2]);
3922
3923
			fclose($pipes[1]);
3924
			fclose($pipes[2]);
3925
			$return_var = proc_close($process);
3926
3927
3928
		}
3929
		
3930
		return $return_var;
3931
		
3932
	}
3933
3934
	/**
3935
	 * Remove thumbnail, also remove recursively if stat is directory
3936
	 *
3937
	 * @param  string  $stat  file stat
3938
	 * @return void
3939
	 * @author Dmitry (dio) Levashov
3940
	 * @author Naoki Sawada
3941
	 * @author Troex Nevelin
3942
	 **/
3943
	protected function rmTmb($stat) {
3944
		if ($stat['mime'] === 'directory') {
3945
			foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
3946
				@set_time_limit(30);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
3947
				$name = $this->basenameCE($p);
3948
				$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...
3949
			}
3950
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
3951
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
3952
			file_exists($tmb) && @unlink($tmb);
3953
			clearstatcache();
3954
		}
3955
	}
3956
	
3957
	/**
3958
	 * Create an gd image according to the specified mime type
3959
	 *
3960
	 * @param string $path image file
3961
	 * @param string $mime
3962
	 * @return gd image resource identifier
3963
	 */
3964
	protected function gdImageCreate($path,$mime){
3965
		switch($mime){
3966
			case 'image/jpeg':
3967
			return imagecreatefromjpeg($path);
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3968
3969
			case 'image/png':
3970
			return imagecreatefrompng($path);
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3971
3972
			case 'image/gif':
3973
			return imagecreatefromgif($path);
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3974
3975
			case 'image/xbm':
3976
			return imagecreatefromxbm($path);
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
3977
		}
3978
		return false;
3979
	}
3980
3981
	/**
3982
	 * Output gd image to file
3983
	 *
3984
	 * @param resource $image gd image resource
3985
	 * @param string $filename The path to save the file to.
3986
	 * @param string $destformat The Image type to use for $filename
3987
	 * @param string $mime The original image mime type
3988
	 */
3989
	protected function gdImage($image, $filename, $destformat, $mime ){
3990
3991
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
3992
			return imagejpeg($image, $filename, 100);
3993
		}
3994
3995
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
3996
			return imagegif($image, $filename, 7);
3997
		}
3998
3999
		return imagepng($image, $filename, 7);
4000
	}
4001
4002
	/**
4003
	 * Assign the proper background to a gd image
4004
	 *
4005
	 * @param resource $image gd image resource
4006
	 * @param string $bgcolor background color in #rrggbb format
4007
	 */
4008
	protected function gdImageBackground($image, $bgcolor){
4009
4010
		if( $bgcolor == 'transparent' ){
4011
			imagesavealpha($image,true);
4012
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
4013
4014
		}else{
4015
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4016
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
4017
		}
4018
4019
		imagefill($image, 0, 0, $bgcolor1);
4020
	}
4021
4022
	/*********************** misc *************************/
4023
	
4024
	/**
4025
	 * Return smart formatted date
4026
	 *
4027
	 * @param  int     $ts  file timestamp
4028
	 * @return string
4029
	 * @author Dmitry (dio) Levashov
4030
	 **/
4031
	// protected function formatDate($ts) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4032
	// 	if ($ts > $this->today) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4033
	// 		return 'Today '.date($this->options['timeFormat'], $ts);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4034
	// 	}
4035
	// 	
0 ignored issues
show
Coding Style introduced by
There is some trailing whitespace on this line which should be avoided as per coding-style.
Loading history...
4036
	// 	if ($ts > $this->yesterday) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4037
	// 		return 'Yesterday '.date($this->options['timeFormat'], $ts);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4038
	// 	} 
4039
	// 	
0 ignored issues
show
Coding Style introduced by
There is some trailing whitespace on this line which should be avoided as per coding-style.
Loading history...
4040
	// 	return date($this->options['dateFormat'], $ts);
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4041
	// }
4042
4043
	/**
4044
	* Find position of first occurrence of string in a string with multibyte support
4045
	*
4046
	* @param  string  $haystack  The string being checked.
4047
	* @param  string  $needle    The string to find in haystack.
4048
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
4049
	* @return int|bool
4050
	* @author Alexey Sukhotin
4051
	**/
4052
	protected function stripos($haystack , $needle , $offset = 0) {
4053
		if (function_exists('mb_stripos')) {
4054
			return mb_stripos($haystack , $needle , $offset);
4055
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
4056
			return mb_strpos(mb_strtolower($haystack), mb_strtolower($needle), $offset);
4057
		} 
4058
		return stripos($haystack , $needle , $offset);
4059
	}
4060
4061
	/**
4062
	 * Get server side available archivers
4063
	 * 
4064
	 * @param bool $use_cache
4065
	 * @return array
4066
	 */
4067
	protected function getArchivers($use_cache = true) {
4068
4069
		$sessionKey = 'ARCHIVERS_CACHE';
4070
		if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
4071
			return $this->sessionCache[$sessionKey];
4072
		}
4073
		
4074
		$arcs = array(
4075
			'create'  => array(),
4076
			'extract' => array()
4077
		);
4078
		
4079
		if (function_exists('proc_open')) {
4080
		
4081
			$this->procExec('tar --version', $o, $ctar);
4082
			
4083
			if ($ctar == 0) {
4084
				$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
4085
				$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
4086
				unset($o);
4087
				$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...
4088 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...
4089
					$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
4090
					$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
4091
				}
4092
				unset($o);
4093
				$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...
4094 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...
4095
					$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
4096
					$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
4097
				}
4098
				unset($o);
4099
				$test = $this->procExec('xz --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...
4100 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...
4101
					$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
4102
					$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
4103
				}
4104
			}
4105
			unset($o);
4106
			$this->procExec('zip -v', $o, $c);
4107
			if ($c == 0) {
4108
				$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
4109
			}
4110
			unset($o);
4111
			$this->procExec('unzip --help', $o, $c);
4112
			if ($c == 0) {
4113
				$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
4114
			}
4115
			unset($o);
4116
			$this->procExec('rar --version', $o, $c);
4117
			if ($c == 0 || $c == 7) {
4118
				$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
4119
				$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
4120
			} else {
4121
				unset($o);
4122
				$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...
4123
				if ($c==0 || $c == 7) {
4124
					$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
4125
				}
4126
			}
4127
			unset($o);
4128
			$this->procExec('7za --help', $o, $c);
4129
			if ($c == 0) {
4130
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
4131
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
4132
				
4133
				if (empty($arcs['create']['application/zip'])) {
4134
					$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
4135
				}
4136 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...
4137
					$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4138
				}
4139
				if (empty($arcs['create']['application/x-tar'])) {
4140
					$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
4141
				}
4142 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...
4143
					$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4144
				}
4145
			} else if (substr(PHP_OS,0,3) === 'WIN') {
4146
				// check `7z` for Windows server.
4147
				unset($o);
4148
				$this->procExec('7z', $o, $c);
4149
				if ($c == 0) {
4150
					$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
4151
					$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
4152
					
4153
					if (empty($arcs['create']['application/zip'])) {
4154
						$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
4155
					}
4156
					if (empty($arcs['extract']['application/zip'])) {
4157
						$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4158
					}
4159
					if (empty($arcs['create']['application/x-tar'])) {
4160
						$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
4161
					}
4162
					if (empty($arcs['extract']['application/x-tar'])) {
4163
						$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4164
					}
4165
				}
4166
			}
4167
		
4168
		}
4169
		
4170
		// Use PHP ZipArchive Class
4171
		if (class_exists('ZipArchive')) {
4172
			if (empty($arcs['create']['application/zip'])) {
4173
				$arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveZip', 'ext' => 'zip');
4174
			}
4175 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...
4176
				$arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveUnzip', 'ext' => 'zip');
4177
			}
4178
		}
4179
		
4180
		$this->sessionCache[$sessionKey] = $arcs;
4181
		return $arcs;
4182
	}
4183
4184
	/**
4185
	 * Resolve relative / (Unix-like)absolute path
4186
	 * 
4187
	 * @param string $path  target path
4188
	 * @param string $base  base path
4189
	 * @return string
4190
	 */
4191
	protected function getFullPath($path, $base) {
4192
		$separator = $this->separator;
4193
		$systemroot = $this->systemRoot;
4194
4195
		$sepquoted = preg_quote($separator, '#');
4196
4197
		// normalize `/../`
4198
		$normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#';
4199
		while(preg_match($normreg, $path)) {
4200
			$path = preg_replace($normreg, '$1', $path);
4201
		}
4202
		
4203
		// 'Here'
4204
		if ($path === '' || $path === '.' . $separator) return $base;
4205
		
4206
		// Absolute path
4207
		if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
4208
			return $path;
4209
		}
4210
		
4211
		$preg_separator = '#' . $sepquoted . '#';
4212
		
4213
		// Relative path from 'Here'
4214
		if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.' || substr($path, 0, 3) !== '..' . $separator) {
4215
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4216
			if ($arrn[0] !== '.') {
4217
				array_unshift($arrn, '.');
4218
			}
4219
			$arrn[0] = $base;
4220
			return join($separator, $arrn);
4221
		}
4222
		
4223
		// Relative path from dirname()
4224
		if (substr($path, 0, 3) === '../') {
4225
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4226
			$arrp = preg_split($preg_separator, $base, -1, PREG_SPLIT_NO_EMPTY);
4227
		
4228
			while (! empty($arrn) && $arrn[0] === '..') {
4229
				array_shift($arrn);
4230
				array_pop($arrp);
4231
			}
4232
			$path = ! empty($arrp) ? $systemroot . join($separator, array_merge($arrp, $arrn)) :
4233
				(! empty($arrn) ? $systemroot . join($separator, $arrn) : $systemroot);
4234
		}
4235
		
4236
		return $path;
4237
	}
4238
4239
	/**
4240
	 * Remove directory recursive on local file system
4241
	 *
4242
	 * @param string $dir Target dirctory path
4243
	 * @return boolean
4244
	 * @author Naoki Sawada
4245
	 */
4246
	public function rmdirRecursive($dir) {
4247
		if (!is_link($dir) && is_dir($dir)) {
4248
			@chmod($dir, 0777);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4249
			foreach (array_diff(scandir($dir), array('.', '..')) as $file) {
4250
				@set_time_limit(30);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4251
				$path = $dir . DIRECTORY_SEPARATOR . $file;
4252
				if (!is_link($dir) && is_dir($path)) {
4253
					$this->rmdirRecursive($path);
4254
				} else {
4255
					@chmod($path, 0666);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4257
				}
4258
			}
4259
			return @rmdir($dir);
4260
		} else if (is_file($dir) || is_link($dir)) {
4261
			@chmod($dir, 0666);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4262
			return @unlink($dir);
4263
		}
4264
		return false;
4265
	}
4266
4267
	/**
4268
	 * Create archive and return its path
4269
	 *
4270
	 * @param  string  $dir    target dir
4271
	 * @param  array   $files  files names list
4272
	 * @param  string  $name   archive name
4273
	 * @param  array   $arc    archiver options
4274
	 * @return string|bool
4275
	 * @author Dmitry (dio) Levashov, 
4276
	 * @author Alexey Sukhotin
4277
	 * @author Naoki Sawada
4278
	 **/
4279
	protected function makeArchive($dir, $files, $name, $arc) {
4280
		if ($arc['cmd'] === 'phpfunction') {
4281 View Code Duplication
			if (is_callable($arc['argc'])) {
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...
4282
				call_user_func_array($arc['argc'], array($dir, $files, $name));
4283
			}
4284
		} else {
4285
			$cwd = getcwd();
4286
			chdir($dir);
4287
			
4288
			$files = array_map('escapeshellarg', $files);
4289
			
4290
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
4291
			$this->procExec($cmd, $o, $c);
4292
			chdir($cwd);
4293
		}
4294
		$path = $dir.DIRECTORY_SEPARATOR.$name;
4295
		return file_exists($path) ? $path : false;
4296
	}
4297
	
4298
	/**
4299
	 * Unpack archive
4300
	 *
4301
	 * @param  string  $path   archive path
4302
	 * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
4303
	 * @param  bool    $remove remove archive ( unlink($path) )
4304
	 * @return void
4305
	 * @author Dmitry (dio) Levashov
4306
	 * @author Alexey Sukhotin
4307
	 * @author Naoki Sawada
4308
	 **/
4309
	protected function unpackArchive($path, $arc, $remove = true) {
4310
		$dir = dirname($path);
4311
		if ($arc['cmd'] === 'phpfunction') {
4312 View Code Duplication
			if (is_callable($arc['argc'])) {
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...
4313
				call_user_func_array($arc['argc'], array($path, $dir));
4314
			}
4315
		} else {
4316
			$cwd = getcwd();
4317
			chdir($dir);
4318
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
4319
			$this->procExec($cmd, $o, $c);
4320
			chdir($cwd);
4321
		}
4322
		$remove && unlink($path);
4323
	}
4324
	
4325
	/**
4326
	 * Create Zip archive using PHP class ZipArchive
4327
	 * 
4328
	 * @param  string        $dir      target dir
4329
	 * @param  array         $files    files names list
4330
	 * @param  string|object $zipPath  Zip archive name
4331
	 * @return void
4332
	 * @author Naoki Sawada
4333
	 */
4334
	protected static function zipArchiveZip($dir, $files, $zipPath) {
4335
		try {
4336
			if ($start = is_string($zipPath)) {
4337
				$zip = new ZipArchive();
4338
				if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
4339
					$zip = false;
4340
				}
4341
			} else {
4342
				$zip = $zipPath;
4343
			}
4344
			if ($zip) {
4345
				foreach($files as $file) {
4346
					$path = $dir . DIRECTORY_SEPARATOR . $file;
4347
					if (is_dir($path)) {
4348
						$zip->addEmptyDir($file);
4349
						$_files = array();
4350
						if ($handle = opendir($path)) {
4351
							while (false !== ($entry = readdir($handle))) {
4352
								if ($entry !== "." && $entry !== "..") {
4353
									$_files[] = $file . DIRECTORY_SEPARATOR . $entry;
4354
								}
4355
							}
4356
							closedir($handle);
4357
						}
4358
						if ($_files) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $_files of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
4359
							self::zipArchiveZip($dir, $_files, $zip);
4360
						}
4361
					} else {
4362
						$zip->addFile($path, $file);
4363
					}
4364
				}
4365
				$start && $zip->close();
4366
			}
4367
		} catch (Exception $e) {
4368
			return false;
4369
		}
4370
		return true;
4371
	}
4372
	
4373
	/**
4374
	 * Unpack Zip archive using PHP class ZipArchive
4375
	 * 
4376
	 * @param  string $zipPath  Zip archive name
4377
	 * @param  string $toDir    Extract to path
4378
	 * @return bool
4379
	 * @author Naoki Sawada
4380
	 */
4381
	protected static function zipArchiveUnzip($zipPath, $toDir) {
4382
		try {
4383
			$zip = new ZipArchive();
4384
			if ($zip->open($zipPath) === true) {
4385
				$zip->extractTo($toDir);
4386
				$zip->close();
4387
			}
4388
		} catch (Exception $e) {
4389
			return false;
4390
		}
4391
		return true;
4392
	}
4393
	
4394
	/**==================================* abstract methods *====================================**/
4395
	
4396
	/*********************** paths/urls *************************/
4397
	
4398
	/**
4399
	 * Return parent directory path
4400
	 *
4401
	 * @param  string  $path  file path
4402
	 * @return string
4403
	 * @author Dmitry (dio) Levashov
4404
	 **/
4405
	abstract protected function _dirname($path);
1 ignored issue
show
Coding Style introduced by
Method name "_dirname" should not be prefixed with an underscore to indicate visibility
Loading history...
4406
4407
	/**
4408
	 * Return file name
4409
	 *
4410
	 * @param  string  $path  file path
4411
	 * @return string
4412
	 * @author Dmitry (dio) Levashov
4413
	 **/
4414
	abstract protected function _basename($path);
1 ignored issue
show
Coding Style introduced by
Method name "_basename" should not be prefixed with an underscore to indicate visibility
Loading history...
4415
4416
	/**
4417
	 * Join dir name and file name and return full path.
4418
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
4419
	 *
4420
	 * @param  string  $dir   dir path
4421
	 * @param  string  $name  file name
4422
	 * @return string
4423
	 * @author Dmitry (dio) Levashov
4424
	 **/
4425
	abstract protected function _joinPath($dir, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_joinPath" should not be prefixed with an underscore to indicate visibility
Loading history...
4426
4427
	/**
4428
	 * Return normalized path 
4429
	 *
4430
	 * @param  string  $path  file path
4431
	 * @return string
4432
	 * @author Dmitry (dio) Levashov
4433
	 **/
4434
	abstract protected function _normpath($path);
1 ignored issue
show
Coding Style introduced by
Method name "_normpath" should not be prefixed with an underscore to indicate visibility
Loading history...
4435
4436
	/**
4437
	 * Return file path related to root dir
4438
	 *
4439
	 * @param  string  $path  file path
4440
	 * @return string
4441
	 * @author Dmitry (dio) Levashov
4442
	 **/
4443
	abstract protected function _relpath($path);
1 ignored issue
show
Coding Style introduced by
Method name "_relpath" should not be prefixed with an underscore to indicate visibility
Loading history...
4444
	
4445
	/**
4446
	 * Convert path related to root dir into real path
4447
	 *
4448
	 * @param  string  $path  rel file path
4449
	 * @return string
4450
	 * @author Dmitry (dio) Levashov
4451
	 **/
4452
	abstract protected function _abspath($path);
1 ignored issue
show
Coding Style introduced by
Method name "_abspath" should not be prefixed with an underscore to indicate visibility
Loading history...
4453
	
4454
	/**
4455
	 * Return fake path started from root dir.
4456
	 * Required to show path on client side.
4457
	 *
4458
	 * @param  string  $path  file path
4459
	 * @return string
4460
	 * @author Dmitry (dio) Levashov
4461
	 **/
4462
	abstract protected function _path($path);
1 ignored issue
show
Coding Style introduced by
Method name "_path" should not be prefixed with an underscore to indicate visibility
Loading history...
4463
	
4464
	/**
4465
	 * Return true if $path is children of $parent
4466
	 *
4467
	 * @param  string  $path    path to check
4468
	 * @param  string  $parent  parent path
4469
	 * @return bool
4470
	 * @author Dmitry (dio) Levashov
4471
	 **/
4472
	abstract protected function _inpath($path, $parent);
1 ignored issue
show
Coding Style introduced by
Method name "_inpath" should not be prefixed with an underscore to indicate visibility
Loading history...
4473
	
4474
	/**
4475
	 * Return stat for given path.
4476
	 * Stat contains following fields:
4477
	 * - (int)    size    file size in b. required
4478
	 * - (int)    ts      file modification time in unix time. required
4479
	 * - (string) mime    mimetype. required for folders, others - optionally
4480
	 * - (bool)   read    read permissions. required
4481
	 * - (bool)   write   write permissions. required
4482
	 * - (bool)   locked  is object locked. optionally
4483
	 * - (bool)   hidden  is object hidden. optionally
4484
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
4485
	 * - (string) target  for symlinks - link target path. optionally
4486
	 *
4487
	 * If file does not exists - returns empty array or false.
4488
	 *
4489
	 * @param  string  $path    file path 
4490
	 * @return array|false
4491
	 * @author Dmitry (dio) Levashov
4492
	 **/
4493
	abstract protected function _stat($path);
1 ignored issue
show
Coding Style introduced by
Method name "_stat" should not be prefixed with an underscore to indicate visibility
Loading history...
4494
	
4495
4496
	/***************** file stat ********************/
4497
4498
		
4499
	/**
4500
	 * Return true if path is dir and has at least one childs directory
4501
	 *
4502
	 * @param  string  $path  dir path
4503
	 * @return bool
4504
	 * @author Dmitry (dio) Levashov
4505
	 **/
4506
	abstract protected function _subdirs($path);
1 ignored issue
show
Coding Style introduced by
Method name "_subdirs" should not be prefixed with an underscore to indicate visibility
Loading history...
4507
	
4508
	/**
4509
	 * Return object width and height
4510
	 * Ususaly used for images, but can be realize for video etc...
4511
	 *
4512
	 * @param  string  $path  file path
4513
	 * @param  string  $mime  file mime type
4514
	 * @return string
4515
	 * @author Dmitry (dio) Levashov
4516
	 **/
4517
	abstract protected function _dimensions($path, $mime);
1 ignored issue
show
Coding Style introduced by
Method name "_dimensions" should not be prefixed with an underscore to indicate visibility
Loading history...
4518
	
4519
	/******************** file/dir content *********************/
4520
4521
	/**
4522
	 * Return files list in directory
4523
	 *
4524
	 * @param  string  $path  dir path
4525
	 * @return array
4526
	 * @author Dmitry (dio) Levashov
4527
	 **/
4528
	abstract protected function _scandir($path);
1 ignored issue
show
Coding Style introduced by
Method name "_scandir" should not be prefixed with an underscore to indicate visibility
Loading history...
4529
	
4530
	/**
4531
	 * Open file and return file pointer
4532
	 *
4533
	 * @param  string  $path  file path
4534
	 * @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...
4535
	 * @return resource|false
4536
	 * @author Dmitry (dio) Levashov
4537
	 **/
4538
	abstract protected function _fopen($path, $mode="rb");
1 ignored issue
show
Coding Style introduced by
Method name "_fopen" should not be prefixed with an underscore to indicate visibility
Loading history...
4539
	
4540
	/**
4541
	 * Close opened file
4542
	 * 
4543
	 * @param  resource  $fp    file pointer
4544
	 * @param  string    $path  file path
4545
	 * @return bool
4546
	 * @author Dmitry (dio) Levashov
4547
	 **/
4548
	abstract protected function _fclose($fp, $path='');
1 ignored issue
show
Coding Style introduced by
Method name "_fclose" should not be prefixed with an underscore to indicate visibility
Loading history...
4549
	
4550
	/********************  file/dir manipulations *************************/
4551
	
4552
	/**
4553
	 * Create dir and return created dir path or false on failed
4554
	 *
4555
	 * @param  string  $path  parent dir path
4556
	 * @param string  $name  new directory name
4557
	 * @return string|bool
4558
	 * @author Dmitry (dio) Levashov
4559
	 **/
4560
	abstract protected function _mkdir($path, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_mkdir" should not be prefixed with an underscore to indicate visibility
Loading history...
4561
	
4562
	/**
4563
	 * Create file and return it's path or false on failed
4564
	 *
4565
	 * @param  string  $path  parent dir path
4566
	 * @param string  $name  new file name
4567
	 * @return string|bool
4568
	 * @author Dmitry (dio) Levashov
4569
	 **/
4570
	abstract protected function _mkfile($path, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_mkfile" should not be prefixed with an underscore to indicate visibility
Loading history...
4571
	
4572
	/**
4573
	 * Create symlink
4574
	 *
4575
	 * @param  string  $source     file to link to
4576
	 * @param  string  $targetDir  folder to create link in
4577
	 * @param  string  $name       symlink name
4578
	 * @return bool
4579
	 * @author Dmitry (dio) Levashov
4580
	 **/
4581
	abstract protected function _symlink($source, $targetDir, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_symlink" should not be prefixed with an underscore to indicate visibility
Loading history...
4582
	
4583
	/**
4584
	 * Copy file into another file (only inside one volume)
4585
	 *
4586
	 * @param  string  $source  source file path
4587
	 * @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...
4588
	 * @param  string  $name    file name
4589
	 * @return bool
4590
	 * @author Dmitry (dio) Levashov
4591
	 **/
4592
	abstract protected function _copy($source, $targetDir, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_copy" should not be prefixed with an underscore to indicate visibility
Loading history...
4593
	
4594
	/**
4595
	 * Move file into another parent dir.
4596
	 * Return new file path or false.
4597
	 *
4598
	 * @param  string  $source  source file path
4599
	 * @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...
4600
	 * @param  string  $name    file name
4601
	 * @return string|bool
4602
	 * @author Dmitry (dio) Levashov
4603
	 **/
4604
	abstract protected function _move($source, $targetDir, $name);
1 ignored issue
show
Coding Style introduced by
Method name "_move" should not be prefixed with an underscore to indicate visibility
Loading history...
4605
	
4606
	/**
4607
	 * Remove file
4608
	 *
4609
	 * @param  string  $path  file path
4610
	 * @return bool
4611
	 * @author Dmitry (dio) Levashov
4612
	 **/
4613
	abstract protected function _unlink($path);
1 ignored issue
show
Coding Style introduced by
Method name "_unlink" should not be prefixed with an underscore to indicate visibility
Loading history...
4614
4615
	/**
4616
	 * Remove dir
4617
	 *
4618
	 * @param  string  $path  dir path
4619
	 * @return bool
4620
	 * @author Dmitry (dio) Levashov
4621
	 **/
4622
	abstract protected function _rmdir($path);
1 ignored issue
show
Coding Style introduced by
Method name "_rmdir" should not be prefixed with an underscore to indicate visibility
Loading history...
4623
4624
	/**
4625
	 * Create new file and write into it from file pointer.
4626
	 * Return new file path or false on error.
4627
	 *
4628
	 * @param  resource  $fp   file pointer
4629
	 * @param  string    $dir  target dir path
4630
	 * @param  string    $name file name
4631
	 * @param  array     $stat file stat (required by some virtual fs)
4632
	 * @return bool|string
4633
	 * @author Dmitry (dio) Levashov
4634
	 **/
4635
	abstract protected function _save($fp, $dir, $name, $stat);
1 ignored issue
show
Coding Style introduced by
Method name "_save" should not be prefixed with an underscore to indicate visibility
Loading history...
4636
	
4637
	/**
4638
	 * Get file contents
4639
	 *
4640
	 * @param  string  $path  file path
4641
	 * @return string|false
4642
	 * @author Dmitry (dio) Levashov
4643
	 **/
4644
	abstract protected function _getContents($path);
1 ignored issue
show
Coding Style introduced by
Method name "_getContents" should not be prefixed with an underscore to indicate visibility
Loading history...
4645
	
4646
	/**
4647
	 * Write a string to a file
4648
	 *
4649
	 * @param  string  $path     file path
4650
	 * @param  string  $content  new file content
4651
	 * @return bool
4652
	 * @author Dmitry (dio) Levashov
4653
	 **/
4654
	abstract protected function _filePutContents($path, $content);
1 ignored issue
show
Coding Style introduced by
Method name "_filePutContents" should not be prefixed with an underscore to indicate visibility
Loading history...
4655
4656
	/**
4657
	 * Extract files from archive
4658
	 *
4659
	 * @param  string  $path file path
4660
	 * @param  array   $arc  archiver options
4661
	 * @return bool
4662
	 * @author Dmitry (dio) Levashov, 
4663
	 * @author Alexey Sukhotin
4664
	 **/
4665
	abstract protected function _extract($path, $arc);
1 ignored issue
show
Coding Style introduced by
Method name "_extract" should not be prefixed with an underscore to indicate visibility
Loading history...
4666
4667
	/**
4668
	 * Create archive and return its path
4669
	 *
4670
	 * @param  string  $dir    target dir
4671
	 * @param  array   $files  files names list
4672
	 * @param  string  $name   archive name
4673
	 * @param  array   $arc    archiver options
4674
	 * @return string|bool
4675
	 * @author Dmitry (dio) Levashov, 
4676
	 * @author Alexey Sukhotin
4677
	 **/
4678
	abstract protected function _archive($dir, $files, $name, $arc);
1 ignored issue
show
Coding Style introduced by
Method name "_archive" should not be prefixed with an underscore to indicate visibility
Loading history...
4679
4680
	/**
4681
	 * Detect available archivers
4682
	 *
4683
	 * @return void
4684
	 * @author Dmitry (dio) Levashov, 
4685
	 * @author Alexey Sukhotin
4686
	 **/
4687
	abstract protected function _checkArchivers();
1 ignored issue
show
Coding Style introduced by
Method name "_checkArchivers" should not be prefixed with an underscore to indicate visibility
Loading history...
4688
4689
	/**
4690
	 * Change file mode (chmod)
4691
	 *
4692
	 * @param  string  $path  file path
4693
	 * @param  string  $mode  octal string such as '0755'
4694
	 * @return bool
4695
	 * @author David Bartle,
4696
	 **/
4697
	abstract protected function _chmod($path, $mode);
1 ignored issue
show
Coding Style introduced by
Method name "_chmod" should not be prefixed with an underscore to indicate visibility
Loading history...
4698
4699
	
4700
} // END class
2 ignored issues
show
Coding Style introduced by
According to PSR2, the closing brace of classes should be placed on the next line directly after the body.

Below you find some examples:

// Incorrect placement according to PSR2
class MyClass
{
    public function foo()
    {

    }
    // This blank line is not allowed.

}

// Correct
class MyClass
{
    public function foo()
    {

    } // No blank lines after this line.
}
Loading history...
Coding Style introduced by
The closing brace of a class hould be on a line by itself.
Loading history...
4701