elFinderVolumeDriver::getItemsInHand()   F
last analyzed

Complexity

Conditions 25
Paths 506

Size

Total Lines 74
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 51
nc 506
nop 3
dl 0
loc 74
rs 3.4377
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Base class for elFinder volume.
4
 * Provide 2 layers:
5
 *  1. Public API (commands)
6
 *  2. abstract fs API.
7
 *
8
 * All abstract methods begin with "_"
9
 *
10
 * @author Dmitry (dio) Levashov
11
 * @author Troex Nevelin
12
 * @author Alexey Sukhotin
13
 **/
14
abstract class elFinderVolumeDriver
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

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

namespace YourVendor;

class YourClass { }

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

Loading history...
15
{
16
    /**
17
     * Net mount key.
18
     *
19
     * @var string
20
     **/
21
    public $netMountKey = '';
22
23
    /**
24
     * Request args
25
     * $_POST or $_GET values.
26
     *
27
     * @var array
28
     */
29
    protected $ARGS = [];
30
31
    /**
32
     * Driver id
33
     * Must be started from letter and contains [a-z0-9]
34
     * Used as part of volume id.
35
     *
36
     * @var string
37
     **/
38
    protected $driverId = 'a';
39
40
    /**
41
     * Volume id - used as prefix for files hashes.
42
     *
43
     * @var string
44
     **/
45
    protected $id = '';
46
47
    /**
48
     * Flag - volume "mounted" and available.
49
     *
50
     * @var bool
51
     **/
52
    protected $mounted = false;
53
54
    /**
55
     * Root directory path.
56
     *
57
     * @var string
58
     **/
59
    protected $root = '';
60
61
    /**
62
     * Root basename | alias.
63
     *
64
     * @var string
65
     **/
66
    protected $rootName = '';
67
68
    /**
69
     * Default directory to open.
70
     *
71
     * @var string
72
     **/
73
    protected $startPath = '';
74
75
    /**
76
     * Base URL.
77
     *
78
     * @var string
79
     **/
80
    protected $URL = '';
81
82
    /**
83
     * A file save destination path when a temporary content URL is required
84
     * on a network volume or the like
85
     * If not specified, it tries to use "Connector Path/../files/.tmb".
86
     *
87
     * @var string
88
     */
89
    protected $tmpLinkPath = '';
90
91
    /**
92
     * A file save destination URL when a temporary content URL is required
93
     * on a network volume or the like
94
     * If not specified, it tries to use "Connector URL/../files/.tmb".
95
     *
96
     * @var string
97
     */
98
    protected $tmpLinkUrl = '';
99
100
    /**
101
     * Thumbnails dir path.
102
     *
103
     * @var string
104
     **/
105
    protected $tmbPath = '';
106
107
    /**
108
     * Is thumbnails dir writable.
109
     *
110
     * @var bool
111
     **/
112
    protected $tmbPathWritable = false;
113
114
    /**
115
     * Thumbnails base URL.
116
     *
117
     * @var string
118
     **/
119
    protected $tmbURL = '';
120
121
    /**
122
     * Thumbnails size in px.
123
     *
124
     * @var int
125
     **/
126
    protected $tmbSize = 48;
127
128
    /**
129
     * Image manipulation lib name
130
     * auto|imagick|gd|convert.
131
     *
132
     * @var string
133
     **/
134
    protected $imgLib = 'auto';
135
136
    /**
137
     * Video to Image converter.
138
     *
139
     * @var array
140
     */
141
    protected $imgConverter = [];
142
143
    /**
144
     * Library to crypt files name.
145
     *
146
     * @var string
147
     **/
148
    protected $cryptLib = '';
149
150
    /**
151
     * Archivers config.
152
     *
153
     * @var array
154
     **/
155
    protected $archivers = [
156
        'create' => [],
157
        'extract' => [],
158
    ];
159
160
    /**
161
     * Server character encoding.
162
     *
163
     * @var string or null
164
     **/
165
    protected $encoding = null;
166
167
    /**
168
     * How many subdirs levels return for tree.
169
     *
170
     * @var int
171
     **/
172
    protected $treeDeep = 1;
173
174
    /**
175
     * Errors from last failed action.
176
     *
177
     * @var array
178
     **/
179
    protected $error = [];
180
181
    /**
182
     * Today 24:00 timestamp.
183
     *
184
     * @var int
185
     **/
186
    protected $today = 0;
187
188
    /**
189
     * Yesterday 24:00 timestamp.
190
     *
191
     * @var int
192
     **/
193
    protected $yesterday = 0;
194
195
    /**
196
     * Force make dirctory on extract.
197
     *
198
     * @var int
199
     **/
200
    protected $extractToNewdir = 'auto';
201
202
    /**
203
     * Object configuration.
204
     *
205
     * @var array
206
     **/
207
    protected $options = [
208
        // Driver ID (Prefix of volume ID), Normally, the value specified for each volume driver is used.
209
        'driverId' => '',
210
        // Id (Suffix of volume ID), Normally, the number incremented according to the specified number of volumes is used.
211
        'id' => '',
212
        // revision id of root directory that uses for caching control of root stat
213
        'rootRev' => '',
214
        // driver type it uses volume root's CSS class name. e.g. 'group' -> Adds 'elfinder-group' to CSS class name.
215
        'type' => '',
216
        // root directory path
217
        'path' => '',
218
        // Folder hash value on elFinder to be the parent of this volume
219
        'phash' => '',
220
        // Folder hash value on elFinder to trash bin of this volume, it require 'copyJoin' to true
221
        'trashHash' => '',
222
        // open this path on initial request instead of root path
223
        'startPath' => '',
224
        // how many subdirs levels return per request
225
        'treeDeep' => 1,
226
        // root url, not set to disable sending URL to client (replacement for old "fileURL" option)
227
        'URL' => '',
228
        // directory link url to own manager url with folder hash (`true`, `false` or default `'auto'`: URL is empty then `true` else `false`)
229
        'dirUrlOwn' => 'auto',
230
        // directory separator. required by client to show paths correctly
231
        'separator' => DIRECTORY_SEPARATOR,
232
        // Server character encoding (default is '': UTF-8)
233
        'encoding' => '',
234
        // for convert character encoding (default is '': Not change locale)
235
        'locale' => '',
236
        // URL of volume icon (16x16 pixel image file)
237
        'icon' => '',
238
        // CSS Class of volume root in tree
239
        'rootCssClass' => '',
240
        // Items to disable session caching
241
        'noSessionCache' => [],
242
        // enable i18n folder name that convert name to elFinderInstance.messages['folder_'+name]
243
        'i18nFolderName' => false,
244
        // Search timeout (sec)
245
        'searchTimeout' => 30,
246
        // Search exclusion directory regex pattern (require demiliter e.g. '#/path/to/exclude_directory#i')
247
        'searchExDirReg' => '',
248
        // library to crypt/uncrypt files names (not implemented)
249
        'cryptLib' => '',
250
        // how to detect files mimetypes. (auto/internal/finfo/mime_content_type)
251
        'mimeDetect' => 'auto',
252
        // mime.types file path (for mimeDetect==internal)
253
        'mimefile' => '',
254
        // Static extension/MIME of general server side scripts to security issues
255
        'staticMineMap' => [
256
            'php:*' => 'text/x-php',
257
            'php3:*' => 'text/x-php',
258
            'php4:*' => 'text/x-php',
259
            'php5:*' => 'text/x-php',
260
            'phtml:*' => 'text/x-php',
261
            'cgi:*' => 'text/x-httpd-cgi',
262
            'pl:*' => 'text/x-perl',
263
            'asp:*' => 'text/x-asap',
264
            'aspx:*' => 'text/x-asap',
265
            'py:*' => 'text/x-python',
266
            'rb:*' => 'text/x-ruby',
267
            'jsp:*' => 'text/x-jsp',
268
        ],
269
        // mime type normalize map : Array '[ext]:[detected mime type]' => '[normalized mime]'
270
        'mimeMap' => [
271
            'md:application/x-genesis-rom' => 'text/x-markdown',
272
            'md:text/plain' => 'text/x-markdown',
273
            'markdown:text/plain' => 'text/x-markdown',
274
            'css:text/x-asm' => 'text/css',
275
            'ico:image/vnd.microsoft.icon' => 'image/x-icon',
276
            'csv:text/plain' => 'text/csv',
277
            'm4a:video/mp4' => 'audio/mp4',
278
            'oga:application/ogg' => 'audio/ogg',
279
            'ogv:application/ogg' => 'video/ogg',
280
            'zip:application/x-zip' => 'application/zip',
281
        ],
282
        // An option to add MimeMap to the `mimeMap` option
283
        // Array '[ext]:[detected mime type]' => '[normalized mime]'
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...
284
        'additionalMimeMap' => [],
285
        // MIME regex of send HTTP header "Content-Disposition: inline" or allow preview in quicklook
286
        // '.' is allow inline of all of MIME types
287
        // '$^' is not allow inline of all of MIME types
288
        'dispInlineRegex' => '^(?:(?:image|video|audio)|(?:text/plain|application/pdf)$)',
289
        // temporary content URL's base path
290
        'tmpLinkPath' => '',
291
        // temporary content URL's base URL
292
        'tmpLinkUrl' => '',
293
        // directory for thumbnails
294
        'tmbPath' => '.tmb',
295
        // mode to create thumbnails dir
296
        'tmbPathMode' => 0777,
297
        // thumbnails dir URL. Set it if store thumbnails outside root directory
298
        'tmbURL' => '',
299
        // thumbnails size (px)
300
        'tmbSize' => 48,
301
        // thumbnails crop (true - crop, false - scale image to fit thumbnail size)
302
        'tmbCrop' => true,
303
        // thumbnails background color (hex #rrggbb or 'transparent')
304
        'tmbBgColor' => 'transparent',
305
        // image rotate fallback background color (hex #rrggbb)
306
        'bgColorFb' => '#ffffff',
307
        // image manipulations library
308
        'imgLib' => 'auto',
309
        // Fallback self image to thumbnail (nothing imgLib)
310
        'tmbFbSelf' => true,
311
        // Video to Image converters ['TYPE or MIME' => ['func' => function($file){ /* Converts $file to Image */ return true; }, 'maxlen' => (int)TransferLength]]
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
312
        'imgConverter' => [],
313
        // Max length of transfer to image converter
314
        'tmbVideoConvLen' => 10000000,
315
        // Captre point seccond
316
        'tmbVideoConvSec' => 6,
317
        // Resource path of fallback icon images defailt: php/resouces
318
        'resourcePath' => '',
319
        // Jpeg image saveing quality
320
        'jpgQuality' => 100,
321
        // on paste file -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
322
        'copyOverwrite' => true,
323
        // if true - join new and old directories content on paste
324
        'copyJoin' => true,
325
        // on upload -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
326
        'uploadOverwrite' => true,
327
        // mimetypes allowed to upload
328
        'uploadAllow' => [],
329
        // mimetypes not allowed to upload
330
        'uploadDeny' => [],
331
        // order to proccess uploadAllow and uploadDeny options
332
        'uploadOrder' => ['deny', 'allow'],
333
        // maximum upload file size. NOTE - this is size for every uploaded files
334
        'uploadMaxSize' => 0,
335
        // maximum number of chunked upload connection. `-1` to disable chunked upload
336
        'uploadMaxConn' => 3,
337
        // maximum get file size. NOTE - Maximum value is 50% of PHP memory_limit
338
        'getMaxSize' => 0,
339
        // files dates format
340
        'dateFormat' => 'j M Y H:i',
341
        // files time format
342
        'timeFormat' => 'H:i',
343
        // if true - every folder will be check for children folders, -1 - every folder will be check asynchronously, false -  all folders will be marked as having subfolders
344
        'checkSubfolders' => true, // true, false or -1
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...
345
        // allow to copy from this volume to other ones?
346
        'copyFrom' => true,
347
        // allow to copy from other volumes to this one?
348
        'copyTo' => true,
349
        // cmd duplicate suffix format e.g. '_%s_' to without spaces
350
        'duplicateSuffix' => ' %s ',
351
        // unique name numbar format e.g. '(%d)' to (1), (2)...
352
        'uniqueNumFormat' => '%d',
353
        // list of commands disabled on this root
354
        'disabled' => [],
355
        // enable file owner, group & mode info, `false` to inactivate "chmod" command.
356
        'statOwner' => false,
357
        // allow exec chmod of read-only files
358
        'allowChmodReadOnly' => false,
359
        // regexp or function name to validate new file name
360
        'acceptedName' => '/^[^\.].*/', // Notice: overwritten it in some volume drivers contractor
361
        // regexp or function name to validate new directory name
362
        'acceptedDirname' => '', // used `acceptedName` if empty value
363
            // function/class method to control files permissions
364
        'accessControl' => null,
365
        // some data required by access control
366
        'accessControlData' => null,
367
        // default permissions.
368
        'defaults' => [
369
            'read' => true,
370
            'write' => true,
371
            'locked' => false,
372
            'hidden' => false,
373
        ],
374
        // files attributes
375
        'attributes' => [],
376
        // max allowed archive files size (0 - no limit)
377
        'maxArcFilesSize' => 0,
378
        // Allowed archive's mimetypes to create. Leave empty for all available types.
379
        'archiveMimes' => [],
380
        // Manual config for archivers. See example below. Leave empty for auto detect
381
        'archivers' => [],
382
        // plugin settings
383
        'plugin' => [],
384
        // Is support parent directory time stamp update on add|remove|rename item
385
        // Default `null` is auto detection that is LocalFileSystem, FTP or Dropbox are `true`
386
        'syncChkAsTs' => null,
387
        // Long pooling sync checker function for syncChkAsTs is true
388
        // Calls with args (TARGET DIRCTORY PATH, STAND-BY(sec), OLD TIMESTAMP, VOLUME DRIVER INSTANCE, ELFINDER INSTANCE)
389
        // This function must return the following values. Changed: New Timestamp or Same: Old Timestamp or Error: false
390
        // Default `null` is try use elFinderVolumeLocalFileSystem::localFileSystemInotify() on LocalFileSystem driver
391
        // another driver use elFinder stat() checker
392
        'syncCheckFunc' => null,
393
        // Long polling sync stand-by time (sec)
394
        'plStandby' => 30,
395
        // Sleep time (sec) for elFinder stat() checker (syncChkAsTs is true)
396
        'tsPlSleep' => 10,
397
        // Sleep time (sec) for elFinder ls() checker (syncChkAsTs is false)
398
        'lsPlSleep' => 30,
399
        // Client side sync interval minimum (ms)
400
        // Default `null` is auto set to ('tsPlSleep' or 'lsPlSleep') * 1000
401
        // `0` to disable auto sync
402
        'syncMinMs' => null,
403
        // required to fix bug on macos
404
        // However, we recommend to use the Normalizer plugin instead this option
405
        'utf8fix' => false,
406
         //                           й                 ё              Й               Ё              Ø         Å
407
        'utf8patterns' => ["\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"],
408
        'utf8replace' => ["\u0439",        "\u0451",       "\u0419",       "\u0401",       "\u00d8", "\u00c5"],
409
    ];
410
411
    /**
412
     * Defaults permissions.
413
     *
414
     * @var array
415
     **/
416
    protected $defaults = [
417
        'read' => true,
418
        'write' => true,
419
        'locked' => false,
420
        'hidden' => false,
421
    ];
422
423
    /**
424
     * Access control function/class.
425
     *
426
     * @var mixed
427
     **/
428
    protected $attributes = [];
429
430
    /**
431
     * Access control function/class.
432
     *
433
     * @var mixed
434
     **/
435
    protected $access = null;
436
437
    /**
438
     * Mime types allowed to upload.
439
     *
440
     * @var array
441
     **/
442
    protected $uploadAllow = [];
443
444
    /**
445
     * Mime types denied to upload.
446
     *
447
     * @var array
448
     **/
449
    protected $uploadDeny = [];
450
451
    /**
452
     * Order to validate uploadAllow and uploadDeny.
453
     *
454
     * @var array
455
     **/
456
    protected $uploadOrder = [];
457
458
    /**
459
     * Maximum allowed upload file size.
460
     * Set as number or string with unit - "10M", "500K", "1G".
461
     *
462
     * @var int|string
463
     **/
464
    protected $uploadMaxSize = 0;
465
466
    /**
467
     * Run time setting of overwrite items on upload.
468
     *
469
     * @var string
470
     */
471
    protected $uploadOverwrite = true;
472
473
    /**
474
     * Maximum allowed get file size.
475
     * Set as number or string with unit - "10M", "500K", "1G".
476
     *
477
     * @var int|string
478
     **/
479
    protected $getMaxSize = -1;
480
481
    /**
482
     * Mimetype detect method.
483
     *
484
     * @var string
485
     **/
486
    protected $mimeDetect = 'auto';
487
488
    /**
489
     * Finfo object for mimeDetect == 'finfo'.
490
     *
491
     * @var object
492
     **/
493
    protected $finfo = null;
494
495
    /**
496
     * List of disabled client's commands.
497
     *
498
     * @var array
499
     **/
500
    protected $disabled = [];
501
502
    /**
503
     * default extensions/mimetypes for mimeDetect == 'internal'.
504
     *
505
     * @var array
506
     **/
507
    protected static $mimetypes = [
508
        // applications
509
        'ai' => 'application/postscript',
510
        'eps' => 'application/postscript',
511
        'exe' => 'application/x-executable',
512
        'doc' => 'application/msword',
513
        'dot' => 'application/msword',
514
        'xls' => 'application/vnd.ms-excel',
515
        'xlt' => 'application/vnd.ms-excel',
516
        'xla' => 'application/vnd.ms-excel',
517
        'ppt' => 'application/vnd.ms-powerpoint',
518
        'pps' => 'application/vnd.ms-powerpoint',
519
        'pdf' => 'application/pdf',
520
        'xml' => 'application/xml',
521
        'swf' => 'application/x-shockwave-flash',
522
        'torrent' => 'application/x-bittorrent',
523
        'jar' => 'application/x-jar',
524
        // open office (finfo detect as application/zip)
525
        'odt' => 'application/vnd.oasis.opendocument.text',
526
        'ott' => 'application/vnd.oasis.opendocument.text-template',
527
        'oth' => 'application/vnd.oasis.opendocument.text-web',
528
        'odm' => 'application/vnd.oasis.opendocument.text-master',
529
        'odg' => 'application/vnd.oasis.opendocument.graphics',
530
        'otg' => 'application/vnd.oasis.opendocument.graphics-template',
531
        'odp' => 'application/vnd.oasis.opendocument.presentation',
532
        'otp' => 'application/vnd.oasis.opendocument.presentation-template',
533
        'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
534
        'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
535
        'odc' => 'application/vnd.oasis.opendocument.chart',
536
        'odf' => 'application/vnd.oasis.opendocument.formula',
537
        'odb' => 'application/vnd.oasis.opendocument.database',
538
        'odi' => 'application/vnd.oasis.opendocument.image',
539
        'oxt' => 'application/vnd.openofficeorg.extension',
540
        // MS office 2007 (finfo detect as application/zip)
541
        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
542
        'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
543
        'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
544
        'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
545
        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
546
        'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
547
        'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
548
        'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
549
        'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
550
        'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
551
        'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
552
        'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
553
        'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
554
        'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
555
        'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
556
        'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
557
        'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
558
        'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
559
        'sldm' => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
560
        // archives
561
        'gz' => 'application/x-gzip',
562
        'tgz' => 'application/x-gzip',
563
        'bz' => 'application/x-bzip2',
564
        'bz2' => 'application/x-bzip2',
565
        'tbz' => 'application/x-bzip2',
566
        'xz' => 'application/x-xz',
567
        'zip' => 'application/zip',
568
        'rar' => 'application/x-rar',
569
        'tar' => 'application/x-tar',
570
        '7z' => 'application/x-7z-compressed',
571
        // texts
572
        'txt' => 'text/plain',
573
        'php' => 'text/x-php',
574
        'html' => 'text/html',
575
        'htm' => 'text/html',
576
        'js' => 'text/javascript',
577
        'css' => 'text/css',
578
        'rtf' => 'text/rtf',
579
        'rtfd' => 'text/rtfd',
580
        'py' => 'text/x-python',
581
        'java' => 'text/x-java-source',
582
        'rb' => 'text/x-ruby',
583
        'sh' => 'text/x-shellscript',
584
        'pl' => 'text/x-perl',
585
        //'xml'   => 'text/xml',
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...
586
        'sql' => 'text/x-sql',
587
        'c' => 'text/x-csrc',
588
        'h' => 'text/x-chdr',
589
        'cpp' => 'text/x-c++src',
590
        'hh' => 'text/x-c++hdr',
591
        'log' => 'text/plain',
592
        'csv' => 'text/csv',
593
        'md' => 'text/x-markdown',
594
        'markdown' => 'text/x-markdown',
595
        // images
596
        'bmp' => 'image/x-ms-bmp',
597
        'jpg' => 'image/jpeg',
598
        'jpeg' => 'image/jpeg',
599
        'gif' => 'image/gif',
600
        'png' => 'image/png',
601
        'tif' => 'image/tiff',
602
        'tiff' => 'image/tiff',
603
        'tga' => 'image/x-targa',
604
        'psd' => 'image/vnd.adobe.photoshop',
605
        //'ai'    => 'image/vnd.adobe.photoshop',
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...
606
        'xbm' => 'image/xbm',
607
        'pxm' => 'image/pxm',
608
        //audio
609
        'mp3' => 'audio/mpeg',
610
        'mid' => 'audio/midi',
611
        'ogg' => 'audio/ogg',
612
        'oga' => 'audio/ogg',
613
        'm4a' => 'audio/mp4',
614
        'wav' => 'audio/wav',
615
        'wma' => 'audio/x-ms-wma',
616
        // video
617
        'avi' => 'video/x-msvideo',
618
        'dv' => 'video/x-dv',
619
        'mp4' => 'video/mp4',
620
        'mpeg' => 'video/mpeg',
621
        'mpg' => 'video/mpeg',
622
        'mov' => 'video/quicktime',
623
        'wm' => 'video/x-ms-wmv',
624
        'flv' => 'video/x-flv',
625
        'mkv' => 'video/x-matroska',
626
        'webm' => 'video/webm',
627
        'ogv' => 'video/ogg',
628
        'ogm' => 'video/ogg',
629
        ];
630
631
    /**
632
     * Directory separator - required by client.
633
     *
634
     * @var string
635
     **/
636
    protected $separator = DIRECTORY_SEPARATOR;
637
638
    /**
639
     * System Root path (Unix like: '/', Windows: '\', 'C:\' or 'D:\'...).
640
     *
641
     * @var string
642
     **/
643
    protected $systemRoot = DIRECTORY_SEPARATOR;
644
645
    /**
646
     * Mimetypes allowed to display.
647
     *
648
     * @var array
649
     **/
650
    protected $onlyMimes = [];
651
652
    /**
653
     * Store files moved or overwrited files info.
654
     *
655
     * @var array
656
     **/
657
    protected $removed = [];
658
659
    /**
660
     * Store files added files info.
661
     *
662
     * @var array
663
     **/
664
    protected $added = [];
665
666
    /**
667
     * Cache storage.
668
     *
669
     * @var array
670
     **/
671
    protected $cache = [];
672
673
    /**
674
     * Cache by folders.
675
     *
676
     * @var array
677
     **/
678
    protected $dirsCache = [];
679
680
    /**
681
     * You should use `$this->sessionCache['subdirs']` instead.
682
     *
683
     * @var array
684
     * @deprecated
685
     */
686
    protected $subdirsCache = [];
687
688
    /**
689
     * This volume session cache.
690
     *
691
     * @var array
692
     */
693
    protected $sessionCache;
694
695
    /**
696
     * Session caching item list.
697
     *
698
     * @var array
699
     */
700
    protected $sessionCaching = ['rootstat' => true, 'subdirs' => true];
701
702
    /**
703
     * elFinder session wrapper object.
704
     *
705
     * @var elFinderSessionInterface
706
     */
707
    protected $session;
708
709
    /**
710
     * Search start time.
711
     *
712
     * @var int
713
     */
714
    protected $searchStart;
715
716
    /**
717
     * Current query word on doSearch.
718
     *
719
     * @var string
720
     **/
721
    protected $doSearchCurrentQuery = [];
722
723
    /**
724
     * Is root modified (for clear root stat cache).
725
     *
726
     * @var bool
727
     */
728
    protected $rootModified = false;
729
730
    /**
731
     * Is disable of command `url`.
732
     *
733
     * @var string
734
     */
735
    protected $disabledGetUrl = false;
736
737
    /**
738
     * Accepted filename validator.
739
     *
740
     * @var string | callable
741
     */
742
    protected $nameValidator;
743
744
    /**
745
     * Accepted dirname validator.
746
     *
747
     * @var string | callable
748
     */
749
    protected $dirnameValidator;
750
751
    /**
752
     * Flag - mimetypes from externail file was loaded.
753
     *
754
     * @var bool
755
     **/
756
    private static $mimetypesLoaded = false;
757
758
    /*********************************************************************/
759
    /*                              PUBLIC API                           */
760
    /*********************************************************************/
761
762
    /**
763
     * Return driver id. Used as a part of volume id.
764
     *
765
     * @return string
766
     * @author Dmitry (dio) Levashov
767
     **/
768
    public function driverId()
769
    {
770
        return $this->driverId;
771
    }
772
773
    /**
774
     * Return volume id.
775
     *
776
     * @return string
777
     * @author Dmitry (dio) Levashov
778
     **/
779
    public function id()
780
    {
781
        return $this->id;
782
    }
783
784
    /**
785
     * Assign elFinder session wrapper object.
786
     *
787
     * @param  $session  elFinderSessionInterface
788
     */
789
    public function setSession($session)
790
    {
791
        $this->session = $session;
792
    }
793
794
    /**
795
     * Save session cache data
796
     * Calls this function before umount this volume on elFinder::exec().
797
     *
798
     * @return void
799
     */
800
    public function saveSessionCache()
801
    {
802
        $this->session->set($this->id, $this->sessionCache);
803
    }
804
805
    /**
806
     * Return debug info for client.
807
     *
808
     * @return array
809
     * @author Dmitry (dio) Levashov
810
     **/
811
    public function debug()
812
    {
813
        return [
814
            'id' => $this->id(),
815
            'name' => strtolower(substr(get_class($this), strlen('elfinderdriver'))),
816
            'mimeDetect' => $this->mimeDetect,
817
            'imgLib' => $this->imgLib,
818
        ];
819
    }
820
821
    /**
822
     * chmod a file or folder.
823
     *
824
     * @param  string   $hash    file or folder hash to chmod
825
     * @param  string   $mode    octal string representing new permissions
826
     * @return array|false
827
     * @author David Bartle
828
     **/
829
    public function chmod($hash, $mode)
830
    {
831
        if ($this->commandDisabled('chmod')) {
832
            return $this->setError(elFinder::ERROR_PERM_DENIED);
833
        }
834
835
        if (! ($file = $this->file($hash))) {
836
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
837
        }
838
839 View Code Duplication
        if (! $this->options['allowChmodReadOnly']) {
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...
840
            if (! $this->attr($this->decode($hash), 'write', null, ($file['mime'] === 'directory'))) {
841
                return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
842
            }
843
        }
844
845
        $path = $this->decode($hash);
846
        $write = $file['write'];
847
848 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...
849
            return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
850
        }
851
852
        $this->clearcache();
853
        if ($path == $this->root) {
854
            $this->rootModified = true;
855
        }
856
857
        if ($file = $this->stat($path)) {
858
            $files = [$file];
859
            if ($file['mime'] === 'directory' && $write !== $file['write']) {
860
                foreach ($this->getScandir($path) as $stat) {
861
                    if ($this->mimeAccepted($stat['mime'])) {
862
                        $files[] = $stat;
863
                    }
864
                }
865
            }
866
867
            return $files;
868
        } else {
869
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
870
        }
871
    }
872
873
    /**
874
     * stat a file or folder for elFinder cmd exec.
875
     *
876
     * @param  string   $hash    file or folder hash to chmod
877
     * @return array
878
     * @author Naoki Sawada
879
     **/
880
    public function fstat($hash)
881
    {
882
        $path = $this->decode($hash);
883
884
        return $this->stat($path);
885
    }
886
887
    /**
888
     * Clear PHP stat cache & all of inner stat caches.
889
     */
890
    public function clearstatcache()
891
    {
892
        clearstatcache();
893
        $this->clearcache();
894
    }
895
896
    /**
897
     * Clear inner stat caches for target hash.
898
     *
899
     * @param string $hash
900
     */
901
    public function clearcaches($hash = null)
902
    {
903
        if ($hash === null) {
904
            $this->clearcache();
905
        } else {
906
            $path = $this->decode($hash);
907
            unset($this->cache[$path], $this->dirsCache[$path]);
908
        }
909
    }
910
911
    /**
912
     * "Mount" volume.
913
     * Return true if volume available for read or write,
914
     * false - otherwise.
915
     *
916
     * @param array $opts
917
     * @return bool
918
     * @author Dmitry (dio) Levashov
919
     * @author Alexey Sukhotin
920
     */
921
    public function mount(array $opts)
0 ignored issues
show
Coding Style introduced by
mount uses the super-global variable $_SERVER which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

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

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

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

// Better
class Router
{
    private $host;

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

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

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

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

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
922
    {
923
        $this->options = array_merge($this->options, $opts);
924
925 View Code Duplication
        if (! isset($this->options['path']) || $this->options['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...
926
            return $this->setError('Path undefined.');
927
        }
928
929
        if (! $this->session) {
930
            return $this->setError('Session wrapper dose not set. Need to `$volume->setSession(elFinderSessionInterface);` before mount.');
931
        }
932
        if (! ($this->session instanceof elFinderSessionInterface)) {
933
            return $this->setError('Session wrapper instance must be "elFinderSessionInterface".');
934
        }
935
936
        // set driverId
937
        if (! empty($this->options['driverId'])) {
938
            $this->driverId = $this->options['driverId'];
939
        }
940
941
        $this->id = $this->driverId.(! empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
942
        $this->root = $this->normpathCE($this->options['path']);
943
        $this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
944
        $this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator;
945
946
        // set server encoding
947
        if (! empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') {
948
            $this->encoding = $this->options['encoding'];
949
        } else {
950
            $this->encoding = null;
951
        }
952
953
        // set ARGS
954
        $this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST : $_GET;
955
956
        $argInit = ! empty($this->ARGS['init']);
957
958
        // session cache
959
        if ($argInit) {
960
            $this->session->set($this->id, []);
961
        }
962
        $this->sessionCache = $this->session->get($this->id, []);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->session->get($this->id, array()) of type * is incompatible with the declared type array of property $sessionCache.

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...
963
964
        // default file attribute
965
        $this->defaults = [
966
            'read' => isset($this->options['defaults']['read']) ? (bool) $this->options['defaults']['read'] : true,
967
            'write' => isset($this->options['defaults']['write']) ? (bool) $this->options['defaults']['write'] : true,
968
            'locked' => isset($this->options['defaults']['locked']) ? (bool) $this->options['defaults']['locked'] : false,
969
            'hidden' => isset($this->options['defaults']['hidden']) ? (bool) $this->options['defaults']['hidden'] : false,
970
        ];
971
972
        // root attributes
973
        $this->attributes[] = [
974
            'pattern' => '~^'.preg_quote($this->separator).'$~',
975
            'locked' => true,
976
            'hidden' => false,
977
        ];
978
        // set files attributes
979 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...
980
            foreach ($this->options['attributes'] as $a) {
981
                // attributes must contain pattern and at least one rule
982
                if (! empty($a['pattern']) || count($a) > 1) {
983
                    $this->attributes[] = $a;
984
                }
985
            }
986
        }
987
988
        if (! empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
989
            $this->access = $this->options['accessControl'];
990
        }
991
992
        $this->today = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
993
        $this->yesterday = $this->today - 86400;
994
995
        if (! $this->init()) {
996
            return false;
997
        }
998
999
        // check some options is arrays
1000
        $this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
1001
            ? $this->options['uploadAllow']
1002
            : [];
1003
1004
        $this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
1005
            ? $this->options['uploadDeny']
1006
            : [];
1007
1008
        $this->options['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))
1009
            ? $this->options['uiCmdMap']
1010
            : [];
1011
1012
        if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
1013
            $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
1014
            $this->uploadOrder = [trim($parts[0]), trim($parts[1])];
1015
        } else { // telephat_mode off
1016
            $this->uploadOrder = ! empty($this->options['uploadOrder']) ? $this->options['uploadOrder'] : ['deny', 'allow'];
1017
        }
1018
1019
        if (! empty($this->options['uploadMaxSize'])) {
1020
            $this->uploadMaxSize = elFinder::getIniBytes('', $this->options['uploadMaxSize']);
0 ignored issues
show
Documentation Bug introduced by
It seems like \elFinder::getIniBytes('...tions['uploadMaxSize']) can also be of type double. However, the property $uploadMaxSize is declared as type integer|string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1021
        }
1022
        // Set maximum to PHP_INT_MAX
1023
        if (! defined('PHP_INT_MAX')) {
1024
            define('PHP_INT_MAX', 2147483647);
1025
        }
1026
        if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) {
1027
            $this->uploadMaxSize = PHP_INT_MAX;
1028
        }
1029
1030
        // Set to get maximum size to 50% of memory_limit
1031
        $memLimit = elFinder::getIniBytes('memory_limit') / 2;
1032
        if ($memLimit > 0) {
1033
            $this->getMaxSize = empty($this->options['getMaxSize']) ? $memLimit : min($memLimit, elFinder::getIniBytes('', $this->options['getMaxSize']));
0 ignored issues
show
Documentation Bug introduced by
It seems like empty($this->options['ge...options['getMaxSize'])) can also be of type double. However, the property $getMaxSize is declared as type integer|string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1034
        } else {
1035
            $this->getMaxSize = -1;
1036
        }
1037
1038
        $this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
1039
            ? array_values(array_diff($this->options['disabled'], ['open'])) // 'open' is required
1040
            : [];
1041
1042
        $this->cryptLib = $this->options['cryptLib'];
1043
        $this->mimeDetect = $this->options['mimeDetect'];
1044
1045
        // find available mimetype detect method
1046
        $type = strtolower($this->options['mimeDetect']);
1047
        $type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
1048
        $regexp = '/text\/x\-(php|c\+\+)/';
1049
1050
        if (($type == 'finfo' || $type == 'auto')
1051
        && class_exists('finfo', false)) {
1052
            $tmpFileInfo = explode(';', finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
1053
        } else {
1054
            $tmpFileInfo = false;
1055
        }
1056
1057
        $type = 'internal';
1058
        if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
1059
            $type = 'finfo';
1060
            $this->finfo = finfo_open(FILEINFO_MIME);
1061
        } elseif (($type == 'mime_content_type' || $type == 'auto') && function_exists('mime_content_type')) {
1062
            $_mimetypes = explode(';', mime_content_type(__FILE__));
1063
            if (preg_match($regexp, array_shift($_mimetypes))) {
1064
                $type = 'mime_content_type';
1065
            }
1066
        }
1067
        $this->mimeDetect = $type;
1068
1069
        // load mimes from external file for mimeDetect == 'internal'
1070
        // based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
1071
        // file must be in file directory or in parent one
1072
        if ($this->mimeDetect == 'internal' && ! self::$mimetypesLoaded) {
1073
            self::$mimetypesLoaded = true;
1074
            $this->mimeDetect = 'internal';
1075
            $file = false;
1076
            if (! empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
1077
                $file = $this->options['mimefile'];
1078
            } elseif (elFinder::$defaultMimefile && file_exists(elFinder::$defaultMimefile)) {
1079
                $file = elFinder::$defaultMimefile;
1080
            } elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
1081
                $file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
1082
            } elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
1083
                $file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
1084
            }
1085
1086 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...
1087
                $mimecf = file($file);
1088
1089
                foreach ($mimecf as $line_num => $line) {
1090
                    if (! preg_match('/^\s*#/', $line)) {
1091
                        $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
1092
                        for ($i = 1, $size = count($mime); $i < $size; $i++) {
1093
                            if (! isset(self::$mimetypes[$mime[$i]])) {
1094
                                self::$mimetypes[$mime[$i]] = $mime[0];
1095
                            }
1096
                        }
1097
                    }
1098
                }
1099
            }
1100
        }
1101
1102
        $this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias'];
1103
1104
        // This get's triggered if $this->root == '/' and alias is empty.
1105
        // Maybe modify _basename instead?
1106
        if ($this->rootName === '') {
1107
            $this->rootName = $this->separator;
1108
        }
1109
1110
        $root = $this->stat($this->root);
1111
1112
        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...
1113
            return $this->setError('Root folder does not exist.');
1114
        }
1115
        if (! $root['read'] && ! $root['write']) {
1116
            return $this->setError('Root folder has not read and write permissions.');
1117
        }
1118
1119
        if ($root['read']) {
1120
            if ($argInit) {
1121
                // check startPath - path to open by default instead of root
1122
                $startPath = $this->options['startPath'] ? $this->normpathCE($this->options['startPath']) : '';
1123
                if ($startPath) {
1124
                    $start = $this->stat($startPath);
1125
                    if (! empty($start)
1126
                    && $start['mime'] == 'directory'
1127
                    && $start['read']
1128
                    && empty($start['hidden'])
1129
                    && $this->inpathCE($startPath, $this->root)) {
1130
                        $this->startPath = $startPath;
1131
                        if (substr($this->startPath, -1, 1) == $this->options['separator']) {
1132
                            $this->startPath = substr($this->startPath, 0, -1);
1133
                        }
1134
                    }
1135
                }
1136
            }
1137
        } else {
1138
            $this->options['URL'] = '';
1139
            $this->options['tmbURL'] = '';
1140
            $this->options['tmbPath'] = '';
1141
            // read only volume
1142
            array_unshift($this->attributes, [
1143
                'pattern' => '/.*/',
1144
                'read' => false,
1145
            ]);
1146
        }
1147
        $this->treeDeep = $this->options['treeDeep'] > 0 ? (int) $this->options['treeDeep'] : 1;
1148
        $this->tmbSize = $this->options['tmbSize'] > 0 ? (int) $this->options['tmbSize'] : 48;
1149
        $this->URL = $this->options['URL'];
1150
        if ($this->URL && preg_match('|[^/?&=]$|', $this->URL)) {
1151
            $this->URL .= '/';
1152
        }
1153
        if (strtolower($this->options['dirUrlOwn']) === 'auto') {
1154
            $this->options['dirUrlOwn'] = $this->URL ? false : true;
1155
        } else {
1156
            $this->options['dirUrlOwn'] = (bool) $this->options['dirUrlOwn'];
1157
        }
1158
1159
        $this->tmbURL = ! empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
1160
        if ($this->tmbURL && $this->tmbURL !== 'self' && preg_match('|[^/?&=]$|', $this->tmbURL)) {
1161
            $this->tmbURL .= '/';
1162
        }
1163
1164
        $this->nameValidator = ! empty($this->options['acceptedName']) && (is_string($this->options['acceptedName']) || is_callable($this->options['acceptedName']))
0 ignored issues
show
Documentation Bug introduced by
It seems like !empty($this->options['a...ns['acceptedName'] : '' of type callable is incompatible with the declared type string of property $nameValidator.

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...
1165
            ? $this->options['acceptedName']
1166
            : '';
1167
1168
        $this->dirnameValidator = ! empty($this->options['acceptedDirname']) && (is_callable($this->options['acceptedDirname']) || (is_string($this->options['acceptedDirname']) && preg_match($this->options['acceptedDirname'], '') !== false))
0 ignored issues
show
Documentation Bug introduced by
It seems like !empty($this->options['a... : $this->nameValidator of type callable is incompatible with the declared type string of property $dirnameValidator.

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...
1169
            ? $this->options['acceptedDirname']
1170
            : $this->nameValidator;
1171
1172
        $this->_checkArchivers();
1173
        // manual control archive types to create
1174 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...
1175
            foreach ($this->archivers['create'] as $mime => $v) {
1176
                if (! in_array($mime, $this->options['archiveMimes'])) {
1177
                    unset($this->archivers['create'][$mime]);
1178
                }
1179
            }
1180
        }
1181
1182
        // manualy add archivers
1183 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...
1184
            foreach ($this->options['archivers']['create'] as $mime => $conf) {
1185
                if (strpos($mime, 'application/') === 0
1186
                && ! empty($conf['cmd'])
1187
                && isset($conf['argc'])
1188
                && ! empty($conf['ext'])
1189
                && ! isset($this->archivers['create'][$mime])) {
1190
                    $this->archivers['create'][$mime] = $conf;
1191
                }
1192
            }
1193
        }
1194
1195 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...
1196
            foreach ($this->options['archivers']['extract'] as $mime => $conf) {
1197
                if (strpos($mime, 'application/') === 0
1198
                && ! empty($conf['cmd'])
1199
                && isset($conf['argc'])
1200
                && ! empty($conf['ext'])
1201
                && ! isset($this->archivers['extract'][$mime])) {
1202
                    $this->archivers['extract'][$mime] = $conf;
1203
                }
1204
            }
1205
        }
1206
1207
        if (! empty($this->options['noSessionCache']) && is_array($this->options['noSessionCache'])) {
1208
            foreach ($this->options['noSessionCache'] as $_key) {
1209
                $this->sessionCaching[$_key] = false;
1210
                unset($this->sessionCache[$_key]);
1211
            }
1212
        }
1213
        if ($this->sessionCaching['subdirs']) {
1214
            if (! isset($this->sessionCache['subdirs'])) {
1215
                $this->sessionCache['subdirs'] = [];
1216
            }
1217
        }
1218
1219
        $this->configure();
1220
1221
        // Normarize disabled (array_merge`for type array of JSON)
1222
        $this->disabled = array_values(array_unique($this->disabled));
1223
1224
        // fix sync interval
1225 View Code Duplication
        if ($this->options['syncMinMs'] !== 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...
1226
            $this->options['syncMinMs'] = max($this->options[$this->options['syncChkAsTs'] ? 'tsPlSleep' : 'lsPlSleep'] * 1000, intval($this->options['syncMinMs']));
1227
        }
1228
1229
        // ` copyJoin` is required for the trash function
1230
        if ($this->options['trashHash'] && empty($this->options['copyJoin'])) {
1231
            $this->options['trashHash'] = '';
1232
        }
1233
1234
        // set tmpLinkPath
1235
        if (elFinder::$tmpLinkPath && ! $this->options['tmpLinkPath']) {
1236
            $this->options['tmpLinkPath'] = elFinder::$tmpLinkPath;
1237
        }
1238
        if ($this->options['tmpLinkPath'] && is_writable($this->options['tmpLinkPath'])) {
1239
            $this->tmpLinkPath = realpath($this->options['tmpLinkPath']);
1240
        } elseif (! $this->options['URL'] && is_writable('../files/.tmb')) {
1241
            $this->tmpLinkPath = realpath('../files/.tmb');
1242
            $this->options['tmpLinkUrl'] = '';
1243
            if (! elFinder::$tmpLinkPath) {
1244
                elFinder::$tmpLinkPath = $this->tmpLinkPath;
1245
                elFinder::$tmpLinkUrl = '';
1246
            }
1247
        }
1248
1249
        // set tmpLinkUrl
1250
        if (elFinder::$tmpLinkUrl && ! $this->options['tmpLinkUrl']) {
1251
            $this->options['tmpLinkUrl'] = elFinder::$tmpLinkUrl;
1252
        }
1253
        if ($this->options['tmpLinkUrl']) {
1254
            $this->tmpLinkUrl = $this->options['tmpLinkUrl'];
1255
        }
1256
        if ($this->tmpLinkPath && ! $this->tmpLinkUrl) {
1257
            $cur = realpath('./');
1258
            $i = 0;
1259
            while ($cur !== $this->systemRoot && strpos($this->tmpLinkPath, $cur) !== 0) {
1260
                $i++;
1261
                $cur = dirname($cur);
1262
            }
1263
            list($req) = explode('?', $_SERVER['REQUEST_URI']);
1264
            $reqs = explode('/', dirname($req));
1265
            $uri = implode('/', array_slice($reqs, 0, count($reqs) - 1)).substr($this->tmpLinkPath, strlen($cur));
1266
            $https = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off');
1267
            $this->tmpLinkUrl = ($https ? 'https://' : 'http://')
1268
                .$_SERVER['SERVER_NAME'] // host
1269
                .(((! $https && $_SERVER['SERVER_PORT'] == 80) || ($https && $_SERVER['SERVER_PORT'] == 443)) ? '' : (':'.$_SERVER['SERVER_PORT']))  // port
1270
                .$uri;
1271
            if (! elFinder::$tmpLinkUrl) {
1272
                elFinder::$tmpLinkUrl = $this->tmpLinkUrl;
1273
            }
1274
        }
1275
1276
        // remove last '/'
1277
        if ($this->tmpLinkPath) {
1278
            $this->tmpLinkPath = rtrim($this->tmpLinkPath, '/');
1279
        }
1280
        if ($this->tmpLinkUrl) {
1281
            $this->tmpLinkUrl = rtrim($this->tmpLinkUrl, '/');
1282
        }
1283
1284
        // to update options cache
1285
        $this->updateCache($this->root, $root);
1286
1287
        return $this->mounted = true;
1288
    }
1289
1290
    /**
1291
     * Some "unmount" stuffs - may be required by virtual fs.
1292
     *
1293
     * @return void
1294
     * @author Dmitry (dio) Levashov
1295
     **/
1296
    public function umount()
1297
    {
1298
    }
1299
1300
    /**
1301
     * Remove session cache of this volume.
1302
     */
1303
    public function clearSessionCache()
1304
    {
1305
        $this->sessionCache = [];
1306
    }
1307
1308
    /**
1309
     * Return error message from last failed action.
1310
     *
1311
     * @return array
1312
     * @author Dmitry (dio) Levashov
1313
     **/
1314
    public function error()
1315
    {
1316
        return $this->error;
1317
    }
1318
1319
    /**
1320
     * Return is uploadable that given file name.
1321
     *
1322
     * @param  string  $name  file name
1323
     * @param  bool    $allowUnknown
1324
     * @return bool
1325
     * @author Naoki Sawada
1326
     **/
1327
    public function isUploadableByName($name, $allowUnknown = false)
1328
    {
1329
        $mimeByName = $this->mimetype($name, true);
1330
1331
        return ($allowUnknown && $mimeByName === 'unknown') || $this->allowPutMime($mimeByName);
1332
    }
1333
1334
    /**
1335
     * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes).
1336
     *
1337
     * @return array
1338
     * @author Naoki Sawada
1339
     */
1340
    public function getMimeTable()
1341
    {
1342
        // load mime.types
1343
        ! self::$mimetypesLoaded && self::mimetypeInternalDetect();
1344
1345
        return self::$mimetypes;
1346
    }
1347
1348
    /**
1349
     * Return file extention detected by MIME type.
1350
     *
1351
     * @param  string  $mime    MIME type
1352
     * @param  string  $suffix  Additional suffix
1353
     * @return string
1354
     * @author Naoki Sawada
1355
     */
1356
    public function getExtentionByMime($mime, $suffix = '')
1357
    {
1358
        static $extTable = null;
1359
1360
        if (is_null($extTable)) {
1361
            $extTable = array_flip(array_unique($this->getMimeTable()));
1362
            foreach (array_keys($this->options['mimeMap']) as $pair) {
1363
                list($ext, $_mime) = explode(':', $pair);
1364
                if ($_mime !== '*' && ! isset($extTable[$_mime])) {
1365
                    $extTable[$_mime] = $ext;
1366
                }
1367
            }
1368
        }
1369
1370
        if ($mime && isset($extTable[$mime])) {
1371
            return $suffix ? ($extTable[$mime].$suffix) : $extTable[$mime];
1372
        }
1373
1374
        return '';
1375
    }
1376
1377
    /**
1378
     * Set mimetypes allowed to display to client.
1379
     *
1380
     * @param  array  $mimes
1381
     * @return void
1382
     * @author Dmitry (dio) Levashov
1383
     **/
1384
    public function setMimesFilter($mimes)
1385
    {
1386
        if (is_array($mimes)) {
1387
            $this->onlyMimes = $mimes;
1388
        }
1389
    }
1390
1391
    /**
1392
     * Return root folder hash.
1393
     *
1394
     * @return string
1395
     * @author Dmitry (dio) Levashov
1396
     **/
1397
    public function root()
1398
    {
1399
        return $this->encode($this->root);
1400
    }
1401
1402
    /**
1403
     * Return target path hash.
1404
     *
1405
     * @param  string $path
1406
     * @param  string $name
1407
     * @author Naoki Sawada
1408
     * @return string
1409
     */
1410
    public function getHash($path, $name = '')
1411
    {
1412
        if ($name !== '') {
1413
            $path = $this->joinPathCE($path, $name);
1414
        }
1415
1416
        return $this->encode($path);
1417
    }
1418
1419
    /**
1420
     * Return decoded path of target hash
1421
     * This method do not check the stat of target
1422
     * Use method `realpath()` to do check of the stat of target.
1423
     *
1424
     * @param  string $hash
1425
     * @author Naoki Sawada
1426
     * @return string
1427
     */
1428
    public function getPath($hash)
1429
    {
1430
        return $this->decode($hash);
1431
    }
1432
1433
    /**
1434
     * Return root or startPath hash.
1435
     *
1436
     * @return string
1437
     * @author Dmitry (dio) Levashov
1438
     **/
1439
    public function defaultPath()
1440
    {
1441
        return $this->encode($this->startPath ? $this->startPath : $this->root);
1442
    }
1443
1444
    /**
1445
     * Return volume options required by client:.
1446
     *
1447
     * @param $hash
1448
     * @return array
1449
     * @author Dmitry (dio) Levashov
1450
     */
1451
    public function options($hash)
1452
    {
1453
        $create = $createext = [];
1454
        if (isset($this->archivers['create']) && is_array($this->archivers['create'])) {
1455
            foreach ($this->archivers['create'] as $m => $v) {
1456
                $create[] = $m;
1457
                $createext[$m] = $v['ext'];
1458
            }
1459
        }
1460
        $opts = [
1461
            'path' => $hash ? $this->path($hash) : '',
1462
            'url' => $this->URL,
1463
            'tmbUrl' => (! $this->imgLib && $this->options['tmbFbSelf']) ? 'self' : $this->tmbURL,
1464
            'disabled' => $this->disabled,
1465
            'separator' => $this->separator,
1466
            'copyOverwrite' => intval($this->options['copyOverwrite']),
1467
            'uploadOverwrite' => intval($this->options['uploadOverwrite']),
1468
            'uploadMaxSize' => intval($this->uploadMaxSize),
1469
            'uploadMaxConn' => intval($this->options['uploadMaxConn']),
1470
            'uploadMime' => [
1471
                'firstOrder' => isset($this->uploadOrder[0]) ? $this->uploadOrder[0] : 'deny',
1472
                'allow' => $this->uploadAllow,
1473
                'deny' => $this->uploadDeny,
1474
            ],
1475
            'dispInlineRegex' => $this->options['dispInlineRegex'],
1476
            'jpgQuality' => intval($this->options['jpgQuality']),
1477
            'archivers' => [
1478
                'create' => $create,
1479
                'extract' => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : [],
1480
                'createext' => $createext,
1481
            ],
1482
            'uiCmdMap' => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap'])) ? $this->options['uiCmdMap'] : [],
1483
            'syncChkAsTs' => intval($this->options['syncChkAsTs']),
1484
            'syncMinMs' => intval($this->options['syncMinMs']),
1485
            'i18nFolderName' => intval($this->options['i18nFolderName']),
1486
            'tmbCrop' => intval($this->options['tmbCrop']),
1487
        ];
1488
        if (! empty($this->options['trashHash'])) {
1489
            $opts['trashHash'] = $this->options['trashHash'];
1490
        }
1491
        if ($hash === null) {
1492
            // call from getRootStatExtra()
1493
            if (! empty($this->options['icon'])) {
1494
                $opts['icon'] = $this->options['icon'];
1495
            }
1496
            if (! empty($this->options['rootCssClass'])) {
1497
                $opts['csscls'] = $this->options['rootCssClass'];
1498
            }
1499
            if (isset($this->options['netkey'])) {
1500
                $opts['netkey'] = $this->options['netkey'];
1501
            }
1502
        }
1503
1504
        return $opts;
1505
    }
1506
1507
    /**
1508
     * Get option value of this volume.
1509
     *
1510
     * @param string $name  target option name
1511
     * @return null|mixed   target option value
1512
     * @author Naoki Sawada
1513
     */
1514
    public function getOption($name)
1515
    {
1516
        return isset($this->options[$name]) ? $this->options[$name] : null;
1517
    }
1518
1519
    /**
1520
     * Get plugin values of this options.
1521
     *
1522
     * @param string $name  Plugin name
1523
     * @return null|array   Plugin values
1524
     * @author Naoki Sawada
1525
     */
1526
    public function getOptionsPlugin($name = '')
1527
    {
1528
        if ($name) {
1529
            return isset($this->options['plugin'][$name]) ? $this->options['plugin'][$name] : [];
1530
        } else {
1531
            return $this->options['plugin'];
1532
        }
1533
    }
1534
1535
    /**
1536
     * Return true if command disabled in options.
1537
     *
1538
     * @param  string  $cmd  command name
1539
     * @return bool
1540
     * @author Dmitry (dio) Levashov
1541
     **/
1542
    public function commandDisabled($cmd)
1543
    {
1544
        return in_array($cmd, $this->disabled);
1545
    }
1546
1547
    /**
1548
     * Return true if mime is required mimes list.
1549
     *
1550
     * @param  string     $mime   mime type to check
1551
     * @param  array      $mimes  allowed mime types list or not set to use client mimes list
1552
     * @param  bool|null  $empty  what to return on empty list
1553
     * @return bool|null
1554
     * @author Dmitry (dio) Levashov
1555
     * @author Troex Nevelin
1556
     **/
1557
    public function mimeAccepted($mime, $mimes = null, $empty = true)
1558
    {
1559
        $mimes = is_array($mimes) ? $mimes : $this->onlyMimes;
1560
        if (empty($mimes)) {
1561
            return $empty;
1562
        }
1563
1564
        return $mime == 'directory'
1565
            || in_array('all', $mimes)
1566
            || in_array('All', $mimes)
1567
            || in_array($mime, $mimes)
1568
            || in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
1569
    }
1570
1571
    /**
1572
     * Return true if voume is readable.
1573
     *
1574
     * @return bool
1575
     * @author Dmitry (dio) Levashov
1576
     **/
1577
    public function isReadable()
1578
    {
1579
        $stat = $this->stat($this->root);
1580
1581
        return $stat['read'];
1582
    }
1583
1584
    /**
1585
     * Return true if copy from this volume allowed.
1586
     *
1587
     * @return bool
1588
     * @author Dmitry (dio) Levashov
1589
     **/
1590
    public function copyFromAllowed()
1591
    {
1592
        return (bool) $this->options['copyFrom'];
1593
    }
1594
1595
    /**
1596
     * Return file path related to root with convert encoging.
1597
     *
1598
     * @param  string   $hash  file hash
1599
     * @return string
1600
     * @author Dmitry (dio) Levashov
1601
     **/
1602
    public function path($hash)
1603
    {
1604
        return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash))));
1605
    }
1606
1607
    /**
1608
     * Return file real path if file exists.
1609
     *
1610
     * @param  string  $hash  file hash
1611
     * @return string | false
1612
     * @author Dmitry (dio) Levashov
1613
     **/
1614
    public function realpath($hash)
1615
    {
1616
        $path = $this->decode($hash);
1617
1618
        return $this->stat($path) ? $path : false;
1619
    }
1620
1621
    /**
1622
     * Return list of moved/overwrited files.
1623
     *
1624
     * @return array
1625
     * @author Dmitry (dio) Levashov
1626
     **/
1627
    public function removed()
1628
    {
1629
        if ($this->removed) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->removed 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...
1630
            $unsetSubdir = isset($this->sessionCache['subdirs']) ? true : false;
1631
            foreach ($this->removed as $item) {
1632
                if ($item['mime'] === 'directory') {
1633
                    $path = $this->decode($item['hash']);
1634
                    if ($unsetSubdir) {
1635
                        unset($this->sessionCache['subdirs'][$path]);
1636
                    }
1637
                    if ($item['phash'] !== '') {
1638
                        $parent = $this->decode($item['phash']);
1639
                        unset($this->cache[$parent]);
1640
                        if ($unsetSubdir) {
1641
                            unset($this->sessionCache['subdirs'][$parent]);
1642
                        }
1643
                    }
1644
                }
1645
            }
1646
        }
1647
1648
        return $this->removed;
1649
    }
1650
1651
    /**
1652
     * Return list of added files.
1653
     *
1654
     * @deprecated
1655
     * @return array
1656
     * @author Naoki Sawada
1657
     **/
1658
    public function added()
1659
    {
1660
        return $this->added;
1661
    }
1662
1663
    /**
1664
     * Clean removed files list.
1665
     *
1666
     * @return void
1667
     * @author Dmitry (dio) Levashov
1668
     **/
1669
    public function resetRemoved()
1670
    {
1671
        $this->resetResultStat();
1672
    }
1673
1674
    /**
1675
     * Clean added/removed files list.
1676
     *
1677
     * @return void
1678
     **/
1679
    public function resetResultStat()
1680
    {
1681
        $this->removed = [];
1682
        $this->added = [];
1683
    }
1684
1685
    /**
1686
     * Return file/dir hash or first founded child hash with required attr == $val.
1687
     *
1688
     * @param  string   $hash  file hash
1689
     * @param  string   $attr  attribute name
1690
     * @param  bool     $val   attribute value
1691
     * @return string|false
1692
     * @author Dmitry (dio) Levashov
1693
     **/
1694
    public function closest($hash, $attr, $val)
1695
    {
1696
        return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1697
    }
1698
1699
    /**
1700
     * Return file info or false on error.
1701
     *
1702
     * @param  string $hash file hash
1703
     * @return array|false
1704
     * @internal param bool $realpath add realpath field to file info
1705
     * @author Dmitry (dio) Levashov
1706
     */
1707
    public function file($hash)
1708
    {
1709
        $file = $this->stat($this->decode($hash));
1710
1711
        return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1712
    }
1713
1714
    /**
1715
     * Return folder info.
1716
     *
1717
     * @param  string $hash folder hash
1718
     * @param bool $resolveLink
1719
     * @return array|false
1720
     * @internal param bool $hidden return hidden file info
1721
     * @author Dmitry (dio) Levashov
1722
     */
1723
    public function dir($hash, $resolveLink = false)
1724
    {
1725
        if (($dir = $this->file($hash)) == false) {
1726
            return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1727
        }
1728
1729
        if ($resolveLink && ! empty($dir['thash'])) {
1730
            $dir = $this->file($dir['thash']);
1731
        }
1732
1733
        return $dir && $dir['mime'] == 'directory' && empty($dir['hidden'])
1734
            ? $dir
1735
            : $this->setError(elFinder::ERROR_NOT_DIR);
1736
    }
1737
1738
    /**
1739
     * Return directory content or false on error.
1740
     *
1741
     * @param  string   $hash   file hash
1742
     * @return array|false
1743
     * @author Dmitry (dio) Levashov
1744
     **/
1745
    public function scandir($hash)
1746
    {
1747
        if (($dir = $this->dir($hash)) == false) {
1748
            return false;
1749
        }
1750
1751
        $path = $this->decode($hash);
1752
        if ($res = $dir['read']
1753
            ? $this->getScandir($path)
1754
            : $this->setError(elFinder::ERROR_PERM_DENIED)) {
1755
            $dirs = null;
1756 View Code Duplication
            if ($this->sessionCaching['subdirs'] && isset($this->sessionCache['subdirs'][$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...
1757
                $dirs = $this->sessionCache['subdirs'][$path];
1758
            }
1759
            if ($dirs !== null || (isset($dir['dirs']) && $dir['dirs'] != 1)) {
1760
                $_dir = $dir;
1761
                if ($dirs || $this->subdirs($hash)) {
1762
                    $dir['dirs'] = 1;
1763
                } else {
1764
                    unset($dir['dirs']);
1765
                }
1766
                if ($dir !== $_dir) {
1767
                    $this->updateCache($path, $dir);
0 ignored issues
show
Bug introduced by
It seems like $dir defined by $this->dir($hash) on line 1747 can also be of type boolean; however, elFinderVolumeDriver::updateCache() does only seem to accept array, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1768
                }
1769
            }
1770
        }
1771
1772
        return $res;
1773
    }
1774
1775
    /**
1776
     * Return dir files names list.
1777
     *
1778
     * @param  string $hash file hash
1779
     * @param null $intersect
1780
     * @return array
1781
     * @author Dmitry (dio) Levashov
1782
     */
1783
    public function ls($hash, $intersect = null)
1784
    {
1785
        if (($dir = $this->dir($hash)) == false || ! $dir['read']) {
1786
            return false;
1787
        }
1788
1789
        $list = [];
1790
        $path = $this->decode($hash);
1791
1792
        $check = [];
1793
        if ($intersect) {
1794
            $check = array_flip($intersect);
1795
        }
1796
1797
        foreach ($this->getScandir($path) as $stat) {
1798
            if (empty($stat['hidden']) && (! $check || isset($check[$stat['name']])) && $this->mimeAccepted($stat['mime'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $check 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...
1799
                $list[$stat['hash']] = $stat['name'];
1800
            }
1801
        }
1802
1803
        return $list;
1804
    }
1805
1806
    /**
1807
     * Return subfolders for required folder or false on error.
1808
     *
1809
     * @param  string   $hash  folder hash or empty string to get tree from root folder
1810
     * @param  int      $deep  subdir deep
1811
     * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1812
     * @return array|false
1813
     * @author Dmitry (dio) Levashov
1814
     **/
1815
    public function tree($hash = '', $deep = 0, $exclude = '')
1816
    {
1817
        $path = $hash ? $this->decode($hash) : $this->root;
1818
1819 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...
1820
            return false;
1821
        }
1822
1823
        $dirs = $this->gettree($path, $deep > 0 ? $deep - 1 : $this->treeDeep - 1, $exclude ? $this->decode($exclude) : null);
1824
        array_unshift($dirs, $dir);
1825
1826
        return $dirs;
1827
    }
1828
1829
    /**
1830
     * Return part of dirs tree from required dir up to root dir.
1831
     *
1832
     * @param  string    $hash   directory hash
1833
     * @param  bool|null $lineal only lineal parents
1834
     * @param  string    $until  hash that is enough to that extent >= 2.1.24
0 ignored issues
show
Bug introduced by
There is no parameter named $until. 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...
1835
     * @return array
1836
     * @author Dmitry (dio) Levashov
1837
     **/
1838
    public function parents($hash, $lineal = false)
1839
    {
1840
        if (($current = $this->dir($hash)) == false) {
1841
            return false;
1842
        }
1843
1844
        $args = func_get_args();
1845
        // checks 3rd param `$until` (elFinder >= 2.1.24)
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
1846
        $until = '';
1847
        if (isset($args[2])) {
1848
            $until = $args[2];
1849
        }
1850
1851
        $path = $this->decode($hash);
1852
        $tree = [];
1853
1854
        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...
1855
            elFinder::extendTimeLimit();
1856
            $path = $this->dirnameCE($path);
1857
            if (! ($stat = $this->stat($path)) || ! empty($stat['hidden']) || ! $stat['read']) {
1858
                return false;
1859
            }
1860
1861
            array_unshift($tree, $stat);
1862
            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...
1863
                foreach ($this->gettree($path, 0) as $dir) {
1864
                    elFinder::extendTimeLimit();
1865
                    if (! isset($tree[$dir['hash']])) {
1866
                        $tree[$dir['hash']] = $dir;
1867
                    }
1868
                }
1869
            }
1870
1871
            if ($until && $until === $this->encode($path)) {
1872
                break;
1873
            }
1874
        }
1875
1876
        return $tree ? array_values($tree) : [$current];
1877
    }
1878
1879
    /**
1880
     * Create thumbnail for required file and return its name of false on failed.
1881
     *
1882
     * @param $hash
1883
     * @return false|string
1884
     * @author Dmitry (dio) Levashov
1885
     */
1886
    public function tmb($hash)
1887
    {
1888
        $path = $this->decode($hash);
1889
        $stat = $this->stat($path);
1890
1891
        if (isset($stat['tmb'])) {
1892
            $res = $stat['tmb'] == '1' ? $this->createTmb($path, $stat) : $stat['tmb'];
1893
            if (! $res) {
1894
                list($type) = explode('/', $stat['mime']);
1895
                $fallback = $this->options['resourcePath'].DIRECTORY_SEPARATOR.strtolower($type).'.png';
1896
                if (is_file($fallback)) {
1897
                    $res = $this->tmbname($stat);
1898
                    if (! copy($fallback, $this->tmbPath.DIRECTORY_SEPARATOR.$res)) {
1899
                        $res = false;
1900
                    }
1901
                }
1902
            }
1903
1904
            return $res;
1905
        }
1906
1907
        return false;
1908
    }
1909
1910
    /**
1911
     * Return file size / total directory size.
1912
     *
1913
     * @param  string   file hash
1914
     * @return int
1915
     * @author Dmitry (dio) Levashov
1916
     **/
1917
    public function size($hash)
1918
    {
1919
        return $this->countSize($this->decode($hash));
1920
    }
1921
1922
    /**
1923
     * Open file for reading and return file pointer.
1924
     *
1925
     * @param  string   file hash
1926
     * @return resource
1927
     * @author Dmitry (dio) Levashov
1928
     **/
1929
    public function open($hash)
1930
    {
1931 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...
1932
        || $file['mime'] == 'directory') {
1933
            return false;
1934
        }
1935
1936
        return $this->fopenCE($this->decode($hash), 'rb');
1937
    }
1938
1939
    /**
1940
     * Close file pointer.
1941
     *
1942
     * @param  resource  $fp   file pointer
1943
     * @param  string    $hash file hash
1944
     * @return void
1945
     * @author Dmitry (dio) Levashov
1946
     **/
1947
    public function close($fp, $hash)
1948
    {
1949
        $this->fcloseCE($fp, $this->decode($hash));
1950
    }
1951
1952
    /**
1953
     * Create directory and return dir info.
1954
     *
1955
     * @param  string   $dsthash  destination directory hash
1956
     * @param  string   $name directory name
1957
     * @return array|false
1958
     * @author Dmitry (dio) Levashov
1959
     **/
1960
    public function mkdir($dsthash, $name)
1961
    {
1962
        if ($this->commandDisabled('mkdir')) {
1963
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1964
        }
1965
1966
        if (! $this->nameAccepted($name, true)) {
1967
            return $this->setError(elFinder::ERROR_INVALID_DIRNAME);
1968
        }
1969
1970
        if (($dir = $this->dir($dsthash)) == false) {
1971
            return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash);
1972
        }
1973
1974
        $path = $this->decode($dsthash);
1975
1976
        if (! $dir['write'] || ! $this->allowCreate($path, $name, true)) {
1977
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1978
        }
1979
1980
        $dst = $this->joinPathCE($path, $name);
1981
        $stat = $this->isNameExists($dst);
1982
        if (! empty($stat)) {
1983
            return $this->setError(elFinder::ERROR_EXISTS, $name);
1984
        }
1985
        $this->clearcache();
1986
1987
        $mkpath = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)));
1988
        if ($mkpath) {
1989
            $this->updateSubdirsCache($path, true);
1990
            $this->updateSubdirsCache($mkpath, false);
1991
        }
1992
1993
        return $mkpath ? $this->stat($mkpath) : false;
1994
    }
1995
1996
    /**
1997
     * Create empty file and return its info.
1998
     *
1999
     * @param  string   $dst  destination directory
2000
     * @param  string   $name file name
2001
     * @return array|false
2002
     * @author Dmitry (dio) Levashov
2003
     **/
2004
    public function mkfile($dst, $name)
2005
    {
2006
        if ($this->commandDisabled('mkfile')) {
2007
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2008
        }
2009
2010
        if (! $this->nameAccepted($name, false)) {
2011
            return $this->setError(elFinder::ERROR_INVALID_NAME);
2012
        }
2013
2014
        $mimeByName = $this->mimetype($name, true);
2015
        if ($mimeByName && ! $this->allowPutMime($mimeByName)) {
2016
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name);
2017
        }
2018
2019 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...
2020
            return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
2021
        }
2022
2023
        $path = $this->decode($dst);
2024
2025
        if (! $dir['write'] || ! $this->allowCreate($path, $name, false)) {
2026
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2027
        }
2028
2029
        if ($this->isNameExists($this->joinPathCE($path, $name))) {
2030
            return $this->setError(elFinder::ERROR_EXISTS, $name);
2031
        }
2032
2033
        $this->clearcache();
2034
2035
        return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
2036
    }
2037
2038
    /**
2039
     * Rename file and return file info.
2040
     *
2041
     * @param  string  $hash  file hash
2042
     * @param  string  $name  new file name
2043
     * @return array|false
2044
     * @author Dmitry (dio) Levashov
2045
     **/
2046
    public function rename($hash, $name)
2047
    {
2048
        if ($this->commandDisabled('rename')) {
2049
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2050
        }
2051
2052
        if (! ($file = $this->file($hash))) {
2053
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2054
        }
2055
2056
        if ($name === $file['name']) {
2057
            return $file;
2058
        }
2059
2060
        if (! empty($file['locked'])) {
2061
            return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
2062
        }
2063
2064
        $isDir = ($file['mime'] === 'directory');
2065
2066
        if (! $this->nameAccepted($name, $isDir)) {
2067
            return $this->setError(elFinder::ERROR_INVALID_DIRNAME);
2068
        }
2069
2070
        if (! $isDir) {
2071
            $mimeByName = $this->mimetype($name, true);
2072
            if ($mimeByName && ! $this->allowPutMime($mimeByName)) {
2073
                return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name);
2074
            }
2075
        }
2076
2077
        $path = $this->decode($hash);
2078
        $dir = $this->dirnameCE($path);
2079
        $stat = $this->isNameExists($this->joinPathCE($dir, $name));
2080
        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...
2081
            return $this->setError(elFinder::ERROR_EXISTS, $name);
2082
        }
2083
2084
        if (! $this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) {
2085
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2086
        }
2087
2088
        $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...
2089
2090
        if ($path = $this->convEncOut($this->_move($this->convEncIn($path), $this->convEncIn($dir), $this->convEncIn($name)))) {
2091
            $this->clearcache();
2092
2093
            return $this->stat($path);
2094
        }
2095
2096
        return false;
2097
    }
2098
2099
    /**
2100
     * Create file copy with suffix "copy number" and return its info.
2101
     *
2102
     * @param  string   $hash    file hash
2103
     * @param  string   $suffix  suffix to add to file name
2104
     * @return array|false
2105
     * @author Dmitry (dio) Levashov
2106
     **/
2107
    public function duplicate($hash, $suffix = 'copy')
2108
    {
2109
        if ($this->commandDisabled('duplicate')) {
2110
            return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
2111
        }
2112
2113
        if (($file = $this->file($hash)) == false) {
2114
            return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
2115
        }
2116
2117
        $path = $this->decode($hash);
2118
        $dir = $this->dirnameCE($path);
2119
        $name = $this->uniqueName($dir, $file['name'], sprintf($this->options['duplicateSuffix'], $suffix));
2120
2121
        if (! $this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) {
2122
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2123
        }
2124
2125
        return ($path = $this->copy($path, $dir, $name)) == false
2126
            ? false
2127
            : $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copy($path, $dir, $name) on line 2125 can also be of type boolean or null; 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...
2128
    }
2129
2130
    /**
2131
     * Save uploaded file.
2132
     * On success return array with new file stat and with removed file hash (if existed file was replaced).
2133
     *
2134
     * @param  resource $fp file pointer
2135
     * @param  string $dst destination folder hash
2136
     * @param $name
2137
     * @param  string $tmpname file tmp name - required to detect mime type
2138
     * @param  array $hashes exists files hash array with filename as key
2139
     * @return array|false
2140
     * @internal param string $src file name
2141
     * @author Dmitry (dio) Levashov
2142
     */
2143
    public function upload($fp, $dst, $name, $tmpname, $hashes = [])
2144
    {
2145
        if ($this->commandDisabled('upload')) {
2146
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2147
        }
2148
2149 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...
2150
            return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
2151
        }
2152
2153
        if (empty($dir['write'])) {
2154
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2155
        }
2156
2157
        if (! $this->nameAccepted($name, false)) {
2158
            return $this->setError(elFinder::ERROR_INVALID_NAME);
2159
        }
2160
2161
        $mimeByName = '';
2162
        if ($this->mimeDetect === 'internal') {
2163
            $mime = $this->mimetype($name, true);
2164
        } else {
2165
            $mime = $this->mimetype($tmpname, $name);
2166
            $mimeByName = $this->mimetype($name, true);
2167
            if ($mime === 'unknown') {
2168
                $mime = $mimeByName;
2169
            }
2170
        }
2171
2172 View Code Duplication
        if (! $this->allowPutMime($mime) || ($mimeByName && ! $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...
2173
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
2174
        }
2175
2176
        $tmpsize = sprintf('%u', filesize($tmpname));
2177
        if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) {
2178
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
2179
        }
2180
2181
        $dstpath = $this->decode($dst);
2182 View Code Duplication
        if (isset($hashes[$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...
2183
            $test = $this->decode($hashes[$name]);
2184
            $file = $this->stat($test);
2185
        } else {
2186
            $test = $this->joinPathCE($dstpath, $name);
2187
            $file = $this->isNameExists($test);
2188
        }
2189
2190
        $this->clearcache();
2191
2192
        if ($file && $file['name'] === $name) { // file exists and check filename for item ID based filesystem
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...
2193
            if ($this->uploadOverwrite) {
2194 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...
2195
                    return $this->setError(elFinder::ERROR_PERM_DENIED);
2196
                } elseif ($file['mime'] == 'directory') {
2197
                    return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
2198
                }
2199
                $this->remove($test);
2200
            } else {
2201
                $name = $this->uniqueName($dstpath, $name, '-', false);
2202
            }
2203
        }
2204
2205
        $stat = [
2206
            'mime' => $mime,
2207
            'width' => 0,
2208
            'height' => 0,
2209
            'size' => $tmpsize, ];
2210
2211
        // $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...
2212
        if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
2213
            $stat['width'] = $s[0];
2214
            $stat['height'] = $s[1];
2215
        }
2216
        // $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...
2217
        if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) {
2218
            return false;
2219
        }
2220
2221
        $stat = $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->saveCE($fp, $dstpath, $name, $stat) on line 2217 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...
2222
        // Try get URL
2223
        if (empty($stat['url']) && ($url = $this->getContentUrl($stat['hash']))) {
2224
            $stat['url'] = $url;
2225
        }
2226
2227
        return $stat;
2228
    }
2229
2230
    /**
2231
     * Paste files.
2232
     *
2233
     * @param  object $volume source volume
2234
     * @param $src
2235
     * @param  string $dst destination dir hash
2236
     * @param  bool $rmSrc remove source after copy?
2237
     * @param array $hashes
2238
     * @return array|false
2239
     * @internal param string $source file hash
2240
     * @author Dmitry (dio) Levashov
2241
     */
2242
    public function paste($volume, $src, $dst, $rmSrc = false, $hashes = [])
2243
    {
2244
        $err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
2245
2246
        if ($this->commandDisabled('paste')) {
2247
            return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
2248
        }
2249
2250
        if (($file = $volume->file($src, $rmSrc)) == false) {
2251
            return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
2252
        }
2253
2254
        $name = $file['name'];
2255
        $errpath = $volume->path($file['hash']);
2256
2257 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...
2258
            return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
2259
        }
2260
2261
        if (! $dir['write'] || ! $file['read']) {
2262
            return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
2263
        }
2264
2265
        $destination = $this->decode($dst);
2266
2267
        if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
2268
            return $rmSrc
2269
                ? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
2270
                : $this->setError($err, $errpath, empty($file['thash']) ? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK);
2271
        }
2272
2273 View Code Duplication
        if (isset($hashes[$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...
2274
            $test = $this->decode($hashes[$name]);
2275
            $stat = $this->stat($test);
2276
        } else {
2277
            $test = $this->joinPathCE($destination, $name);
2278
            $stat = $this->isNameExists($test);
2279
        }
2280
        $this->clearcache();
2281
        $dstDirExists = false;
2282
        if ($stat && $stat['name'] === $name) { // file exists and check filename for item ID based filesystem
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...
2283
            if ($this->options['copyOverwrite']) {
2284
                // do not replace file with dir or dir with file
2285
                if (! $this->isSameType($file['mime'], $stat['mime'])) {
2286
                    return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash']));
2287
                }
2288
                // existed file is not writable
2289
                if (! $stat['write']) {
2290
                    return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
2291
                }
2292
                if ($this->options['copyJoin']) {
2293
                    if ($stat['locked']) {
2294
                        return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
2295
                    }
2296
                } else {
2297
                    // existed file locked or has locked child
2298
                    if (($locked = $this->closestByAttr($test, 'locked', true))) {
2299
                        $stat = $this->stat($locked);
2300
2301
                        return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
2302
                    }
2303
                }
2304
                // target is entity file of alias
2305
                if ($volume === $this && ((isset($file['target']) && $test == $file['target']) || $test == $this->decode($src))) {
2306
                    return $this->setError(elFinder::ERROR_REPLACE, $errpath);
2307
                }
2308
                // remove existed file
2309
                if (! $this->options['copyJoin'] || $stat['mime'] !== 'directory') {
2310
                    if (! $this->remove($test)) {
2311
                        return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash']));
2312
                    }
2313
                } elseif ($stat['mime'] === 'directory') {
2314
                    $dstDirExists = true;
2315
                }
2316
            } else {
2317
                $name = $this->uniqueName($destination, $name, ' ', false);
2318
            }
2319
        }
2320
2321
        // copy/move inside current volume
2322
        if ($volume === $this) { //  changing == operand to === fixes issue #1285 - Paul Canning 24/03/2016
2323
            $source = $this->decode($src);
2324
            // do not copy into itself
2325
            if ($this->inpathCE($destination, $source)) {
2326
                return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
2327
            }
2328
            $rmDir = false;
2329
            if ($rmSrc) {
2330
                if ($dstDirExists) {
2331
                    $rmDir = true;
2332
                    $method = 'copy';
2333
                } else {
2334
                    $method = 'move';
2335
                }
2336
            } else {
2337
                $method = 'copy';
2338
            }
2339
            $this->clearcache();
2340
            if ($res = ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false) {
2341
                if ($rmDir) {
2342
                    $this->remove($source);
2343
                }
2344
            } else {
2345
                return false;
2346
            }
2347
        } else {
2348
            // copy/move from another volume
2349
            if (! $this->options['copyTo'] || ! $volume->copyFromAllowed()) {
2350
                return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
2351
            }
2352
2353
            if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
2354
                return false;
2355
            }
2356
2357
            if ($rmSrc) {
2358
                if (! $volume->rm($src)) {
2359
                    return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
2360
                }
2361
            }
2362
            $res = $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copyFrom($volume,...c, $destination, $name) on line 2353 can also be of type boolean or null; 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...
2363
        }
2364
2365
        return $res;
2366
    }
2367
2368
    /**
2369
     * Return path to archive of target items.
2370
     *
2371
     * @param  array  $hashes
2372
     * @return string archive path
2373
     * @author Naoki Sawada
2374
     */
2375
    public function zipdl($hashes)
2376
    {
2377
        if ($this->commandDisabled('zipdl')) {
2378
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2379
        }
2380
2381
        $archivers = $this->getArchivers();
2382
        $cmd = null;
2383
        if (! $archivers || empty($archivers['create'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $archivers 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...
2384
            return false;
2385
        }
2386
        $archivers = $archivers['create'];
2387
        foreach (['zip', 'tgz'] as $ext) {
2388
            $mime = self::$mimetypes[$ext];
2389
            if (isset($archivers[$mime])) {
2390
                $cmd = $archivers[$mime];
2391
                break;
2392
            }
2393
        }
2394
        if (! $cmd) {
2395
            $cmd = $archivers[0];
2396
            $ext = $cmd['ext'];
2397
            $mime = $this->mimetype('file.'.$ext, true);
2398
        }
2399
        $res = false;
2400
        $mixed = false;
2401
        $hashes = array_values($hashes);
2402
        $dirname = dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[0])));
2403
        $cnt = count($hashes);
2404
        if ($cnt > 1) {
2405
            for ($i = 1; $i < $cnt; $i++) {
2406
                if ($dirname !== dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[$i])))) {
2407
                    $mixed = true;
2408
                    break;
2409
                }
2410
            }
2411
        }
2412
        if ($mixed || $this->root == $this->dirnameCE($this->decode($hashes[0]))) {
2413
            $prefix = $this->rootName;
2414
        } else {
2415
            $prefix = basename($dirname);
2416
        }
2417
        if ($dir = $this->getItemsInHand($hashes)) {
2418
            $tmppre = (substr(PHP_OS, 0, 3) === 'WIN') ? 'zdl' : 'elfzdl';
2419
            $pdir = dirname($dir);
2420
            // garbage collection
2421
            $ttl = 7200; // expire 2h
2422
            $time = time();
2423
            foreach (glob($pdir.DIRECTORY_SEPARATOR.$tmppre.'*') as $_file) {
2424
                if (filemtime($_file) + $ttl < $time) {
2425
                    unlink($_file);
2426
                }
2427
            }
2428
            $files = self::localScandir($dir);
2429
            if ($files && ($arc = tempnam($dir, $tmppre))) {
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...
2430
                unlink($arc);
2431
                $arc = $arc.'.'.$ext;
0 ignored issues
show
Bug introduced by
The variable $ext 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...
2432
                $name = basename($arc);
2433
                if ($arc = $this->makeArchive($dir, $files, $name, $cmd)) {
2434
                    $file = tempnam($pdir, $tmppre);
2435
                    unlink($file);
2436
                    $res = rename($arc, $file);
2437
                    $this->rmdirRecursive($dir);
2438
                }
2439
            }
2440
        }
2441
2442
        return $res ? ['path' => $file, 'ext' => $ext, 'mime' => $mime, 'prefix' => $prefix] : false;
0 ignored issues
show
Bug introduced by
The variable $file 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...
Bug introduced by
The variable $mime 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...
2443
    }
2444
2445
    /**
2446
     * Return file contents.
2447
     *
2448
     * @param  string  $hash  file hash
2449
     * @return string|false
2450
     * @author Dmitry (dio) Levashov
2451
     **/
2452
    public function getContents($hash)
2453
    {
2454
        $file = $this->file($hash);
2455
2456
        if (! $file) {
2457
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2458
        }
2459
2460
        if ($file['mime'] == 'directory') {
2461
            return $this->setError(elFinder::ERROR_NOT_FILE);
2462
        }
2463
2464
        if (! $file['read']) {
2465
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2466
        }
2467
2468
        if ($this->getMaxSize > 0 && $file['size'] > $this->getMaxSize) {
2469
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
2470
        }
2471
2472
        return $this->_getContents($this->convEncIn($this->decode($hash), true));
2473
    }
2474
2475
    /**
2476
     * Put content in text file and return file info.
2477
     *
2478
     * @param  string  $hash     file hash
2479
     * @param  string  $content  new file content
2480
     * @return array
2481
     * @author Dmitry (dio) Levashov
2482
     **/
2483
    public function putContents($hash, $content)
2484
    {
2485
        if ($this->commandDisabled('edit')) {
2486
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2487
        }
2488
2489
        $path = $this->decode($hash);
2490
2491
        if (! ($file = $this->file($hash))) {
2492
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2493
        }
2494
2495
        if (! $file['write']) {
2496
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2497
        }
2498
2499
        // check data cheme
2500
        if (preg_match('~^data:(.+?/.+?);base64,~', $content, $m)) {
2501
            $dMime = $m[1];
2502
            if ($file['size'] > 0 && $dMime !== $file['mime']) {
2503
                return $this->setError(elFinder::ERROR_PERM_DENIED);
2504
            }
2505
            $content = base64_decode(substr($content, strlen($m[0])));
2506
        }
2507
2508
        // check MIME
2509
        $name = $this->basenameCE($path);
2510
        $mime = '';
2511
        $mimeByName = $this->mimetype($name, true);
2512
        if ($this->mimeDetect !== 'internal') {
2513
            if ($tp = tmpfile()) {
2514
                fwrite($tp, $content);
2515
                $info = stream_get_meta_data($tp);
2516
                $filepath = $info['uri'];
2517
                $mime = $this->mimetype($filepath, $name);
2518
                fclose($tp);
2519
            }
2520
        }
2521 View Code Duplication
        if (! $this->allowPutMime($mimeByName) || ($mime && ! $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...
2522
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
2523
        }
2524
2525
        $this->clearcache();
2526
2527
        return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false;
2528
    }
2529
2530
    /**
2531
     * Extract files from archive.
2532
     *
2533
     * @param  string $hash archive hash
2534
     * @param null $makedir
2535
     * @return array|bool
2536
     * @author Dmitry (dio) Levashov,
2537
     * @author Alexey Sukhotin
2538
     */
2539
    public function extract($hash, $makedir = null)
2540
    {
2541
        if ($this->commandDisabled('extract')) {
2542
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2543
        }
2544
2545
        if (($file = $this->file($hash)) == false) {
2546
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2547
        }
2548
2549
        $archiver = isset($this->archivers['extract'][$file['mime']])
2550
            ? $this->archivers['extract'][$file['mime']]
2551
            : false;
2552
2553
        if (! $archiver) {
2554
            return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
2555
        }
2556
2557
        $path = $this->decode($hash);
2558
        $parent = $this->stat($this->dirnameCE($path));
2559
2560
        if (! $file['read'] || ! $parent['write']) {
2561
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2562
        }
2563
        $this->clearcache();
2564
        $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...
2565
2566
        if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) {
2567
            if (is_array($path)) {
2568
                foreach ($path as $_k => $_p) {
2569
                    $path[$_k] = $this->stat($_p);
2570
                }
2571
            } else {
2572
                $path = $this->stat($path);
2573
            }
2574
2575
            return $path;
2576
        } else {
2577
            return false;
2578
        }
2579
    }
2580
2581
    /**
2582
     * Add files to archive.
2583
     *
2584
     * @param $hashes
2585
     * @param $mime
2586
     * @param string $name
2587
     * @return array|bool
2588
     */
2589
    public function archive($hashes, $mime, $name = '')
2590
    {
2591
        if ($this->commandDisabled('archive')) {
2592
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2593
        }
2594
2595
        if ($name !== '' && ! $this->nameAccepted($name, false)) {
2596
            return $this->setError(elFinder::ERROR_INVALID_NAME);
2597
        }
2598
2599
        $archiver = isset($this->archivers['create'][$mime])
2600
            ? $this->archivers['create'][$mime]
2601
            : false;
2602
2603
        if (! $archiver) {
2604
            return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
2605
        }
2606
2607
        $files = [];
2608
2609
        foreach ($hashes as $hash) {
2610 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...
2611
                return $this->setError(elFinder::ERROR_FILE_NOT_FOUND, '#' + $hash);
2612
            }
2613
            if (! $file['read']) {
2614
                return $this->setError(elFinder::ERROR_PERM_DENIED);
2615
            }
2616
            $path = $this->decode($hash);
2617
            if (! isset($dir)) {
2618
                $dir = $this->dirnameCE($path);
2619
                $stat = $this->stat($dir);
2620
                if (! $stat['write']) {
2621
                    return $this->setError(elFinder::ERROR_PERM_DENIED);
2622
                }
2623
            }
2624
2625
            $files[] = $this->basenameCE($path);
2626
        }
2627
2628
        if ($name === '') {
2629
            $name = count($files) == 1 ? $files[0] : 'Archive';
2630
        } else {
2631
            $name = str_replace(['/', '\\'], '_', preg_replace('/\.'.preg_quote($archiver['ext'], '/').'$/i', '', $name));
2632
        }
2633
        $name .= '.'.$archiver['ext'];
2634
        $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...
2635
        $this->clearcache();
2636
2637
        return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false;
2638
    }
2639
2640
    /**
2641
     * Resize image.
2642
     *
2643
     * @param  string   $hash    image file
2644
     * @param  int      $width   new width
2645
     * @param  int      $height  new height
2646
     * @param  int      $x       X start poistion for crop
2647
     * @param  int      $y       Y start poistion for crop
2648
     * @param  string   $mode    action how to mainpulate image
2649
     * @param  string   $bg      background color
2650
     * @param  int      $degree  rotete degree
2651
     * @param  int      $jpgQuality  JEPG quality (1-100)
2652
     * @return array|false
2653
     * @author Dmitry (dio) Levashov
2654
     * @author Alexey Sukhotin
2655
     * @author nao-pon
2656
     * @author Troex Nevelin
2657
     **/
2658
    public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0, $jpgQuality = null)
2659
    {
2660
        if ($this->commandDisabled('resize')) {
2661
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2662
        }
2663
2664
        if (($file = $this->file($hash)) == false) {
2665
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2666
        }
2667
2668
        if (! $file['write'] || ! $file['read']) {
2669
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2670
        }
2671
2672
        $path = $this->decode($hash);
2673
2674
        $work_path = $this->getWorkFile($this->encoding ? $this->convEncIn($path, true) : $path);
2675
2676
        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...
2677
            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...
2678
                unlink($work_path);
2679
            }
2680
2681
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2682
        }
2683
2684
        if ($this->imgLib !== 'imagick' && $this->imgLib !== 'convert') {
2685
            if (elFinder::isAnimationGif($work_path)) {
2686
                return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
2687
            }
2688
        }
2689
2690
        switch ($mode) {
2691
2692 View Code Duplication
            case 'propresize':
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...
2693
                $result = $this->imgResize($work_path, $width, $height, true, true, null, $jpgQuality);
2694
                break;
2695
2696
            case 'crop':
2697
                $result = $this->imgCrop($work_path, $width, $height, $x, $y, null, $jpgQuality);
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...
2698
                break;
2699
2700
            case 'fitsquare':
2701
                $result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality);
2702
                break;
2703
2704
            case 'rotate':
2705
                $result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['bgColorFb']), null, $jpgQuality);
2706
                break;
2707
2708 View Code Duplication
            default:
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...
2709
                $result = $this->imgResize($work_path, $width, $height, false, true, null, $jpgQuality);
2710
                break;
2711
        }
2712
2713
        $ret = false;
2714
        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...
2715
            $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...
2716
            $this->clearstatcache();
2717
            $stat = $this->stat($path);
2718
            $fstat = stat($work_path);
2719
            $stat['size'] = $fstat['size'];
2720
            $stat['ts'] = $fstat['mtime'];
2721
            if ($imgsize = getimagesize($work_path)) {
2722
                $stat['width'] = $imgsize[0];
2723
                $stat['height'] = $imgsize[1];
2724
                $stat['mime'] = $imgsize['mime'];
2725
            }
2726
            if ($path !== $work_path) {
2727
                if ($fp = fopen($work_path, 'rb')) {
2728
                    $ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat);
2729
                    fclose($fp);
2730
                }
2731
            } else {
2732
                $ret = true;
2733
            }
2734
            if ($ret) {
2735
                $this->clearcache();
2736
                $ret = $this->stat($path);
2737
                $ret['width'] = $stat['width'];
2738
                $ret['height'] = $stat['height'];
2739
            }
2740
        }
2741
        if ($path !== $work_path) {
2742
            is_file($work_path) && unlink($work_path);
2743
        }
2744
2745
        return $ret;
2746
    }
2747
2748
    /**
2749
     * Remove file/dir.
2750
     *
2751
     * @param  string  $hash  file hash
2752
     * @return bool
2753
     * @author Dmitry (dio) Levashov
2754
     **/
2755
    public function rm($hash)
2756
    {
2757
        return $this->commandDisabled('rm')
2758
            ? $this->setError(elFinder::ERROR_PERM_DENIED)
2759
            : $this->remove($this->decode($hash));
2760
    }
2761
2762
    /**
2763
     * Search files.
2764
     *
2765
     * @param  string $q search string
2766
     * @param  array $mimes
2767
     * @param null $hash
2768
     * @return array
2769
     * @author Dmitry (dio) Levashov
2770
     */
2771
    public function search($q, $mimes, $hash = null)
2772
    {
2773
        $dir = null;
2774
        if ($hash) {
2775
            $dir = $this->decode($hash);
2776
            $stat = $this->stat($dir);
2777
            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...
2778
                $q = '';
2779
            }
2780
        }
2781
        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...
2782
            $mimes = array_intersect($mimes, $this->onlyMimes);
2783
            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...
2784
                $q = '';
2785
            }
2786
        }
2787
        $this->searchStart = time();
2788
2789
        $qs = preg_split('/"([^"]+)"| +/', $q, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
2790
        $query = $excludes = [];
2791
        foreach ($qs as $_q) {
2792
            $_q = trim($_q);
2793
            if ($_q !== '') {
2794
                if ($_q[0] === '-') {
2795
                    if (isset($_q[1])) {
2796
                        $excludes[] = substr($_q, 1);
2797
                    }
2798
                } else {
2799
                    $query[] = $_q;
2800
                }
2801
            }
2802
        }
2803
        if (! $query) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $query 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...
2804
            $q = '';
2805
        } else {
2806
            $q = implode(' ', $query);
2807
            $this->doSearchCurrentQuery = [
0 ignored issues
show
Documentation Bug introduced by
It seems like array('q' => $q, 'excludes' => $excludes) of type array<string,string|arra...g","excludes":"array"}> is incompatible with the declared type string of property $doSearchCurrentQuery.

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...
2808
                'q' => $q,
2809
                'excludes' => $excludes,
2810
            ];
2811
        }
2812
2813
        // valided regex $this->options['searchExDirReg']
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...
2814 View Code Duplication
        if ($this->options['searchExDirReg']) {
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...
2815
            if (false === preg_match($this->options['searchExDirReg'], '')) {
2816
                $this->options['searchExDirReg'] = '';
2817
            }
2818
        }
2819
2820
        return ($q === '' || $this->commandDisabled('search'))
2821
            ? []
2822
            : $this->doSearch(is_null($dir) ? $this->root : $dir, $q, $mimes);
2823
    }
2824
2825
    /**
2826
     * Return image dimensions.
2827
     *
2828
     * @param  string  $hash  file hash
2829
     * @return array
2830
     * @author Dmitry (dio) Levashov
2831
     **/
2832
    public function dimensions($hash)
2833
    {
2834
        if (($file = $this->file($hash)) == false) {
2835
            return false;
2836
        }
2837
2838
        return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime']));
2839
    }
2840
2841
    /**
2842
     * Return has subdirs.
2843
     *
2844
     * @param  string  $hash  file hash
2845
     * @return bool
2846
     * @author Naoki Sawada
2847
     **/
2848
    public function subdirs($hash)
2849
    {
2850
        return (bool) $this->subdirsCE($this->decode($hash));
2851
    }
2852
2853
    /**
2854
     * Return content URL (for netmout volume driver)
2855
     * If file.url == 1 requests from JavaScript client with XHR.
2856
     *
2857
     * @param string $hash  file hash
2858
     * @param array $options  options array
2859
     * @return bool|string
2860
     * @author Naoki Sawada
2861
     */
2862
    public function getContentUrl($hash, $options = [])
2863
    {
2864
        if (($file = $this->file($hash)) === false) {
2865
            return false;
2866
        }
2867
        if (empty($file['url'])) {
2868
            if ($this->URL) {
2869
                $path = str_replace($this->separator, '/', substr($this->decode($hash), strlen($this->root) + 1));
2870
                if ($this->encoding) {
2871
                    $path = $this->convEncIn($path, true);
2872
                }
2873
                $path = str_replace('%2F', '/', rawurlencode($path));
2874
2875
                return $this->URL.$path;
2876
            }
2877
2878
            return false;
2879
        } else {
2880
            if ($file['url'] != 1) {
2881
                return $file['url'];
2882
            } elseif (! empty($options['temporary']) && $this->tmpLinkPath) {
2883
                $name = 'temp_'.md5($hash);
2884
                $path = $this->tmpLinkPath.DIRECTORY_SEPARATOR.$name;
2885
                $contents = $this->getContents($hash);
0 ignored issues
show
Unused Code introduced by
$contents 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...
2886
                $gc = create_function('$p,$t', 'foreach(glob($p) as $f) { (filemtime($f) < (time() - $t)) && 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...
2887
                /*$gc = function($p,$t) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% 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...
2888
                    foreach(glob($p) as $f) {
2889
                        (filemtime($f) < (time() - $t)) && unlink($f);
2890
                    }
2891
                };*/
2892
                register_shutdown_function($gc, $this->tmpLinkPath.DIRECTORY_SEPARATOR.'temp_*', elFinder::$tmpLinkLifeTime);
2893
                if (file_put_contents($path, $this->getContents($hash))) {
2894
                    return $this->tmpLinkUrl.'/'.$name;
2895
                }
2896
            }
2897
2898
            return false;
2899
        }
2900
    }
2901
2902
    /**
2903
     * Return temp path.
2904
     *
2905
     * @return string
2906
     * @author Naoki Sawada
2907
     */
2908
    public function getTempPath()
2909
    {
2910
        $tempPath = null;
2911
        if (isset($this->tmpPath) && $this->tmpPath && is_writable($this->tmpPath)) {
2912
            $tempPath = $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...
2913
        } elseif (isset($this->tmp) && $this->tmp && is_writable($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...
2914
            $tempPath = $this->tmp;
2915
        } elseif (function_exists('sys_get_temp_dir')) {
2916
            $tempPath = sys_get_temp_dir();
2917
        } elseif ($this->tmbPathWritable) {
2918
            $tempPath = $this->tmbPath;
2919
        }
2920
        if ($tempPath && DIRECTORY_SEPARATOR !== '/') {
2921
            $tempPath = str_replace('/', DIRECTORY_SEPARATOR, $tempPath);
2922
        }
2923
2924
        return $tempPath;
2925
    }
2926
2927
    /**
2928
     * (Make &) Get upload taget dirctory hash.
2929
     *
2930
     * @param string $baseTargetHash
2931
     * @param string $path
2932
     * @param array  $result
2933
     * @return bool|string
2934
     * @author Naoki Sawada
2935
     */
2936
    public function getUploadTaget($baseTargetHash, $path, &$result)
2937
    {
2938
        $base = $this->decode($baseTargetHash);
2939
        $targetHash = $baseTargetHash;
2940
        $path = ltrim($path, $this->separator);
2941
        $dirs = explode($this->separator, $path);
2942
        array_pop($dirs);
2943
        foreach ($dirs as $dir) {
2944
            $targetPath = $this->joinPathCE($base, $dir);
2945
            if (! $_realpath = $this->realpath($this->encode($targetPath))) {
2946
                if ($stat = $this->mkdir($targetHash, $dir)) {
2947
                    $result['added'][] = $stat;
2948
                    $targetHash = $stat['hash'];
2949
                    $base = $this->decode($targetHash);
2950
                } else {
2951
                    return false;
2952
                }
2953
            } else {
2954
                $targetHash = $this->encode($_realpath);
2955
                if ($this->dir($targetHash)) {
2956
                    $base = $this->decode($targetHash);
2957
                } else {
2958
                    return false;
2959
                }
2960
            }
2961
        }
2962
2963
        return $targetHash;
2964
    }
2965
2966
    /**
2967
     * Return this uploadMaxSize value.
2968
     *
2969
     * @return int
2970
     * @author Naoki Sawada
2971
     */
2972
    public function getUploadMaxSize()
2973
    {
2974
        return $this->uploadMaxSize;
2975
    }
2976
2977
    public function setUploadOverwrite($var)
2978
    {
2979
        $this->uploadOverwrite = (bool) $var;
0 ignored issues
show
Documentation Bug introduced by
The property $uploadOverwrite was declared of type string, but (bool) $var is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
2980
    }
2981
2982
    /**
2983
     * Image file utility.
2984
     *
2985
     * @param string $mode     'resize', 'rotate', 'propresize', 'crop', 'fitsquare'
2986
     * @param string $src      Image file local path
2987
     * @param array  $options  excute options
2988
     * @return bool
2989
     * @author Naoki Sawada
2990
     */
2991
    public function imageUtil($mode, $src, $options = [])
2992
    {
2993
        if (! isset($options['jpgQuality'])) {
2994
            $options['jpgQuality'] = intval($this->options['jpgQuality']);
2995
        }
2996
        if (! isset($options['bgcolor'])) {
2997
            $options['bgcolor'] = '#ffffff';
2998
        }
2999
        if (! isset($options['bgColorFb'])) {
3000
            $options['bgColorFb'] = $this->options['bgColorFb'];
3001
        }
3002
3003
        // check 'width' ,'height'
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
3004
        if (in_array($mode, ['resize', 'propresize', 'crop', 'fitsquare'])) {
3005
            if (empty($options['width']) || empty($options['height'])) {
3006
                return false;
3007
            }
3008
        }
3009
3010
        switch ($mode) {
3011
            case 'rotate':
3012
                if (empty($options['degree'])) {
3013
                    return true;
3014
                }
3015
3016
                return (bool) $this->imgRotate($src, $options['degree'], $options['bgColorFb'], null, $options['jpgQuality']);
3017
3018 View Code Duplication
            case 'resize':
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...
3019
                return (bool) $this->imgResize($src, $options['width'], $options['height'], false, true, null, $options['jpgQuality'], $options);
3020
3021 View Code Duplication
            case 'propresize':
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...
3022
                return (bool) $this->imgResize($src, $options['width'], $options['height'], true, true, null, $options['jpgQuality'], $options);
3023
3024
            case 'crop':
3025
                if (isset($options['x']) && isset($options['y'])) {
3026
                    return (bool) $this->imgCrop($src, $options['width'], $options['height'], $options['x'], $options['y'], null, $options['jpgQuality']);
3027
                }
3028
                break;
3029
3030
            case 'fitsquare':
3031
                return (bool) $this->imgSquareFit($src, $options['width'], $options['height'], 'center', 'middle', $options['bgcolor'], null, $options['jpgQuality']);
3032
3033
        }
3034
3035
        return false;
3036
    }
3037
3038
    /**
3039
     * Convert Video To Image by ffmpeg.
3040
     *
3041
     * @param  $file video source file path
3042
     * @param  $stat file stat array
3043
     * @return bool
3044
     * @author Naoki Sawada
3045
     */
3046
    public function ffmpegToImg($file, $stat)
0 ignored issues
show
Coding Style introduced by
ffmpegToImg uses the super-global variable $GLOBALS 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...
3047
    {
3048
        $name = basename($file);
3049
        $path = dirname($file);
3050
        $tmp = $path.DIRECTORY_SEPARATOR.md5($name);
3051
        $GLOBALS['elFinderTempFiles'][] = $tmp; // regist to remove at the end
3052
        if (rename($file, $tmp)) {
3053
            // specific start time by file name (xxx^[sec].[extention] - video^3.mp4)
3054
            if (preg_match('/\^(\d+(?:\.\d+)?)\.[^.]+$/', $stat['name'], $_m)) {
3055
                $ss = $_m[1];
3056
            } else {
3057
                $ss = $this->options['tmbVideoConvSec'];
3058
            }
3059
            $cmd = sprintf('ffmpeg -i %s -ss 00:00:%.3f -vframes 1 -f image2 %s', escapeshellarg($tmp), $ss, escapeshellarg($file));
3060
            $r = $this->procExec($cmd);
3061
            unlink($tmp);
3062
3063
            return $r === 0;
3064
        }
3065
3066
        return false;
3067
    }
3068
3069
    /**
3070
     * Return new unique name based on file name and suffix.
3071
     *
3072
     * @param $dir
3073
     * @param $name
3074
     * @param  string $suffix suffix append to name
3075
     * @param bool $checkNum
3076
     * @param int $start
3077
     * @return string
3078
     * @internal param string $path file path
3079
     * @author Dmitry (dio) Levashov
3080
     */
3081
    public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1)
3082
    {
3083
        $ext = '';
3084
3085
        if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
3086
            $ext = '.'.$m[1];
3087
            $name = substr($name, 0, strlen($name) - strlen($m[0]));
3088
        }
3089
3090
        if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) {
3091
            $i = (int) $m[2];
3092
            $name = substr($name, 0, strlen($name) - strlen($m[2]));
3093
        } else {
3094
            $i = $start;
3095
            $name .= $suffix;
3096
        }
3097
        $max = $i + 100000;
3098
3099
        while ($i <= $max) {
3100
            $n = $name.($i > 0 ? sprintf($this->options['uniqueNumFormat'], $i) : '').$ext;
3101
3102
            if (! $this->isNameExists($this->joinPathCE($dir, $n))) {
3103
                $this->clearcache();
3104
3105
                return $n;
3106
            }
3107
            $i++;
3108
        }
3109
3110
        return $name.md5($dir).$ext;
3111
    }
3112
3113
    /**
3114
     * Converts character encoding from UTF-8 to server's one.
3115
     *
3116
     * @param  mixed  $var           target string or array var
3117
     * @param  bool   $restoreLocale do retore global locale, default is false
3118
     * @param  string $unknown       replaces character for unknown
3119
     * @return mixed
3120
     * @author Naoki Sawada
3121
     */
3122
    public function convEncIn($var = null, $restoreLocale = false, $unknown = '_')
3123
    {
3124
        return (! $this->encoding) ? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown);
3125
    }
3126
3127
    /**
3128
     * Converts character encoding from server's one to UTF-8.
3129
     *
3130
     * @param  mixed  $var           target string or array var
3131
     * @param  bool   $restoreLocale do retore global locale, default is true
3132
     * @param  string $unknown       replaces character for unknown
3133
     * @return mixed
3134
     * @author Naoki Sawada
3135
     */
3136
    public function convEncOut($var = null, $restoreLocale = true, $unknown = '_')
3137
    {
3138
        return (! $this->encoding) ? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown);
3139
    }
3140
3141
    /**
3142
     * Get image size array with `dimensions`.
3143
     *
3144
     * @param string $path path need convert encoding to server encoding
3145
     * @param string $mime file mime type
3146
     * @return array|false
3147
     */
3148
    public function getImageSize($path, $mime = '')
3149
    {
3150
        $size = false;
3151
        if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') {
3152
            if ($work = $this->getWorkFile($path)) {
3153
                if ($size = getimagesize($work)) {
3154
                    $size['dimensions'] = $size[0].'x'.$size[1];
3155
                }
3156
            }
3157
            is_file($work) && unlink($work);
3158
        }
3159
3160
        return $size;
3161
    }
3162
3163
    /**
3164
     * Remove directory recursive on local file system.
3165
     *
3166
     * @param string $dir Target dirctory path
3167
     * @return bool
3168
     * @author Naoki Sawada
3169
     */
3170
    public function rmdirRecursive($dir)
3171
    {
3172
        return self::localRmdirRecursive($dir);
3173
    }
3174
3175
    /*********************************************************************/
3176
    /*                            INITIALIZATION                         */
3177
    /*********************************************************************/
3178
3179
    /**
3180
     * Prepare driver before mount volume.
3181
     * Return true if volume is ready.
3182
     *
3183
     * @return bool
3184
     * @author Dmitry (dio) Levashov
3185
     **/
3186
    protected function init()
3187
    {
3188
        return true;
3189
    }
3190
3191
    /**
3192
     * Configure after successfull mount.
3193
     * By default set thumbnails path and image manipulation library.
3194
     *
3195
     * @return void
3196
     * @author Dmitry (dio) Levashov
3197
     **/
3198
    protected function configure()
3199
    {
3200
        // set thumbnails path
3201
        $path = $this->options['tmbPath'];
3202
        if ($path) {
3203 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...
3204
                if (mkdir($path)) {
3205
                    chmod($path, $this->options['tmbPathMode']);
3206
                } else {
3207
                    $path = '';
3208
                }
3209
            }
3210
3211
            if (is_dir($path) && is_readable($path)) {
3212
                $this->tmbPath = $path;
3213
                $this->tmbPathWritable = is_writable($path);
3214
            }
3215
        }
3216
        // set resouce path
3217
        if (! is_dir($this->options['resourcePath'])) {
3218
            $this->options['resourcePath'] = dirname(__FILE__).DIRECTORY_SEPARATOR.'resources';
3219
        }
3220
3221
        // set image manipulation library
3222
        $type = preg_match('/^(imagick|gd|convert|auto)$/i', $this->options['imgLib'])
3223
            ? strtolower($this->options['imgLib'])
3224
            : 'auto';
3225
3226
        $imgLibFallback = extension_loaded('imagick') ? 'imagick' : (function_exists('gd_info') ? 'gd' : '');
0 ignored issues
show
Unused Code introduced by
$imgLibFallback 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...
3227
        if (($type === 'imagick' || $type === 'auto') && extension_loaded('imagick')) {
3228
            $this->imgLib = 'imagick';
3229
        } elseif (($type === 'gd' || $type === 'auto') && function_exists('gd_info')) {
3230
            $this->imgLib = 'gd';
3231
        } else {
3232
            $convertCache = 'imgLibConvert';
3233
            if (($convertCmd = $this->session->get($convertCache, false)) !== false) {
3234
                $this->imgLib = $convertCmd;
3235
            } else {
3236
                $this->imgLib = ($this->procExec('convert -version') === 0) ? 'convert' : '';
3237
                $this->session->set($convertCache, $this->imgLib);
3238
            }
3239
        }
3240
        if ($type !== 'auto' && $this->imgLib === '') {
3241
            // fallback
3242
            $this->imgLib = extension_loaded('imagick') ? 'imagick' : (function_exists('gd_info') ? 'gd' : '');
3243
        }
3244
3245
        // check video to img converter
3246 View Code Duplication
        if (! empty($this->options['imgConverter']) && is_array($this->options['imgConverter'])) {
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...
3247
            foreach ($this->options['imgConverter'] as $_type => $_converter) {
3248
                if (isset($_converter['func'])) {
3249
                    $this->imgConverter[strtolower($_type)] = $_converter;
3250
                }
3251
            }
3252
        }
3253
        if (! isset($this->imgConverter['video'])) {
3254
            $videoLibCache = 'videoLib';
3255
            if (($videoLibCmd = $this->session->get($videoLibCache, false)) === false) {
3256
                $videoLibCmd = ($this->procExec('ffmpeg -version') === 0) ? 'ffmpeg' : '';
3257
                $this->session->set($videoLibCache, $videoLibCmd);
3258
            }
3259
            if ($videoLibCmd) {
3260
                $this->imgConverter['video'] = [
3261
                    'func' => [$this, $videoLibCmd.'ToImg'],
3262
                    'maxlen' => $this->options['tmbVideoConvLen'],
3263
                ];
3264
            }
3265
        }
3266
3267
        // check archivers
3268
        if (empty($this->archivers['create'])) {
3269
            $this->disabled[] = 'archive';
3270
        }
3271
        if (empty($this->archivers['extract'])) {
3272
            $this->disabled[] = 'extract';
3273
        }
3274
        $_arc = $this->getArchivers();
3275
        if (empty($_arc['create'])) {
3276
            $this->disabled[] = 'zipdl';
3277
        }
3278
3279
        // check 'statOwner' for command `chmod`
3280
        if (empty($this->options['statOwner'])) {
3281
            $this->disabled[] = 'chmod';
3282
        }
3283
3284
        // check 'mimeMap'
3285
        if (! is_array($this->options['mimeMap'])) {
3286
            $this->options['mimeMap'] = [];
3287
        }
3288 View Code Duplication
        if (is_array($this->options['staticMineMap']) && $this->options['staticMineMap']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3289
            $this->options['mimeMap'] = array_merge($this->options['mimeMap'], $this->options['staticMineMap']);
3290
        }
3291 View Code Duplication
        if (is_array($this->options['additionalMimeMap']) && $this->options['additionalMimeMap']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3292
            $this->options['mimeMap'] = array_merge($this->options['mimeMap'], $this->options['additionalMimeMap']);
3293
        }
3294
3295
        // check 'url' in disabled commands
3296
        if (in_array('url', $this->disabled)) {
3297
            $this->disabledGetUrl = true;
0 ignored issues
show
Documentation Bug introduced by
The property $disabledGetUrl was declared of type string, but true is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
3298
        }
3299
3300
        // set run time setting uploadOverwrite
3301
        $this->uploadOverwrite = $this->options['uploadOverwrite'];
3302
    }
3303
3304
    /**
3305
     * @deprecated
3306
     */
3307
    protected function sessionRestart()
3308
    {
3309
        $this->sessionCache = $this->session->start()->get($this->id, []);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->session->start()->get($this->id, array()) of type * is incompatible with the declared type array of property $sessionCache.

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...
3310
3311
        return true;
3312
    }
3313
3314
    /**
3315
     * Save error message.
3316
     *
3317
     * @param  array  error
3318
     * @return false
3319
     * @author Naoki Sawada
3320
     **/
3321
    protected function setError()
3322
    {
3323
        $this->error = [];
3324
        $this->addError(func_get_args());
3325
3326
        return false;
3327
    }
3328
3329
    /**
3330
     * Add error message.
3331
     *
3332
     * @param  array  error
3333
     * @return false
3334
     * @author Dmitry(dio) Levashov
3335
     **/
3336
    protected function addError()
3337
    {
3338
        foreach (func_get_args() as $err) {
3339
            if (is_array($err)) {
3340
                $this->error = array_merge($this->error, $err);
3341
            } else {
3342
                $this->error[] = $err;
3343
            }
3344
        }
3345
3346
        return false;
3347
    }
3348
3349
    /*********************************************************************/
3350
    /*                               FS API                              */
3351
    /*********************************************************************/
3352
3353
    /***************** server encoding support *******************/
3354
3355
    /**
3356
     * Return parent directory path (with convert encoding).
3357
     *
3358
     * @param  string  $path  file path
3359
     * @return string
3360
     * @author Naoki Sawada
3361
     **/
3362
    protected function dirnameCE($path)
3363
    {
3364
        $dirname = (! $this->encoding) ? $this->_dirname($path) : $this->convEncOut($this->_dirname($this->convEncIn($path)));
3365
        // check to infinite loop prevention
3366
        return ($dirname != $path) ? $dirname : '';
3367
    }
3368
3369
    /**
3370
     * Return file name (with convert encoding).
3371
     *
3372
     * @param  string  $path  file path
3373
     * @return string
3374
     * @author Naoki Sawada
3375
     **/
3376
    protected function basenameCE($path)
3377
    {
3378
        return (! $this->encoding) ? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path)));
3379
    }
3380
3381
    /**
3382
     * Join dir name and file name and return full path. (with convert encoding)
3383
     * Some drivers (db) use int as path - so we give to concat path to driver itself.
3384
     *
3385
     * @param  string  $dir   dir path
3386
     * @param  string  $name  file name
3387
     * @return string
3388
     * @author Naoki Sawada
3389
     **/
3390
    protected function joinPathCE($dir, $name)
3391
    {
3392
        return (! $this->encoding) ? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name)));
3393
    }
3394
3395
    /**
3396
     * Return normalized path (with convert encoding).
3397
     *
3398
     * @param  string  $path  file path
3399
     * @return string
3400
     * @author Naoki Sawada
3401
     **/
3402
    protected function normpathCE($path)
3403
    {
3404
        return (! $this->encoding) ? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path)));
3405
    }
3406
3407
    /**
3408
     * Return file path related to root dir (with convert encoding).
3409
     *
3410
     * @param  string  $path  file path
3411
     * @return string
3412
     * @author Naoki Sawada
3413
     **/
3414
    protected function relpathCE($path)
3415
    {
3416
        return (! $this->encoding) ? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path)));
3417
    }
3418
3419
    /**
3420
     * Convert path related to root dir into real path (with convert encoding).
3421
     *
3422
     * @param  string  $path  rel file path
3423
     * @return string
3424
     * @author Naoki Sawada
3425
     **/
3426
    protected function abspathCE($path)
3427
    {
3428
        return (! $this->encoding) ? $this->_abspath($path) : $this->convEncOut($this->_abspath($this->convEncIn($path)));
3429
    }
3430
3431
    /**
3432
     * Return true if $path is children of $parent (with convert encoding).
3433
     *
3434
     * @param  string  $path    path to check
3435
     * @param  string  $parent  parent path
3436
     * @return bool
3437
     * @author Naoki Sawada
3438
     **/
3439
    protected function inpathCE($path, $parent)
3440
    {
3441
        return (! $this->encoding) ? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent)));
3442
    }
3443
3444
    /**
3445
     * Open file and return file pointer (with convert encoding).
3446
     *
3447
     * @param  string $path file path
3448
     * @param string $mode
3449
     * @return false|resource
3450
     * @internal param bool $write open file for writing
3451
     * @author Naoki Sawada
3452
     */
3453
    protected function fopenCE($path, $mode = 'rb')
3454
    {
3455
        return (! $this->encoding) ? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode));
3456
    }
3457
3458
    /**
3459
     * Close opened file (with convert encoding).
3460
     *
3461
     * @param  resource  $fp    file pointer
3462
     * @param  string    $path  file path
3463
     * @return bool
3464
     * @author Naoki Sawada
3465
     **/
3466
    protected function fcloseCE($fp, $path = '')
3467
    {
3468
        return (! $this->encoding) ? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path)));
3469
    }
3470
3471
    /**
3472
     * Create new file and write into it from file pointer. (with convert encoding)
3473
     * Return new file path or false on error.
3474
     *
3475
     * @param  resource  $fp   file pointer
3476
     * @param  string    $dir  target dir path
3477
     * @param  string    $name file name
3478
     * @param  array     $stat file stat (required by some virtual fs)
3479
     * @return bool|string
3480
     * @author Naoki Sawada
3481
     **/
3482
    protected function saveCE($fp, $dir, $name, $stat)
3483
    {
3484
        return (! $this->encoding) ? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat)));
3485
    }
3486
3487
    /**
3488
     * Return true if path is dir and has at least one childs directory (with convert encoding).
3489
     *
3490
     * @param  string  $path  dir path
3491
     * @return bool
3492
     * @author Naoki Sawada
3493
     **/
3494
    protected function subdirsCE($path)
3495
    {
3496 View Code Duplication
        if ($this->sessionCaching['subdirs']) {
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...
3497
            if (isset($this->sessionCache['subdirs'][$path]) && ! $this->isMyReload()) {
3498
                return $this->sessionCache['subdirs'][$path];
3499
            }
3500
        }
3501
        $hasdir = (bool) ((! $this->encoding) ? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path))));
3502
        $this->updateSubdirsCache($path, $hasdir);
3503
3504
        return $hasdir;
3505
    }
3506
3507
    /**
3508
     * Return files list in directory (with convert encoding).
3509
     *
3510
     * @param  string  $path  dir path
3511
     * @return array
3512
     * @author Naoki Sawada
3513
     **/
3514
    protected function scandirCE($path)
3515
    {
3516
        return (! $this->encoding) ? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path)));
3517
    }
3518
3519
    /**
3520
     * Create symlink (with convert encoding).
3521
     *
3522
     * @param  string  $source     file to link to
3523
     * @param  string  $targetDir  folder to create link in
3524
     * @param  string  $name       symlink name
3525
     * @return bool
3526
     * @author Naoki Sawada
3527
     **/
3528
    protected function symlinkCE($source, $targetDir, $name)
3529
    {
3530
        return (! $this->encoding) ? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name)));
3531
    }
3532
3533
    /***************** paths *******************/
3534
3535
    /**
3536
     * Encode path into hash.
3537
     *
3538
     * @param  string  file path
3539
     * @return string
3540
     * @author Dmitry (dio) Levashov
3541
     * @author Troex Nevelin
3542
     **/
3543
    protected function encode($path)
3544
    {
3545
        if ($path !== '') {
3546
3547
            // cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
3548
            $p = $this->relpathCE($path);
3549
            // if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
3550
            if ($p === '') {
3551
                $p = $this->separator;
3552
            }
3553
3554
            // TODO crypt path and return hash
3555
            $hash = $this->crypt($p);
3556
            // hash is used as id in HTML that means it must contain vaild chars
3557
            // make base64 html safe and append prefix in begining
3558
            $hash = strtr(base64_encode($hash), '+/=', '-_.');
3559
            // remove dots '.' at the end, before it was '=' in base64
3560
            $hash = rtrim($hash, '.');
3561
            // append volume id to make hash unique
3562
            return $this->id.$hash;
3563
        }
3564
        //TODO: Add return statement here
3565
    }
3566
3567
    /**
3568
     * Decode path from hash.
3569
     *
3570
     * @param  string  file hash
3571
     * @return string
3572
     * @author Dmitry (dio) Levashov
3573
     * @author Troex Nevelin
3574
     **/
3575
    protected function decode($hash)
3576
    {
3577
        if (strpos($hash, $this->id) === 0) {
3578
            // cut volume id after it was prepended in encode
3579
            $h = substr($hash, strlen($this->id));
3580
            // replace HTML safe base64 to normal
3581
            $h = base64_decode(strtr($h, '-_.', '+/='));
3582
            // TODO uncrypt hash and return path
3583
            $path = $this->uncrypt($h);
3584
            // append ROOT to path after it was cut in encode
3585
            return $this->abspathCE($path); //$this->root.($path === $this->separator ? '' : $this->separator.$path);
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
3586
        }
3587
        //TODO: Add return statement here
3588
    }
3589
3590
    /**
3591
     * Return crypted path
3592
     * Not implemented.
3593
     *
3594
     * @param  string  path
3595
     * @return mixed
3596
     * @author Dmitry (dio) Levashov
3597
     **/
3598
    protected function crypt($path)
3599
    {
3600
        return $path;
3601
    }
3602
3603
    /**
3604
     * Return uncrypted path
3605
     * Not implemented.
3606
     *
3607
     * @param  mixed  hash
3608
     * @return mixed
3609
     * @author Dmitry (dio) Levashov
3610
     **/
3611
    protected function uncrypt($hash)
3612
    {
3613
        return $hash;
3614
    }
3615
3616
    /**
3617
     * Validate file name based on $this->options['acceptedName'] regexp or function.
3618
     *
3619
     * @param  string  $name  file name
3620
     * @return bool
3621
     * @author Dmitry (dio) Levashov
3622
     **/
3623
    protected function nameAccepted($name, $isDir = false)
3624
    {
3625
        if (json_encode($name) === false) {
3626
            return false;
3627
        }
3628
        $nameValidator = $isDir ? $this->dirnameValidator : $this->nameValidator;
3629
        if ($nameValidator) {
3630
            if (is_callable($nameValidator)) {
3631
                $res = call_user_func($nameValidator, $name);
3632
3633
                return $res;
3634
            }
3635
            if (preg_match($nameValidator, '') !== false) {
3636
                return preg_match($nameValidator, $name);
3637
            }
3638
        }
3639
3640
        return true;
3641
    }
3642
3643
    /**
3644
     * Return session rootstat cache key.
3645
     *
3646
     * @return string
3647
     */
3648
    protected function getRootstatCachekey()
3649
    {
3650
        return md5($this->root.(isset($this->options['alias']) ? $this->options['alias'] : ''));
3651
    }
3652
3653
    /**
3654
     * Converts character encoding (base function).
3655
     *
3656
     * @param  mixed $var target string or array var
3657
     * @param  string $from from character encoding
3658
     * @param  string $to to character encoding
3659
     * @param  string $locale local locale
3660
     * @param $restoreLocale
3661
     * @param  string $unknown replaces character for unknown
3662
     * @return mixed
3663
     */
3664
    protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_')
3665
    {
3666
        if (strtoupper($from) !== strtoupper($to)) {
3667
            if ($locale) {
3668
                setlocale(LC_ALL, $locale);
3669
            }
3670
            if (is_array($var)) {
3671
                $_ret = [];
3672
                foreach ($var as $_k => $_v) {
3673
                    $_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_');
3674
                }
3675
                $var = $_ret;
3676
            } else {
3677
                $_var = false;
3678
                if (is_string($var)) {
3679
                    $_var = $var;
3680
                    if (false !== ($_var = iconv($from, $to.'//TRANSLIT', $_var))) {
3681
                        $_var = str_replace('?', $unknown, $_var);
3682
                    }
3683
                }
3684
                if ($_var !== false) {
3685
                    $var = $_var;
3686
                }
3687
            }
3688
            if ($restoreLocale) {
3689
                setlocale(LC_ALL, elFinder::$locale);
3690
            }
3691
        }
3692
3693
        return $var;
3694
    }
3695
3696
    /*********************** util mainly for inheritance class *********************/
3697
3698
    /**
3699
     * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called.
3700
     * When needing the unique file to a path, give $path to parameter.
3701
     *
3702
     * @param  string       $path for get unique file to a path
3703
     * @return string|false
3704
     * @author Naoki Sawada
3705
     */
3706
    protected function getTempFile($path = '')
3707
    {
3708
        static $cache = [];
3709
        static $rmfunc;
3710
3711
        $key = '';
3712
        if ($path !== '') {
3713
            $key = $this->id.'#'.$path;
3714
            if (isset($cache[$key])) {
3715
                return $cache[$key];
3716
            }
3717
        }
3718
3719
        if ($tmpdir = $this->getTempPath()) {
3720
            if (! $rmfunc) {
3721
                $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...
3722
            }
3723
            $name = tempnam($tmpdir, 'ELF');
3724
            if ($key) {
3725
                $cache[$key] = $name;
3726
            }
3727
            register_shutdown_function($rmfunc, $name);
3728
3729
            return $name;
3730
        }
3731
3732
        return false;
3733
    }
3734
3735
    /**
3736
     * File path of local server side work file path.
3737
     *
3738
     * @param  string $path path need convert encoding to server encoding
3739
     * @return string
3740
     * @author Naoki Sawada
3741
     */
3742
    protected function getWorkFile($path)
3743
    {
3744
        if ($work = $this->getTempFile()) {
3745
            if ($wfp = fopen($work, 'wb')) {
3746
                if ($fp = $this->_fopen($path)) {
3747
                    while (! feof($fp)) {
3748
                        fwrite($wfp, fread($fp, 8192));
3749
                    }
3750
                    $this->_fclose($fp, $path);
3751
                    fclose($wfp);
3752
3753
                    return $work;
3754
                }
3755
            }
3756
        }
3757
3758
        return false;
3759
    }
3760
3761
    /**
3762
     * Delete dirctory trees.
3763
     *
3764
     * @param string $localpath path need convert encoding to server encoding
3765
     * @return bool
3766
     * @author Naoki Sawada
3767
     */
3768
    protected function delTree($localpath)
3769
    {
3770
        foreach ($this->_scandir($localpath) as $p) {
3771
            elFinder::extendTimeLimit();
3772
            $stat = $this->stat($this->convEncOut($p));
3773
            $this->convEncIn();
3774
            ($stat['mime'] === 'directory') ? $this->delTree($p) : $this->_unlink($p);
3775
        }
3776
3777
        return $this->_rmdir($localpath);
3778
    }
3779
3780
    /**
3781
     * Copy items to a new temporary directory on the local server.
3782
     *
3783
     * @param  array  $hashes  target hashes
3784
     * @param  string $dir     destination directory (for recurcive)
3785
     * @param  string $canLink it can use link() (for recurcive)
3786
     * @return string|false    saved path name
3787
     * @author Naoki Sawada
3788
     */
3789
    protected function getItemsInHand($hashes, $dir = null, $canLink = null)
3790
    {
3791
        static $totalSize = 0;
3792
        if (is_null($dir)) {
3793
            $totalSize = 0;
3794
            if (! $tmpDir = $this->getTempPath()) {
3795
                return false;
3796
            }
3797
            $dir = tempnam($tmpDir, 'elf');
3798
            if (! unlink($dir) || ! mkdir($dir, 0700, true)) {
3799
                return false;
3800
            }
3801
            register_shutdown_function([$this, 'rmdirRecursive'], $dir);
3802
        }
3803
        if (is_null($canLink)) {
3804
            $canLink = ($this instanceof elFinderVolumeLocalFileSystem);
3805
        }
3806
        elFinder::extendTimeLimit();
3807
        $res = true;
3808
        $files = [];
3809
        foreach ($hashes as $hash) {
3810
            if (($file = $this->file($hash)) == false) {
3811
                continue;
3812
            }
3813
            if (! $file['read']) {
3814
                continue;
3815
            }
3816
3817
            $name = $file['name'];
3818
            // for call from search results
3819
            if (isset($files[$name])) {
3820
                $name = preg_replace('/^(.*?)(\..*)?$/', '$1_'.$files[$name]++.'$2', $name);
3821
            } else {
3822
                $files[$name] = 1;
3823
            }
3824
            $target = $dir.DIRECTORY_SEPARATOR.$name;
3825
3826
            if ($file['mime'] === 'directory') {
3827
                $chashes = [];
3828
                $_files = $this->scandir($hash);
3829
                foreach ($_files as $_file) {
0 ignored issues
show
Bug introduced by
The expression $_files of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
3830
                    if ($file['read']) {
3831
                        $chashes[] = $_file['hash'];
3832
                    }
3833
                }
3834
                if (($res = mkdir($target, 0700, true)) && $chashes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $chashes 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...
3835
                    $res = $this->getItemsInHand($chashes, $target, $canLink);
0 ignored issues
show
Bug introduced by
It seems like $canLink can also be of type boolean; however, elFinderVolumeDriver::getItemsInHand() does only seem to accept string|null, 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...
3836
                }
3837
                if (! $res) {
3838
                    break;
3839
                }
3840
                ! empty($file['ts']) && touch($target, $file['ts']);
3841
            } else {
3842
                $path = $this->decode($hash);
3843
                if (! $canLink || ! ($canLink = link($path, $target))) {
3844
                    if ($fp = $this->fopenCE($path)) {
3845
                        if ($tfp = fopen($target, 'wb')) {
3846
                            $totalSize += stream_copy_to_stream($fp, $tfp);
3847
                            fclose($tfp);
3848
                        }
3849
                        ! empty($file['ts']) && touch($target, $file['ts']);
3850
                        $this->fcloseCE($fp, $path);
3851
                    }
3852
                } else {
3853
                    $totalSize += filesize($path);
3854
                }
3855
                if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $totalSize) {
3856
                    $res = $this->setError(elFinder::ERROR_ARC_MAXSIZE);
3857
                }
3858
            }
3859
        }
3860
3861
        return $res ? $dir : false;
3862
    }
3863
3864
    /*********************** file stat *********************/
3865
3866
    /**
3867
     * Check file attribute.
3868
     *
3869
     * @param  string  $path  file path
3870
     * @param  string  $name  attribute name (read|write|locked|hidden)
3871
     * @param  bool    $val   attribute value returned by file system
3872
     * @param  bool    $isDir path is directory (true: directory, false: file)
3873
     * @return bool
3874
     * @author Dmitry (dio) Levashov
3875
     **/
3876
    protected function attr($path, $name, $val = null, $isDir = null)
3877
    {
3878
        if (! isset($this->defaults[$name])) {
3879
            return false;
3880
        }
3881
3882
        $relpath = $this->relpathCE($path);
3883
        if ($this->separator !== '/') {
3884
            $relpath = str_replace($this->separator, '/', $relpath);
3885
        }
3886
        $relpath = '/'.$relpath;
3887
3888
        $perm = null;
3889
3890
        if ($this->access) {
3891
            $perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir, $relpath);
3892
            if ($perm !== null) {
3893
                return (bool) $perm;
3894
            }
3895
        }
3896
3897
        for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
3898
            $attrs = $this->attributes[$i];
3899
3900
            if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $relpath)) {
3901
                $perm = $attrs[$name];
3902
            }
3903
        }
3904
3905
        return $perm === null ? (is_null($val) ? $this->defaults[$name] : $val) : (bool) $perm;
3906
    }
3907
3908
    /**
3909
     * Return true if file with given name can be created in given folder.
3910
     *
3911
     * @param string $dir parent dir path
3912
     * @param string $name new file name
3913
     * @param null $isDir
3914
     * @return bool
3915
     * @author Dmitry (dio) Levashov
3916
     */
3917
    protected function allowCreate($dir, $name, $isDir = null)
3918
    {
3919
        return $this->attr($this->joinPathCE($dir, $name), 'write', true, $isDir);
3920
    }
3921
3922
    /**
3923
     * Return true if file MIME type can save with check uploadOrder config.
3924
     *
3925
     * @param string $mime
3926
     * @return bool
3927
     */
3928
    protected function allowPutMime($mime)
3929
    {
3930
        // logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
3931
        $allow = $this->mimeAccepted($mime, $this->uploadAllow, null);
3932
        $deny = $this->mimeAccepted($mime, $this->uploadDeny, null);
3933
        $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...
3934
        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...
3935
            $res = false; // default is deny
3936
            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...
3937
                $res = true;
3938
            }// else (both match | no match | match only deny) { deny }
3939
        } else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
3940
            $res = true; // default is allow
3941
            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...
3942
                $res = false;
3943
            } // else (both match | no match | match only allow) { allow }
3944
        }
3945
3946
        return $res;
3947
    }
3948
3949
    /**
3950
     * Return fileinfo.
3951
     *
3952
     * @param  string  $path  file cache
3953
     * @return array
3954
     * @author Dmitry (dio) Levashov
3955
     **/
3956
    protected function stat($path)
3957
    {
3958
        if ($path === false || is_null($path)) {
3959
            return false;
3960
        }
3961
        $is_root = ($path == $this->root);
3962
        if ($is_root) {
3963
            $rootKey = $this->getRootstatCachekey();
3964
            if ($this->sessionCaching['rootstat'] && ! isset($this->sessionCache['rootstat'])) {
3965
                $this->sessionCache['rootstat'] = [];
3966
            }
3967
            if (! isset($this->cache[$path]) && ! $this->isMyReload()) {
3968
                // need $path as key for netmount/netunmount
3969
                if ($this->sessionCaching['rootstat'] && isset($this->sessionCache['rootstat'][$rootKey])) {
3970
                    if ($ret = $this->sessionCache['rootstat'][$rootKey]) {
3971
                        if ($this->options['rootRev'] === $ret['rootRev']) {
3972 View Code Duplication
                            if (isset($this->options['phash'])) {
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...
3973
                                $ret['isroot'] = 1;
3974
                                $ret['phash'] = $this->options['phash'];
3975
                            }
3976
3977
                            return $ret;
3978
                        }
3979
                    }
3980
                }
3981
            }
3982
        }
3983
        $ret = isset($this->cache[$path])
3984
            ? $this->cache[$path]
3985
            : $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
3986
        if ($is_root && $this->sessionCaching['rootstat']) {
3987
            if ($ret) {
3988
                $this->rootModified = false;
3989
                $this->sessionCache['rootstat'][$rootKey] = $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...
3990 View Code Duplication
                if (isset($this->options['phash'])) {
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...
3991
                    $ret['isroot'] = 1;
3992
                    $ret['phash'] = $this->options['phash'];
3993
                }
3994
            } else {
3995
                unset($this->sessionCache['rootstat'][$rootKey]);
3996
            }
3997
        }
3998
3999
        return $ret;
4000
    }
4001
4002
    /**
4003
     * Get root stat extra key values.
4004
     *
4005
     * @return array stat extras
4006
     * @author Naoki Sawada
4007
     */
4008
    protected function getRootStatExtra()
4009
    {
4010
        $stat = [];
4011
        if ($this->rootName) {
4012
            $stat['name'] = $this->rootName;
4013
        }
4014
        $stat['rootRev'] = $this->options['rootRev'];
4015
        $stat['options'] = $this->options(null);
4016
4017
        return $stat;
4018
    }
4019
4020
    /**
4021
     * Return fileinfo based on filename
4022
     * For item ID based path file system
4023
     * Please override if needed on each drivers.
4024
     *
4025
     * @param  string  $path  file cache
4026
     * @return array
4027
     */
4028
    protected function isNameExists($path)
4029
    {
4030
        return $this->stat($path);
4031
    }
4032
4033
    /**
4034
     * Put file stat in cache and return it.
4035
     *
4036
     * @param  string  $path   file path
4037
     * @param  array   $stat   file stat
4038
     * @return array
4039
     * @author Dmitry (dio) Levashov
4040
     **/
4041
    protected function updateCache($path, $stat)
4042
    {
4043
        if (empty($stat) || ! is_array($stat)) {
4044
            return $this->cache[$path] = [];
4045
        }
4046
4047
        $stat['hash'] = $this->encode($path);
4048
4049
        $root = $path == $this->root;
4050
        $parent = '';
4051
4052
        if ($root) {
4053
            $stat = array_merge($stat, $this->getRootStatExtra());
4054
        } else {
4055
            if (! isset($stat['name']) || $stat['name'] === '') {
4056
                $stat['name'] = $this->basenameCE($path);
4057
            }
4058
            if (empty($stat['phash'])) {
4059
                $parent = $this->dirnameCE($path);
4060
                $stat['phash'] = $this->encode($parent);
4061
            } else {
4062
                $parent = $this->decode($stat['phash']);
4063
            }
4064
        }
4065
4066
        // name check
4067
        if (! $jeName = json_encode($stat['name'])) {
4068
            return $this->cache[$path] = [];
4069
        }
4070
        // fix name if required
4071
        if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
4072
            $stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], $jeName));
4073
        }
4074
4075
        if (empty($stat['mime'])) {
4076
            $stat['mime'] = $this->mimetype($stat['name'], true);
4077
        }
4078
4079
        // @todo move dateformat to client
4080
        // $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...
4081
        // 	? $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...
4082
        // 	: 'unknown';
4083
4084
        if (! isset($stat['size'])) {
4085
            $stat['size'] = 'unknown';
4086
        }
4087
4088
        if ($isDir = ($stat['mime'] === 'directory')) {
4089
            $stat['volumeid'] = $this->id;
4090
        }
4091
4092
        $stat['read'] = intval($this->attr($path, 'read', isset($stat['read']) ? (bool) $stat['read'] : null, $isDir));
4093
        $stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? (bool) $stat['write'] : null, $isDir));
4094
        if ($root) {
4095
            $stat['locked'] = 1;
4096
            if ($this->options['type'] !== '') {
4097
                $stat['type'] = $this->options['type'];
4098
            }
4099
        } else {
4100
            // lock when parent directory is not writable
4101
            if (! isset($stat['locked'])) {
4102
                $pstat = $this->stat($parent);
4103
                if (isset($pstat['write']) && ! $pstat['write']) {
4104
                    $stat['locked'] = true;
4105
                }
4106
            }
4107
            if ($this->attr($path, 'locked', isset($stat['locked']) ? (bool) $stat['locked'] : null, $isDir)) {
4108
                $stat['locked'] = 1;
4109
            } else {
4110
                unset($stat['locked']);
4111
            }
4112
        }
4113
4114
        if ($root) {
4115
            unset($stat['hidden']);
4116
        } elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? (bool) $stat['hidden'] : null, $isDir)
4117
        || ! $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...
4118
            $stat['hidden'] = 1;
4119
        } else {
4120
            unset($stat['hidden']);
4121
        }
4122
4123
        if ($stat['read'] && empty($stat['hidden'])) {
4124
            if ($isDir) {
4125
                // caching parent's subdirs
4126
                if ($parent) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parent 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...
4127
                    $this->updateSubdirsCache($parent, true);
4128
                }
4129
                // for dir - check for subdirs
4130
                if ($this->options['checkSubfolders']) {
4131
                    if (! isset($stat['dirs']) && intval($this->options['checkSubfolders']) === -1) {
4132
                        $stat['dirs'] = -1;
4133
                    }
4134
                    if (isset($stat['dirs'])) {
4135
                        if ($stat['dirs']) {
4136
                            if ($stat['dirs'] == -1) {
4137
                                $stat['dirs'] = ($this->sessionCaching['subdirs'] && isset($this->sessionCache['subdirs'][$path])) ? (int) $this->sessionCache['subdirs'][$path] : -1;
4138
                            } else {
4139
                                $stat['dirs'] = 1;
4140
                            }
4141
                        } else {
4142
                            unset($stat['dirs']);
4143
                        }
4144
                    } elseif (! empty($stat['alias']) && ! empty($stat['target'])) {
4145
                        $stat['dirs'] = isset($this->cache[$stat['target']])
4146
                            ? intval(isset($this->cache[$stat['target']]['dirs']))
4147
                            : $this->subdirsCE($stat['target']);
4148
                    } elseif ($this->subdirsCE($path)) {
4149
                        $stat['dirs'] = 1;
4150
                    }
4151
                } else {
4152
                    $stat['dirs'] = 1;
4153
                }
4154
                if ($this->options['dirUrlOwn'] === true) {
4155
                    $stat['url'] = '#elf_'.$stat['hash'];
4156
                }
4157
            } else {
4158
                // for files - check for thumbnails
4159
                $p = isset($stat['target']) ? $stat['target'] : $path;
4160
                if ($this->tmbURL && ! isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
4161
                    $tmb = $this->gettmb($p, $stat);
4162
                    $stat['tmb'] = $tmb ? $tmb : 1;
4163
                }
4164
            }
4165
            if (! isset($stat['url']) && $this->URL && $this->encoding) {
4166
                $_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
4167
                $stat['url'] = rtrim($this->URL, '/').'/'.str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN') ? $_path : $this->convEncIn($_path, true)));
4168
            }
4169
        } else {
4170
            if ($isDir) {
4171
                unset($stat['dirs']);
4172
            }
4173
        }
4174
4175
        if (! empty($stat['alias']) && ! empty($stat['target'])) {
4176
            $stat['thash'] = $this->encode($stat['target']);
4177
            //$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...
4178
            unset($stat['target']);
4179
        }
4180
4181
        $this->cache[$path] = $stat;
4182
4183
        if ($root && $this->sessionCaching['rootstat']) {
4184
            // to update session cache
4185
            $this->stat($path);
4186
        }
4187
4188
        return $stat;
4189
    }
4190
4191
    /**
4192
     * Get stat for folder content and put in cache.
4193
     *
4194
     * @param  string  $path
4195
     * @return void
4196
     * @author Dmitry (dio) Levashov
4197
     **/
4198
    protected function cacheDir($path)
4199
    {
4200
        $this->dirsCache[$path] = [];
4201
        $hasDir = false;
4202
4203
        foreach ($this->scandirCE($path) as $p) {
4204
            if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
4205
                if (! $hasDir && $stat['mime'] === 'directory') {
4206
                    $hasDir = true;
4207
                }
4208
                $this->dirsCache[$path][] = $p;
4209
            }
4210
        }
4211
4212
        $this->updateSubdirsCache($path, $hasDir);
4213
    }
4214
4215
    /**
4216
     * Clean cache.
4217
     *
4218
     * @return void
4219
     * @author Dmitry (dio) Levashov
4220
     **/
4221
    protected function clearcache()
4222
    {
4223
        $this->cache = $this->dirsCache = [];
4224
    }
4225
4226
    /**
4227
     * Return file mimetype.
4228
     *
4229
     * @param  string      $path file path
4230
     * @param  string|bool $name
4231
     * @return string
4232
     * @author Dmitry (dio) Levashov
4233
     */
4234
    protected function mimetype($path, $name = '')
4235
    {
4236
        $type = '';
4237
        $nameCheck = false;
4238
4239
        if ($name === '') {
4240
            $name = $path;
4241
        } elseif ($name === true) {
4242
            $name = $path;
4243
            $nameCheck = true;
4244
        }
4245
        $ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
4246
        if (! $nameCheck && is_readable($path)) {
4247
            if (filesize($path) > 0) {
4248
                if ($this->mimeDetect == 'finfo') {
4249
                    if ($type = finfo_file($this->finfo, $path)) {
4250
                        if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
4251
                            if (isset(self::$mimetypes[$ext])) {
4252
                                $type = self::$mimetypes[$ext];
4253
                            }
4254
                        } elseif ($ext === 'js' && preg_match('~^text/~', $type)) {
4255
                            $type = 'text/javascript';
4256
                        }
4257
                    } else {
4258
                        $type = 'unknown';
4259
                    }
4260
                } elseif ($this->mimeDetect == 'mime_content_type') {
4261
                    $type = mime_content_type($path);
4262
                }
4263
            } else {
4264
                $type = 'text/plain';
4265
            }
4266
        }
4267
        if (! $type) {
4268
            $type = self::mimetypeInternalDetect($path);
4269
        }
4270
4271
        if ($type === 'unknown' && $this->mimeDetect != 'internal') {
4272
            $type = self::mimetypeInternalDetect($path);
4273
        }
4274
4275
        $type = explode(';', $type);
4276
        $type = trim($type[0]);
4277
4278
        if (in_array($type, ['application/x-empty', 'inode/x-empty'])) {
4279
            // finfo return this mime for empty files
4280
            $type = 'text/plain';
4281
        }
4282
4283
        // mime type normalization
4284
        $_checkKey = strtolower($ext.':'.$type);
4285
        if (isset($this->options['mimeMap'][$_checkKey])) {
4286
            $type = $this->options['mimeMap'][$_checkKey];
4287
        } else {
4288
            $_checkKey = strtolower($ext.':*');
4289
            if (isset($this->options['mimeMap'][$_checkKey])) {
4290
                $type = $this->options['mimeMap'][$_checkKey];
4291
            }
4292
        }
4293
4294
        return $type;
4295
    }
4296
4297
    /**
4298
     * Detect file mimetype using "internal" method or Loading mime.types with $path = ''.
4299
     *
4300
     * @param  string  $path  file path
4301
     * @return string
4302
     * @author Dmitry (dio) Levashov
4303
     **/
4304
    protected static function mimetypeInternalDetect($path = '')
4305
    {
4306
        // load default MIME table file "mime.types"
4307
        if (! self::$mimetypesLoaded) {
4308
            self::$mimetypesLoaded = true;
4309
            $file = elFinder::$defaultMimefile;
4310
            if ($file === '' || ! is_readable($file)) {
4311
                $file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
4312
            }
4313 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...
4314
                $mimecf = file($file);
4315
                foreach ($mimecf as $line_num => $line) {
4316
                    if (! preg_match('/^\s*#/', $line)) {
4317
                        $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
4318
                        for ($i = 1, $size = count($mime); $i < $size; $i++) {
4319
                            if (! isset(self::$mimetypes[$mime[$i]])) {
4320
                                self::$mimetypes[$mime[$i]] = $mime[0];
4321
                            }
4322
                        }
4323
                    }
4324
                }
4325
            }
4326
        }
4327
        $ext = '';
4328
        if ($path) {
4329
            $pinfo = pathinfo($path);
4330
            $ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
4331
        }
4332
4333
        return ($ext && isset(self::$mimetypes[$ext])) ? self::$mimetypes[$ext] : 'unknown';
4334
    }
4335
4336
    /**
4337
     * Return file/total directory size.
4338
     *
4339
     * @param  string  $path  file path
4340
     * @return int
4341
     * @author Dmitry (dio) Levashov
4342
     **/
4343
    protected function countSize($path)
4344
    {
4345
        elFinder::extendTimeLimit();
4346
4347
        $result = ['size' => 0, 'files' => 0, 'dirs' => 0];
4348
        $stat = $this->stat($path);
4349
4350
        if (empty($stat) || ! $stat['read'] || ! empty($stat['hidden'])) {
4351
            $result['size'] = 'unknown';
4352
4353
            return $result;
4354
        }
4355
4356
        if ($stat['mime'] != 'directory') {
4357
            $result['size'] = intval($stat['size']);
4358
            $result['files'] = 1;
4359
4360
            return $result;
4361
        }
4362
4363
        $subdirs = $this->options['checkSubfolders'];
4364
        $this->options['checkSubfolders'] = true;
4365
        foreach ($this->getScandir($path) as $stat) {
4366
            if ($isDir = ($stat['mime'] === 'directory' && $stat['read'])) {
4367
                $result['dirs']++;
4368
            } else {
4369
                $result['files']++;
4370
            }
4371
            $res = $isDir
4372
                ? $this->countSize($this->decode($stat['hash']))
4373
                : (isset($stat['size']) ? ['size' => intval($stat['size'])] : []);
4374 View Code Duplication
            if (! empty($res['size']) && is_numeric($res['size'])) {
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...
4375
                $result['size'] += $res['size'];
4376
            }
4377 View Code Duplication
            if (! empty($res['files']) && is_numeric($res['files'])) {
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...
4378
                $result['files'] += $res['files'];
4379
            }
4380 View Code Duplication
            if (! empty($res['dirs']) && is_numeric($res['dirs'])) {
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...
4381
                $result['dirs'] += $res['dirs'];
4382
            }
4383
        }
4384
        $this->options['checkSubfolders'] = $subdirs;
4385
4386
        return $result;
4387
    }
4388
4389
    /**
4390
     * Return true if all mimes is directory or files.
4391
     *
4392
     * @param  string  $mime1  mimetype
4393
     * @param  string  $mime2  mimetype
4394
     * @return bool
4395
     * @author Dmitry (dio) Levashov
4396
     **/
4397
    protected function isSameType($mime1, $mime2)
4398
    {
4399
        return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
4400
    }
4401
4402
    /**
4403
     * If file has required attr == $val - return file path,
4404
     * If dir has child with has required attr == $val - return child path.
4405
     *
4406
     * @param  string   $path  file path
4407
     * @param  string   $attr  attribute name
4408
     * @param  bool     $val   attribute value
4409
     * @return string|false
4410
     * @author Dmitry (dio) Levashov
4411
     **/
4412
    protected function closestByAttr($path, $attr, $val)
4413
    {
4414
        $stat = $this->stat($path);
4415
4416
        if (empty($stat)) {
4417
            return false;
4418
        }
4419
4420
        $v = isset($stat[$attr]) ? $stat[$attr] : false;
4421
4422
        if ($v == $val) {
4423
            return $path;
4424
        }
4425
4426
        return $stat['mime'] == 'directory'
4427
            ? $this->childsByAttr($path, $attr, $val)
4428
            : false;
4429
    }
4430
4431
    /**
4432
     * Return first found children with required attr == $val.
4433
     *
4434
     * @param  string   $path  file path
4435
     * @param  string   $attr  attribute name
4436
     * @param  bool     $val   attribute value
4437
     * @return string|false
4438
     * @author Dmitry (dio) Levashov
4439
     **/
4440
    protected function childsByAttr($path, $attr, $val)
4441
    {
4442
        foreach ($this->scandirCE($path) as $p) {
4443
            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...
4444
                return $_p;
4445
            }
4446
        }
4447
4448
        return false;
4449
    }
4450
4451
    protected function isMyReload($target = '', $ARGtarget = '')
4452
    {
4453
        if ($this->rootModified || (! empty($this->ARGS['cmd']) && $this->ARGS['cmd'] === 'parents')) {
4454
            return true;
4455
        }
4456
        if (! empty($this->ARGS['reload'])) {
4457
            if ($ARGtarget === '') {
4458
                $ARGtarget = isset($this->ARGS['target']) ? $this->ARGS['target']
4459
                    : ((isset($this->ARGS['targets']) && is_array($this->ARGS['targets']) && count($this->ARGS['targets']) === 1) ?
4460
                        $this->ARGS['targets'][0] : '');
4461
            }
4462
            if ($ARGtarget !== '') {
4463
                $ARGtarget = strval($ARGtarget);
4464
                if ($target === '') {
4465
                    return strpos($ARGtarget, $this->id) === 0;
4466
                } else {
4467
                    $target = strval($target);
4468
4469
                    return $target === $ARGtarget;
4470
                }
4471
            }
4472
        }
4473
4474
        return false;
4475
    }
4476
4477
    /**
4478
     * Update subdirs cache data.
4479
     *
4480
     * @param string $path
4481
     * @param bool   $subdirs
4482
     *
4483
     * @returnv void
4484
     */
4485
    protected function updateSubdirsCache($path, $subdirs)
4486
    {
4487
        if (isset($this->cache[$path])) {
4488
            if ($subdirs) {
4489
                $this->cache[$path]['dirs'] = 1;
4490
            } else {
4491
                unset($this->cache[$path]['dirs']);
4492
            }
4493
        }
4494
        if ($this->sessionCaching['subdirs']) {
4495
            $this->sessionCache['subdirs'][$path] = $subdirs;
4496
        }
4497
        if ($this->sessionCaching['rootstat'] && $path == $this->root) {
4498
            unset($this->sessionCache['rootstat'][$this->getRootstatCachekey()]);
4499
        }
4500
    }
4501
4502
    /*****************  get content *******************/
4503
4504
    /**
4505
     * Return required dir's files info.
4506
     * If onlyMimes is set - return only dirs and files of required mimes.
4507
     *
4508
     * @param  string  $path  dir path
4509
     * @return array
4510
     * @author Dmitry (dio) Levashov
4511
     **/
4512
    protected function getScandir($path)
4513
    {
4514
        $files = [];
4515
4516
        ! isset($this->dirsCache[$path]) && $this->cacheDir($path);
4517
4518
        foreach ($this->dirsCache[$path] as $p) {
4519
            if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
4520
                $files[] = $stat;
4521
            }
4522
        }
4523
4524
        return $files;
4525
    }
4526
4527
    /**
4528
     * Return subdirs tree.
4529
     *
4530
     * @param  string $path parent dir path
4531
     * @param  int $deep tree deep
4532
     * @param string $exclude
4533
     * @return array
4534
     * @author Dmitry (dio) Levashov
4535
     */
4536
    protected function gettree($path, $deep, $exclude = '')
4537
    {
4538
        $dirs = [];
4539
4540
        ! isset($this->dirsCache[$path]) && $this->cacheDir($path);
4541
4542
        foreach ($this->dirsCache[$path] as $p) {
4543
            $stat = $this->stat($p);
4544
4545
            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...
4546
                $dirs[] = $stat;
4547
                if ($deep > 0 && ! empty($stat['dirs'])) {
4548
                    $dirs = array_merge($dirs, $this->gettree($p, $deep - 1));
4549
                }
4550
            }
4551
        }
4552
4553
        return $dirs;
4554
    }
4555
4556
    /**
4557
     * Recursive files search.
4558
     *
4559
     * @param  string  $path   dir path
4560
     * @param  string  $q      search string
4561
     * @param  array   $mimes
4562
     * @return array
4563
     * @author Dmitry (dio) Levashov
4564
     **/
4565
    protected function doSearch($path, $q, $mimes)
4566
    {
4567
        $result = [];
4568
4569
        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
4570 View Code Duplication
        if ($timeout && $timeout < time()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4571
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
4572
4573
            return $result;
4574
        }
4575
4576
        foreach ($this->scandirCE($path) as $p) {
4577
            elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);
4578
4579 View Code Duplication
            if ($timeout && ($this->error || $timeout < time())) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->error 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...
4580
                ! $this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->error 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...
4581
                break;
4582
            }
4583
4584
            $stat = $this->stat($p);
4585
4586
            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...
4587
                continue;
4588
            }
4589
4590
            if (! empty($stat['hidden']) || ! $this->mimeAccepted($stat['mime'], $mimes)) {
4591
                continue;
4592
            }
4593
4594
            $name = $stat['name'];
4595
4596 View Code Duplication
            if ($this->doSearchCurrentQuery['excludes']) {
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...
4597
                foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {
0 ignored issues
show
Bug introduced by
The expression $this->doSearchCurrentQuery['excludes'] of type string is not traversable.
Loading history...
4598
                    if ($this->stripos($name, $exclude) !== false) {
4599
                        continue 2;
4600
                    }
4601
                }
4602
            }
4603
4604
            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...
4605
                $stat['path'] = $this->path($stat['hash']);
4606
                if ($this->URL && ! isset($stat['url'])) {
4607
                    $path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
4608
                    if ($this->encoding) {
4609
                        $path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
4610
                    } else {
4611
                        $path = str_replace('%2F', '/', rawurlencode($path));
4612
                    }
4613
                    $stat['url'] = $this->URL.$path;
4614
                }
4615
4616
                $result[] = $stat;
4617
            }
4618
            if ($stat['mime'] == 'directory' && $stat['read'] && ! isset($stat['alias'])) {
4619
                if (! $this->options['searchExDirReg'] || ! preg_match($this->options['searchExDirReg'], $p)) {
4620
                    $result = array_merge($result, $this->doSearch($p, $q, $mimes));
4621
                }
4622
            }
4623
        }
4624
4625
        return $result;
4626
    }
4627
4628
    /**********************  manuipulations  ******************/
4629
4630
    /**
4631
     * Copy file/recursive copy dir only in current volume.
4632
     * Return new file path or false.
4633
     *
4634
     * @param  string  $src   source path
4635
     * @param  string  $dst   destination dir path
4636
     * @param  string  $name  new file name (optionaly)
4637
     * @return string|false
4638
     * @author Dmitry (dio) Levashov
4639
     **/
4640
    protected function copy($src, $dst, $name)
4641
    {
4642
        elFinder::extendTimeLimit();
4643
4644
        $srcStat = $this->stat($src);
4645
4646
        if (! empty($srcStat['thash'])) {
4647
            $target = $this->decode($srcStat['thash']);
4648
            if (! $this->inpathCE($target, $this->root)) {
4649
                return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
4650
            }
4651
            $stat = $this->stat($target);
4652
            $this->clearcache();
4653
4654
            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...
4655
                ? $this->joinPathCE($dst, $name)
4656
                : $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
4657
        }
4658
4659
        if ($srcStat['mime'] === 'directory') {
4660
            $testStat = $this->isNameExists($this->joinPathCE($dst, $name));
4661
            $this->clearcache();
4662
4663 View Code Duplication
            if (($testStat && $testStat['mime'] !== 'directory') || (! $testStat && ! $testStat = $this->mkdir($this->encode($dst), $name))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $testStat 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 $testStat 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...
4664
                return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
4665
            }
4666
4667
            $dst = $this->decode($testStat['hash']);
4668
4669
            foreach ($this->getScandir($src) as $stat) {
4670
                if (empty($stat['hidden'])) {
4671
                    $name = $stat['name'];
4672
                    $_src = $this->decode($stat['hash']);
4673
                    if (! $this->copy($_src, $dst, $name)) {
4674
                        $this->remove($dst, true); // fall back
4675
                        return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
4676
                    }
4677
                }
4678
            }
4679
4680
            $this->added[] = $testStat;
4681
4682
            return $dst;
4683
        }
4684
4685 View Code Duplication
        if ($this->options['copyJoin']) {
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...
4686
            $test = $this->joinPathCE($dst, $name);
4687
            if ($testStat = $this->isNameExists($test)) {
0 ignored issues
show
Unused Code introduced by
$testStat 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...
4688
                $this->remove($test);
4689
            }
4690
        } else {
4691
            $testStat = false;
0 ignored issues
show
Unused Code introduced by
$testStat 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...
4692
        }
4693
        if ($res = $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) {
4694
            $path = is_string($res) ? $res : $this->joinPathCE($dst, $name);
4695
            $this->clearcache();
4696
            if ($this->ARGS['cmd'] !== 'duplicate') {
4697
                $this->added[] = $this->stat($path);
4698
            }
4699
4700
            return $path;
4701
        }
4702
4703
        return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
4704
    }
4705
4706
    /**
4707
     * Move file
4708
     * Return new file path or false.
4709
     *
4710
     * @param  string  $src   source path
4711
     * @param  string  $dst   destination dir path
4712
     * @param  string  $name  new file name
4713
     * @return string|false
4714
     * @author Dmitry (dio) Levashov
4715
     **/
4716
    protected function move($src, $dst, $name)
4717
    {
4718
        $stat = $this->stat($src);
4719
        $stat['realpath'] = $src;
4720
        $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...
4721
        $this->clearcache();
4722
4723
        if ($res = $this->convEncOut($this->_move($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) {
4724
            $this->removed[] = $stat;
4725
            if ($stat['mime'] === 'directory') {
4726
                $this->updateSubdirsCache($dst, true);
4727
            }
4728
4729
            return is_string($res) ? $res : $this->joinPathCE($dst, $name);
4730
        }
4731
4732
        return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
4733
    }
4734
4735
    /**
4736
     * Copy file from another volume.
4737
     * Return new file path or false.
4738
     *
4739
     * @param  object  $volume       source volume
4740
     * @param  string  $src          source file hash
4741
     * @param  string  $destination  destination dir path
4742
     * @param  string  $name         file name
4743
     * @return string|false
4744
     * @author Dmitry (dio) Levashov
4745
     **/
4746
    protected function copyFrom($volume, $src, $destination, $name)
4747
    {
4748
        elFinder::extendTimeLimit();
4749
4750
        if (($source = $volume->file($src)) == false) {
4751
            return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
4752
        }
4753
4754
        $srcIsDir = ($source['mime'] === 'directory');
4755
4756
        $errpath = $volume->path($source['hash']);
4757
4758
        if (! $this->nameAccepted($source['name'], $srcIsDir)) {
4759
            return $this->setError(elFinder::ERROR_COPY, $errpath, $srcIsDir ? elFinder::ERROR_INVALID_DIRNAME : elFinder::ERROR_INVALID_NAME);
4760
        }
4761
4762
        if (! $source['read']) {
4763
            return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
4764
        }
4765
4766
        if ($srcIsDir) {
4767
            $test = $this->isNameExists($this->joinPathCE($destination, $name));
4768
            $this->clearcache();
4769
4770 View Code Duplication
            if (($test && $test['mime'] != 'directory') || (! $test && ! $test = $this->mkdir($this->encode($destination), $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...
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...
4771
                return $this->setError(elFinder::ERROR_COPY, $errpath);
4772
            }
4773
4774
            //$path = $this->joinPathCE($destination, $name);
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% 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...
4775
            $path = $this->decode($test['hash']);
4776
4777
            foreach ($volume->scandir($src) as $entr) {
4778
                if (! $this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
4779
                    $this->remove($path, true); // fall back
4780
                    return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
4781
                }
4782
            }
4783
4784
            $this->added[] = $test;
4785
        } else {
4786
            // MIME check
4787
            $mimeByName = $this->mimetype($source['name'], true);
4788
            if ($source['mime'] === $mimeByName) {
4789
                $mimeByName = '';
4790
            }
4791 View Code Duplication
            if (! $this->allowPutMime($source['mime']) || ($mimeByName && ! $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...
4792
                return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
4793
            }
4794
4795
            if (strpos($source['mime'], 'image') === 0 && ($dim = $volume->dimensions($src))) {
4796
                $s = explode('x', $dim);
4797
                $source['width'] = $s[0];
4798
                $source['height'] = $s[1];
4799
            }
4800
4801
            if (($fp = $volume->open($src)) == false
4802
            || ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
4803
                $fp && $volume->close($fp, $src);
4804
4805
                return $this->setError(elFinder::ERROR_COPY, $errpath);
4806
            }
4807
            $volume->close($fp, $src);
4808
4809
            $this->added[] = $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->saveCE($fp, $destination, $name, $source) on line 4802 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...
4810
        }
4811
4812
        return $path;
4813
    }
4814
4815
    /**
4816
     * Remove file/ recursive remove dir.
4817
     *
4818
     * @param  string  $path   file path
4819
     * @param  bool    $force  try to remove even if file locked
4820
     * @return bool
4821
     * @author Dmitry (dio) Levashov
4822
     **/
4823
    protected function remove($path, $force = false)
4824
    {
4825
        $stat = $this->stat($path);
4826
4827
        if (empty($stat)) {
4828
            return $this->setError(elFinder::ERROR_RM, $path, elFinder::ERROR_FILE_NOT_FOUND);
4829
        }
4830
4831
        $stat['realpath'] = $path;
4832
        $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...
4833
        $this->clearcache();
4834
4835
        if (! $force && ! empty($stat['locked'])) {
4836
            return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
4837
        }
4838
4839
        if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
4840
            $ret = $this->delTree($this->convEncIn($path));
4841
            $this->convEncOut();
4842
            if (! $ret) {
4843
                return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
4844
            }
4845 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...
4846
            if ($this->convEncOut(! $this->_unlink($this->convEncIn($path)))) {
4847
                return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
4848
            }
4849
        }
4850
4851
        $this->removed[] = $stat;
4852
4853
        return true;
4854
    }
4855
4856
    /************************* thumbnails **************************/
4857
4858
    /**
4859
     * Return thumbnail file name for required file.
4860
     *
4861
     * @param  array  $stat  file stat
4862
     * @return string
4863
     * @author Dmitry (dio) Levashov
4864
     **/
4865
    protected function tmbname($stat)
4866
    {
4867
        return $stat['hash'].$stat['ts'].'.png';
4868
    }
4869
4870
    /**
4871
     * Return thumnbnail name if exists.
4872
     *
4873
     * @param  string  $path file path
4874
     * @param  array   $stat file stat
4875
     * @return string|false
4876
     * @author Dmitry (dio) Levashov
4877
     **/
4878
    protected function gettmb($path, $stat)
4879
    {
4880
        if ($this->tmbURL && $this->tmbPath) {
4881
            // file itself thumnbnail
4882
            if (strpos($path, $this->tmbPath) === 0) {
4883
                return basename($path);
4884
            }
4885
4886
            $name = $this->tmbname($stat);
4887
            if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
4888
                return $name;
4889
            }
4890
        }
4891
4892
        return false;
4893
    }
4894
4895
    /**
4896
     * Return true if thumnbnail for required file can be created.
4897
     *
4898
     * @param  string  $path  thumnbnail path
4899
     * @param  array   $stat  file stat
4900
     * @param  bool    $checkTmbPath
4901
     * @return string|bool
4902
     * @author Dmitry (dio) Levashov
4903
     **/
4904
    protected function canCreateTmb($path, $stat, $checkTmbPath = true)
4905
    {
4906
        if ((! $checkTmbPath || $this->tmbPathWritable)
4907
            && (! $this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
4908
        ) {
4909
            $mime = strtolower($stat['mime']);
4910
            list($type) = explode('/', $mime);
4911
            if (! empty($this->imgConverter)) {
4912
                if (isset($this->imgConverter[$mime])) {
4913
                    return true;
4914
                }
4915
                if (isset($this->imgConverter[$type])) {
4916
                    return true;
4917
                }
4918
            }
4919
4920
            return $this->imgLib
4921
                && ($type === 'image')
4922
                && ($this->imgLib == 'gd' ? in_array($stat['mime'], ['image/jpeg', 'image/png', 'image/gif', 'image/x-ms-bmp']) : true);
4923
        }
4924
4925
        return false;
4926
    }
4927
4928
    /**
4929
     * Return true if required file can be resized.
4930
     * By default - the same as canCreateTmb.
4931
     *
4932
     * @param  string  $path  thumnbnail path
4933
     * @param  array   $stat  file stat
4934
     * @return string|bool
4935
     * @author Dmitry (dio) Levashov
4936
     **/
4937
    protected function canResize($path, $stat)
4938
    {
4939
        return $this->canCreateTmb($path, $stat, false);
4940
    }
4941
4942
    /**
4943
     * Create thumnbnail and return it's URL on success.
4944
     *
4945
     * @param  string $path file path
4946
     * @param $stat
4947
     * @return false|string
4948
     * @internal param string $mime file mime type
4949
     * @author Dmitry (dio) Levashov
4950
     */
4951
    protected function createTmb($path, $stat)
4952
    {
4953
        if (! $stat || ! $this->canCreateTmb($path, $stat)) {
4954
            return false;
4955
        }
4956
4957
        $name = $this->tmbname($stat);
4958
        $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
4959
4960
        $maxlength = -1;
4961
        $imgConverter = null;
4962
4963
        // check imgConverter
4964
        $mime = strtolower($stat['mime']);
4965
        list($type) = explode('/', $mime);
4966
        if (isset($this->imgConverter[$mime])) {
4967
            $imgConverter = $this->imgConverter[$mime]['func'];
4968 View Code Duplication
            if (! empty($this->imgConverter[$mime]['maxlen'])) {
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...
4969
                $maxlength = intval($this->imgConverter[$mime]['maxlen']);
4970
            }
4971
        } elseif (isset($this->imgConverter[$type])) {
4972
            $imgConverter = $this->imgConverter[$type]['func'];
4973 View Code Duplication
            if (! empty($this->imgConverter[$type]['maxlen'])) {
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...
4974
                $maxlength = intval($this->imgConverter[$type]['maxlen']);
4975
            }
4976
        }
4977
        if ($imgConverter && ! is_callable($imgConverter)) {
4978
            return false;
4979
        }
4980
4981
        // copy image into tmbPath so some drivers does not store files on local fs
4982
        if (($src = $this->fopenCE($path, 'rb')) == false) {
4983
            return false;
4984
        }
4985
4986
        if (($trg = fopen($tmb, 'wb')) == false) {
4987
            $this->fcloseCE($src, $path);
4988
4989
            return false;
4990
        }
4991
4992
        stream_copy_to_stream($src, $trg, $maxlength);
4993
4994
        $this->fcloseCE($src, $path);
4995
        fclose($trg);
4996
4997
        // call imgConverter
4998
        if ($imgConverter) {
4999
            if (! call_user_func_array($imgConverter, [$tmb, $stat, $this])) {
5000
                file_exists($tmb) && unlink($tmb);
5001
5002
                return false;
5003
            }
5004
        }
5005
5006
        $result = false;
5007
5008
        $tmbSize = $this->tmbSize;
5009
5010
        if ($this->imgLib === 'imagick') {
5011
            try {
5012
                $imagickTest = new imagick($tmb);
5013
                $imagickTest->clear();
5014
                $imagickTest = true;
5015
            } catch (Exception $e) {
5016
                $imagickTest = false;
5017
            }
5018
        }
5019
5020
        if (($this->imgLib === 'imagick' && ! $imagickTest) || ($s = getimagesize($tmb)) === false) {
0 ignored issues
show
Bug introduced by
The variable $imagickTest 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...
5021
            if ($this->imgLib === 'imagick') {
5022
                $bgcolor = $this->options['tmbBgColor'];
5023
                if ($bgcolor === 'transparent') {
5024
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
5025
                }
5026
                try {
5027
                    $imagick = new imagick();
5028
                    $imagick->setBackgroundColor(new ImagickPixel($bgcolor));
5029
                    $imagick->readImage($this->getExtentionByMime($stat['mime'], ':').$tmb);
5030
                    $imagick->setImageFormat('png');
5031
                    $imagick->writeImage($tmb);
5032
                    $imagick->clear();
5033
                    if (($s = getimagesize($tmb)) !== false) {
5034
                        $result = true;
5035
                    }
5036
                } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
5037
                }
5038
            }
5039
            if (! $result) {
5040
                file_exists($tmb) && unlink($tmb);
5041
5042
                return false;
5043
            }
5044
            $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...
5045
        }
5046
5047
        /* If image smaller or equal thumbnail size - just fitting to thumbnail square */
5048
        if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
5049
            $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
5050
        } else {
5051
            if ($this->options['tmbCrop']) {
5052
                $result = $tmb;
5053
                /* Resize and crop if image bigger than thumbnail */
5054
                if (! (($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
0 ignored issues
show
Bug introduced by
The variable $s 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...
5055
                    $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
5056
                }
5057
5058
                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...
5059
                    $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize) / 2) : 0;
5060
                    $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize) / 2) : 0;
5061
                    $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...
5062
                } else {
5063
                    $result = false;
5064
                }
5065
            } else {
5066
                $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
5067
            }
5068
5069
            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...
5070
                if ($s = getimagesize($tmb)) {
5071
                    if ($s[0] !== $tmbSize || $s[1] !== $tmbSize) {
5072
                        $result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
5073
                    }
5074
                }
5075
            }
5076
        }
5077
5078
        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...
5079
            unlink($tmb);
5080
5081
            return false;
5082
        }
5083
5084
        return $name;
5085
    }
5086
5087
    /**
5088
     * Resize image.
5089
     *
5090
     * @param  string   $path               image file
5091
     * @param  int      $width              new width
5092
     * @param  int      $height             new height
5093
     * @param  bool	    $keepProportions    crop image
5094
     * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
5095
     * @param  string   $destformat         image destination format
5096
     * @param  int      $jpgQuality         JEPG quality (1-100)
5097
     * @param  array    $options            Other extra options
5098
     * @return string|false
5099
     * @author Dmitry (dio) Levashov
5100
     * @author Alexey Sukhotin
5101
     **/
5102
    protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null, $jpgQuality = null, $options = [])
5103
    {
5104
        if (($s = getimagesize($path)) == false) {
5105
            return false;
5106
        }
5107
5108
        $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...
5109
5110
        if (! $jpgQuality) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $jpgQuality of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
5111
            $jpgQuality = $this->options['jpgQuality'];
5112
        }
5113
5114
        list($orig_w, $orig_h) = [$s[0], $s[1]];
5115
        list($size_w, $size_h) = [$width, $height];
5116
5117
        if (empty($options['unenlarge']) || $orig_w > $size_w || $orig_h > $size_h) {
5118
            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...
5119
                /* Resizing by biggest side */
5120
                if ($resizeByBiggerSide) {
5121 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...
5122
                        $size_h = round($orig_h * $width / $orig_w);
5123
                        $size_w = $width;
5124
                    } else {
5125
                        $size_w = round($orig_w * $height / $orig_h);
5126
                        $size_h = $height;
5127
                    }
5128 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...
5129
                    if ($orig_w > $orig_h) {
5130
                        $size_w = round($orig_w * $height / $orig_h);
5131
                        $size_h = $height;
5132
                    } else {
5133
                        $size_h = round($orig_h * $width / $orig_w);
5134
                        $size_w = $width;
5135
                    }
5136
                }
5137
            }
5138
        } else {
5139
            $size_w = $orig_w;
5140
            $size_h = $orig_h;
5141
        }
5142
5143
        elFinder::extendTimeLimit(300);
5144
        switch ($this->imgLib) {
5145
            case 'imagick':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5146
5147
                try {
5148
                    $img = new imagick($path);
5149
                } catch (Exception $e) {
5150
                    return false;
5151
                }
5152
5153
                // Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
5154
                // resize bench: http://app-mgng.rhcloud.com/9
5155
                // resize sample: http://www.dylanbeattie.net/magick/filters/result.html
5156
                $filter = ($destformat === 'png' /* createTmb */) ? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
5157
5158
                $ani = ($img->getNumberImages() > 1);
5159
                if ($ani && is_null($destformat)) {
5160
                    $img = $img->coalesceImages();
5161
                    do {
5162
                        $img->resizeImage($size_w, $size_h, $filter, 1);
5163
                    } while ($img->nextImage());
5164
                    $img = $img->optimizeImageLayers();
5165
                    $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...
5166
                } else {
5167
                    if ($ani) {
5168
                        $img->setFirstIterator();
5169
                    }
5170
                    if (strtoupper($img->getImageFormat()) === 'JPEG') {
5171
                        $img->setImageCompression(imagick::COMPRESSION_JPEG);
5172
                        $img->setImageCompressionQuality($jpgQuality);
5173
                        if (isset($options['preserveExif']) && ! $options['preserveExif']) {
5174
                            try {
5175
                                $orientation = $img->getImageOrientation();
5176
                            } catch (ImagickException $e) {
5177
                                $orientation = 0;
5178
                            }
5179
                            $img->stripImage();
5180
                            if ($orientation) {
5181
                                $img->setImageOrientation($orientation);
5182
                            }
5183
                        }
5184
                    }
5185
                    $img->resizeImage($size_w, $size_h, $filter, true);
5186
                    if ($destformat) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $destformat 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...
5187
                        $result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
Documentation introduced by
$img is of type object<Imagick>, but the function expects a resource.

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...
5188
                    } else {
5189
                        $result = $img->writeImage($path);
5190
                    }
5191
                }
5192
5193
                $img->clear();
5194
5195
                return $result ? $path : false;
5196
5197
                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...
5198
5199
            case 'convert':
5200
                extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s));
0 ignored issues
show
Bug introduced by
$this->imageMagickConver...ormat, $jpgQuality, $s) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
5201
                $filter = ($destformat === 'png' /* createTmb */) ? '-filter Box' : '-filter Lanczos';
5202
                $strip = (isset($options['preserveExif']) && ! $options['preserveExif']) ? ' -strip' : '';
0 ignored issues
show
Unused Code introduced by
$strip 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...
5203
                //$cmd = sprintf('convert%s%s +repage %s -resize %dx%d +repage%s %s %s', $coalesce, $jpgQuality, $filter, $size_w, $size_h, $deconstruct, $quotedPath, $quotedDstPath);
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% 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...
5204
                $cmd = sprintf('convert %s%s%s %s -geometry %dx%d! %s %s', $quotedPath, $coalesce, $jpgQuality, $filter, $size_w, $size_h, $deconstruct, $quotedDstPath);
5205
5206
                $result = false;
5207
                if ($this->procExec($cmd) === 0) {
5208
                    $result = true;
5209
                }
5210
5211
                return $result ? $path : false;
5212
5213
                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...
5214
5215
            case 'gd':
5216
                $img = $this->gdImageCreate($path, $s['mime']);
5217
5218
                if ($img && false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
5219
                    $bgNum = false;
5220 View Code Duplication
                    if ($s[2] === IMAGETYPE_GIF && (! $destformat || $destformat === 'gif')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $destformat of type string|null is loosely compared to false; 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...
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...
5221
                        $bgIdx = imagecolortransparent($img);
5222
                        if ($bgIdx !== -1) {
5223
                            $c = imagecolorsforindex($img, $bgIdx);
5224
                            $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']);
5225
                            imagefill($tmp, 0, 0, $bgNum);
5226
                            imagecolortransparent($tmp, $bgNum);
5227
                        }
5228
                    }
5229
                    if ($bgNum === false) {
5230
                        $this->gdImageBackground($tmp, 'transparent');
5231
                    }
5232
5233
                    if (! imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
5234
                        return false;
5235
                    }
5236
5237
                    $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
5238
5239
                    imagedestroy($img);
5240
                    imagedestroy($tmp);
5241
5242
                    return $result ? $path : false;
5243
                }
5244
                break;
5245
        }
5246
5247
        return false;
5248
    }
5249
5250
    /**
5251
     * Crop image.
5252
     *
5253
     * @param  string   $path               image file
5254
     * @param  int      $width              crop width
5255
     * @param  int      $height             crop height
5256
     * @param  bool	    $x                  crop left offset
5257
     * @param  bool	    $y                  crop top offset
5258
     * @param  string   $destformat         image destination format
5259
     * @param  int      $jpgQuality         JEPG quality (1-100)
5260
     * @return string|false
5261
     * @author Dmitry (dio) Levashov
5262
     * @author Alexey Sukhotin
5263
     **/
5264
    protected function imgCrop($path, $width, $height, $x, $y, $destformat = null, $jpgQuality = null)
5265
    {
5266
        if (($s = getimagesize($path)) == false) {
5267
            return false;
5268
        }
5269
5270
        $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...
5271
5272
        if (! $jpgQuality) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $jpgQuality of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
5273
            $jpgQuality = $this->options['jpgQuality'];
5274
        }
5275
5276
        elFinder::extendTimeLimit(300);
5277
        switch ($this->imgLib) {
5278
            case 'imagick':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5279
5280
                try {
5281
                    $img = new imagick($path);
5282
                } catch (Exception $e) {
5283
                    return false;
5284
                }
5285
5286
                $ani = ($img->getNumberImages() > 1);
5287
                if ($ani && is_null($destformat)) {
5288
                    $img = $img->coalesceImages();
5289
                    do {
5290
                        $img->setImagePage($s[0], $s[1], 0, 0);
5291
                        $img->cropImage($width, $height, $x, $y);
5292
                        $img->setImagePage($width, $height, 0, 0);
5293
                    } while ($img->nextImage());
5294
                    $img = $img->optimizeImageLayers();
5295
                    $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...
5296
                } else {
5297
                    if ($ani) {
5298
                        $img->setFirstIterator();
5299
                    }
5300
                    $img->setImagePage($s[0], $s[1], 0, 0);
5301
                    $img->cropImage($width, $height, $x, $y);
5302
                    $img->setImagePage($width, $height, 0, 0);
5303
                    $result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
Documentation introduced by
$img is of type object<Imagick>, but the function expects a resource.

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...
5304
                }
5305
5306
                $img->clear();
5307
5308
                return $result ? $path : false;
5309
5310
                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...
5311
5312
            case 'convert':
5313
                extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s));
0 ignored issues
show
Bug introduced by
$this->imageMagickConver...ormat, $jpgQuality, $s) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
5314
                $cmd = sprintf('convert %s%s%s -crop %dx%d+%d+%d%s %s', $quotedPath, $coalesce, $jpgQuality, $width, $height, $x, $y, $deconstruct, $quotedDstPath);
5315
5316
                $result = false;
5317
                if ($this->procExec($cmd) === 0) {
5318
                    $result = true;
5319
                }
5320
5321
                return $result ? $path : false;
5322
5323
                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...
5324
5325
            case 'gd':
5326
                $img = $this->gdImageCreate($path, $s['mime']);
5327
5328
                if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) {
5329
                    $bgNum = false;
5330 View Code Duplication
                    if ($s[2] === IMAGETYPE_GIF && (! $destformat || $destformat === 'gif')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $destformat of type string|null is loosely compared to false; 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...
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...
5331
                        $bgIdx = imagecolortransparent($img);
5332
                        if ($bgIdx !== -1) {
5333
                            $c = imagecolorsforindex($img, $bgIdx);
5334
                            $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']);
5335
                            imagefill($tmp, 0, 0, $bgNum);
5336
                            imagecolortransparent($tmp, $bgNum);
5337
                        }
5338
                    }
5339
                    if ($bgNum === false) {
5340
                        $this->gdImageBackground($tmp, 'transparent');
5341
                    }
5342
5343
                    $size_w = $width;
5344
                    $size_h = $height;
5345
5346
                    if ($s[0] < $width || $s[1] < $height) {
5347
                        $size_w = $s[0];
5348
                        $size_h = $s[1];
5349
                    }
5350
5351
                    if (! imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
5352
                        return false;
5353
                    }
5354
5355
                    $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
5356
5357
                    imagedestroy($img);
5358
                    imagedestroy($tmp);
5359
5360
                    return $result ? $path : false;
5361
                }
5362
                break;
5363
        }
5364
5365
        return false;
5366
    }
5367
5368
    /**
5369
     * Put image to square.
5370
     *
5371
     * @param  string $path image file
5372
     * @param  int $width square width
5373
     * @param  int $height square height
5374
     * @param int|string $align reserved
5375
     * @param int|string $valign reserved
5376
     * @param  string $bgcolor square background color in #rrggbb format
5377
     * @param  string $destformat image destination format
5378
     * @param  int $jpgQuality JEPG quality (1-100)
5379
     * @return false|string
5380
     * @author Dmitry (dio) Levashov
5381
     * @author Alexey Sukhotin
5382
     */
5383
    protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null, $jpgQuality = 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...
5384
    {
5385
        if (($s = getimagesize($path)) == false) {
5386
            return false;
5387
        }
5388
5389
        $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...
5390
5391
        /* Coordinates for image over square aligning */
5392
        $y = ceil(abs($height - $s[1]) / 2);
5393
        $x = ceil(abs($width - $s[0]) / 2);
5394
5395
        if (! $jpgQuality) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $jpgQuality of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
5396
            $jpgQuality = $this->options['jpgQuality'];
5397
        }
5398
5399
        elFinder::extendTimeLimit(300);
5400
        switch ($this->imgLib) {
5401
            case 'imagick':
5402
                try {
5403
                    $img = new imagick($path);
5404
                } catch (Exception $e) {
5405
                    return false;
5406
                }
5407
5408
                if ($bgcolor === 'transparent') {
5409
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
5410
                }
5411
                $ani = ($img->getNumberImages() > 1);
5412
                if ($ani && is_null($destformat)) {
5413
                    $img1 = new Imagick();
5414
                    $img1->setFormat('gif');
5415
                    $img = $img->coalesceImages();
5416
                    do {
5417
                        $gif = new Imagick();
5418
                        $gif->newImage($width, $height, new ImagickPixel($bgcolor));
5419
                        $gif->setImageColorspace($img->getImageColorspace());
5420
                        $gif->setImageFormat('gif');
5421
                        $gif->compositeImage($img, imagick::COMPOSITE_OVER, $x, $y);
5422
                        $gif->setImageDelay($img->getImageDelay());
5423
                        $gif->setImageIterations($img->getImageIterations());
5424
                        $img1->addImage($gif);
5425
                        $gif->clear();
5426
                    } while ($img->nextImage());
5427
                    $img1 = $img1->optimizeImageLayers();
5428
                    $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...
5429
                } else {
5430
                    if ($ani) {
5431
                        $img->setFirstIterator();
5432
                    }
5433
                    $img1 = new Imagick();
5434
                    $img1->newImage($width, $height, new ImagickPixel($bgcolor));
5435
                    $img1->setImageColorspace($img->getImageColorspace());
5436
                    $img1->compositeImage($img, imagick::COMPOSITE_OVER, $x, $y);
5437
                    $result = $this->imagickImage($img1, $path, $destformat, $jpgQuality);
0 ignored issues
show
Documentation introduced by
$img1 is of type object<Imagick>, but the function expects a resource.

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...
5438
                }
5439
5440
                $img1->clear();
5441
                $img->clear();
5442
5443
                return $result ? $path : false;
5444
5445
                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...
5446
5447
            case 'convert':
5448
                extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s));
0 ignored issues
show
Bug introduced by
$this->imageMagickConver...ormat, $jpgQuality, $s) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
5449
                if ($bgcolor === 'transparent') {
5450
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
5451
                }
5452
                $cmd = sprintf('convert -size %dx%d "xc:%s" png:- | convert%s%s png:-  %s -geometry +%d+%d -compose over -composite%s %s', $width, $height, $bgcolor, $coalesce, $jpgQuality, $quotedPath, $x, $y, $deconstruct, $quotedDstPath);
5453
5454
                $result = false;
5455
                if ($this->procExec($cmd) === 0) {
5456
                    $result = true;
5457
                }
5458
5459
                return $result ? $path : false;
5460
5461
                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...
5462
5463
            case 'gd':
5464
                $img = $this->gdImageCreate($path, $s['mime']);
5465
5466
                if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) {
5467
                    $this->gdImageBackground($tmp, $bgcolor);
5468
                    if ($bgcolor === 'transparent' && ($destformat === 'png' || $s[2] === IMAGETYPE_PNG)) {
5469
                        $bgNum = imagecolorallocatealpha($tmp, 255, 255, 255, 127);
5470
                        imagefill($tmp, 0, 0, $bgNum);
5471
                    }
5472
5473
                    if (! imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
5474
                        return false;
5475
                    }
5476
5477
                    $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
5478
5479
                    imagedestroy($img);
5480
                    imagedestroy($tmp);
5481
5482
                    return $result ? $path : false;
5483
                }
5484
                break;
5485
        }
5486
5487
        return false;
5488
    }
5489
5490
    /**
5491
     * Rotate image.
5492
     *
5493
     * @param  string   $path               image file
5494
     * @param  int      $degree             rotete degrees
5495
     * @param  string   $bgcolor            square background color in #rrggbb format
5496
     * @param  string   $destformat         image destination format
5497
     * @param  int      $jpgQuality         JEPG quality (1-100)
5498
     * @return string|false
5499
     * @author nao-pon
5500
     * @author Troex Nevelin
5501
     **/
5502
    protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null, $jpgQuality = null)
5503
    {
5504
        if (($s = getimagesize($path)) == false || $degree % 360 === 0) {
5505
            return false;
5506
        }
5507
5508
        $result = false;
5509
5510
        // try lossless rotate
5511
        if ($degree % 90 === 0 && in_array($s[2], [IMAGETYPE_JPEG, IMAGETYPE_JPEG2000])) {
5512
            $count = ($degree / 90) % 4;
5513
            $exiftran = [
5514
                1 => '-9',
5515
                2 => '-1',
5516
                3 => '-2',
5517
            ];
5518
            $jpegtran = [
5519
                1 => '90',
5520
                2 => '180',
5521
                3 => '270',
5522
            ];
5523
            $quotedPath = escapeshellarg($path);
5524
            $cmds = [];
5525
            if ($this->procExec('exiftran -h') === 0) {
5526
                $cmds[] = 'exiftran -i '.$exiftran[$count].' '.$path;
5527
            }
5528
            if ($this->procExec('jpegtran -version') === 0) {
5529
                $cmds[] = 'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath;
5530
            }
5531
            foreach ($cmds as $cmd) {
5532
                if ($this->procExec($cmd) === 0) {
5533
                    $result = true;
5534
                    break;
5535
                }
5536
            }
5537
            if ($result) {
5538
                return $path;
5539
            }
5540
        }
5541
5542
        if (! $jpgQuality) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $jpgQuality of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
5543
            $jpgQuality = $this->options['jpgQuality'];
5544
        }
5545
5546
        elFinder::extendTimeLimit(300);
5547
        switch ($this->imgLib) {
5548
            case 'imagick':
5549
                try {
5550
                    $img = new imagick($path);
5551
                } catch (Exception $e) {
5552
                    return false;
5553
                }
5554
5555 View Code Duplication
                if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) {
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...
5556
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
5557
                }
5558
                if ($img->getNumberImages() > 1) {
5559
                    $img = $img->coalesceImages();
5560
                    do {
5561
                        $img->rotateImage(new ImagickPixel($bgcolor), $degree);
5562
                    } while ($img->nextImage());
5563
                    $img = $img->optimizeImageLayers();
5564
                    $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...
5565
                } else {
5566
                    $img->rotateImage(new ImagickPixel($bgcolor), $degree);
5567
                    $result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
Documentation introduced by
$img is of type object<Imagick>, but the function expects a resource.

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...
5568
                }
5569
                $img->clear();
5570
5571
                return $result ? $path : false;
5572
5573
                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...
5574
5575
            case 'convert':
5576
                extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s));
0 ignored issues
show
Bug introduced by
$this->imageMagickConver...ormat, $jpgQuality, $s) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
5577 View Code Duplication
                if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) {
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...
5578
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
5579
                }
5580
                $cmd = sprintf('convert %s%s%s -background "%s" -rotate %d%s %s', $quotedPath, $coalesce, $jpgQuality, $bgcolor, $degree, $deconstruct, $quotedDstPath);
5581
5582
                $result = false;
5583
                if ($this->procExec($cmd) === 0) {
5584
                    $result = true;
5585
                }
5586
5587
                return $result ? $path : false;
5588
5589
                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...
5590
5591
            case 'gd':
5592
                $img = $this->gdImageCreate($path, $s['mime']);
5593
5594
                $degree = 360 - $degree;
5595
5596
                $bgNum = -1;
5597
                $bgIdx = false;
5598
                if ($s[2] === IMAGETYPE_GIF) {
5599
                    $bgIdx = imagecolortransparent($img);
5600
                    if ($bgIdx !== -1) {
5601
                        $c = imagecolorsforindex($img, $bgIdx);
5602
                        $w = imagesx($img);
5603
                        $h = imagesy($img);
5604
                        $newImg = imagecreatetruecolor($w, $h);
5605
                        imagepalettecopy($newImg, $img);
5606
                        $bgNum = imagecolorallocate($newImg, $c['red'], $c['green'], $c['blue']);
5607
                        imagefill($newImg, 0, 0, $bgNum);
5608
                        imagecolortransparent($newImg, $bgNum);
5609
                        imagecopy($newImg, $img, 0, 0, 0, 0, $w, $h);
5610
                        imagedestroy($img);
5611
                        $img = $newImg;
5612
                        $newImg = null;
0 ignored issues
show
Unused Code introduced by
$newImg 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...
5613
                    }
5614
                } elseif ($s[2] === IMAGETYPE_PNG) {
5615
                    $bgNum = imagecolorallocatealpha($img, 255, 255, 255, 127);
5616
                }
5617 View Code Duplication
                if ($bgNum === -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...
5618
                    list($r, $g, $b) = sscanf($bgcolor, '#%02x%02x%02x');
5619
                    $bgNum = imagecolorallocate($img, $r, $g, $b);
5620
                }
5621
5622
                $tmp = imagerotate($img, $degree, $bgNum);
5623
                if ($bgIdx !== -1) {
5624
                    imagecolortransparent($tmp, $bgNum);
5625
                }
5626
5627
                $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
5628
5629
                imagedestroy($img);
5630
                imagedestroy($tmp);
5631
5632
                return $result ? $path : false;
5633
5634
                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...
5635
        }
5636
5637
        return false;
5638
    }
5639
5640
    /**
5641
     * Execute shell command.
5642
     *
5643
     * @param  string $command command line
5644
     * @param  array $output stdout strings
5645
     * @param array|int $return_var process exit code
5646
     * @param  array $error_output stderr strings
5647
     * @return int exit code
5648
     * @author Alexey Sukhotin
5649
     */
5650
    protected function procExec($command, array &$output = null, &$return_var = -1, array &$error_output = null)
5651
    {
5652
        static $allowed = null;
5653
5654
        if ($allowed === null) {
5655
            if ($allowed = function_exists('proc_open')) {
5656
                if ($disabled = ini_get('disable_functions')) {
5657
                    $funcs = array_map('trim', explode(',', $disabled));
5658
                    $allowed = ! in_array('proc_open', $funcs);
5659
                }
5660
            }
5661
        }
5662
5663
        if (! $allowed) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $allowed 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...
5664
            $return_var = -1;
5665
5666
            return $return_var;
5667
        }
5668
5669
        if (! $command) {
5670
            $return_var = 0;
5671
5672
            return $return_var;
5673
        }
5674
5675
        $descriptorspec = [
5676
            0 => ['pipe', 'r'],  // stdin
5677
            1 => ['pipe', 'w'],  // stdout
5678
            2 => ['pipe', 'w'],   // stderr
5679
        ];
5680
5681
        $process = proc_open($command, $descriptorspec, $pipes, null, null);
5682
5683
        if (is_resource($process)) {
5684
            fclose($pipes[0]);
5685
5686
            $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...
5687
            $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...
5688
5689
            $output = stream_get_contents($pipes[1]);
5690
            $error_output = stream_get_contents($pipes[2]);
5691
5692
            fclose($pipes[1]);
5693
            fclose($pipes[2]);
5694
            $return_var = proc_close($process);
5695
        } else {
5696
            $return_var = -1;
5697
        }
5698
5699
        return $return_var;
5700
    }
5701
5702
    /**
5703
     * Remove thumbnail, also remove recursively if stat is directory.
5704
     *
5705
     * @param  string  $stat  file stat
5706
     * @return void
5707
     * @author Dmitry (dio) Levashov
5708
     * @author Naoki Sawada
5709
     * @author Troex Nevelin
5710
     **/
5711
    protected function rmTmb($stat)
5712
    {
5713
        if ($stat['mime'] === 'directory') {
5714
            foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
5715
                elFinder::extendTimeLimit(30);
5716
                $name = $this->basenameCE($p);
5717
                $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...
5718
            }
5719
        } elseif (! empty($stat['tmb']) && $stat['tmb'] != '1') {
5720
            $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
5721
            file_exists($tmb) && unlink($tmb);
5722
            clearstatcache();
5723
        }
5724
    }
5725
5726
    /**
5727
     * Create an gd image according to the specified mime type.
5728
     *
5729
     * @param string $path image file
5730
     * @param string $mime
5731
     * @return gd image resource identifier
5732
     */
5733
    protected function gdImageCreate($path, $mime)
5734
    {
5735
        switch ($mime) {
5736
            case 'image/jpeg':
5737
            return imagecreatefromjpeg($path);
5738
5739
            case 'image/png':
5740
            return imagecreatefrompng($path);
5741
5742
            case 'image/gif':
5743
            return imagecreatefromgif($path);
5744
5745
            case 'image/x-ms-bmp':
5746
            if (! function_exists('imagecreatefrombmp')) {
5747
                include_once dirname(__FILE__).'/libs/GdBmp.php';
5748
            }
5749
5750
            return imagecreatefrombmp($path);
5751
5752
            case 'image/xbm':
5753
            return imagecreatefromxbm($path);
5754
5755
            case 'image/xpm':
5756
            return imagecreatefromxpm($path);
5757
        }
5758
5759
        return false;
5760
    }
5761
5762
    /**
5763
     * Output gd image to file.
5764
     *
5765
     * @param resource $image gd image resource
5766
     * @param string $filename The path to save the file to.
5767
     * @param string $destformat The Image type to use for $filename
5768
     * @param string $mime The original image mime type
5769
     * @param int $jpgQuality JEPG quality (1-100)
5770
     * @return bool
5771
     */
5772
    protected function gdImage($image, $filename, $destformat, $mime, $jpgQuality = null)
5773
    {
5774
        if (! $jpgQuality) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $jpgQuality of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
5775
            $jpgQuality = $this->options['jpgQuality'];
5776
        }
5777
        if ($destformat) {
5778
            switch ($destformat) {
5779
                case 'jpg':
5780
                    $mime = 'image/jpeg';
5781
                    break;
5782
                case 'gif':
5783
                    $mime = 'image/gif';
5784
                    break;
5785
                case 'png':
5786
                default:
5787
                    $mime = 'image/png';
5788
                    break;
5789
            }
5790
        }
5791
        switch ($mime) {
5792
            case 'image/gif':
5793
                return imagegif($image, $filename);
5794
            case 'image/jpeg':
5795
                return imagejpeg($image, $filename, $jpgQuality);
5796
            case 'image/wbmp':
5797
                return imagewbmp($image, $filename);
5798
            case 'image/png':
5799
            default:
5800
                return imagepng($image, $filename);
5801
        }
5802
    }
5803
5804
    /**
5805
     * Output imagick image to file.
5806
     *
5807
     * @param resource $img imagick image resource
5808
     * @param string $filename The path to save the file to.
5809
     * @param string $destformat The Image type to use for $filename
5810
     * @param int $jpgQuality JEPG quality (1-100)
5811
     * @return bool
5812
     */
5813
    protected function imagickImage($img, $filename, $destformat, $jpgQuality = null)
5814
    {
5815
        if (! $jpgQuality) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $jpgQuality of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
5816
            $jpgQuality = $this->options['jpgQuality'];
5817
        }
5818
5819
        try {
5820
            if ($destformat) {
5821
                if ($destformat === 'gif') {
5822
                    $img->setImageFormat('gif');
0 ignored issues
show
Bug introduced by
The method setImageFormat cannot be called on $img (of type resource).

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...
5823
                } elseif ($destformat === 'png') {
5824
                    $img->setImageFormat('png');
0 ignored issues
show
Bug introduced by
The method setImageFormat cannot be called on $img (of type resource).

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...
5825
                } elseif ($destformat === 'jpg') {
5826
                    $img->setImageFormat('jpeg');
0 ignored issues
show
Bug introduced by
The method setImageFormat cannot be called on $img (of type resource).

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...
5827
                }
5828
            }
5829
            if (strtoupper($img->getImageFormat()) === 'JPEG') {
0 ignored issues
show
Bug introduced by
The method getImageFormat cannot be called on $img (of type resource).

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...
5830
                $img->setImageCompression(imagick::COMPRESSION_JPEG);
0 ignored issues
show
Bug introduced by
The method setImageCompression cannot be called on $img (of type resource).

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...
5831
                $img->setImageCompressionQuality($jpgQuality);
0 ignored issues
show
Bug introduced by
The method setImageCompressionQuality cannot be called on $img (of type resource).

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...
5832
                try {
5833
                    $orientation = $img->getImageOrientation();
0 ignored issues
show
Bug introduced by
The method getImageOrientation cannot be called on $img (of type resource).

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...
5834
                } catch (ImagickException $e) {
5835
                    $orientation = 0;
5836
                }
5837
                $img->stripImage();
0 ignored issues
show
Bug introduced by
The method stripImage cannot be called on $img (of type resource).

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...
5838
                if ($orientation) {
5839
                    $img->setImageOrientation($orientation);
0 ignored issues
show
Bug introduced by
The method setImageOrientation cannot be called on $img (of type resource).

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...
5840
                }
5841
            }
5842
            $result = $img->writeImage($filename);
0 ignored issues
show
Bug introduced by
The method writeImage cannot be called on $img (of type resource).

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...
5843
        } catch (Exception $e) {
5844
            $result = false;
5845
        }
5846
5847
        return $result;
5848
    }
5849
5850
    /**
5851
     * Assign the proper background to a gd image.
5852
     *
5853
     * @param resource $image gd image resource
5854
     * @param string $bgcolor background color in #rrggbb format
5855
     */
5856
    protected function gdImageBackground($image, $bgcolor)
5857
    {
5858
        if ($bgcolor === 'transparent') {
5859
            imagealphablending($image, false);
5860
            imagesavealpha($image, true);
5861 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...
5862
            list($r, $g, $b) = sscanf($bgcolor, '#%02x%02x%02x');
5863
            $bgcolor1 = imagecolorallocate($image, $r, $g, $b);
5864
            imagefill($image, 0, 0, $bgcolor1);
5865
        }
5866
    }
5867
5868
    /**
5869
     * Prepare variables for exec convert of ImageMagick.
5870
     *
5871
     * @param  string  $path
5872
     * @param  string  $destformat
5873
     * @param  int     $jpgQuality
5874
     * @param  array   $imageSize
5875
     * @return array
5876
     */
5877
    protected function imageMagickConvertPrepare($path, $destformat, $jpgQuality, $imageSize = null)
5878
    {
5879
        if (is_null($imageSize)) {
5880
            $imageSize = getimagesize($path);
5881
        }
5882
        if (! $imageSize) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $imageSize 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...
5883
            return [];
5884
        }
5885
        $srcType = $this->getExtentionByMime($imageSize['mime'], ':');
5886
        $ani = false;
5887
        $cmd = 'identify '.escapeshellarg($srcType.$path);
5888
        if ($this->procExec($cmd, $o) === 0) {
5889
            $ani = preg_split('/(?:\r\n|\n|\r)/', trim($o));
5890
            if (count($ani) < 2) {
5891
                $ani = false;
5892
            }
5893
        }
5894
        $coalesce = $index = '';
5895
        $deconstruct = ' +repage';
5896
        if ($ani) {
5897
            if (is_null($destformat)) {
5898
                $coalesce = ' -coalesce -repage 0x0';
5899
                $deconstruct = ' +repage -deconstruct -layers optimize';
5900
            } else {
5901
                $index = '[0]';
5902
                if ($srcType === 'ico:') {
5903
                    foreach ($ani as $_i => $_info) {
5904
                        if (preg_match('/ (\d+)x(\d+) /', $_info, $m)) {
5905
                            if ($m[1] == $imageSize[0] && $m[2] == $imageSize[1]) {
5906
                                $index = '['.$_i.']';
5907
                                break;
5908
                            }
5909
                        }
5910
                    }
5911
                }
5912
            }
5913
        }
5914
        if ($imageSize[2] === IMAGETYPE_JPEG || $imageSize[2] === IMAGETYPE_JPEG2000) {
5915
            $jpgQuality = ' -quality '.$jpgQuality;
5916
        } else {
5917
            $jpgQuality = '';
5918
        }
5919
        $quotedPath = escapeshellarg($srcType.$path.$index);
5920
        $quotedDstPath = escapeshellarg(($destformat ? ($destformat.':') : $srcType).$path);
5921
5922
        return compact('ani', 'index', 'coalesce', 'deconstruct', 'jpgQuality', 'quotedPath', 'quotedDstPath');
5923
    }
5924
5925
    /*********************** misc *************************/
5926
5927
    /**
5928
     * Return smart formatted date.
5929
     *
5930
     * @param  int     $ts  file timestamp
5931
     * @return string
5932
     * @author Dmitry (dio) Levashov
5933
     **/
5934
    // 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...
5935
    // 	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...
5936
    // 		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...
5937
    // 	}
5938
    //
5939
    // 	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...
5940
    // 		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...
5941
    // 	}
5942
    //
5943
    // 	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...
5944
    // }
5945
5946
    /**
5947
     * Find position of first occurrence of string in a string with multibyte support.
5948
     *
5949
     * @param  string  $haystack  The string being checked.
5950
     * @param  string  $needle    The string to find in haystack.
5951
     * @param  int     $offset    The search offset. If it is not specified, 0 is used.
5952
     * @return int|bool
5953
     * @author Alexey Sukhotin
5954
     **/
5955
    protected function stripos($haystack, $needle, $offset = 0)
5956
    {
5957
        if (function_exists('mb_stripos')) {
5958
            return mb_stripos($haystack, $needle, $offset, 'UTF-8');
5959
        } elseif (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
5960
            return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset);
5961
        }
5962
5963
        return stripos($haystack, $needle, $offset);
5964
    }
5965
5966
    /**
5967
     * Get server side available archivers.
5968
     *
5969
     * @param bool $use_cache
5970
     * @return array
5971
     */
5972
    protected function getArchivers($use_cache = true)
5973
    {
5974
        $sessionKey = 'ARCHIVERS_CACHE';
5975
        if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
5976
            return $this->sessionCache[$sessionKey];
5977
        }
5978
5979
        $arcs = [
5980
            'create' => [],
5981
            'extract' => [],
5982
        ];
5983
5984
        if ($this->procExec('') === 0) {
5985
            $this->procExec('tar --version', $o, $ctar);
5986
5987
            if ($ctar == 0) {
5988
                $arcs['create']['application/x-tar'] = ['cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar'];
5989
                $arcs['extract']['application/x-tar'] = ['cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar', 'toSpec' => '-C '];
5990
                unset($o);
5991
                $this->procExec('gzip --version', $o, $c);
5992 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...
5993
                    $arcs['create']['application/x-gzip'] = ['cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz'];
5994
                    $arcs['extract']['application/x-gzip'] = ['cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz', 'toSpec' => '-C '];
5995
                }
5996
                unset($o);
5997
                $this->procExec('bzip2 --version', $o, $c);
5998 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...
5999
                    $arcs['create']['application/x-bzip2'] = ['cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz'];
6000
                    $arcs['extract']['application/x-bzip2'] = ['cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz', 'toSpec' => '-C '];
6001
                }
6002
                unset($o);
6003
                $this->procExec('xz --version', $o, $c);
6004 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...
6005
                    $arcs['create']['application/x-xz'] = ['cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz'];
6006
                    $arcs['extract']['application/x-xz'] = ['cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz', 'toSpec' => '-C '];
6007
                }
6008
            }
6009
            unset($o);
6010
            $this->procExec('zip -v', $o, $c);
6011 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...
6012
                $arcs['create']['application/zip'] = ['cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip'];
6013
            }
6014
            unset($o);
6015
            $this->procExec('unzip --help', $o, $c);
6016 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...
6017
                $arcs['extract']['application/zip'] = ['cmd' => 'unzip', 'argc' => '',  'ext' => 'zip', 'toSpec' => '-d '];
6018
            }
6019
            unset($o);
6020
            $this->procExec('rar --version', $o, $c);
6021 View Code Duplication
            if ($c == 0 || $c == 7) {
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...
6022
                $arcs['create']['application/x-rar'] = ['cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar'];
6023
            }
6024
            unset($o);
6025
            $this->procExec('unrar', $o, $c);
6026 View Code Duplication
            if ($c == 0 || $c == 7) {
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...
6027
                $arcs['extract']['application/x-rar'] = ['cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar', 'toSpec' => ''];
6028
            }
6029
            unset($o);
6030
            $this->procExec('7za --help', $o, $c);
6031
            if ($c == 0) {
6032
                $arcs['create']['application/x-7z-compressed'] = ['cmd' => '7za', 'argc' => 'a', 'ext' => '7z'];
6033
                $arcs['extract']['application/x-7z-compressed'] = ['cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z', 'toSpec' => '-o'];
6034
6035
                if (empty($arcs['create']['application/zip'])) {
6036
                    $arcs['create']['application/zip'] = ['cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip'];
6037
                }
6038 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...
6039
                    $arcs['extract']['application/zip'] = ['cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip', 'toSpec' => '-o'];
6040
                }
6041
                if (empty($arcs['create']['application/x-tar'])) {
6042
                    $arcs['create']['application/x-tar'] = ['cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar'];
6043
                }
6044 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...
6045
                    $arcs['extract']['application/x-tar'] = ['cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar', 'toSpec' => '-o'];
6046
                }
6047
            } elseif (substr(PHP_OS, 0, 3) === 'WIN') {
6048
                // check `7z` for Windows server.
6049
                unset($o);
6050
                $this->procExec('7z', $o, $c);
6051
                if ($c == 0) {
6052
                    $arcs['create']['application/x-7z-compressed'] = ['cmd' => '7z', 'argc' => 'a', 'ext' => '7z'];
6053
                    $arcs['extract']['application/x-7z-compressed'] = ['cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z', 'toSpec' => '-o'];
6054
6055
                    if (empty($arcs['create']['application/zip'])) {
6056
                        $arcs['create']['application/zip'] = ['cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip'];
6057
                    }
6058 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...
6059
                        $arcs['extract']['application/zip'] = ['cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip', 'toSpec' => '-o'];
6060
                    }
6061
                    if (empty($arcs['create']['application/x-tar'])) {
6062
                        $arcs['create']['application/x-tar'] = ['cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar'];
6063
                    }
6064 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...
6065
                        $arcs['extract']['application/x-tar'] = ['cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar', 'toSpec' => '-o'];
6066
                    }
6067 View Code Duplication
                    if (empty($arcs['extract']['application/x-rar'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
6068
                        $arcs['extract']['application/x-rar'] = ['cmd' => '7z', 'argc' => 'x -trar -y', 'ext' => 'rar', 'toSpec' => '-o'];
6069
                    }
6070
                }
6071
            }
6072
        }
6073
6074
        // Use PHP ZipArchive Class
6075
        if (class_exists('ZipArchive', false)) {
6076
            if (empty($arcs['create']['application/zip'])) {
6077
                $arcs['create']['application/zip'] = ['cmd' => 'phpfunction', 'argc' => ['self', 'zipArchiveZip'], 'ext' => 'zip'];
6078
            }
6079
            if (empty($arcs['extract']['application/zip'])) {
6080
                $arcs['extract']['application/zip'] = ['cmd' => 'phpfunction', 'argc' => ['self', 'zipArchiveUnzip'], 'ext' => 'zip'];
6081
            }
6082
        }
6083
6084
        $this->sessionCache[$sessionKey] = $arcs;
6085
6086
        return $arcs;
6087
    }
6088
6089
    /**
6090
     * Resolve relative / (Unix-like)absolute path.
6091
     *
6092
     * @param string $path  target path
6093
     * @param string $base  base path
6094
     * @return string
6095
     */
6096
    protected function getFullPath($path, $base)
6097
    {
6098
        $separator = $this->separator;
6099
        $systemroot = $this->systemRoot;
6100
6101
        if ($base[0] === $separator && strpos($base, 0, strlen($systemroot)) !== $systemroot) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of strpos($base, 0, strlen($systemroot)) (integer) and $systemroot (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
6102
            $base = $systemroot.substr($base, 1);
6103
        }
6104
6105
        // 'Here'
6106
        if ($path === '' || $path === '.'.$separator) {
6107
            return $base;
6108
        }
6109
6110
        $sepquoted = preg_quote($separator, '#');
6111
6112
        if (substr($path, 0, 3) === '..'.$separator) {
6113
            $path = $base.$separator.$path;
6114
        }
6115
        // normalize `/../`
6116
        $normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#'; // '#(/)[^\/]+/\.\./#'
6117
        while (preg_match($normreg, $path)) {
6118
            $path = preg_replace($normreg, '$1', $path, 1);
6119
        }
6120
6121
        // Absolute path
6122
        if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
6123
            return $path;
6124
        }
6125
6126
        $preg_separator = '#'.$sepquoted.'#';
6127
6128
        // Relative path from 'Here'
6129
        if (substr($path, 0, 2) === '.'.$separator || $path[0] !== '.') {
6130
            $arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
6131
            if ($arrn[0] !== '.') {
6132
                array_unshift($arrn, '.');
6133
            }
6134
            $arrn[0] = $base;
6135
6136
            return implode($separator, $arrn);
6137
        }
6138
6139
        return $path;
6140
    }
6141
6142
    /**
6143
     * Create archive and return its path.
6144
     *
6145
     * @param  string  $dir    target dir
6146
     * @param  array   $files  files names list
6147
     * @param  string  $name   archive name
6148
     * @param  array   $arc    archiver options
6149
     * @return string|bool
6150
     * @author Dmitry (dio) Levashov,
6151
     * @author Alexey Sukhotin
6152
     * @author Naoki Sawada
6153
     **/
6154
    protected function makeArchive($dir, $files, $name, $arc)
6155
    {
6156
        if ($arc['cmd'] === 'phpfunction') {
6157 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...
6158
                call_user_func_array($arc['argc'], [$dir, $files, $name]);
6159
            }
6160
        } else {
6161
            $cwd = getcwd();
6162
            if (chdir($dir)) {
6163
                foreach ($files as $i => $file) {
6164
                    $files[$i] = '.'.DIRECTORY_SEPARATOR.$file;
6165
                }
6166
                $files = array_map('escapeshellarg', $files);
6167
6168
                $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
6169
                $this->procExec($cmd, $o, $c);
6170
                chdir($cwd);
6171
            } else {
6172
                return false;
6173
            }
6174
        }
6175
        $path = $dir.DIRECTORY_SEPARATOR.$name;
6176
6177
        return file_exists($path) ? $path : false;
6178
    }
6179
6180
    /**
6181
     * Unpack archive.
6182
     *
6183
     * @param  string      $path  archive path
6184
     * @param  array       $arc   archiver command and arguments (same as in $this->archivers)
6185
     * @param  bool|string $mode  bool: remove archive ( unlink($path) ) | string: extract to directory
6186
     * @return void
6187
     * @author Dmitry (dio) Levashov
6188
     * @author Alexey Sukhotin
6189
     * @author Naoki Sawada
6190
     **/
6191
    protected function unpackArchive($path, $arc, $mode = true)
6192
    {
6193
        if (is_string($mode)) {
6194
            $dir = $mode;
6195
            $chdir = null;
6196
            $remove = false;
6197
        } else {
6198
            $dir = dirname($path);
6199
            $chdir = $dir;
6200
            $remove = $mode;
6201
        }
6202
        $dir = realpath($dir);
6203
        $path = realpath($path);
6204
        if ($arc['cmd'] === 'phpfunction') {
6205 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...
6206
                call_user_func_array($arc['argc'], [$path, $dir]);
6207
            }
6208
        } else {
6209
            $cwd = getcwd();
6210
            if (! $chdir || chdir($dir)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $chdir of type null|string is loosely compared to false; 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...
6211
                if ($chdir) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $chdir of type null|string 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...
6212
                    $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
6213
                } else {
6214
                    $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($path).' '.$arc['toSpec'].escapeshellarg($dir);
6215
                }
6216
                $this->procExec($cmd, $o, $c);
6217
                $chdir && chdir($cwd);
0 ignored issues
show
Bug Best Practice introduced by
The expression $chdir of type null|string 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...
6218
            }
6219
        }
6220
        $remove && unlink($path);
6221
    }
6222
6223
    /**
6224
     * Check and filter the extracted items.
6225
     *
6226
     * @param  string $path    target local path
6227
     * @param  array  $checks  types to check default: ['symlink', 'name', 'writable', 'mime']
6228
     * @return array  ['symlinks' => [], 'names' => [], 'writables' => [], 'mimes' => [], 'rmNames' => [], 'totalSize' => 0]
6229
     * @author Naoki Sawada
6230
     */
6231
    protected function checkExtractItems($path, $checks = null)
6232
    {
6233
        if (is_null($checks) || ! is_array($checks)) {
6234
            $checks = ['symlink', 'name', 'writable', 'mime'];
6235
        }
6236
        $chkSymlink = in_array('symlink', $checks);
6237
        $chkName = in_array('name', $checks);
6238
        $chkWritable = in_array('writable', $checks);
6239
        $chkMime = in_array('mime', $checks);
6240
6241
        $res = [
6242
            'symlinks' => [],
6243
            'names' => [],
6244
            'writables' => [],
6245
            'mimes' => [],
6246
            'rmNames' => [],
6247
            'totalSize' => 0,
6248
        ];
6249
6250
        if (is_dir($path)) {
6251
            foreach (self::localScandir($path) as $name) {
6252
                $p = $path.DIRECTORY_SEPARATOR.$name;
6253
                if ($chkSymlink && is_link($p)) {
6254
                    self::localRmdirRecursive($p);
6255
                    $res['symlinks'][] = $p;
6256
                    $res['rmNames'][] = $name;
6257
                    continue;
6258
                }
6259
                $isDir = is_dir($p);
6260
                if ($chkName && ! $this->nameAccepted($name, $isDir)) {
6261
                    self::localRmdirRecursive($p);
6262
                    $res['names'][] = $p;
6263
                    $res['rmNames'][] = $name;
6264
                    continue;
6265
                }
6266
                if ($chkWritable && ! $this->attr($p, 'write', null, $isDir)) {
6267
                    self::localRmdirRecursive($p);
6268
                    $res['writables'][] = $p;
6269
                    $res['rmNames'][] = $name;
6270
                    continue;
6271
                }
6272 View Code Duplication
                if ($chkMime && ($mimeByName = self::mimetypeInternalDetect($name)) && $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...
6273
                    self::localRmdirRecursive($p);
6274
                    $res['mimes'][] = $p;
6275
                    $res['rmNames'][] = $name;
6276
                    continue;
6277
                }
6278
                if ($isDir) {
6279
                    $cRes = $this->checkExtractItems($p, $checks);
6280
                    foreach ($cRes as $k => $v) {
6281
                        if (is_array($v)) {
6282
                            $res[$k] = array_merge($res[$k], $cRes[$k]);
6283
                        } else {
6284
                            $res[$k] += $cRes[$k];
6285
                        }
6286
                    }
6287
                } else {
6288
                    $res['totalSize'] += sprintf('%u', filesize($p));
6289
                }
6290
            }
6291
            $res['rmNames'] = array_unique($res['rmNames']);
6292
        } else {
6293
            if ($chkSymlink && is_link($path)) {
6294
                unlink($path);
6295
                $res['symlinks'][] = $path;
6296
                $res['rmNames'][] = basename($path);
6297
            } elseif ($chkName && ! $this->nameAccepted($name, false)) {
0 ignored issues
show
Bug introduced by
The variable $name seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
6298
                unlink($path);
6299
                $res['names'][] = $path;
6300
                $res['rmNames'][] = $name;
0 ignored issues
show
Bug introduced by
The variable $name seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
6301
            } elseif ($chkWritable && ! $this->attr($path, 'write', null, $isDir)) {
0 ignored issues
show
Bug introduced by
The variable $isDir seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
6302
                unlink($path);
6303
                $res['writables'][] = $path;
6304
                $res['rmNames'][] = $name;
0 ignored issues
show
Bug introduced by
The variable $name seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
6305 View Code Duplication
            } elseif ($chkMime && ($mimeByName = self::mimetypeInternalDetect($name)) && $mimeByName !== 'unknown' && ! $this->allowPutMime($mimeByName)) {
0 ignored issues
show
Bug introduced by
The variable $name seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

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...
6306
                unlink($path);
6307
                $res['mimes'][] = $path;
6308
                $res['rmNames'][] = $name;
0 ignored issues
show
Bug introduced by
The variable $name seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
6309
            } else {
6310
                $res['totalSize'] += sprintf('%u', filesize($path));
6311
            }
6312
        }
6313
6314
        return $res;
6315
    }
6316
6317
    /**
6318
     * Return files of target directory that is dotfiles excludes.
6319
     *
6320
     * @param  string $dir target directory path
6321
     * @return array
6322
     * @throws Exception
6323
     * @author Naoki Sawada
6324
     */
6325
    protected static function localScandir($dir)
6326
    {
6327
        // PHP function scandir() is not work well in specific environment. I dont know why.
6328
        // ref. https://github.com/Studio-42/elFinder/issues/1248
6329
        $files = [];
6330
        if ($dh = opendir($dir)) {
6331
            while (false !== ($file = readdir($dh))) {
6332
                if ($file !== '.' && $file !== '..') {
6333
                    $files[] = $file;
6334
                }
6335
            }
6336
            closedir($dh);
6337
        } else {
6338
            throw new Exception('Can not open local directory.');
6339
        }
6340
6341
        return $files;
6342
    }
6343
6344
    /**
6345
     * Remove directory recursive on local file system.
6346
     *
6347
     * @param string $dir Target dirctory path
6348
     * @return bool
6349
     * @author Naoki Sawada
6350
     */
6351
    protected static function localRmdirRecursive($dir)
6352
    {
6353
        if (! is_link($dir) && is_dir($dir)) {
6354
            chmod($dir, 0777);
6355
            if ($handle = opendir($dir)) {
6356
                while (false !== ($file = readdir($handle))) {
6357
                    if ($file === '.' || $file === '..') {
6358
                        continue;
6359
                    }
6360
                    elFinder::extendTimeLimit(30);
6361
                    $path = $dir.DIRECTORY_SEPARATOR.$file;
6362
                    if (! is_link($dir) && is_dir($path)) {
6363
                        self::localRmdirRecursive($path);
6364
                    } else {
6365
                        chmod($path, 0666);
6366
                        unlink($path);
6367
                    }
6368
                }
6369
                closedir($handle);
6370
            }
6371
6372
            return rmdir($dir);
6373
        } elseif (is_file($dir) || is_link($dir)) {
6374
            chmod($dir, 0666);
6375
6376
            return unlink($dir);
6377
        }
6378
6379
        return false;
6380
    }
6381
6382
    /**
6383
     * Move item recursive on local file system.
6384
     *
6385
     * @param string $src
6386
     * @param string $target
6387
     * @param string $overWrite
6388
     * @param string $copyJoin
6389
     * @return bool
6390
     * @author Naoki Sawada
6391
     */
6392
    protected static function localMoveRecursive($src, $target, $overWrite = true, $copyJoin = true)
6393
    {
6394
        $res = false;
6395
        if (! file_exists($target)) {
6396
            return rename($src, $target);
6397
        }
6398
        if (! $copyJoin || ! is_dir($target)) {
6399
            if ($overWrite) {
6400
                if (is_dir($target)) {
6401
                    $del = self::localRmdirRecursive($target);
6402
                } else {
6403
                    $del = unlink($target);
6404
                }
6405
                if ($del) {
6406
                    return rename($src, $target);
6407
                }
6408
            }
6409
        } else {
6410
            foreach (self::localScandir($src) as $item) {
6411
                $res |= self::localMoveRecursive($src.DIRECTORY_SEPARATOR.$item, $target.DIRECTORY_SEPARATOR.$item, $overWrite, $copyJoin);
6412
            }
6413
        }
6414
6415
        return (bool) $res;
6416
    }
6417
6418
    /**
6419
     * Create Zip archive using PHP class ZipArchive.
6420
     *
6421
     * @param  string        $dir      target dir
6422
     * @param  array         $files    files names list
6423
     * @param  string|object $zipPath  Zip archive name
6424
     * @return bool
6425
     * @author Naoki Sawada
6426
     */
6427
    protected static function zipArchiveZip($dir, $files, $zipPath)
6428
    {
6429
        try {
6430
            if ($start = is_string($zipPath)) {
6431
                $zip = new ZipArchive();
6432
                if ($zip->open($dir.DIRECTORY_SEPARATOR.$zipPath, ZipArchive::CREATE) !== true) {
6433
                    $zip = false;
6434
                }
6435
            } else {
6436
                $zip = $zipPath;
6437
            }
6438
            if ($zip) {
6439
                foreach ($files as $file) {
6440
                    $path = $dir.DIRECTORY_SEPARATOR.$file;
6441
                    if (is_dir($path)) {
6442
                        $zip->addEmptyDir($file);
6443
                        $_files = [];
6444
                        if ($handle = opendir($path)) {
6445
                            while (false !== ($entry = readdir($handle))) {
6446
                                if ($entry !== '.' && $entry !== '..') {
6447
                                    $_files[] = $file.DIRECTORY_SEPARATOR.$entry;
6448
                                }
6449
                            }
6450
                            closedir($handle);
6451
                        }
6452
                        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...
6453
                            self::zipArchiveZip($dir, $_files, $zip);
6454
                        }
6455
                    } else {
6456
                        $zip->addFile($path, $file);
6457
                    }
6458
                }
6459
                $start && $zip->close();
6460
            }
6461
        } catch (Exception $e) {
6462
            return false;
6463
        }
6464
6465
        return true;
6466
    }
6467
6468
    /**
6469
     * Unpack Zip archive using PHP class ZipArchive.
6470
     *
6471
     * @param  string $zipPath  Zip archive name
6472
     * @param  string $toDir    Extract to path
6473
     * @return bool
6474
     * @author Naoki Sawada
6475
     */
6476
    protected static function zipArchiveUnzip($zipPath, $toDir)
6477
    {
6478
        try {
6479
            $zip = new ZipArchive();
6480
            if ($zip->open($zipPath) === true) {
6481
                $zip->extractTo($toDir);
6482
                $zip->close();
6483
            }
6484
        } catch (Exception $e) {
6485
            return false;
6486
        }
6487
6488
        return true;
6489
    }
6490
6491
    /**
6492
     * Recursive symlinks search.
6493
     *
6494
     * @param  string  $path  file/dir path
6495
     * @return bool
6496
     * @author Dmitry (dio) Levashov
6497
     **/
6498
    protected static function localFindSymlinks($path)
6499
    {
6500
        if (is_link($path)) {
6501
            return true;
6502
        }
6503
6504
        if (is_dir($path)) {
6505
            foreach (self::localScandir($path) as $name) {
6506
                $p = $path.DIRECTORY_SEPARATOR.$name;
6507
                if (is_link($p)) {
6508
                    return true;
6509
                }
6510
                if (is_dir($p) && $this->_findSymlinks($p)) {
0 ignored issues
show
Bug introduced by
The variable $this 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...
6511
                    return true;
6512
                }
6513
            }
6514
        }
6515
6516
        return false;
6517
    }
6518
6519
    /**==================================* abstract methods *====================================**/
6520
6521
    /*********************** paths/urls *************************/
6522
6523
    /**
6524
     * Return parent directory path.
6525
     *
6526
     * @param  string  $path  file path
6527
     * @return string
6528
     * @author Dmitry (dio) Levashov
6529
     **/
6530
    abstract protected function _dirname($path);
6531
6532
    /**
6533
     * Return file name.
6534
     *
6535
     * @param  string  $path  file path
6536
     * @return string
6537
     * @author Dmitry (dio) Levashov
6538
     **/
6539
    abstract protected function _basename($path);
6540
6541
    /**
6542
     * Join dir name and file name and return full path.
6543
     * Some drivers (db) use int as path - so we give to concat path to driver itself.
6544
     *
6545
     * @param  string  $dir   dir path
6546
     * @param  string  $name  file name
6547
     * @return string
6548
     * @author Dmitry (dio) Levashov
6549
     **/
6550
    abstract protected function _joinPath($dir, $name);
6551
6552
    /**
6553
     * Return normalized path.
6554
     *
6555
     * @param  string  $path  file path
6556
     * @return string
6557
     * @author Dmitry (dio) Levashov
6558
     **/
6559
    abstract protected function _normpath($path);
6560
6561
    /**
6562
     * Return file path related to root dir.
6563
     *
6564
     * @param  string  $path  file path
6565
     * @return string
6566
     * @author Dmitry (dio) Levashov
6567
     **/
6568
    abstract protected function _relpath($path);
6569
6570
    /**
6571
     * Convert path related to root dir into real path.
6572
     *
6573
     * @param  string  $path  rel file path
6574
     * @return string
6575
     * @author Dmitry (dio) Levashov
6576
     **/
6577
    abstract protected function _abspath($path);
6578
6579
    /**
6580
     * Return fake path started from root dir.
6581
     * Required to show path on client side.
6582
     *
6583
     * @param  string  $path  file path
6584
     * @return string
6585
     * @author Dmitry (dio) Levashov
6586
     **/
6587
    abstract protected function _path($path);
6588
6589
    /**
6590
     * Return true if $path is children of $parent.
6591
     *
6592
     * @param  string  $path    path to check
6593
     * @param  string  $parent  parent path
6594
     * @return bool
6595
     * @author Dmitry (dio) Levashov
6596
     **/
6597
    abstract protected function _inpath($path, $parent);
6598
6599
    /**
6600
     * Return stat for given path.
6601
     * Stat contains following fields:
6602
     * - (int)    size    file size in b. required
6603
     * - (int)    ts      file modification time in unix time. required
6604
     * - (string) mime    mimetype. required for folders, others - optionally
6605
     * - (bool)   read    read permissions. required
6606
     * - (bool)   write   write permissions. required
6607
     * - (bool)   locked  is object locked. optionally
6608
     * - (bool)   hidden  is object hidden. optionally
6609
     * - (string) alias   for symlinks - link target path relative to root path. optionally
6610
     * - (string) target  for symlinks - link target path. optionally.
6611
     *
6612
     * If file does not exists - returns empty array or false.
6613
     *
6614
     * @param  string  $path    file path
6615
     * @return array|false
6616
     * @author Dmitry (dio) Levashov
6617
     **/
6618
    abstract protected function _stat($path);
6619
6620
    /***************** file stat ********************/
6621
6622
    /**
6623
     * Return true if path is dir and has at least one childs directory.
6624
     *
6625
     * @param  string  $path  dir path
6626
     * @return bool
6627
     * @author Dmitry (dio) Levashov
6628
     **/
6629
    abstract protected function _subdirs($path);
6630
6631
    /**
6632
     * Return object width and height
6633
     * Ususaly used for images, but can be realize for video etc...
6634
     *
6635
     * @param  string  $path  file path
6636
     * @param  string  $mime  file mime type
6637
     * @return string
6638
     * @author Dmitry (dio) Levashov
6639
     **/
6640
    abstract protected function _dimensions($path, $mime);
6641
6642
    /******************** file/dir content *********************/
6643
6644
    /**
6645
     * Return files list in directory.
6646
     *
6647
     * @param  string  $path  dir path
6648
     * @return array
6649
     * @author Dmitry (dio) Levashov
6650
     **/
6651
    abstract protected function _scandir($path);
6652
6653
    /**
6654
     * Open file and return file pointer.
6655
     *
6656
     * @param  string $path file path
6657
     * @param  string $mode open mode
6658
     * @return resource|false
6659
     * @author Dmitry (dio) Levashov
6660
     **/
6661
    abstract protected function _fopen($path, $mode = 'rb');
6662
6663
    /**
6664
     * Close opened file.
6665
     *
6666
     * @param  resource  $fp    file pointer
6667
     * @param  string    $path  file path
6668
     * @return bool
6669
     * @author Dmitry (dio) Levashov
6670
     **/
6671
    abstract protected function _fclose($fp, $path = '');
6672
6673
    /********************  file/dir manipulations *************************/
6674
6675
    /**
6676
     * Create dir and return created dir path or false on failed.
6677
     *
6678
     * @param  string  $path  parent dir path
6679
     * @param string  $name  new directory name
6680
     * @return string|bool
6681
     * @author Dmitry (dio) Levashov
6682
     **/
6683
    abstract protected function _mkdir($path, $name);
6684
6685
    /**
6686
     * Create file and return it's path or false on failed.
6687
     *
6688
     * @param  string  $path  parent dir path
6689
     * @param string  $name  new file name
6690
     * @return string|bool
6691
     * @author Dmitry (dio) Levashov
6692
     **/
6693
    abstract protected function _mkfile($path, $name);
6694
6695
    /**
6696
     * Create symlink.
6697
     *
6698
     * @param  string  $source     file to link to
6699
     * @param  string  $targetDir  folder to create link in
6700
     * @param  string  $name       symlink name
6701
     * @return bool
6702
     * @author Dmitry (dio) Levashov
6703
     **/
6704
    abstract protected function _symlink($source, $targetDir, $name);
6705
6706
    /**
6707
     * Copy file into another file (only inside one volume).
6708
     *
6709
     * @param  string $source source file path
6710
     * @param $targetDir
6711
     * @param  string $name file name
6712
     * @return bool|string
6713
     * @internal param string $target target dir path
6714
     * @author Dmitry (dio) Levashov
6715
     */
6716
    abstract protected function _copy($source, $targetDir, $name);
6717
6718
    /**
6719
     * Move file into another parent dir.
6720
     * Return new file path or false.
6721
     *
6722
     * @param  string $source source file path
6723
     * @param $targetDir
6724
     * @param  string $name file name
6725
     * @return bool|string
6726
     * @internal param string $target target dir path
6727
     * @author Dmitry (dio) Levashov
6728
     */
6729
    abstract protected function _move($source, $targetDir, $name);
6730
6731
    /**
6732
     * Remove file.
6733
     *
6734
     * @param  string  $path  file path
6735
     * @return bool
6736
     * @author Dmitry (dio) Levashov
6737
     **/
6738
    abstract protected function _unlink($path);
6739
6740
    /**
6741
     * Remove dir.
6742
     *
6743
     * @param  string  $path  dir path
6744
     * @return bool
6745
     * @author Dmitry (dio) Levashov
6746
     **/
6747
    abstract protected function _rmdir($path);
6748
6749
    /**
6750
     * Create new file and write into it from file pointer.
6751
     * Return new file path or false on error.
6752
     *
6753
     * @param  resource  $fp   file pointer
6754
     * @param  string    $dir  target dir path
6755
     * @param  string    $name file name
6756
     * @param  array     $stat file stat (required by some virtual fs)
6757
     * @return bool|string
6758
     * @author Dmitry (dio) Levashov
6759
     **/
6760
    abstract protected function _save($fp, $dir, $name, $stat);
6761
6762
    /**
6763
     * Get file contents.
6764
     *
6765
     * @param  string  $path  file path
6766
     * @return string|false
6767
     * @author Dmitry (dio) Levashov
6768
     **/
6769
    abstract protected function _getContents($path);
6770
6771
    /**
6772
     * Write a string to a file.
6773
     *
6774
     * @param  string  $path     file path
6775
     * @param  string  $content  new file content
6776
     * @return bool
6777
     * @author Dmitry (dio) Levashov
6778
     **/
6779
    abstract protected function _filePutContents($path, $content);
6780
6781
    /**
6782
     * Extract files from archive.
6783
     *
6784
     * @param  string  $path file path
6785
     * @param  array   $arc  archiver options
6786
     * @return bool
6787
     * @author Dmitry (dio) Levashov,
6788
     * @author Alexey Sukhotin
6789
     **/
6790
    abstract protected function _extract($path, $arc);
6791
6792
    /**
6793
     * Create archive and return its path.
6794
     *
6795
     * @param  string  $dir    target dir
6796
     * @param  array   $files  files names list
6797
     * @param  string  $name   archive name
6798
     * @param  array   $arc    archiver options
6799
     * @return string|bool
6800
     * @author Dmitry (dio) Levashov,
6801
     * @author Alexey Sukhotin
6802
     **/
6803
    abstract protected function _archive($dir, $files, $name, $arc);
6804
6805
    /**
6806
     * Detect available archivers.
6807
     *
6808
     * @return void
6809
     * @author Dmitry (dio) Levashov,
6810
     * @author Alexey Sukhotin
6811
     **/
6812
    abstract protected function _checkArchivers();
6813
6814
    /**
6815
     * Change file mode (chmod).
6816
     *
6817
     * @param  string  $path  file path
6818
     * @param  string  $mode  octal string such as '0755'
6819
     * @return bool
6820
     * @author David Bartle,
6821
     **/
6822
    abstract protected function _chmod($path, $mode);
6823
} // END class
6824