Completed
Push — 2.x ( 47af29...c7d790 )
by Naoki
06:16
created

elFinderVolumeDriver::mimetypeInternalDetect()   C

Complexity

Conditions 9
Paths 12

Size

Total Lines 23
Code Lines 15

Duplication

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

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
690
				foreach ($this->getScandir($path) as $stat) {
691
					if ($this->mimeAccepted($stat['mime'])) {
692
						$files[] = $stat;
693
					}
694
				}
695
			}
696
			return $files;
697
		} else {
698
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
699
		}
700
	}
701
	
702
	/**
703
	 * "Mount" volume.
704
	 * Return true if volume available for read or write, 
705
	 * false - otherwise
706
	 *
707
	 * @return bool
708
	 * @author Dmitry (dio) Levashov
709
	 * @author Alexey Sukhotin
710
	 **/
711
	public function mount(array $opts) {
0 ignored issues
show
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...
712
		if (!isset($opts['path']) || $opts['path'] === '') {
713
			return $this->setError('Path undefined.');;
714
		}
715
		
716
		$this->options = array_merge($this->options, $opts);
717
		$this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
718
		$this->root = $this->normpathCE($this->options['path']);
719
		$this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
720
		$this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator;
721
		
722
		// set server encoding
723
		if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') {
724
			$this->encoding = $this->options['encoding'];
725
		} else {
726
			$this->encoding = null;
727
		}
728
		
729
		$argInit = !empty($this->ARGS['init']);
730
		
731
		// session cache
732
		if ($argInit || ! isset($_SESSION[elFinder::$sessionCacheKey][$this->id])) {
733
			$_SESSION[elFinder::$sessionCacheKey][$this->id] = array();
734
		}
735
		$this->sessionCache = &$_SESSION[elFinder::$sessionCacheKey][$this->id];
736
		
737
		// default file attribute
738
		$this->defaults = array(
739
			'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
740
			'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
741
			'locked'  => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false,
742
			'hidden'  => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false
743
		);
744
745
		// root attributes
746
		$this->attributes[] = array(
747
			'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
748
			'locked'  => true,
749
			'hidden'  => false
750
		);
751
		// set files attributes
752 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...
753
			
754
			foreach ($this->options['attributes'] as $a) {
755
				// attributes must contain pattern and at least one rule
756
				if (!empty($a['pattern']) || count($a) > 1) {
757
					$this->attributes[] = $a;
758
				}
759
			}
760
		}
761
762
		if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
763
			$this->access = $this->options['accessControl'];
764
		}
765
		
766
		$this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
767
		$this->yesterday = $this->today-86400;
768
		
769
		// 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...
770
		if (!$this->init()) {
771
			return false;
772
		}
773
		
774
		// check some options is arrays
775
		$this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
776
			? $this->options['uploadAllow']
777
			: array();
778
			
779
		$this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
780
			? $this->options['uploadDeny']
781
			: array();
782
783
		if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
784
			$parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
785
			$this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
786
		} else { // telephat_mode off
787
			$this->uploadOrder = $this->options['uploadOrder'];
788
		}
789
			
790
		if (!empty($this->options['uploadMaxSize'])) {
791
			$size = ''.$this->options['uploadMaxSize'];
792
			$unit = strtolower(substr($size, strlen($size) - 1));
793
			$n = 1;
794
			switch ($unit) {
795
				case 'k':
796
					$n = 1024;
797
					break;
798
				case 'm':
799
					$n = 1048576;
800
					break;
801
				case 'g':
802
					$n = 1073741824;
803
			}
804
			$this->uploadMaxSize = intval($size)*$n;
805
		}
806
		// Set maximum to PHP_INT_MAX
807
		if (!defined('PHP_INT_MAX')) {
808
			define('PHP_INT_MAX', 2147483647);
809
		}
810
		if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) {
811
			$this->uploadMaxSize = PHP_INT_MAX;
812
		}
813
		
814
		$this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
815
			? $this->options['disabled']
816
			: array();
817
		
818
		$this->cryptLib   = $this->options['cryptLib'];
819
		$this->mimeDetect = $this->options['mimeDetect'];
820
821
		// find available mimetype detect method
822
		$type = strtolower($this->options['mimeDetect']);
823
		$type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
824
		$regexp = '/text\/x\-(php|c\+\+)/';
825
	
826
		if (($type == 'finfo' || $type == 'auto') 
827
		&& class_exists('finfo', false)) {
828
			$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
829
		} else {
830
			$tmpFileInfo = false;
831
		}
832
	
833
		if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
834
			$type = 'finfo';
835
			$this->finfo = finfo_open(FILEINFO_MIME);
836
		} elseif (($type == 'mime_content_type' || $type == 'auto') 
837
		&& function_exists('mime_content_type')
838
		&& 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...
839
			$type = 'mime_content_type';
840
		} else {
841
			$type = 'internal';
842
		}
843
		$this->mimeDetect = $type;
844
845
		// load mimes from external file for mimeDetect == 'internal'
846
		// based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
847
		// file must be in file directory or in parent one 
848
		if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
849
			self::$mimetypesLoaded = true;
850
			$this->mimeDetect = 'internal';
851
			$file = false;
852
			if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
853
				$file = $this->options['mimefile'];
854
			} elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
855
				$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
856
			} elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
857
				$file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
858
			}
859
860 View Code Duplication
			if ($file && file_exists($file)) {
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...
861
				$mimecf = file($file);
862
863
				foreach ($mimecf as $line_num => $line) {
864
					if (!preg_match('/^\s*#/', $line)) {
865
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
866
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
867
							if (!isset(self::$mimetypes[$mime[$i]])) {
868
								self::$mimetypes[$mime[$i]] = $mime[0];
869
							}
870
						}
871
					}
872
				}
873
			}
874
		}
875
876
		$this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias'];
877
878
		// This get's triggered if $this->root == '/' and alias is empty.
879
		// Maybe modify _basename instead?
880
		if ($this->rootName === '') $this->rootName = $this->separator;
881
882
		$root = $this->stat($this->root);
883
		
884
		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...
885
			return $this->setError('Root folder does not exists.');
886
		}
887
		if (!$root['read'] && !$root['write']) {
888
			return $this->setError('Root folder has not read and write permissions.');
889
		}
890
		
891
		// 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...
892
		
893
		if ($root['read']) {
894
			// check startPath - path to open by default instead of root
895
			if ($this->options['startPath']) {
896
				$start = $this->stat($this->options['startPath']);
897
				if (!empty($start)
898
				&& $start['mime'] == 'directory'
899
				&& $start['read']
900
				&& empty($start['hidden'])
901
				&& $this->inpathCE($this->options['startPath'], $this->root)) {
902
					$this->startPath = $this->options['startPath'];
903
					if (substr($this->startPath, -1, 1) == $this->options['separator']) {
904
						$this->startPath = substr($this->startPath, 0, -1);
905
					}
906
				}
907
			}
908
		} else {
909
			$this->options['URL']     = '';
910
			$this->options['tmbURL']  = '';
911
			$this->options['tmbPath'] = '';
912
			// read only volume
913
			array_unshift($this->attributes, array(
914
				'pattern' => '/.*/',
915
				'read'    => false
916
			));
917
		}
918
		$this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
919
		$this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
920
		$this->URL      = $this->options['URL'];
921
		if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
922
			$this->URL .= '/';
923
		}
924
925
		$this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
926
		if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
927
			$this->tmbURL .= '/';
928
		}
929
		
930
		$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...
931
			? $this->options['acceptedName']
932
			: '';
933
934
		$this->_checkArchivers();
935
		// manual control archive types to create
936 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...
937
			foreach ($this->archivers['create'] as $mime => $v) {
938
				if (!in_array($mime, $this->options['archiveMimes'])) {
939
					unset($this->archivers['create'][$mime]);
940
				}
941
			}
942
		}
943
		
944
		// manualy add archivers
945 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...
946
			foreach ($this->options['archivers']['create'] as $mime => $conf) {
947
				if (strpos($mime, 'application/') === 0 
948
				&& !empty($conf['cmd']) 
949
				&& isset($conf['argc']) 
950
				&& !empty($conf['ext'])
951
				&& !isset($this->archivers['create'][$mime])) {
952
					$this->archivers['create'][$mime] = $conf;
953
				}
954
			}
955
		}
956
		
957 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...
958
			foreach ($this->options['archivers']['extract'] as $mime => $conf) {
959
				if (strpos($mime, 'application/') === 0
960
				&& !empty($conf['cmd']) 
961
				&& isset($conf['argc']) 
962
				&& !empty($conf['ext'])
963
				&& !isset($this->archivers['extract'][$mime])) {
964
					$this->archivers['extract'][$mime] = $conf;
965
				}
966
			}
967
		}
968
969
		$this->configure();
970
		// 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...
971
		// 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...
972
		return $this->mounted = true;
973
	}
974
	
975
	/**
976
	 * Some "unmount" stuffs - may be required by virtual fs
977
	 *
978
	 * @return void
979
	 * @author Dmitry (dio) Levashov
980
	 **/
981
	public function umount() {
982
	}
983
	
984
	/**
985
	 * Return error message from last failed action
986
	 *
987
	 * @return array
988
	 * @author Dmitry (dio) Levashov
989
	 **/
990
	public function error() {
991
		return $this->error;
992
	}
993
	
994
	/**
995
	 * Return is uploadable that given file name 
996
	 *
997
	 * @param  string  $name  file name
998
	 * @param  bool    $allowUnknown
999
	 * @return bool
1000
	 * @author Naoki Sawada
1001
	 **/
1002
	public function isUploadableByName($name, $allowUnknown = true) {
1003
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1004
		return (($allowUnknown && $mimeByName === 'unknown') || $this->allowPutMime($mimeByName));
1005
	}
1006
	
1007
	/**
1008
	 * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes)
1009
	 * 
1010
	 * @return array
1011
	 * @author Naoki Sawada
1012
	 */
1013
	public function getMimeTable() {
1014
		return elFinderVolumeDriver::$mimetypes;
1015
	}
1016
	
1017
	/**
1018
	 * Set mimetypes allowed to display to client
1019
	 *
1020
	 * @param  array  $mimes
1021
	 * @return void
1022
	 * @author Dmitry (dio) Levashov
1023
	 **/
1024
	public function setMimesFilter($mimes) {
1025
		if (is_array($mimes)) {
1026
			$this->onlyMimes = $mimes;
1027
		}
1028
	}
1029
	
1030
	/**
1031
	 * Return root folder hash
1032
	 *
1033
	 * @return string
1034
	 * @author Dmitry (dio) Levashov
1035
	 **/
1036
	public function root() {
1037
		return $this->encode($this->root);
1038
	}
1039
	
1040
	/**
1041
	 * Return target path hash
1042
	 * 
1043
	 * @param  string $path
1044
	 * @param  string $name
1045
	 * @author Naoki Sawada
1046
	 */
1047
	public function getHash($path, $name = '') {
1048
		if ($name !== '') {
1049
			$path = $this->joinPathCE($path, $name);
1050
		}
1051
		return $this->encode($path);
1052
	}
1053
	
1054
	/**
1055
	 * Return root or startPath hash
1056
	 *
1057
	 * @return string
1058
	 * @author Dmitry (dio) Levashov
1059
	 **/
1060
	public function defaultPath() {
1061
		return $this->encode($this->startPath ? $this->startPath : $this->root);
1062
	}
1063
		
1064
	/**
1065
	 * Return volume options required by client:
1066
	 *
1067
	 * @return array
1068
	 * @author Dmitry (dio) Levashov
1069
	 **/
1070
	public function options($hash) {
1071
		$create = $createext = array();
1072
		if (isset($this->archivers['create']) && is_array($this->archivers['create'])) {
1073
			foreach($this->archivers['create'] as $m => $v) {
1074
				$create[] = $m;
1075
				$createext[$m] = $v['ext'];
1076
			}
1077
		}
1078
		return array(
1079
			'path'            => $this->path($hash),
1080
			'url'             => $this->URL,
1081
			'tmbUrl'          => $this->tmbURL,
1082
			'disabled'        => array_merge(array_unique($this->disabled)), // `array_merge` for type array of JSON
1083
			'separator'       => $this->separator,
1084
			'copyOverwrite'   => intval($this->options['copyOverwrite']),
1085
			'uploadOverwrite' => intval($this->options['uploadOverwrite']),
1086
			'uploadMaxSize'   => intval($this->uploadMaxSize),
1087
			'dispInlineRegex' => $this->options['dispInlineRegex'],
1088
			'archivers'       => array(
1089
				'create'    => $create,
1090
				'extract'   => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(),
1091
				'createext' => $createext
1092
			),
1093
			'uiCmdMap'        => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array()
1094
		);
1095
	}
1096
	
1097
	/**
1098
	 * Get option value of this volume
1099
	 * 
1100
	 * @param string $name  target option name
1101
	 * @return NULL|mixed   target option value
1102
	 * @author Naoki Sawada
1103
	 */
1104
	public function getOption($name) {
1105
		return isset($this->options[$name])? $this->options[$name] : null;
1106
	}
1107
	
1108
	/**
1109
	 * Get plugin values of this options
1110
	 * 
1111
	 * @param string $name  Plugin name
1112
	 * @return NULL|array   Plugin values
1113
	 * @author Naoki Sawada
1114
	 */
1115
	public function getOptionsPlugin($name = '') {
1116
		if ($name) {
1117
			return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array();
1118
		} else {
1119
			return $this->options['plugin'];
1120
		}
1121
	}
1122
	
1123
	/**
1124
	 * Return true if command disabled in options
1125
	 *
1126
	 * @param  string  $cmd  command name
1127
	 * @return bool
1128
	 * @author Dmitry (dio) Levashov
1129
	 **/
1130
	public function commandDisabled($cmd) {
1131
		return in_array($cmd, $this->disabled);
1132
	}
1133
	
1134
	/**
1135
	 * Return true if mime is required mimes list
1136
	 *
1137
	 * @param  string     $mime   mime type to check
1138
	 * @param  array      $mimes  allowed mime types list or not set to use client mimes list
1139
	 * @param  bool|null  $empty  what to return on empty list
1140
	 * @return bool|null
1141
	 * @author Dmitry (dio) Levashov
1142
	 * @author Troex Nevelin
1143
	 **/
1144
	public function mimeAccepted($mime, $mimes = null, $empty = true) {
1145
		$mimes = is_array($mimes) ? $mimes : $this->onlyMimes;
1146
		if (empty($mimes)) {
1147
			return $empty;
1148
		}
1149
		return $mime == 'directory'
1150
			|| in_array('all', $mimes)
1151
			|| in_array('All', $mimes)
1152
			|| in_array($mime, $mimes)
1153
			|| in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
1154
	}
1155
	
1156
	/**
1157
	 * Return true if voume is readable.
1158
	 *
1159
	 * @return bool
1160
	 * @author Dmitry (dio) Levashov
1161
	 **/
1162
	public function isReadable() {
1163
		$stat = $this->stat($this->root);
1164
		return $stat['read'];
1165
	}
1166
	
1167
	/**
1168
	 * Return true if copy from this volume allowed
1169
	 *
1170
	 * @return bool
1171
	 * @author Dmitry (dio) Levashov
1172
	 **/
1173
	public function copyFromAllowed() {
1174
		return !!$this->options['copyFrom'];
1175
	}
1176
	
1177
	/**
1178
	 * Return file path related to root with convert encoging
1179
	 *
1180
	 * @param  string   $hash  file hash
1181
	 * @return string
1182
	 * @author Dmitry (dio) Levashov
1183
	 **/
1184
	public function path($hash) {
1185
		return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash))));
1186
	}
1187
	
1188
	/**
1189
	 * Return file real path if file exists
1190
	 *
1191
	 * @param  string  $hash  file hash
1192
	 * @return string
1193
	 * @author Dmitry (dio) Levashov
1194
	 **/
1195
	public function realpath($hash) {
1196
		$path = $this->decode($hash);
1197
		return $this->stat($path) ? $path : false;
1198
	}
1199
	
1200
	/**
1201
	 * Return list of moved/overwrited files
1202
	 *
1203
	 * @return array
1204
	 * @author Dmitry (dio) Levashov
1205
	 **/
1206
	public function removed() {
1207
		return $this->removed;
1208
	}
1209
	
1210
	/**
1211
	 * Clean removed files list
1212
	 *
1213
	 * @return void
1214
	 * @author Dmitry (dio) Levashov
1215
	 **/
1216
	public function resetRemoved() {
1217
		$this->removed = array();
1218
	}
1219
	
1220
	/**
1221
	 * Return file/dir hash or first founded child hash with required attr == $val
1222
	 *
1223
	 * @param  string   $hash  file hash
1224
	 * @param  string   $attr  attribute name
1225
	 * @param  bool     $val   attribute value
1226
	 * @return string|false
1227
	 * @author Dmitry (dio) Levashov
1228
	 **/
1229
	public function closest($hash, $attr, $val) {
1230
		return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1231
	}
1232
	
1233
	/**
1234
	 * Return file info or false on error
1235
	 *
1236
	 * @param  string   $hash      file hash
1237
	 * @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...
1238
	 * @return array|false
1239
	 * @author Dmitry (dio) Levashov
1240
	 **/
1241
	public function file($hash) {
1242
		$path = $this->decode($hash);
1243
		
1244
		$file = $this->stat($path);
1245
		
1246
		if ($hash === $this->root()) {
1247
			$file['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array();
1248
			$file['disabled'] = array_merge(array_unique($this->disabled)); // `array_merge` for type array of JSON
1249
		}
1250
		
1251
		return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1252
	}
1253
	
1254
	/**
1255
	 * Return folder info
1256
	 *
1257
	 * @param  string   $hash  folder hash
1258
	 * @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...
1259
	 * @return array|false
1260
	 * @author Dmitry (dio) Levashov
1261
	 **/
1262
	public function dir($hash, $resolveLink=false) {
1263
		if (($dir = $this->file($hash)) == false) {
1264
			return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1265
		}
1266
1267
		if ($resolveLink && !empty($dir['thash'])) {
1268
			$dir = $this->file($dir['thash']);
1269
		}
1270
		
1271
		return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) 
1272
			? $dir 
1273
			: $this->setError(elFinder::ERROR_NOT_DIR);
1274
	}
1275
	
1276
	/**
1277
	 * Return directory content or false on error
1278
	 *
1279
	 * @param  string   $hash   file hash
1280
	 * @return array|false
1281
	 * @author Dmitry (dio) Levashov
1282
	 **/
1283
	public function scandir($hash) {
1284
		if (($dir = $this->dir($hash)) == false) {
1285
			return false;
1286
		}
1287
		
1288
		return $dir['read']
1289
			? $this->getScandir($this->decode($hash))
1290
			: $this->setError(elFinder::ERROR_PERM_DENIED);
1291
	}
1292
1293
	/**
1294
	 * Return dir files names list
1295
	 * 
1296
	 * @param  string  $hash   file hash
1297
	 * @return array
1298
	 * @author Dmitry (dio) Levashov
1299
	 **/
1300
	public function ls($hash) {
1301 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...
1302
			return false;
1303
		}
1304
		
1305
		$list = array();
1306
		$path = $this->decode($hash);
1307
		
1308
		foreach ($this->getScandir($path) as $stat) {
1309
			if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) {
1310
				$list[] = $stat['name'];
1311
			}
1312
		}
1313
1314
		return $list;
1315
	}
1316
1317
	/**
1318
	 * Return subfolders for required folder or false on error
1319
	 *
1320
	 * @param  string   $hash  folder hash or empty string to get tree from root folder
1321
	 * @param  int      $deep  subdir deep
1322
	 * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1323
	 * @return array|false
1324
	 * @author Dmitry (dio) Levashov
1325
	 **/
1326
	public function tree($hash='', $deep=0, $exclude='') {
1327
		$path = $hash ? $this->decode($hash) : $this->root;
1328
		
1329 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...
1330
			return false;
1331
		}
1332
		
1333
		$dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1334
		array_unshift($dirs, $dir);
1335
		return $dirs;
1336
	}
1337
	
1338
	/**
1339
	 * Return part of dirs tree from required dir up to root dir
1340
	 *
1341
	 * @param  string    $hash   directory hash
1342
	 * @param  bool|null $lineal only lineal parents
1343
	 * @return array
1344
	 * @author Dmitry (dio) Levashov
1345
	 **/
1346
	public function parents($hash, $lineal = false) {
1347
		if (($current = $this->dir($hash)) == false) {
1348
			return false;
1349
		}
1350
1351
		$path = $this->decode($hash);
1352
		$tree = array();
1353
		
1354
		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...
1355
			$path = $this->dirnameCE($path);
1356
			$stat = $this->stat($path);
1357
			if (!empty($stat['hidden']) || !$stat['read']) {
1358
				return false;
1359
			}
1360
			
1361
			array_unshift($tree, $stat);
1362
			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...
1363
				foreach ($this->gettree($path, 0) as $dir) {
1364
					if (!in_array($dir, $tree)) {
1365
						$tree[] = $dir;
1366
					}
1367
				}
1368
			}
1369
		}
1370
1371
		return $tree ? $tree : array($current);
1372
	}
1373
	
1374
	/**
1375
	 * Create thumbnail for required file and return its name of false on failed
1376
	 *
1377
	 * @return string|false
1378
	 * @author Dmitry (dio) Levashov
1379
	 **/
1380
	public function tmb($hash) {
1381
		$path = $this->decode($hash);
1382
		$stat = $this->stat($path);
1383
		
1384
		if (isset($stat['tmb'])) {
1385
			return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1386
		}
1387
		return false;
1388
	}
1389
	
1390
	/**
1391
	 * Return file size / total directory size
1392
	 *
1393
	 * @param  string   file hash
1394
	 * @return int
1395
	 * @author Dmitry (dio) Levashov
1396
	 **/
1397
	public function size($hash) {
1398
		return $this->countSize($this->decode($hash));
1399
	}
1400
	
1401
	/**
1402
	 * Open file for reading and return file pointer
1403
	 *
1404
	 * @param  string   file hash
1405
	 * @return Resource
1406
	 * @author Dmitry (dio) Levashov
1407
	 **/
1408
	public function open($hash) {
1409 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...
1410
		|| $file['mime'] == 'directory') {
1411
			return false;
1412
		}
1413
		
1414
		return $this->fopenCE($this->decode($hash), 'rb');
1415
	}
1416
	
1417
	/**
1418
	 * Close file pointer
1419
	 *
1420
	 * @param  Resource  $fp   file pointer
1421
	 * @param  string    $hash file hash
1422
	 * @return void
1423
	 * @author Dmitry (dio) Levashov
1424
	 **/
1425
	public function close($fp, $hash) {
1426
		$this->fcloseCE($fp, $this->decode($hash));
1427
	}
1428
	
1429
	/**
1430
	 * Create directory and return dir info
1431
	 *
1432
	 * @param  string   $dsthash  destination directory hash
1433
	 * @param  string   $name directory name
1434
	 * @return array|false
1435
	 * @author Dmitry (dio) Levashov
1436
	 **/
1437
	public function mkdir($dsthash, $name) {
1438
		if ($this->commandDisabled('mkdir')) {
1439
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1440
		}
1441
		
1442
		if (!$this->nameAccepted($name)) {
1443
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1444
		}
1445
		
1446
		if (($dir = $this->dir($dsthash)) == false) {
1447
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash);
1448
		}
1449
		
1450
		$path = $this->decode($dsthash);
1451
		
1452
		if (!$dir['write'] || !$this->allowCreate($path, $name, true)) {
1453
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1454
		}
1455
		
1456
		$dst  = $this->joinPathCE($path, $name);
1457
		$stat = $this->stat($dst); 
1458
		if (!empty($stat)) { 
1459
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1460
		}
1461
		$this->clearcache();
1462
		return ($path = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1463
	}
1464
	
1465
	/**
1466
	 * Create empty file and return its info
1467
	 *
1468
	 * @param  string   $dst  destination directory
1469
	 * @param  string   $name file name
1470
	 * @return array|false
1471
	 * @author Dmitry (dio) Levashov
1472
	 **/
1473
	public function mkfile($dst, $name) {
1474
		if ($this->commandDisabled('mkfile')) {
1475
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1476
		}
1477
		
1478
		if (!$this->nameAccepted($name)) {
1479
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1480
		}
1481
		
1482 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...
1483
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1484
		}
1485
		
1486
		$path = $this->decode($dst);
1487
		
1488
		if (!$dir['write'] || !$this->allowCreate($path, $name, false)) {
1489
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1490
		}
1491
		
1492
		if ($this->stat($this->joinPathCE($path, $name))) {
1493
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1494
		}
1495
		
1496
		$this->clearcache();
1497
		return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1498
	}
1499
	
1500
	/**
1501
	 * Rename file and return file info
1502
	 *
1503
	 * @param  string  $hash  file hash
1504
	 * @param  string  $name  new file name
1505
	 * @return array|false
1506
	 * @author Dmitry (dio) Levashov
1507
	 **/
1508
	public function rename($hash, $name) {
1509
		if ($this->commandDisabled('rename')) {
1510
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1511
		}
1512
		
1513
		if (!$this->nameAccepted($name)) {
1514
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1515
		}
1516
		
1517
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1518 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...
1519
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1520
		}
1521
		
1522
		if (!($file = $this->file($hash))) {
1523
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1524
		}
1525
		
1526
		if ($name == $file['name']) {
1527
			return $file;
1528
		}
1529
		
1530
		if (!empty($file['locked'])) {
1531
			return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1532
		}
1533
		
1534
		$path = $this->decode($hash);
1535
		$dir  = $this->dirnameCE($path);
1536
		$stat = $this->stat($this->joinPathCE($dir, $name));
1537
		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...
1538
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1539
		}
1540
		
1541 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...
1542
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1543
		}
1544
1545
		$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...
1546
1547
1548 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...
1549
			$this->clearcache();
1550
			return $this->stat($path);
1551
		}
1552
		return false;
1553
	}
1554
	
1555
	/**
1556
	 * Create file copy with suffix "copy number" and return its info
1557
	 *
1558
	 * @param  string   $hash    file hash
1559
	 * @param  string   $suffix  suffix to add to file name
1560
	 * @return array|false
1561
	 * @author Dmitry (dio) Levashov
1562
	 **/
1563
	public function duplicate($hash, $suffix='copy') {
1564
		if ($this->commandDisabled('duplicate')) {
1565
			return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1566
		}
1567
		
1568
		if (($file = $this->file($hash)) == false) {
1569
			return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1570
		}
1571
1572
		$path = $this->decode($hash);
1573
		$dir  = $this->dirnameCE($path);
1574
		$name = $this->uniqueName($dir, $this->basenameCE($path), ' '.$suffix.' ');
1575
1576 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...
1577
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1578
		}
1579
1580
		return ($path = $this->copy($path, $dir, $name)) == false
1581
			? false
1582
			: $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copy($path, $dir, $name) on line 1580 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...
1583
	}
1584
	
1585
	/**
1586
	 * Save uploaded file. 
1587
	 * On success return array with new file stat and with removed file hash (if existed file was replaced)
1588
	 *
1589
	 * @param  Resource $fp      file pointer
1590
	 * @param  string   $dst     destination folder hash
1591
	 * @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...
1592
	 * @param  string   $tmpname file tmp name - required to detect mime type
1593
	 * @return array|false
1594
	 * @author Dmitry (dio) Levashov
1595
	 **/
1596
	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...
1597
		if ($this->commandDisabled('upload')) {
1598
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1599
		}
1600
		
1601 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...
1602
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1603
		}
1604
1605
		if (!$dir['write']) {
1606
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1607
		}
1608
		
1609
		if (!$this->nameAccepted($name)) {
1610
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1611
		}
1612
		
1613
		$mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name);
1614
		$mimeByName = '';
1615
		if ($this->mimeDetect !== 'internal') {
1616
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1617
			if ($mime == 'unknown') {
1618
				$mime = $mimeByName;
1619
			}
1620
		}
1621
1622 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...
1623
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1624
		}
1625
1626
		$tmpsize = sprintf('%u', filesize($tmpname));
1627
		if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) {
1628
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1629
		}
1630
1631
		$dstpath = $this->decode($dst);
1632
		$test    = $this->joinPathCE($dstpath, $name);
1633
		
1634
		$file = $this->stat($test);
1635
		$this->clearcache();
1636
		
1637
		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...
1638
			// check POST data `overwrite` for 3rd party uploader
1639
			$overwrite = isset($_POST['overwrite'])? (bool)$_POST['overwrite'] : $this->options['uploadOverwrite'];
1640
			if ($overwrite) {
1641 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...
1642
					return $this->setError(elFinder::ERROR_PERM_DENIED);
1643
				} elseif ($file['mime'] == 'directory') {
1644
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1645
				} 
1646
				$this->remove($test);
1647
			} else {
1648
				$name = $this->uniqueName($dstpath, $name, '-', false);
1649
			}
1650
		}
1651
		
1652
		$stat = array(
1653
			'mime'   => $mime, 
1654
			'width'  => 0, 
1655
			'height' => 0, 
1656
			'size'   => $tmpsize);
1657
		
1658
		// $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...
1659
		if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
1660
			$stat['width'] = $s[0];
1661
			$stat['height'] = $s[1];
1662
		}
1663
		// $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...
1664
		if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) {
1665
			return false;
1666
		}
1667
		
1668
		
1669
1670
		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 1664 can also be of type boolean; however, elFinderVolumeDriver::stat() does only seem to accept string, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1671
	}
1672
	
1673
	/**
1674
	 * Paste files
1675
	 *
1676
	 * @param  Object  $volume  source volume
1677
	 * @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...
1678
	 * @param  string  $dst     destination dir hash
1679
	 * @param  bool    $rmSrc   remove source after copy?
1680
	 * @return array|false
1681
	 * @author Dmitry (dio) Levashov
1682
	 **/
1683
	public function paste($volume, $src, $dst, $rmSrc = false) {
1684
		$err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
1685
		
1686
		if ($this->commandDisabled('paste')) {
1687
			return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
1688
		}
1689
1690
		if (($file = $volume->file($src, $rmSrc)) == false) {
1691
			return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
1692
		}
1693
1694
		$name = $file['name'];
1695
		$errpath = $volume->path($file['hash']);
1696
		
1697 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...
1698
			return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1699
		}
1700
		
1701
		if (!$dir['write'] || !$file['read']) {
1702
			return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1703
		}
1704
1705
		$destination = $this->decode($dst);
1706
1707
		if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
1708
			return $rmSrc
1709
				? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
1710
				: $this->setError($err, $errpath, !empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK);
1711
		}
1712
1713
		$test = $this->joinPathCE($destination, $name);
1714
		$stat = $this->stat($test);
1715
		$this->clearcache();
1716
		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...
1717
			if ($this->options['copyOverwrite']) {
1718
				// do not replace file with dir or dir with file
1719
				if (!$this->isSameType($file['mime'], $stat['mime'])) {
1720
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash']));
1721
				}
1722
				// existed file is not writable
1723
				if (!$stat['write']) {
1724
					return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1725
				}
1726
				// existed file locked or has locked child
1727
				if (($locked = $this->closestByAttr($test, 'locked', true))) {
1728
					$stat = $this->stat($locked);
1729
					return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
1730
				}
1731
				// target is entity file of alias
1732
				if ($volume == $this && ($test == @$file['target'] || $test == $this->decode($src))) {
1733
					return $this->setError(elFinder::ERROR_REPLACE, $errpath);
1734
				}
1735
				// remove existed file
1736
				if (!$this->remove($test)) {
1737
					return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash']));
1738
				}
1739
			} else {
1740
				$name = $this->uniqueName($destination, $name, ' ', false);
1741
			}
1742
		}
1743
		
1744
		// copy/move inside current volume
1745
		if ($volume == $this) {
1746
			$source = $this->decode($src);
1747
			// do not copy into itself
1748
			if ($this->inpathCE($destination, $source)) {
1749
				return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
1750
			}
1751
			$method = $rmSrc ? 'move' : 'copy';
1752
			return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false;
1753
		}
1754
		
1755
		// copy/move from another volume
1756
		if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
1757
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
1758
		}
1759
		
1760
		if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
1761
			return false;
1762
		}
1763
		
1764
		if ($rmSrc) {
1765
			if (!$volume->rm($src)) {
1766
				return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
1767
			}
1768
		}
1769
		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 1760 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...
1770
	}
1771
	
1772
	/**
1773
	 * Return file contents
1774
	 *
1775
	 * @param  string  $hash  file hash
1776
	 * @return string|false
1777
	 * @author Dmitry (dio) Levashov
1778
	 **/
1779
	public function getContents($hash) {
1780
		$file = $this->file($hash);
1781
		
1782
		if (!$file) {
1783
			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...
1784
		}
1785
		
1786
		if ($file['mime'] == 'directory') {
1787
			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...
1788
		}
1789
		
1790
		if (!$file['read']) {
1791
			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...
1792
		}
1793
		
1794
		return $this->convEncOut($this->_getContents($this->convEncIn($this->decode($hash))));
1795
	}
1796
	
1797
	/**
1798
	 * Put content in text file and return file info.
1799
	 *
1800
	 * @param  string  $hash     file hash
1801
	 * @param  string  $content  new file content
1802
	 * @return array
1803
	 * @author Dmitry (dio) Levashov
1804
	 **/
1805
	public function putContents($hash, $content) {
1806
		if ($this->commandDisabled('edit')) {
1807
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1808
		}
1809
		
1810
		$path = $this->decode($hash);
1811
		
1812
		if (!($file = $this->file($hash))) {
1813
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1814
		}
1815
		
1816
		if (!$file['write']) {
1817
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1818
		}
1819
		
1820
		// check MIME
1821
		$name = $this->basenameCE($path);
1822
		$mime = '';
1823
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1824
		if ($this->mimeDetect !== 'internal') {
1825
			if ($tp = tmpfile()) {
1826
				fwrite($tp, $content);
1827
				$info = stream_get_meta_data($tp);
1828
				$filepath = $info['uri'];
1829
				$mime = $this->mimetype($filepath, $name);
1830
				fclose($tp);
1831
			}
1832
		}
1833 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...
1834
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1835
		}
1836
		
1837
		$this->clearcache();
1838
		return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false;
1839
	}
1840
	
1841
	/**
1842
	 * Extract files from archive
1843
	 *
1844
	 * @param  string  $hash  archive hash
1845
	 * @return array|bool
1846
	 * @author Dmitry (dio) Levashov, 
1847
	 * @author Alexey Sukhotin
1848
	 **/
1849
	public function extract($hash, $makedir = null) {
1850
		if ($this->commandDisabled('extract')) {
1851
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1852
		}
1853
		
1854
		if (($file = $this->file($hash)) == false) {
1855
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1856
		}
1857
		
1858
		$archiver = isset($this->archivers['extract'][$file['mime']])
1859
			? $this->archivers['extract'][$file['mime']]
1860
			: false;
1861
			
1862
		if (!$archiver) {
1863
			return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
1864
		}
1865
		
1866
		$path   = $this->decode($hash);
1867
		$parent = $this->stat($this->dirnameCE($path));
1868
1869
		if (!$file['read'] || !$parent['write']) {
1870
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1871
		}
1872
		$this->clearcache();
1873
		$this->extractToNewdir = is_null($makedir)? 'auto' : (bool)$makedir;
0 ignored issues
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...
1874
		
1875
		if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) {
1876
			if (is_array($path)) {
1877
				foreach ($path as $_k => $_p) {
1878
					$path[$_k] = $this->stat($_p);
1879
				}
1880
			} else {
1881
				$path = $this->stat($path);
1882
			}
1883
			return $path;
1884
		} else {
1885
			return false;
1886
		}
1887
	}
1888
1889
	/**
1890
	 * Add files to archive
1891
	 *
1892
	 * @return void
1893
	 **/
1894
	public function archive($hashes, $mime, $name = '') {
1895
		if ($this->commandDisabled('archive')) {
1896
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1897
		}
1898
1899
		$archiver = isset($this->archivers['create'][$mime])
1900
			? $this->archivers['create'][$mime]
1901
			: false;
1902
			
1903
		if (!$archiver) {
1904
			return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
1905
		}
1906
		
1907
		$files = array();
1908
		
1909
		foreach ($hashes as $hash) {
1910
			if (($file = $this->file($hash)) == false) {
1911
				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...
1912
			}
1913
			if (!$file['read']) {
1914
				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...
1915
			}
1916
			$path = $this->decode($hash);
1917
			if (!isset($dir)) {
1918
				$dir = $this->dirnameCE($path);
1919
				$stat = $this->stat($dir);
1920
				if (!$stat['write']) {
1921
					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...
1922
				}
1923
			}
1924
			
1925
			$files[] = $this->basenameCE($path);
1926
		}
1927
		
1928
		if ($name === '') {
1929
			$name = count($files) == 1 ? $files[0] : 'Archive';
1930
		} else {
1931
			$name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name));
1932
		}
1933
		$name .='.' . $archiver['ext'];
1934
		$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...
1935
		$this->clearcache();
1936
		return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false;
1937
	}
1938
	
1939
	/**
1940
	 * Resize image
1941
	 *
1942
	 * @param  string   $hash    image file
1943
	 * @param  int      $width   new width
1944
	 * @param  int      $height  new height
1945
	 * @param  int      $x       X start poistion for crop
1946
	 * @param  int      $y       Y start poistion for crop
1947
	 * @param  string   $mode    action how to mainpulate image
1948
	 * @return array|false
1949
	 * @author Dmitry (dio) Levashov
1950
	 * @author Alexey Sukhotin
1951
	 * @author nao-pon
1952
	 * @author Troex Nevelin
1953
	 **/
1954
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) {
1955
		if ($this->commandDisabled('resize')) {
1956
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1957
		}
1958
		
1959
		if (($file = $this->file($hash)) == false) {
1960
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1961
		}
1962
		
1963
		if (!$file['write'] || !$file['read']) {
1964
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1965
		}
1966
		
1967
		$path = $this->decode($hash);
1968
		
1969
		$work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path);
1970
1971
		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...
1972
			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...
1973
				@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...
1974
			}
1975
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1976
		}
1977
1978
		if ($this->imgLib != 'imagick') {
1979
			if (elFinder::isAnimationGif($work_path)) {
1980
				return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
1981
			}
1982
		}
1983
1984
		switch($mode) {
1985
			
1986
			case 'propresize':
1987
				$result = $this->imgResize($work_path, $width, $height, true, true);
1988
				break;
1989
1990
			case 'crop':
1991
				$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...
1992
				break;
1993
1994
			case 'fitsquare':
1995
				$result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']));
1996
				break;
1997
1998
			case 'rotate':
1999
				$result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['tmbBgColor']));
2000
				break;
2001
2002
			default:
2003
				$result = $this->imgResize($work_path, $width, $height, false, true);
2004
				break;
2005
		}
2006
		
2007
		$ret = false;
2008
		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...
2009
			$stat = $this->stat($path);
2010
			clearstatcache();
2011
			$fstat = stat($work_path);
2012
			$stat['size'] = $fstat['size'];
2013
			$stat['ts'] = $fstat['mtime'];
2014
			if ($imgsize = @getimagesize($work_path)) {
2015
				$stat['width'] = $imgsize[0];
2016
				$stat['height'] = $imgsize[1];
2017
				$stat['mime'] = $imgsize['mime'];
2018
			}
2019
			if ($path !== $work_path) {
2020
				if ($fp = @fopen($work_path, 'rb')) {
2021
					$ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat);
2022
					@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...
2023
				}
2024
			} else {
2025
				$ret = true;
2026
			}
2027
			if ($ret) {
2028
				$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...
2029
				$this->clearcache();
2030
				$ret = $this->stat($path);
2031
				$ret['width'] = $stat['width'];
2032
				$ret['height'] = $stat['height'];
2033
			}
2034
		}
2035
		if ($path !== $work_path) {
2036
			is_file($work_path) && @unlink($work_path);
2037
		}
2038
		
2039
		return $ret;
2040
	}
2041
	
2042
	/**
2043
	 * Remove file/dir
2044
	 *
2045
	 * @param  string  $hash  file hash
2046
	 * @return bool
2047
	 * @author Dmitry (dio) Levashov
2048
	 **/
2049
	public function rm($hash) {
2050
		return $this->commandDisabled('rm')
2051
			? $this->setError(elFinder::ERROR_PERM_DENIED)
2052
			: $this->remove($this->decode($hash));
2053
	}
2054
	
2055
	/**
2056
	 * Search files
2057
	 *
2058
	 * @param  string  $q  search string
2059
	 * @param  array   $mimes
2060
	 * @return array
2061
	 * @author Dmitry (dio) Levashov
2062
	 **/
2063
	public function search($q, $mimes, $hash = null) {
2064
		$dir = null;
2065
		if ($hash) {
2066
			$dir = $this->decode($hash);
2067
			$stat = $this->stat($dir);
2068
			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...
2069
				$q = '';
2070
			}
2071
		}
2072
		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...
2073
			$mimes = array_intersect($mimes, $this->onlyMimes);
2074
			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...
2075
				$q = '';
2076
			}
2077
		}
2078
		return ($q === '' || $this->commandDisabled('search'))
2079
			? array()
2080
			: $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes);
2081
	}
2082
	
2083
	/**
2084
	 * Return image dimensions
2085
	 *
2086
	 * @param  string  $hash  file hash
2087
	 * @return array
2088
	 * @author Dmitry (dio) Levashov
2089
	 **/
2090
	public function dimensions($hash) {
2091
		if (($file = $this->file($hash)) == false) {
2092
			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...
2093
		}
2094
		
2095
		return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime']));
2096
	}
2097
	
2098
	/**
2099
	 * Return content URL (for netmout volume driver)
2100
	 * If file.url == 1 requests from JavaScript client with XHR
2101
	 * 
2102
	 * @param string $hash  file hash
2103
	 * @param array $options  options array
2104
	 * @return boolean|string
2105
	 * @author Naoki Sawada
2106
	 */
2107
	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...
2108 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...
2109
			return false;
2110
		}
2111
		return $file['url'];
2112
	}
2113
	
2114
	/**
2115
	 * Return temp path
2116
	 * 
2117
	 * @return string
2118
	 * @author Naoki Sawada
2119
	 */
2120
	public function getTempPath() {
2121
		if (@ $this->tmpPath) {
2122
			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...
2123
		} else if (@ $this->tmp) {
2124
			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...
2125
		} else if (function_exists('sys_get_temp_dir')) {
2126
			return sys_get_temp_dir();
2127
		} else if (@ $this->tmbPath) {
2128
			return $this->tmbPath;
2129
		} else {
2130
			return null;
2131
		}
2132
	}
2133
	
2134
	/**
2135
	 * (Make &) Get upload taget dirctory hash
2136
	 * 
2137
	 * @param string $baseTargetHash
2138
	 * @param string $path
2139
	 * @param array  $result
2140
	 * @return boolean|string
2141
	 * @author Naoki Sawada
2142
	 */
2143
	public function getUploadTaget($baseTargetHash, $path, & $result) {
2144
		$base = $this->decode($baseTargetHash);
2145
		$targetHash = $baseTargetHash;
2146
		$path = ltrim($path, $this->separator);
2147
		$dirs = explode($this->separator, $path);
2148
		array_pop($dirs);
2149
		foreach($dirs as $dir) {
2150
			$targetPath = $this->joinPathCE($base, $dir);
2151
			if (! $_realpath = $this->realpath($this->encode($targetPath))) {
2152
				if ($stat = $this->mkdir($targetHash, $dir)) {
2153
					$result['added'][] = $stat;
2154
					$targetHash = $stat['hash'];
2155
					$base = $this->decode($targetHash);
2156
				} else {
2157
					return false;
2158
				}
2159
			} else {
2160
				$targetHash = $this->encode($_realpath);
2161
				if ($this->dir($targetHash)) {
2162
					$base = $this->decode($targetHash);
2163
				} else {
2164
					return false;
2165
				}
2166
			}
2167
		}
2168
		return $targetHash;
2169
	}
2170
	
2171
	/**
2172
	 * Return this uploadMaxSize value
2173
	 * 
2174
	 * @return integer
2175
	 * @author Naoki Sawada
2176
	 */
2177
	public function getUploadMaxSize() {
2178
		return $this->uploadMaxSize;
2179
	}
2180
	
2181
	/**
2182
	 * Save error message
2183
	 *
2184
	 * @param  array  error 
2185
	 * @return false
2186
	 * @author Dmitry(dio) Levashov
2187
	 **/
2188
	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...
2189
		
2190
		$this->error = array();
2191
		
2192
		foreach (func_get_args() as $err) {
2193
			if (is_array($err)) {
2194
				$this->error = array_merge($this->error, $err);
2195
			} else {
2196
				$this->error[] = $err;
2197
			}
2198
		}
2199
		
2200
		// $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...
2201
		return false;
2202
	}
2203
	
2204
	/*********************************************************************/
2205
	/*                               FS API                              */
2206
	/*********************************************************************/
2207
	
2208
	/***************** server encoding support *******************/
2209
	
2210
	/**
2211
	 * Return parent directory path (with convert encording)
2212
	 *
2213
	 * @param  string  $path  file path
2214
	 * @return string
2215
	 * @author Naoki Sawada
2216
	 **/
2217
	protected function dirnameCE($path) {
2218
		return (!$this->encoding)? $this->_dirname($path) :	$this->convEncOut($this->_dirname($this->convEncIn($path)));
2219
	}
2220
	
2221
	/**
2222
	 * Return file name (with convert encording)
2223
	 *
2224
	 * @param  string  $path  file path
2225
	 * @return string
2226
	 * @author Naoki Sawada
2227
	 **/
2228
	protected function basenameCE($path) {
2229
		return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path)));
2230
	}
2231
	
2232
	/**
2233
	 * Join dir name and file name and return full path. (with convert encording)
2234
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
2235
	 *
2236
	 * @param  string  $dir   dir path
2237
	 * @param  string  $name  file name
2238
	 * @return string
2239
	 * @author Naoki Sawada
2240
	 **/
2241
	protected function joinPathCE($dir, $name) {
2242
		return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name)));
2243
	}
2244
	
2245
	/**
2246
	 * Return normalized path (with convert encording)
2247
	 *
2248
	 * @param  string  $path  file path
2249
	 * @return string
2250
	 * @author Naoki Sawada
2251
	 **/
2252
	protected function normpathCE($path) {
2253
		return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path)));
2254
	}
2255
	
2256
	/**
2257
	 * Return file path related to root dir (with convert encording)
2258
	 *
2259
	 * @param  string  $path  file path
2260
	 * @return string
2261
	 * @author Naoki Sawada
2262
	 **/
2263
	protected function relpathCE($path) {
2264
		return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path)));
2265
	}
2266
	
2267
	/**
2268
	 * Convert path related to root dir into real path (with convert encording)
2269
	 *
2270
	 * @param  string  $path  rel file path
2271
	 * @return string
2272
	 * @author Naoki Sawada
2273
	 **/
2274
	protected function abspathCE($path) {
2275
		return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path)));
2276
	}
2277
	
2278
	/**
2279
	 * Return true if $path is children of $parent (with convert encording)
2280
	 *
2281
	 * @param  string  $path    path to check
2282
	 * @param  string  $parent  parent path
2283
	 * @return bool
2284
	 * @author Naoki Sawada
2285
	 **/
2286
	protected function inpathCE($path, $parent) {
2287
		return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent)));
2288
	}
2289
	
2290
	/**
2291
	 * Open file and return file pointer (with convert encording)
2292
	 *
2293
	 * @param  string  $path  file path
2294
	 * @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...
2295
	 * @return resource|false
2296
	 * @author Naoki Sawada
2297
	 **/
2298
	protected function fopenCE($path, $mode='rb') {
2299
		return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode));
2300
	}
2301
	
2302
	/**
2303
	 * Close opened file (with convert encording)
2304
	 * 
2305
	 * @param  resource  $fp    file pointer
2306
	 * @param  string    $path  file path
2307
	 * @return bool
2308
	 * @author Naoki Sawada
2309
	 **/
2310
	protected function fcloseCE($fp, $path='') {
2311
		return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path)));
2312
	}
2313
	
2314
	/**
2315
	 * Create new file and write into it from file pointer. (with convert encording)
2316
	 * Return new file path or false on error.
2317
	 *
2318
	 * @param  resource  $fp   file pointer
2319
	 * @param  string    $dir  target dir path
2320
	 * @param  string    $name file name
2321
	 * @param  array     $stat file stat (required by some virtual fs)
2322
	 * @return bool|string
2323
	 * @author Naoki Sawada
2324
	 **/
2325
	protected function saveCE($fp, $dir, $name, $stat) {
2326
		return (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat)));
2327
	}
2328
	
2329
	/**
2330
	 * Return true if path is dir and has at least one childs directory (with convert encording)
2331
	 *
2332
	 * @param  string  $path  dir path
2333
	 * @return bool
2334
	 * @author Naoki Sawada
2335
	 **/
2336
	protected function subdirsCE($path) {
2337
		if (!isset($this->subdirsCache[$path])) {
2338
			$this->subdirsCache[$path] = (!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)));
2339
		}
2340
		return $this->subdirsCache[$path];
2341
	}
2342
	
2343
	/**
2344
	 * Return files list in directory (with convert encording)
2345
	 *
2346
	 * @param  string  $path  dir path
2347
	 * @return array
2348
	 * @author Naoki Sawada
2349
	 **/
2350
	protected function scandirCE($path) {
2351
		return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path)));
2352
	}
2353
	
2354
	/**
2355
	 * Create symlink (with convert encording)
2356
	 *
2357
	 * @param  string  $source     file to link to
2358
	 * @param  string  $targetDir  folder to create link in
2359
	 * @param  string  $name       symlink name
2360
	 * @return bool
2361
	 * @author Naoki Sawada
2362
	 **/
2363
	protected function symlinkCE($source, $targetDir, $name) {
2364
		return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name)));
2365
	}
2366
	
2367
	/***************** paths *******************/
2368
	
2369
	/**
2370
	 * Encode path into hash
2371
	 *
2372
	 * @param  string  file path
2373
	 * @return string
2374
	 * @author Dmitry (dio) Levashov
2375
	 * @author Troex Nevelin
2376
	 **/
2377
	protected function encode($path) {
2378
		if ($path !== '') {
2379
2380
			// cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
2381
			$p = $this->relpathCE($path);
2382
			// if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
2383
			if ($p === '')	{
2384
				$p = DIRECTORY_SEPARATOR;
2385
			}
2386
2387
			// TODO crypt path and return hash
2388
			$hash = $this->crypt($p);
2389
			// hash is used as id in HTML that means it must contain vaild chars
2390
			// make base64 html safe and append prefix in begining
2391
			$hash = strtr(base64_encode($hash), '+/=', '-_.');
2392
			// remove dots '.' at the end, before it was '=' in base64
2393
			$hash = rtrim($hash, '.'); 
2394
			// append volume id to make hash unique
2395
			return $this->id.$hash;
2396
		}
2397
	}
2398
	
2399
	/**
2400
	 * Decode path from hash
2401
	 *
2402
	 * @param  string  file hash
2403
	 * @return string
2404
	 * @author Dmitry (dio) Levashov
2405
	 * @author Troex Nevelin
2406
	 **/
2407
	protected function decode($hash) {
2408
		if (strpos($hash, $this->id) === 0) {
2409
			// cut volume id after it was prepended in encode
2410
			$h = substr($hash, strlen($this->id));
2411
			// replace HTML safe base64 to normal
2412
			$h = base64_decode(strtr($h, '-_.', '+/='));
2413
			// TODO uncrypt hash and return path
2414
			$path = $this->uncrypt($h); 
2415
			// append ROOT to path after it was cut in encode
2416
			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...
2417
		}
2418
	}
2419
	
2420
	/**
2421
	 * Return crypted path 
2422
	 * Not implemented
2423
	 *
2424
	 * @param  string  path
2425
	 * @return mixed
2426
	 * @author Dmitry (dio) Levashov
2427
	 **/
2428
	protected function crypt($path) {
2429
		return $path;
2430
	}
2431
	
2432
	/**
2433
	 * Return uncrypted path 
2434
	 * Not implemented
2435
	 *
2436
	 * @param  mixed  hash
2437
	 * @return mixed
2438
	 * @author Dmitry (dio) Levashov
2439
	 **/
2440
	protected function uncrypt($hash) {
2441
		return $hash;
2442
	}
2443
	
2444
	/**
2445
	 * Validate file name based on $this->options['acceptedName'] regexp or function
2446
	 *
2447
	 * @param  string  $name  file name
2448
	 * @return bool
2449
	 * @author Dmitry (dio) Levashov
2450
	 **/
2451
	protected function nameAccepted($name) {
2452
		if (!json_encode($name)) {
2453
			return false;
2454
		}
2455
		if ($this->nameValidator) {
2456
			if (is_callable($this->nameValidator)) {
2457
				$res = call_user_func($this->nameValidator, $name);
2458
				return $res;
2459
			}
2460
			if (preg_match($this->nameValidator, '') !== false) {
2461
				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...
2462
			}
2463
		}
2464
		return true;
2465
	}
2466
	
2467
	/**
2468
	 * Return new unique name based on file name and suffix
2469
	 *
2470
	 * @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...
2471
	 * @param  string  $suffix  suffix append to name
2472
	 * @return string
2473
	 * @author Dmitry (dio) Levashov
2474
	 **/
2475
	public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) {
2476
		$ext  = '';
2477
2478
		if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
2479
			$ext  = '.'.$m[1];
2480
			$name = substr($name, 0,  strlen($name)-strlen($m[0]));
2481
		} 
2482
		
2483
		if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) {
2484
			$i    = (int)$m[2];
2485
			$name = substr($name, 0, strlen($name)-strlen($m[2]));
2486
		} else {
2487
			$i     = $start;
2488
			$name .= $suffix;
2489
		}
2490
		$max = $i+100000;
2491
2492
		while ($i <= $max) {
2493
			$n = $name.($i > 0 ? $i : '').$ext;
2494
2495
			if (!$this->stat($this->joinPathCE($dir, $n))) {
2496
				$this->clearcache();
2497
				return $n;
2498
			}
2499
			$i++;
2500
		}
2501
		return $name.md5($dir).$ext;
2502
	}
2503
	
2504
	/**
2505
	 * Converts character encoding from UTF-8 to server's one
2506
	 * 
2507
	 * @param  mixed  $var           target string or array var
2508
	 * @param  bool   $restoreLocale do retore global locale, default is false
2509
	 * @param  string $unknown       replaces character for unknown
2510
	 * @return mixed
2511
	 * @author Naoki Sawada
2512
	 */
2513
	public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') {
2514
		return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown);
2515
	}
2516
	
2517
	/**
2518
	 * Converts character encoding from server's one to UTF-8
2519
	 * 
2520
	 * @param  mixed  $var           target string or array var
2521
	 * @param  bool   $restoreLocale do retore global locale, default is true
2522
	 * @param  string $unknown       replaces character for unknown
2523
	 * @return mixed
2524
	 * @author Naoki Sawada
2525
	 */
2526
	public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') {
2527
		return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown);
2528
	}
2529
	
2530
	/**
2531
	 * Converts character encoding (base function)
2532
	 * 
2533
	 * @param  mixed  $var     target string or array var
2534
	 * @param  string $from    from character encoding
2535
	 * @param  string $to      to character encoding
2536
	 * @param  string $locale  local locale
2537
	 * @param  string $unknown replaces character for unknown
2538
	 * @return mixed
2539
	 */
2540
	protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') {
2541
		if (strtoupper($from) !== strtoupper($to)) {
2542
			if ($locale) {
2543
				@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...
2544
			}
2545
			if (is_array($var)) {
2546
				$_ret = array();
2547
				foreach($var as $_k => $_v) {
2548
					$_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_');
2549
				}
2550
				$var = $_ret;
2551
			} else {
2552
				$_var = false;
2553
				if (is_string($var)) {
2554
					$_var = $var;
2555
					if (false !== ($_var = @iconv($from, $to.'//TRANSLIT', $_var))) {
2556
						$_var = str_replace('?', $unknown, $_var);
2557
					}
2558
				}
2559
				if  ($_var !== false) {
2560
					$var = $_var;
2561
				}
2562
			}
2563
			if ($restoreLocale) {
2564
				setlocale(LC_ALL, elFinder::$locale);
2565
			}
2566
		}
2567
		return $var;
2568
	}
2569
	
2570
	/*********************** util mainly for inheritance class *********************/
2571
	
2572
	/**
2573
	 * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called.
2574
	 * When needing the unique file to a path, give $path to parameter.
2575
	 * 
2576
	 * @param  string       $path for get unique file to a path
2577
	 * @return string|false
2578
	 * @author Naoki Sawada
2579
	 */
2580
	protected function getTempFile($path = '') {
2581
		static $cache = array();
2582
		static $rmfunc;
2583
		
2584
		$key = '';
2585
		if ($path !== '') {
2586
			$key = $this->id . '#' . $path;
2587
			if (isset($cache[$key])) {
2588
				return $cache[$key];
2589
			}
2590
		}
2591
		
2592
		if ($tmpdir = $this->getTempPath()) {
2593
			if (!$rmfunc) {
2594
				$rmfunc = create_function('$f', 'is_file($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...
2595
			}
2596
			$name = tempnam($tmpdir, 'ELF');
2597
			if ($key) {
2598
				$cache[$key] = $name;
2599
			}
2600
			register_shutdown_function($rmfunc, $name);
2601
			return $name;
2602
		}
2603
		
2604
		return false;
2605
	}
2606
	
2607
	/**
2608
	 * File path of local server side work file path
2609
	 * 
2610
	 * @param  string $path path need convert encoding to server encoding
2611
	 * @return string
2612
	 * @author Naoki Sawada
2613
	 */
2614
	protected function getWorkFile($path) {
2615
		if ($work = $this->getTempFile()) {
2616
			if ($wfp = fopen($work, 'wb')) {
2617
				if ($fp = $this->_fopen($path)) {
2618
					while(!feof($fp)) {
2619
						fwrite($wfp, fread($fp, 8192));
2620
					}
2621
					$this->_fclose($fp, $path);
2622
					fclose($wfp);
2623
					return $work;
2624
				}
2625
			}
2626
		}
2627
		return false;
2628
	}
2629
	
2630
	/**
2631
	 * Get image size array with `dimensions`
2632
	 *
2633
	 * @param string $path path need convert encoding to server encoding
2634
	 * @param string $mime file mime type
2635
	 * @return array|false
2636
	 */
2637
	public function getImageSize($path, $mime = '') {
2638
		$size = false;
2639
		if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') {
2640
			if ($work = $this->getWorkFile($path)) {
2641
				if ($size = @getimagesize($work)) {
2642
					$size['dimensions'] = $size[0].'x'.$size[1];
2643
				}
2644
			}
2645
			is_file($work) && @unlink($work);
2646
		}
2647
		return $size;
2648
	}
2649
	
2650
	/**
2651
	 * Delete dirctory trees
2652
	 *
2653
	 * @param string $localpath path need convert encoding to server encoding
2654
	 * @return boolean
2655
	 * @author Naoki Sawada
2656
	 */
2657
	protected function delTree($localpath) {
2658
		foreach ($this->_scandir($localpath) as $p) {
2659
			@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...
2660
			$stat = $this->stat($this->convEncOut($p));
2661
			$this->convEncIn();
2662
			($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p);
2663
		}
2664
		return $this->_rmdir($localpath);
2665
	}
2666
	
2667
	/*********************** file stat *********************/
2668
	
2669
	/**
2670
	 * Check file attribute
2671
	 *
2672
	 * @param  string  $path  file path
2673
	 * @param  string  $name  attribute name (read|write|locked|hidden)
2674
	 * @param  bool    $val   attribute value returned by file system
2675
	 * @param  bool    $isDir path is directory (true: directory, false: file)
2676
	 * @return bool
2677
	 * @author Dmitry (dio) Levashov
2678
	 **/
2679
	protected function attr($path, $name, $val=null, $isDir=null) {
2680
		if (!isset($this->defaults[$name])) {
2681
			return false;
2682
		}
2683
		
2684
		
2685
		$perm = null;
2686
		
2687 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...
2688
			$perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir);
2689
2690
			if ($perm !== null) {
2691
				return !!$perm;
2692
			}
2693
		}
2694
		
2695
		if ($this->separator != '/') {
2696
			$path = str_replace($this->separator, '/', $this->relpathCE($path));
2697
		} else {
2698
			$path = $this->relpathCE($path);
2699
		}
2700
2701
		$path = '/'.$path;
2702
2703 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...
2704
			$attrs = $this->attributes[$i];
2705
			
2706
			if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
2707
				$perm = $attrs[$name];
2708
			} 
2709
		}
2710
		
2711
		return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
2712
	}
2713
	
2714
	/**
2715
	 * Return true if file with given name can be created in given folder.
2716
	 *
2717
	 * @param string $dir  parent dir path
2718
	 * @param string $name new file name
2719
	 * @return bool
2720
	 * @author Dmitry (dio) Levashov
2721
	 **/
2722
	protected function allowCreate($dir, $name, $isDir = null) {
2723
		$path = $this->joinPathCE($dir, $name);
2724
		$perm = null;
2725
		
2726 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...
2727
			$perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this, $isDir);			
2728
			if ($perm !== null) {
2729
				return !!$perm;
2730
			}
2731
		}
2732
		
2733
		$testPath = $this->separator.$this->relpathCE($path);
2734
		
2735 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...
2736
			$attrs = $this->attributes[$i];
2737
			
2738
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2739
				$perm = $attrs['write'];
2740
			} 
2741
		}
2742
		
2743
		return $perm === null ? true : $perm;
2744
	}
2745
	
2746
	/**
2747
	 * Return true if file MIME type can save with check uploadOrder config.
2748
	 * 
2749
	 * @param string $mime
2750
	 * @return boolean
2751
	 */
2752
	protected function allowPutMime($mime) {
2753
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
2754
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
2755
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
2756
		$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...
2757
		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...
2758
			$res = false; // default is deny
2759
			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...
2760
				$res = true;
2761
			}// else (both match | no match | match only deny) { deny }
2762
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
2763
			$res = true; // default is allow
2764
			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...
2765
				$res = false;
2766
			} // else (both match | no match | match only allow) { allow }
2767
		}
2768
		return $res;
2769
	}
2770
	
2771
	/**
2772
	 * Return fileinfo 
2773
	 *
2774
	 * @param  string  $path  file cache
2775
	 * @return array
2776
	 * @author Dmitry (dio) Levashov
2777
	 **/
2778
	protected function stat($path) {
2779
		if ($path === false || is_null($path)) {
2780
			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...
2781
		}
2782
		$is_root = ($path === $this->root);
2783
		if ($is_root) {
2784
			$rootKey = md5($path);
2785
			if (!isset($this->sessionCache['rootstat'])) {
2786
				$this->sessionCache['rootstat'] = array();
2787
			}
2788
			if (empty($this->ARGS['reload']) || empty($this->ARGS['target']) || strpos($this->ARGS['target'], $this->id) !== 0) {
2789
				// need $path as key for netmount/netunmount
2790
				if (isset($this->sessionCache['rootstat'][$rootKey])) {
2791
					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...
2792
						return $ret;
2793
					}
2794
				}
2795
			}
2796
		}
2797
		$ret = isset($this->cache[$path])
2798
			? $this->cache[$path]
2799
			: $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
2800
		if ($is_root) {
2801
			$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...
2802
		}
2803
		return $ret;
2804
	}
2805
	
2806
	/**
2807
	 * Put file stat in cache and return it
2808
	 *
2809
	 * @param  string  $path   file path
2810
	 * @param  array   $stat   file stat
2811
	 * @return array
2812
	 * @author Dmitry (dio) Levashov
2813
	 **/
2814
	protected function updateCache($path, $stat) {
2815
		if (empty($stat) || !is_array($stat)) {
2816
			return $this->cache[$path] = array();
2817
		}
2818
2819
		$stat['hash'] = $this->encode($path);
2820
2821
		$root = $path == $this->root;
2822
		$parent = '';
2823
		
2824
		if ($root) {
2825
			if ($this->rootName) {
2826
				$stat['name'] = $this->rootName;
2827
			}
2828
			if (! empty($this->options['icon'])) {
2829
				$stat['icon'] = $this->options['icon'];
2830
			}
2831
			if (! empty($this->options['rootCssClass'])) {
2832
				$stat['csscls'] = $this->options['rootCssClass'];
2833
			}
2834
		} else {
2835
			if (!isset($stat['name']) || $stat['name'] === '') {
2836
				$stat['name'] = $this->basenameCE($path);
2837
			}
2838
			if (empty($stat['phash'])) {
2839
				$parent = $this->dirnameCE($path);
2840
				$stat['phash'] = $this->encode($parent);
2841
			}
2842
		}
2843
		
2844
		// name check
2845
		if (!$jeName = json_encode($stat['name'])) {
2846
			return $this->cache[$path] = array();
2847
		}
2848
		// fix name if required
2849
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2850
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], $jeName));
2851
		}
2852
		
2853
		
2854
		if (empty($stat['mime'])) {
2855
			$stat['mime'] = $this->mimetype($stat['name']);
2856
		}
2857
		
2858
		// @todo move dateformat to client
2859
		// $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...
2860
		// 	? $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...
2861
		// 	: 'unknown';
2862
			
2863
		if (!isset($stat['size'])) {
2864
			$stat['size'] = 'unknown';
2865
		}	
2866
2867
		if ($isDir = ($stat['mime'] === 'directory')) {
2868
			$stat['volumeid'] = $this->id;
2869
		}
2870
		
2871
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir));
2872
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir));
2873
		if ($root) {
2874
			$stat['locked'] = 1;
2875
		} else {
2876
			// lock when parent directory is not writable
2877
			if (!isset($stat['locked'])) {
2878
				$parent = $this->dirnameCE($path);
2879
				$pstat = isset($this->cache[$parent])? $this->cache[$parent] : array();
2880
				if (isset($pstat['write']) && !$pstat['write']) {
2881
					$stat['locked'] = true;
2882
				}
2883
			}
2884
			if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) {
2885
				$stat['locked'] = 1;
2886
			} else {
2887
				unset($stat['locked']);
2888
			}
2889
		}
2890
2891
		if ($root) {
2892
			unset($stat['hidden']);
2893
		} elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) 
2894
		|| !$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...
2895
			$stat['hidden'] = 1;
2896
		} else {
2897
			unset($stat['hidden']);
2898
		}
2899
		
2900
		if ($stat['read'] && empty($stat['hidden'])) {
2901
			
2902
			if ($isDir) {
2903
				// caching parent's subdirs
2904
				if ($parent) {
2905
					$this->subdirsCache[$parent] = true;
2906
				}
2907
				// for dir - check for subdirs
2908
				if ($this->options['checkSubfolders']) {
2909
					if (isset($stat['dirs'])) {
2910
						if ($stat['dirs']) {
2911
							$stat['dirs'] = 1;
2912
						} else {
2913
							unset($stat['dirs']);
2914
						}
2915
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2916
						$stat['dirs'] = isset($this->cache[$stat['target']])
2917
							? intval(isset($this->cache[$stat['target']]['dirs']))
2918
							: $this->subdirsCE($stat['target']);
2919
						
2920
					} elseif ($this->subdirsCE($path)) {
2921
						$stat['dirs'] = 1;
2922
					}
2923
				} else {
2924
					$stat['dirs'] = 1;
2925
				}
2926
			} else {
2927
				// for files - check for thumbnails
2928
				$p = isset($stat['target']) ? $stat['target'] : $path;
2929
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
2930
					$tmb = $this->gettmb($p, $stat);
2931
					$stat['tmb'] = $tmb ? $tmb : 1;
2932
				}
2933
				
2934
			}
2935
			if (!isset($stat['url']) && $this->URL && $this->encoding) {
2936
				$_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
2937
				$stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN')? $_path : $this->convEncIn($_path, true)));
2938
			}
2939
		} else {
2940
			if ($isDir) {
2941
				unset($stat['dirs']);
2942
			}
2943
		}
2944
		
2945
		if (!empty($stat['alias']) && !empty($stat['target'])) {
2946
			$stat['thash'] = $this->encode($stat['target']);
2947
			//$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...
2948
			unset($stat['target']);
2949
		}
2950
		
2951
		if (isset($this->options['netkey']) && $path === $this->root) {
2952
			$stat['netkey'] = $this->options['netkey'];
2953
		}
2954
		
2955
		return $this->cache[$path] = $stat;
2956
	}
2957
	
2958
	/**
2959
	 * Get stat for folder content and put in cache
2960
	 *
2961
	 * @param  string  $path
2962
	 * @return void
2963
	 * @author Dmitry (dio) Levashov
2964
	 **/
2965
	protected function cacheDir($path) {
2966
		$this->dirsCache[$path] = array();
2967
		$this->subdirsCache[$path] = false;
2968
2969
		foreach ($this->scandirCE($path) as $p) {
2970 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...
2971
				if ($stat['mime'] === 'directory') {
2972
					$this->subdirsCache[$path] = true;
2973
				}
2974
				$this->dirsCache[$path][] = $p;
2975
			}
2976
		}
2977
	}
2978
	
2979
	/**
2980
	 * Clean cache
2981
	 *
2982
	 * @return void
2983
	 * @author Dmitry (dio) Levashov
2984
	 **/
2985
	protected function clearcache() {
2986
		$this->cache = $this->dirsCache = array();
2987
		unset($this->sessionCache['rootstat'][md5($this->root)]);
2988
	}
2989
	
2990
	/**
2991
	 * Return file mimetype
2992
	 *
2993
	 * @param  string  $path  file path
2994
	 * @return string
2995
	 * @author Dmitry (dio) Levashov
2996
	 **/
2997
	protected function mimetype($path, $name = '') {
2998
		$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...
2999
		
3000
		if ($name === '') {
3001
			$name = $path;
3002
		}
3003
		$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
3004
		if ($this->mimeDetect == 'finfo') {
3005
			if ($type = @finfo_file($this->finfo, $path)) {
3006
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
3007
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
3008
				} else if ($ext === 'js' && preg_match('~^text/~', $type)) {
3009
					$type = 'text/javascript';
3010
				}
3011
			} else {
3012
				$type = 'unknown';
3013
			}
3014
		} elseif ($this->mimeDetect == 'mime_content_type') {
3015
			$type = mime_content_type($path);
3016
		} else {
3017
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
3018
		}
3019
		
3020
		$type = explode(';', $type);
3021
		$type = trim($type[0]);
3022
3023 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...
3024
			// finfo return this mime for empty files
3025
			$type = 'text/plain';
3026
		} elseif ($type == 'application/x-zip') {
3027
			// http://elrte.org/redmine/issues/163
3028
			$type = 'application/zip';
3029
		}
3030
		
3031
		// mime type normalization
3032
		$_checkKey = strtolower($ext.':'.$type);
3033
		if (isset($this->options['mimeMap'][$_checkKey])) {
3034
			$type = $this->options['mimeMap'][$_checkKey];
3035
		}
3036
		
3037
		return $type == 'unknown' && $this->mimeDetect != 'internal'
3038
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
3039
			: $type;
3040
		
3041
	}
3042
	
3043
	/**
3044
	 * Detect file mimetype using "internal" method
3045
	 *
3046
	 * @param  string  $path  file path
3047
	 * @return string
3048
	 * @author Dmitry (dio) Levashov
3049
	 **/
3050
	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...
3051
		// load default MIME table file "mime.types"
3052
		if (!elFinderVolumeDriver::$mimetypesLoaded) {
3053
			elFinderVolumeDriver::$mimetypesLoaded = true;
3054
			$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
3055 View Code Duplication
			if (is_readable($file)) {
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...
3056
				$mimecf = file($file);
3057
				foreach ($mimecf as $line_num => $line) {
3058
					if (!preg_match('/^\s*#/', $line)) {
3059
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
3060
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
3061
							if (!isset(elFinderVolumeDriver::$mimetypes[$mime[$i]])) {
3062
								elFinderVolumeDriver::$mimetypes[$mime[$i]] = $mime[0];
3063
							}
3064
						}
3065
					}
3066
				}
3067
			}
3068
		}
3069
		$pinfo = pathinfo($path); 
3070
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
3071
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
3072
	}
3073
	
3074
	/**
3075
	 * Return file/total directory size
3076
	 *
3077
	 * @param  string  $path  file path
3078
	 * @return int
3079
	 * @author Dmitry (dio) Levashov
3080
	 **/
3081
	protected function countSize($path) {
3082
		$stat = $this->stat($path);
3083
3084
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3085
			return 'unknown';
3086
		}
3087
		
3088
		if ($stat['mime'] != 'directory') {
3089
			return $stat['size'];
3090
		}
3091
		
3092
		$subdirs = $this->options['checkSubfolders'];
3093
		$this->options['checkSubfolders'] = true;
3094
		$result = 0;
3095
		foreach ($this->getScandir($path) as $stat) {
3096
			$size = $stat['mime'] == 'directory' && $stat['read'] 
3097
				? $this->countSize($this->joinPathCE($path, $stat['name'])) 
3098
				: (isset($stat['size']) ? intval($stat['size']) : 0);
3099
			if ($size > 0) {
3100
				$result += $size;
3101
			}
3102
		}
3103
		$this->options['checkSubfolders'] = $subdirs;
3104
		return $result;
3105
	}
3106
	
3107
	/**
3108
	 * Return true if all mimes is directory or files
3109
	 *
3110
	 * @param  string  $mime1  mimetype
3111
	 * @param  string  $mime2  mimetype
3112
	 * @return bool
3113
	 * @author Dmitry (dio) Levashov
3114
	 **/
3115
	protected function isSameType($mime1, $mime2) {
3116
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3117
	}
3118
	
3119
	/**
3120
	 * If file has required attr == $val - return file path,
3121
	 * If dir has child with has required attr == $val - return child path
3122
	 *
3123
	 * @param  string   $path  file path
3124
	 * @param  string   $attr  attribute name
3125
	 * @param  bool     $val   attribute value
3126
	 * @return string|false
3127
	 * @author Dmitry (dio) Levashov
3128
	 **/
3129
	protected function closestByAttr($path, $attr, $val) {
3130
		$stat = $this->stat($path);
3131
		
3132
		if (empty($stat)) {
3133
			return false;
3134
		}
3135
		
3136
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
3137
		
3138
		if ($v == $val) {
3139
			return $path;
3140
		}
3141
3142
		return $stat['mime'] == 'directory'
3143
			? $this->childsByAttr($path, $attr, $val) 
3144
			: false;
3145
	}
3146
	
3147
	/**
3148
	 * Return first found children with required attr == $val
3149
	 *
3150
	 * @param  string   $path  file path
3151
	 * @param  string   $attr  attribute name
3152
	 * @param  bool     $val   attribute value
3153
	 * @return string|false
3154
	 * @author Dmitry (dio) Levashov
3155
	 **/
3156
	protected function childsByAttr($path, $attr, $val) {
3157
		foreach ($this->scandirCE($path) as $p) {
3158
			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...
3159
				return $_p;
3160
			}
3161
		}
3162
		return false;
3163
	}
3164
	
3165
	/*****************  get content *******************/
3166
	
3167
	/**
3168
	 * Return required dir's files info.
3169
	 * If onlyMimes is set - return only dirs and files of required mimes
3170
	 *
3171
	 * @param  string  $path  dir path
3172
	 * @return array
3173
	 * @author Dmitry (dio) Levashov
3174
	 **/
3175
	protected function getScandir($path) {
3176
		$files = array();
3177
		
3178
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3179
3180
		foreach ($this->dirsCache[$path] as $p) {
3181
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3182
				$files[] = $stat;
3183
			}
3184
		}
3185
3186
		return $files;
3187
	}
3188
	
3189
	
3190
	/**
3191
	 * Return subdirs tree
3192
	 *
3193
	 * @param  string  $path  parent dir path
3194
	 * @param  int     $deep  tree deep
3195
	 * @return array
3196
	 * @author Dmitry (dio) Levashov
3197
	 **/
3198
	protected function gettree($path, $deep, $exclude='') {
3199
		$dirs = array();
3200
		
3201
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3202
3203
		foreach ($this->dirsCache[$path] as $p) {
3204
			$stat = $this->stat($p);
3205
			
3206
			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...
3207
				$dirs[] = $stat;
3208
				if ($deep > 0 && !empty($stat['dirs'])) {
3209
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3210
				}
3211
			}
3212
		}
3213
3214
		return $dirs;
3215
	}	
3216
		
3217
	/**
3218
	 * Recursive files search
3219
	 *
3220
	 * @param  string  $path   dir path
3221
	 * @param  string  $q      search string
3222
	 * @param  array   $mimes
3223
	 * @return array
3224
	 * @author Dmitry (dio) Levashov
3225
	 **/
3226
	protected function doSearch($path, $q, $mimes) {
3227
		$result = array();
3228
3229
		foreach($this->scandirCE($path) as $p) {
3230
			@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...
3231
			$stat = $this->stat($p);
3232
3233
			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...
3234
				continue;
3235
			}
3236
3237
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3238
				continue;
3239
			}
3240
			
3241
			$name = $stat['name'];
3242
3243
			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...
3244
				$stat['path'] = $this->path($stat['hash']);
3245
				if ($this->URL && !isset($stat['url'])) {
3246
					$path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
3247
					if ($this->encoding) {
3248
						$path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
3249
					}
3250
					$stat['url'] = $this->URL . $path;
3251
				}
3252
				
3253
				$result[] = $stat;
3254
			}
3255 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...
3256
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
3257
			}
3258
		}
3259
		
3260
		return $result;
3261
	}
3262
		
3263
	/**********************  manuipulations  ******************/
3264
		
3265
	/**
3266
	 * Copy file/recursive copy dir only in current volume.
3267
	 * Return new file path or false.
3268
	 *
3269
	 * @param  string  $src   source path
3270
	 * @param  string  $dst   destination dir path
3271
	 * @param  string  $name  new file name (optionaly)
3272
	 * @return string|false
3273
	 * @author Dmitry (dio) Levashov
3274
	 **/
3275
	protected function copy($src, $dst, $name) {
3276
		$srcStat = $this->stat($src);
3277
		$this->clearcache();
3278
		
3279
		if (!empty($srcStat['thash'])) {
3280
			$target = $this->decode($srcStat['thash']);
3281
			if (!$this->inpathCE($target, $this->root)) {
3282
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
3283
			}
3284
			$stat   = $this->stat($target);
3285
			$this->clearcache();
3286
			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...
3287
				? $this->joinPathCE($dst, $name)
3288
				: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3289
		} 
3290
		
3291
		if ($srcStat['mime'] == 'directory') {
3292
			$test = $this->stat($this->joinPathCE($dst, $name));
3293
			
3294 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...
3295
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3296
			}
3297
			
3298
			$dst = $this->joinPathCE($dst, $name);
3299
			
3300
			foreach ($this->getScandir($src) as $stat) {
3301
				if (empty($stat['hidden'])) {
3302
					$name = $stat['name'];
3303
					if (!$this->copy($this->joinPathCE($src, $name), $dst, $name)) {
3304
						$this->remove($dst, true); // fall back
3305
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
3306
					}
3307
				}
3308
			}
3309
			$this->clearcache();
3310
			return $dst;
3311
		} 
3312
3313
		return $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))
3314
			? $this->joinPathCE($dst, $name) 
3315
			: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3316
	}
3317
3318
	/**
3319
	 * Move file
3320
	 * Return new file path or false.
3321
	 *
3322
	 * @param  string  $src   source path
3323
	 * @param  string  $dst   destination dir path
3324
	 * @param  string  $name  new file name 
3325
	 * @return string|false
3326
	 * @author Dmitry (dio) Levashov
3327
	 **/
3328
	protected function move($src, $dst, $name) {
3329
		$stat = $this->stat($src);
3330
		$stat['realpath'] = $src;
3331
		$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...
3332
		$this->clearcache();
3333
		
3334 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...
3335
			$this->removed[] = $stat;
3336
3337
			return $this->joinPathCE($dst, $name);
3338
		}
3339
3340
		return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
3341
	}
3342
3343
	/**
3344
	 * Copy file from another volume.
3345
	 * Return new file path or false.
3346
	 *
3347
	 * @param  Object  $volume       source volume
3348
	 * @param  string  $src          source file hash
3349
	 * @param  string  $destination  destination dir path
3350
	 * @param  string  $name         file name
3351
	 * @return string|false
3352
	 * @author Dmitry (dio) Levashov
3353
	 **/
3354
	protected function copyFrom($volume, $src, $destination, $name) {
3355
		
3356
		if (($source = $volume->file($src)) == false) {
3357
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
3358
		}
3359
		
3360
		$errpath = $volume->path($source['hash']);
3361
		
3362
		if (!$this->nameAccepted($source['name'])) {
3363
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
3364
		}
3365
				
3366
		if (!$source['read']) {
3367
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
3368
		}
3369
		
3370
		if ($source['mime'] == 'directory') {
3371
			$stat = $this->stat($this->joinPathCE($destination, $name));
3372
			$this->clearcache();
3373 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...
3374
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3375
			}
3376
			
3377
			$path = $this->joinPathCE($destination, $name);
3378
			
3379
			foreach ($volume->scandir($src) as $entr) {
3380
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
3381
					$this->remove($path, true); // fall back
3382
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
3383
				}
3384
			}
3385
			
3386
		} else {
3387
			// $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...
3388
			// $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...
3389
			if (($dim = $volume->dimensions($src))) {
3390
				$s = explode('x', $dim);
3391
				$source['width']  = $s[0];
3392
				$source['height'] = $s[1];
3393
			}
3394
			
3395
			if (($fp = $volume->open($src)) == false
3396
			|| ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
3397
				$fp && $volume->close($fp, $src);
3398
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3399
			}
3400
			$volume->close($fp, $src);
3401
			
3402
			// MIME check
3403
			$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 3396 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...
3404
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($stat['name']);
3405
			if ($stat['mime'] === $mimeByName) {
3406
				$mimeByName = '';
3407
			}
3408
			if (!$this->allowPutMime($stat['mime']) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
3409
				$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 3396 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...
3410
				return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
3411
			}
3412
		}
3413
		
3414
		return $path;
3415
	}
3416
		
3417
	/**
3418
	 * Remove file/ recursive remove dir
3419
	 *
3420
	 * @param  string  $path   file path
3421
	 * @param  bool    $force  try to remove even if file locked
3422
	 * @return bool
3423
	 * @author Dmitry (dio) Levashov
3424
	 **/
3425
	protected function remove($path, $force = false) {
3426
		$stat = $this->stat($path);
3427
		
3428
		if (empty($stat)) {
3429
			return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']), elFinder::ERROR_FILE_NOT_FOUND);
3430
		}
3431
		
3432
		$stat['realpath'] = $path;
3433
		$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...
3434
		$this->clearcache();
3435
		
3436 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...
3437
			return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
3438
		}
3439
		
3440
		if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
3441
			$ret = $this->delTree($this->convEncIn($path));
3442
			$this->convEncOut();
3443
			if (!$ret) {
3444
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3445
			}
3446 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...
3447
			if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
3448
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3449
			}
3450
		}
3451
3452
		$this->removed[] = $stat;
3453
		return true;
3454
	}
3455
	
3456
3457
	/************************* thumbnails **************************/
3458
		
3459
	/**
3460
	 * Return thumbnail file name for required file
3461
	 *
3462
	 * @param  array  $stat  file stat
3463
	 * @return string
3464
	 * @author Dmitry (dio) Levashov
3465
	 **/
3466
	protected function tmbname($stat) {
3467
		return $stat['hash'].$stat['ts'].'.png';
3468
	}
3469
	
3470
	/**
3471
	 * Return thumnbnail name if exists
3472
	 *
3473
	 * @param  string  $path file path
3474
	 * @param  array   $stat file stat
3475
	 * @return string|false
3476
	 * @author Dmitry (dio) Levashov
3477
	 **/
3478
	protected function gettmb($path, $stat) {
3479
		if ($this->tmbURL && $this->tmbPath) {
3480
			// file itself thumnbnail
3481
			if (strpos($path, $this->tmbPath) === 0) {
3482
				return basename($path);
3483
			}
3484
3485
			$name = $this->tmbname($stat);
3486
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
3487
				return $name;
3488
			}
3489
		}
3490
		return false;
3491
	}
3492
	
3493
	/**
3494
	 * Return true if thumnbnail for required file can be created
3495
	 *
3496
	 * @param  string  $path  thumnbnail path 
3497
	 * @param  array   $stat  file stat
3498
	 * @param  bool    $checkTmbPath
3499
	 * @return string|bool
3500
	 * @author Dmitry (dio) Levashov
3501
	 **/
3502
	protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
3503
		return (!$checkTmbPath || $this->tmbPathWritable) 
3504
			&& (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
3505
			&& $this->imgLib 
3506
			&& strpos($stat['mime'], 'image') === 0 
3507
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
3508
	}
3509
	
3510
	/**
3511
	 * Return true if required file can be resized.
3512
	 * By default - the same as canCreateTmb
3513
	 *
3514
	 * @param  string  $path  thumnbnail path 
3515
	 * @param  array   $stat  file stat
3516
	 * @return string|bool
3517
	 * @author Dmitry (dio) Levashov
3518
	 **/
3519
	protected function canResize($path, $stat) {
3520
		return $this->canCreateTmb($path, $stat, false);
3521
	}
3522
	
3523
	/**
3524
	 * Create thumnbnail and return it's URL on success
3525
	 *
3526
	 * @param  string  $path  file path
3527
	 * @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...
3528
	 * @return string|false
3529
	 * @author Dmitry (dio) Levashov
3530
	 **/
3531
	protected function createTmb($path, $stat) {
3532
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
3533
			return false;
3534
		}
3535
3536
		$name = $this->tmbname($stat);
3537
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
3538
3539
		// copy image into tmbPath so some drivers does not store files on local fs
3540
		if (($src = $this->fopenCE($path, 'rb')) == false) {
3541
			return false;
3542
		}
3543
3544
		if (($trg = fopen($tmb, 'wb')) == false) {
3545
			$this->fcloseCE($src, $path);
3546
			return false;
3547
		}
3548
3549
		while (!feof($src)) {
3550
			fwrite($trg, fread($src, 8192));
3551
		}
3552
3553
		$this->fcloseCE($src, $path);
3554
		fclose($trg);
3555
3556
		$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...
3557
		
3558
		$tmbSize = $this->tmbSize;
3559
		
3560
		if (($s = getimagesize($tmb)) == false) {
3561
			return false;
3562
		}
3563
3564
		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
3565
		if ($s[0] <= $tmbSize && $s[1]	<= $tmbSize) {
3566
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3567
		} else {
3568
		
3569
			if ($this->options['tmbCrop']) {
3570
		
3571
				$result = $tmb;
3572
				/* Resize and crop if image bigger than thumbnail */
3573 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...
3574
					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
3575
				}
3576
		
3577 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...
3578
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
3579
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
3580
					$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...
3581
				} else {
3582
					$result = false;
3583
				}
3584
		
3585
			} else {
3586
				$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
3587
			}
3588
		
3589
			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...
3590
				$result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3591
			}
3592
		}
3593
		
3594
		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...
3595
			unlink($tmb);
3596
			return false;
3597
		}
3598
		
3599
		return $name;
3600
	}
3601
3602
	/**
3603
	 * Resize image
3604
	 *
3605
	 * @param  string   $path               image file
3606
	 * @param  int      $width              new width
3607
	 * @param  int      $height             new height
3608
	 * @param  bool	    $keepProportions    crop image
3609
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
3610
	 * @param  string   $destformat         image destination format
3611
	 * @return string|false
3612
	 * @author Dmitry (dio) Levashov
3613
	 * @author Alexey Sukhotin
3614
	 **/
3615
	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) {
3616
		if (($s = @getimagesize($path)) == false) {
3617
			return false;
3618
		}
3619
3620
		$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...
3621
		
3622
		list($size_w, $size_h) = array($width, $height);
3623
	
3624
		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...
3625
		
3626
			list($orig_w, $orig_h) = array($s[0], $s[1]);
3627
		
3628
			/* Resizing by biggest side */
3629
			if ($resizeByBiggerSide) {
3630 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...
3631
					$size_h = round($orig_h * $width / $orig_w);
3632
					$size_w = $width;
3633
				} else {
3634
					$size_w = round($orig_w * $height / $orig_h);
3635
					$size_h = $height;
3636
				}
3637 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...
3638
				if ($orig_w > $orig_h) {
3639
					$size_w = round($orig_w * $height / $orig_h);
3640
					$size_h = $height;
3641
				} else {
3642
					$size_h = round($orig_h * $width / $orig_w);
3643
					$size_w = $width;
3644
				}
3645
			}
3646
		}
3647
3648
		switch ($this->imgLib) {
3649
			case 'imagick':
3650
				
3651
				try {
3652
					$img = new imagick($path);
3653
				} catch (Exception $e) {
3654
					return false;
3655
				}
3656
3657
				// Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
3658
				// 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...
3659
				// 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...
3660
				$filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
3661
				
3662
				$ani = ($img->getNumberImages() > 1);
3663
				if ($ani && is_null($destformat)) {
3664
					$img = $img->coalesceImages();
3665
					do {
3666
						$img->resizeImage($size_w, $size_h, $filter, 1);
3667
					} while ($img->nextImage());
3668
					$img = $img->optimizeImageLayers();
3669
					$result = $img->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
3670
				} else {
3671
					if ($ani) {
3672
						$img->setFirstIterator();
3673
					}
3674
					$img->resizeImage($size_w, $size_h, $filter, 1);
3675
					$result = $img->writeImage($path);
3676
				}
3677
				
3678
				$img->destroy();
3679
3680
				return $result ? $path : false;
3681
3682
				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...
3683
3684
			case 'gd':
3685
				$img = self::gdImageCreate($path,$s['mime']);
3686
3687
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
3688
				
3689
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3690
					
3691
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
3692
						return false;
3693
					}
3694
		
3695
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3696
3697
					imagedestroy($img);
3698
					imagedestroy($tmp);
3699
3700
					return $result ? $path : false;
3701
3702
				}
3703
				break;
3704
		}
3705
		
3706
		return false;
3707
  	}
3708
  
3709
	/**
3710
	 * Crop image
3711
	 *
3712
	 * @param  string   $path               image file
3713
	 * @param  int      $width              crop width
3714
	 * @param  int      $height             crop height
3715
	 * @param  bool	    $x                  crop left offset
3716
	 * @param  bool	    $y                  crop top offset
3717
	 * @param  string   $destformat         image destination format
3718
	 * @return string|false
3719
	 * @author Dmitry (dio) Levashov
3720
	 * @author Alexey Sukhotin
3721
	 **/
3722
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) {
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
		switch ($this->imgLib) {
3730
			case 'imagick':
3731
				
3732
				try {
3733
					$img = new imagick($path);
3734
				} catch (Exception $e) {
3735
					return false;
3736
				}
3737
				
3738
				$ani = ($img->getNumberImages() > 1);
3739
				if ($ani && is_null($destformat)) {
3740
					$img = $img->coalesceImages();
3741
					do {
3742
						$img->setImagePage($s[0], $s[1], 0, 0);
3743
						$img->cropImage($width, $height, $x, $y);
3744
						$img->setImagePage($width, $height, 0, 0);
3745
					} while ($img->nextImage());
3746
					$img = $img->optimizeImageLayers();
3747
					$result = $img->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
3748
				} else {
3749
					if ($ani) {
3750
						$img->setFirstIterator();
3751
					}
3752
					$img->setImagePage($s[0], $s[1], 0, 0);
3753
					$img->cropImage($width, $height, $x, $y);
3754
					$img->setImagePage($width, $height, 0, 0);
3755
					$result = $img->writeImage($path);
3756
				}
3757
				
3758
				$img->destroy();
3759
3760
				return $result ? $path : false;
3761
3762
				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...
3763
3764
			case 'gd':
3765
				$img = self::gdImageCreate($path,$s['mime']);
3766
3767
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3768
					
3769
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
3770
3771
					$size_w = $width;
3772
					$size_h = $height;
3773
3774
					if ($s[0] < $width || $s[1] < $height) {
3775
						$size_w = $s[0];
3776
						$size_h = $s[1];
3777
					}
3778
3779
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
3780
						return false;
3781
					}
3782
					
3783
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3784
3785
					imagedestroy($img);
3786
					imagedestroy($tmp);
3787
3788
					return $result ? $path : false;
3789
3790
				}
3791
				break;
3792
		}
3793
3794
		return false;
3795
	}
3796
3797
	/**
3798
	 * Put image to square
3799
	 *
3800
	 * @param  string   $path               image file
3801
	 * @param  int      $width              square width
3802
	 * @param  int      $height             square height
3803
	 * @param  int	    $align              reserved
3804
	 * @param  int 	    $valign             reserved
3805
	 * @param  string   $bgcolor            square background color in #rrggbb format
3806
	 * @param  string   $destformat         image destination format
3807
	 * @return string|false
3808
	 * @author Dmitry (dio) Levashov
3809
	 * @author Alexey Sukhotin
3810
	 **/
3811
	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...
3812
		if (($s = @getimagesize($path)) == false) {
3813
			return false;
3814
		}
3815
3816
		$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...
3817
3818
		/* Coordinates for image over square aligning */
3819
		$y = ceil(abs($height - $s[1]) / 2); 
3820
		$x = ceil(abs($width - $s[0]) / 2);
3821
3822
		switch ($this->imgLib) {
3823
			case 'imagick':
3824
				try {
3825
					$img = new imagick($path);
3826
				} catch (Exception $e) {
3827
					return false;
3828
				}
3829
				
3830
				$ani = ($img->getNumberImages() > 1);
3831
				if ($ani && is_null($destformat)) {
3832
					$img1 = new Imagick();
3833
					$img1->setFormat('gif');
3834
					$img = $img->coalesceImages();
3835
					do {
3836
						$gif = new Imagick();
3837
						$gif->newImage($width, $height, new ImagickPixel($bgcolor));
3838
						$gif->setImageColorspace($img->getImageColorspace());
3839
						$gif->setImageFormat('gif');
3840
						$gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3841
						$gif->setImageDelay($img->getImageDelay());
3842
						$gif->setImageIterations($img->getImageIterations());
3843
						$img1->addImage($gif);
3844
						$gif->destroy();
3845
					} while ($img->nextImage());
3846
					$img1 = $img1->optimizeImageLayers();
3847
					$result = $img1->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img1 (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
3848
				} else {
3849
					if ($ani) {
3850
						$img->setFirstIterator();
3851
					}
3852
					$img1 = new Imagick();
3853
					$img1->newImage($width, $height, new ImagickPixel($bgcolor));
3854
					$img1->setImageColorspace($img->getImageColorspace());
3855
					$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...
3856
					$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3857
					$result = $img1->writeImage($path);
3858
				}
3859
				
3860
				$img1->destroy();
3861
				$img->destroy();
3862
				return $result ? $path : false;
3863
3864
				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...
3865
3866
			case 'gd':
3867
				$img = self::gdImageCreate($path,$s['mime']);
3868
3869
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3870
3871
					self::gdImageBackground($tmp,$bgcolor);
3872
3873
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
3874
						return false;
3875
					}
3876
3877
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3878
3879
					imagedestroy($img);
3880
					imagedestroy($tmp);
3881
3882
					return $result ? $path : false;
3883
				}
3884
				break;
3885
		}
3886
3887
		return false;
3888
	}
3889
3890
	/**
3891
	 * Rotate image
3892
	 *
3893
	 * @param  string   $path               image file
3894
	 * @param  int      $degree             rotete degrees
3895
	 * @param  string   $bgcolor            square background color in #rrggbb format
3896
	 * @param  string   $destformat         image destination format
3897
	 * @return string|false
3898
	 * @author nao-pon
3899
	 * @author Troex Nevelin
3900
	 **/
3901
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) {
3902
		if (($s = @getimagesize($path)) == false || $degree % 360 === 0) {
3903
			return false;
3904
		}
3905
3906
		$result = false;
3907
3908
		// try lossless rotate
3909
		if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
3910
			$count = ($degree / 90) % 4;
3911
			$exiftran = array(
3912
				1 => '-9',
3913
				2 => '-1',
3914
				3 => '-2'
3915
			);
3916
			$jpegtran = array(
3917
				1 => '90',
3918
				2 => '180',
3919
				3 => '270'
3920
			);
3921
			$quotedPath = escapeshellarg($path);
3922
			$cmds = array(
3923
				'exiftran -i '.$exiftran[$count].' '.$path,
3924
				'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
3925
			);
3926
			foreach($cmds as $cmd) {
3927
				if ($this->procExec($cmd) === 0) {
3928
					$result = true;
3929
					break;
3930
				}
3931
			}
3932
			if ($result) {
3933
				return $path;
3934
			}
3935
		}
3936
3937
		switch ($this->imgLib) {
3938
			case 'imagick':
3939
				try {
3940
					$img = new imagick($path);
3941
				} catch (Exception $e) {
3942
					return false;
3943
				}
3944
3945
				if ($img->getNumberImages() > 1) {
3946
					$img = $img->coalesceImages();
3947
					do {
3948
						$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3949
					} while ($img->nextImage());
3950
					$img = $img->optimizeImageLayers();
3951
					$result = $img->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
3952
				} else {
3953
					$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3954
					$result = $img->writeImage($path);
3955
				}
3956
				$img->destroy();
3957
				return $result ? $path : false;
3958
3959
				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...
3960
3961
			case 'gd':
3962
				$img = self::gdImageCreate($path,$s['mime']);
3963
3964
				$degree = 360 - $degree;
3965
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3966
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
3967
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
3968
3969
				$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3970
3971
				imageDestroy($img);
3972
				imageDestroy($tmp);
3973
3974
				return $result ? $path : false;
3975
3976
				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...
3977
		}
3978
3979
		return false;
3980
	}
3981
3982
	/**
3983
	 * Execute shell command
3984
	 *
3985
	 * @param  string  $command       command line
3986
	 * @param  array   $output        stdout strings
3987
	 * @param  array   $return_var    process exit code
3988
	 * @param  array   $error_output  stderr strings
3989
	 * @return int     exit code
3990
	 * @author Alexey Sukhotin
3991
	 **/
3992
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
3993
3994
		$descriptorspec = array(
3995
			0 => array("pipe", "r"),  // stdin
3996
			1 => array("pipe", "w"),  // stdout
3997
			2 => array("pipe", "w")   // stderr
3998
		);
3999
4000
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
4001
4002
		if (is_resource($process)) {
4003
4004
			fclose($pipes[0]);
4005
4006
			$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...
4007
			$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...
4008
4009
			$output = stream_get_contents($pipes[1]);
4010
			$error_output = stream_get_contents($pipes[2]);
4011
4012
			fclose($pipes[1]);
4013
			fclose($pipes[2]);
4014
			$return_var = proc_close($process);
4015
4016
4017
		}
4018
		
4019
		return $return_var;
4020
		
4021
	}
4022
4023
	/**
4024
	 * Remove thumbnail, also remove recursively if stat is directory
4025
	 *
4026
	 * @param  string  $stat  file stat
4027
	 * @return void
4028
	 * @author Dmitry (dio) Levashov
4029
	 * @author Naoki Sawada
4030
	 * @author Troex Nevelin
4031
	 **/
4032
	protected function rmTmb($stat) {
4033
		if ($stat['mime'] === 'directory') {
4034
			foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
4035
				@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...
4036
				$name = $this->basenameCE($p);
4037
				$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...
4038
			}
4039
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
4040
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
4041
			file_exists($tmb) && @unlink($tmb);
4042
			clearstatcache();
4043
		}
4044
	}
4045
	
4046
	/**
4047
	 * Create an gd image according to the specified mime type
4048
	 *
4049
	 * @param string $path image file
4050
	 * @param string $mime
4051
	 * @return gd image resource identifier
4052
	 */
4053
	protected function gdImageCreate($path,$mime){
4054
		switch($mime){
4055
			case 'image/jpeg':
4056
			return imagecreatefromjpeg($path);
4057
4058
			case 'image/png':
4059
			return imagecreatefrompng($path);
4060
4061
			case 'image/gif':
4062
			return imagecreatefromgif($path);
4063
4064
			case 'image/xbm':
4065
			return imagecreatefromxbm($path);
4066
		}
4067
		return false;
4068
	}
4069
4070
	/**
4071
	 * Output gd image to file
4072
	 *
4073
	 * @param resource $image gd image resource
4074
	 * @param string $filename The path to save the file to.
4075
	 * @param string $destformat The Image type to use for $filename
4076
	 * @param string $mime The original image mime type
4077
	 */
4078
	protected function gdImage($image, $filename, $destformat, $mime ){
4079
4080
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
4081
			return imagejpeg($image, $filename, 100);
4082
		}
4083
4084
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
4085
			return imagegif($image, $filename, 7);
4086
		}
4087
4088
		return imagepng($image, $filename, 7);
4089
	}
4090
4091
	/**
4092
	 * Assign the proper background to a gd image
4093
	 *
4094
	 * @param resource $image gd image resource
4095
	 * @param string $bgcolor background color in #rrggbb format
4096
	 */
4097
	protected function gdImageBackground($image, $bgcolor){
4098
4099
		if( $bgcolor == 'transparent' ){
4100
			imagesavealpha($image,true);
4101
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
4102
4103
		}else{
4104
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4105
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
4106
		}
4107
4108
		imagefill($image, 0, 0, $bgcolor1);
4109
	}
4110
4111
	/*********************** misc *************************/
4112
	
4113
	/**
4114
	 * Return smart formatted date
4115
	 *
4116
	 * @param  int     $ts  file timestamp
4117
	 * @return string
4118
	 * @author Dmitry (dio) Levashov
4119
	 **/
4120
	// 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...
4121
	// 	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...
4122
	// 		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...
4123
	// 	}
4124
	// 	
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...
4125
	// 	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...
4126
	// 		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...
4127
	// 	} 
4128
	// 	
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...
4129
	// 	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...
4130
	// }
4131
4132
	/**
4133
	* Find position of first occurrence of string in a string with multibyte support
4134
	*
4135
	* @param  string  $haystack  The string being checked.
4136
	* @param  string  $needle    The string to find in haystack.
4137
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
4138
	* @return int|bool
4139
	* @author Alexey Sukhotin
4140
	**/
4141
	protected function stripos($haystack , $needle , $offset = 0) {
4142
		if (function_exists('mb_stripos')) {
4143
			return mb_stripos($haystack , $needle , $offset, 'UTF-8');
4144
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
4145
			return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset);
4146
		} 
4147
		return stripos($haystack , $needle , $offset);
4148
	}
4149
4150
	/**
4151
	 * Get server side available archivers
4152
	 * 
4153
	 * @param bool $use_cache
4154
	 * @return array
4155
	 */
4156
	protected function getArchivers($use_cache = true) {
4157
4158
		$sessionKey = 'ARCHIVERS_CACHE';
4159
		if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
4160
			return $this->sessionCache[$sessionKey];
4161
		}
4162
		
4163
		$arcs = array(
4164
			'create'  => array(),
4165
			'extract' => array()
4166
		);
4167
		
4168
		if (function_exists('proc_open')) {
4169
		
4170
			$this->procExec('tar --version', $o, $ctar);
4171
			
4172
			if ($ctar == 0) {
4173
				$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
4174
				$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
4175
				unset($o);
4176
				$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...
4177 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...
4178
					$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
4179
					$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
4180
				}
4181
				unset($o);
4182
				$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...
4183 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...
4184
					$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
4185
					$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
4186
				}
4187
				unset($o);
4188
				$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...
4189 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...
4190
					$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
4191
					$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
4192
				}
4193
			}
4194
			unset($o);
4195
			$this->procExec('zip -v', $o, $c);
4196
			if ($c == 0) {
4197
				$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
4198
			}
4199
			unset($o);
4200
			$this->procExec('unzip --help', $o, $c);
4201
			if ($c == 0) {
4202
				$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
4203
			}
4204
			unset($o);
4205
			$this->procExec('rar --version', $o, $c);
4206
			if ($c == 0 || $c == 7) {
4207
				$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
4208
				$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
4209
			} else {
4210
				unset($o);
4211
				$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...
4212
				if ($c==0 || $c == 7) {
4213
					$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
4214
				}
4215
			}
4216
			unset($o);
4217
			$this->procExec('7za --help', $o, $c);
4218
			if ($c == 0) {
4219
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
4220
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
4221
				
4222
				if (empty($arcs['create']['application/zip'])) {
4223
					$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
4224
				}
4225 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...
4226
					$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4227
				}
4228
				if (empty($arcs['create']['application/x-tar'])) {
4229
					$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
4230
				}
4231 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...
4232
					$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4233
				}
4234
			} else if (substr(PHP_OS,0,3) === 'WIN') {
4235
				// check `7z` for Windows server.
4236
				unset($o);
4237
				$this->procExec('7z', $o, $c);
4238
				if ($c == 0) {
4239
					$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
4240
					$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
4241
					
4242
					if (empty($arcs['create']['application/zip'])) {
4243
						$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
4244
					}
4245
					if (empty($arcs['extract']['application/zip'])) {
4246
						$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4247
					}
4248
					if (empty($arcs['create']['application/x-tar'])) {
4249
						$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
4250
					}
4251
					if (empty($arcs['extract']['application/x-tar'])) {
4252
						$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4253
					}
4254
				}
4255
			}
4256
		
4257
		}
4258
		
4259
		// Use PHP ZipArchive Class
4260
		if (class_exists('ZipArchive', false)) {
4261
			if (empty($arcs['create']['application/zip'])) {
4262
				$arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveZip', 'ext' => 'zip');
4263
			}
4264 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...
4265
				$arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveUnzip', 'ext' => 'zip');
4266
			}
4267
		}
4268
		
4269
		$this->sessionCache[$sessionKey] = $arcs;
4270
		return $arcs;
4271
	}
4272
4273
	/**
4274
	 * Resolve relative / (Unix-like)absolute path
4275
	 * 
4276
	 * @param string $path  target path
4277
	 * @param string $base  base path
4278
	 * @return string
4279
	 */
4280
	protected function getFullPath($path, $base) {
4281
		$separator = $this->separator;
4282
		$systemroot = $this->systemRoot;
4283
4284
		$sepquoted = preg_quote($separator, '#');
4285
4286
		// normalize `/../`
4287
		$normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#';
4288
		while(preg_match($normreg, $path)) {
4289
			$path = preg_replace($normreg, '$1', $path);
4290
		}
4291
		
4292
		// 'Here'
4293
		if ($path === '' || $path === '.' . $separator) return $base;
4294
		
4295
		// Absolute path
4296
		if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
4297
			return $path;
4298
		}
4299
		
4300
		$preg_separator = '#' . $sepquoted . '#';
4301
		
4302
		// Relative path from 'Here'
4303
		if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.' || substr($path, 0, 3) !== '..' . $separator) {
4304
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4305
			if ($arrn[0] !== '.') {
4306
				array_unshift($arrn, '.');
4307
			}
4308
			$arrn[0] = $base;
4309
			return join($separator, $arrn);
4310
		}
4311
		
4312
		// Relative path from dirname()
4313
		if (substr($path, 0, 3) === '../') {
4314
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4315
			$arrp = preg_split($preg_separator, $base, -1, PREG_SPLIT_NO_EMPTY);
4316
		
4317
			while (! empty($arrn) && $arrn[0] === '..') {
4318
				array_shift($arrn);
4319
				array_pop($arrp);
4320
			}
4321
			$path = ! empty($arrp) ? $systemroot . join($separator, array_merge($arrp, $arrn)) :
4322
				(! empty($arrn) ? $systemroot . join($separator, $arrn) : $systemroot);
4323
		}
4324
		
4325
		return $path;
4326
	}
4327
4328
	/**
4329
	 * Remove directory recursive on local file system
4330
	 *
4331
	 * @param string $dir Target dirctory path
4332
	 * @return boolean
4333
	 * @author Naoki Sawada
4334
	 */
4335
	public function rmdirRecursive($dir) {
4336
		if (!is_link($dir) && is_dir($dir)) {
4337
			@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...
4338
			foreach (array_diff(scandir($dir), array('.', '..')) as $file) {
4339
				@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...
4340
				$path = $dir . DIRECTORY_SEPARATOR . $file;
4341
				if (!is_link($dir) && is_dir($path)) {
4342
					$this->rmdirRecursive($path);
4343
				} else {
4344
					@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...
4345
					@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...
4346
				}
4347
			}
4348
			return @rmdir($dir);
4349
		} else if (is_file($dir) || is_link($dir)) {
4350
			@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...
4351
			return @unlink($dir);
4352
		}
4353
		return false;
4354
	}
4355
4356
	/**
4357
	 * Create archive and return its path
4358
	 *
4359
	 * @param  string  $dir    target dir
4360
	 * @param  array   $files  files names list
4361
	 * @param  string  $name   archive name
4362
	 * @param  array   $arc    archiver options
4363
	 * @return string|bool
4364
	 * @author Dmitry (dio) Levashov, 
4365
	 * @author Alexey Sukhotin
4366
	 * @author Naoki Sawada
4367
	 **/
4368
	protected function makeArchive($dir, $files, $name, $arc) {
4369
		if ($arc['cmd'] === 'phpfunction') {
4370 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...
4371
				call_user_func_array($arc['argc'], array($dir, $files, $name));
4372
			}
4373
		} else {
4374
			$cwd = getcwd();
4375
			chdir($dir);
4376
			
4377
			$files = array_map('escapeshellarg', $files);
4378
			
4379
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
4380
			$this->procExec($cmd, $o, $c);
4381
			chdir($cwd);
4382
		}
4383
		$path = $dir.DIRECTORY_SEPARATOR.$name;
4384
		return file_exists($path) ? $path : false;
4385
	}
4386
	
4387
	/**
4388
	 * Unpack archive
4389
	 *
4390
	 * @param  string  $path   archive path
4391
	 * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
4392
	 * @param  bool    $remove remove archive ( unlink($path) )
4393
	 * @return void
4394
	 * @author Dmitry (dio) Levashov
4395
	 * @author Alexey Sukhotin
4396
	 * @author Naoki Sawada
4397
	 **/
4398
	protected function unpackArchive($path, $arc, $remove = true) {
4399
		$dir = dirname($path);
4400
		if ($arc['cmd'] === 'phpfunction') {
4401 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...
4402
				call_user_func_array($arc['argc'], array($path, $dir));
4403
			}
4404
		} else {
4405
			$cwd = getcwd();
4406
			chdir($dir);
4407
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
4408
			$this->procExec($cmd, $o, $c);
4409
			chdir($cwd);
4410
		}
4411
		$remove && unlink($path);
4412
	}
4413
	
4414
	/**
4415
	 * Create Zip archive using PHP class ZipArchive
4416
	 * 
4417
	 * @param  string        $dir      target dir
4418
	 * @param  array         $files    files names list
4419
	 * @param  string|object $zipPath  Zip archive name
4420
	 * @return void
4421
	 * @author Naoki Sawada
4422
	 */
4423
	protected static function zipArchiveZip($dir, $files, $zipPath) {
4424
		try {
4425
			if ($start = is_string($zipPath)) {
4426
				$zip = new ZipArchive();
4427
				if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
4428
					$zip = false;
4429
				}
4430
			} else {
4431
				$zip = $zipPath;
4432
			}
4433
			if ($zip) {
4434
				foreach($files as $file) {
4435
					$path = $dir . DIRECTORY_SEPARATOR . $file;
4436
					if (is_dir($path)) {
4437
						$zip->addEmptyDir($file);
4438
						$_files = array();
4439
						if ($handle = opendir($path)) {
4440
							while (false !== ($entry = readdir($handle))) {
4441
								if ($entry !== "." && $entry !== "..") {
4442
									$_files[] = $file . DIRECTORY_SEPARATOR . $entry;
4443
								}
4444
							}
4445
							closedir($handle);
4446
						}
4447
						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...
4448
							self::zipArchiveZip($dir, $_files, $zip);
4449
						}
4450
					} else {
4451
						$zip->addFile($path, $file);
4452
					}
4453
				}
4454
				$start && $zip->close();
4455
			}
4456
		} catch (Exception $e) {
4457
			return false;
4458
		}
4459
		return true;
4460
	}
4461
	
4462
	/**
4463
	 * Unpack Zip archive using PHP class ZipArchive
4464
	 * 
4465
	 * @param  string $zipPath  Zip archive name
4466
	 * @param  string $toDir    Extract to path
4467
	 * @return bool
4468
	 * @author Naoki Sawada
4469
	 */
4470
	protected static function zipArchiveUnzip($zipPath, $toDir) {
4471
		try {
4472
			$zip = new ZipArchive();
4473
			if ($zip->open($zipPath) === true) {
4474
				$zip->extractTo($toDir);
4475
				$zip->close();
4476
			}
4477
		} catch (Exception $e) {
4478
			return false;
4479
		}
4480
		return true;
4481
	}
4482
	
4483
	/**==================================* abstract methods *====================================**/
4484
	
4485
	/*********************** paths/urls *************************/
4486
	
4487
	/**
4488
	 * Return parent directory path
4489
	 *
4490
	 * @param  string  $path  file path
4491
	 * @return string
4492
	 * @author Dmitry (dio) Levashov
4493
	 **/
4494
	abstract protected function _dirname($path);
4495
4496
	/**
4497
	 * Return file name
4498
	 *
4499
	 * @param  string  $path  file path
4500
	 * @return string
4501
	 * @author Dmitry (dio) Levashov
4502
	 **/
4503
	abstract protected function _basename($path);
4504
4505
	/**
4506
	 * Join dir name and file name and return full path.
4507
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
4508
	 *
4509
	 * @param  string  $dir   dir path
4510
	 * @param  string  $name  file name
4511
	 * @return string
4512
	 * @author Dmitry (dio) Levashov
4513
	 **/
4514
	abstract protected function _joinPath($dir, $name);
4515
4516
	/**
4517
	 * Return normalized path 
4518
	 *
4519
	 * @param  string  $path  file path
4520
	 * @return string
4521
	 * @author Dmitry (dio) Levashov
4522
	 **/
4523
	abstract protected function _normpath($path);
4524
4525
	/**
4526
	 * Return file path related to root dir
4527
	 *
4528
	 * @param  string  $path  file path
4529
	 * @return string
4530
	 * @author Dmitry (dio) Levashov
4531
	 **/
4532
	abstract protected function _relpath($path);
4533
	
4534
	/**
4535
	 * Convert path related to root dir into real path
4536
	 *
4537
	 * @param  string  $path  rel file path
4538
	 * @return string
4539
	 * @author Dmitry (dio) Levashov
4540
	 **/
4541
	abstract protected function _abspath($path);
4542
	
4543
	/**
4544
	 * Return fake path started from root dir.
4545
	 * Required to show path on client side.
4546
	 *
4547
	 * @param  string  $path  file path
4548
	 * @return string
4549
	 * @author Dmitry (dio) Levashov
4550
	 **/
4551
	abstract protected function _path($path);
4552
	
4553
	/**
4554
	 * Return true if $path is children of $parent
4555
	 *
4556
	 * @param  string  $path    path to check
4557
	 * @param  string  $parent  parent path
4558
	 * @return bool
4559
	 * @author Dmitry (dio) Levashov
4560
	 **/
4561
	abstract protected function _inpath($path, $parent);
4562
	
4563
	/**
4564
	 * Return stat for given path.
4565
	 * Stat contains following fields:
4566
	 * - (int)    size    file size in b. required
4567
	 * - (int)    ts      file modification time in unix time. required
4568
	 * - (string) mime    mimetype. required for folders, others - optionally
4569
	 * - (bool)   read    read permissions. required
4570
	 * - (bool)   write   write permissions. required
4571
	 * - (bool)   locked  is object locked. optionally
4572
	 * - (bool)   hidden  is object hidden. optionally
4573
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
4574
	 * - (string) target  for symlinks - link target path. optionally
4575
	 *
4576
	 * If file does not exists - returns empty array or false.
4577
	 *
4578
	 * @param  string  $path    file path 
4579
	 * @return array|false
4580
	 * @author Dmitry (dio) Levashov
4581
	 **/
4582
	abstract protected function _stat($path);
4583
	
4584
4585
	/***************** file stat ********************/
4586
4587
		
4588
	/**
4589
	 * Return true if path is dir and has at least one childs directory
4590
	 *
4591
	 * @param  string  $path  dir path
4592
	 * @return bool
4593
	 * @author Dmitry (dio) Levashov
4594
	 **/
4595
	abstract protected function _subdirs($path);
4596
	
4597
	/**
4598
	 * Return object width and height
4599
	 * Ususaly used for images, but can be realize for video etc...
4600
	 *
4601
	 * @param  string  $path  file path
4602
	 * @param  string  $mime  file mime type
4603
	 * @return string
4604
	 * @author Dmitry (dio) Levashov
4605
	 **/
4606
	abstract protected function _dimensions($path, $mime);
4607
	
4608
	/******************** file/dir content *********************/
4609
4610
	/**
4611
	 * Return files list in directory
4612
	 *
4613
	 * @param  string  $path  dir path
4614
	 * @return array
4615
	 * @author Dmitry (dio) Levashov
4616
	 **/
4617
	abstract protected function _scandir($path);
4618
	
4619
	/**
4620
	 * Open file and return file pointer
4621
	 *
4622
	 * @param  string  $path  file path
4623
	 * @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...
4624
	 * @return resource|false
4625
	 * @author Dmitry (dio) Levashov
4626
	 **/
4627
	abstract protected function _fopen($path, $mode="rb");
4628
	
4629
	/**
4630
	 * Close opened file
4631
	 * 
4632
	 * @param  resource  $fp    file pointer
4633
	 * @param  string    $path  file path
4634
	 * @return bool
4635
	 * @author Dmitry (dio) Levashov
4636
	 **/
4637
	abstract protected function _fclose($fp, $path='');
4638
	
4639
	/********************  file/dir manipulations *************************/
4640
	
4641
	/**
4642
	 * Create dir and return created dir path or false on failed
4643
	 *
4644
	 * @param  string  $path  parent dir path
4645
	 * @param string  $name  new directory name
4646
	 * @return string|bool
4647
	 * @author Dmitry (dio) Levashov
4648
	 **/
4649
	abstract protected function _mkdir($path, $name);
4650
	
4651
	/**
4652
	 * Create file and return it's path or false on failed
4653
	 *
4654
	 * @param  string  $path  parent dir path
4655
	 * @param string  $name  new file name
4656
	 * @return string|bool
4657
	 * @author Dmitry (dio) Levashov
4658
	 **/
4659
	abstract protected function _mkfile($path, $name);
4660
	
4661
	/**
4662
	 * Create symlink
4663
	 *
4664
	 * @param  string  $source     file to link to
4665
	 * @param  string  $targetDir  folder to create link in
4666
	 * @param  string  $name       symlink name
4667
	 * @return bool
4668
	 * @author Dmitry (dio) Levashov
4669
	 **/
4670
	abstract protected function _symlink($source, $targetDir, $name);
4671
	
4672
	/**
4673
	 * Copy file into another file (only inside one volume)
4674
	 *
4675
	 * @param  string  $source  source file path
4676
	 * @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...
4677
	 * @param  string  $name    file name
4678
	 * @return bool
4679
	 * @author Dmitry (dio) Levashov
4680
	 **/
4681
	abstract protected function _copy($source, $targetDir, $name);
4682
	
4683
	/**
4684
	 * Move file into another parent dir.
4685
	 * Return new file path or false.
4686
	 *
4687
	 * @param  string  $source  source file path
4688
	 * @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...
4689
	 * @param  string  $name    file name
4690
	 * @return string|bool
4691
	 * @author Dmitry (dio) Levashov
4692
	 **/
4693
	abstract protected function _move($source, $targetDir, $name);
4694
	
4695
	/**
4696
	 * Remove file
4697
	 *
4698
	 * @param  string  $path  file path
4699
	 * @return bool
4700
	 * @author Dmitry (dio) Levashov
4701
	 **/
4702
	abstract protected function _unlink($path);
4703
4704
	/**
4705
	 * Remove dir
4706
	 *
4707
	 * @param  string  $path  dir path
4708
	 * @return bool
4709
	 * @author Dmitry (dio) Levashov
4710
	 **/
4711
	abstract protected function _rmdir($path);
4712
4713
	/**
4714
	 * Create new file and write into it from file pointer.
4715
	 * Return new file path or false on error.
4716
	 *
4717
	 * @param  resource  $fp   file pointer
4718
	 * @param  string    $dir  target dir path
4719
	 * @param  string    $name file name
4720
	 * @param  array     $stat file stat (required by some virtual fs)
4721
	 * @return bool|string
4722
	 * @author Dmitry (dio) Levashov
4723
	 **/
4724
	abstract protected function _save($fp, $dir, $name, $stat);
4725
	
4726
	/**
4727
	 * Get file contents
4728
	 *
4729
	 * @param  string  $path  file path
4730
	 * @return string|false
4731
	 * @author Dmitry (dio) Levashov
4732
	 **/
4733
	abstract protected function _getContents($path);
4734
	
4735
	/**
4736
	 * Write a string to a file
4737
	 *
4738
	 * @param  string  $path     file path
4739
	 * @param  string  $content  new file content
4740
	 * @return bool
4741
	 * @author Dmitry (dio) Levashov
4742
	 **/
4743
	abstract protected function _filePutContents($path, $content);
4744
4745
	/**
4746
	 * Extract files from archive
4747
	 *
4748
	 * @param  string  $path file path
4749
	 * @param  array   $arc  archiver options
4750
	 * @return bool
4751
	 * @author Dmitry (dio) Levashov, 
4752
	 * @author Alexey Sukhotin
4753
	 **/
4754
	abstract protected function _extract($path, $arc);
4755
4756
	/**
4757
	 * Create archive and return its path
4758
	 *
4759
	 * @param  string  $dir    target dir
4760
	 * @param  array   $files  files names list
4761
	 * @param  string  $name   archive name
4762
	 * @param  array   $arc    archiver options
4763
	 * @return string|bool
4764
	 * @author Dmitry (dio) Levashov, 
4765
	 * @author Alexey Sukhotin
4766
	 **/
4767
	abstract protected function _archive($dir, $files, $name, $arc);
4768
4769
	/**
4770
	 * Detect available archivers
4771
	 *
4772
	 * @return void
4773
	 * @author Dmitry (dio) Levashov, 
4774
	 * @author Alexey Sukhotin
4775
	 **/
4776
	abstract protected function _checkArchivers();
4777
4778
	/**
4779
	 * Change file mode (chmod)
4780
	 *
4781
	 * @param  string  $path  file path
4782
	 * @param  string  $mode  octal string such as '0755'
4783
	 * @return bool
4784
	 * @author David Bartle,
4785
	 **/
4786
	abstract protected function _chmod($path, $mode);
4787
4788
	
4789
} // END class
4790