Issues (3445)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

ajax/fileManager/elFinderVolumeDriver.class.php (165 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 17 and the first side effect is on line 2.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

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

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

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

// Better
class Router
{
    private $host;

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

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

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

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

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

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

// Better
class Router
{
    private $host;

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

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

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

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

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
687
        // set ARGS
688
        $this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST'? $_POST : $_GET;
689
        // set thumbnails path
690
        $path = $this->options['tmbPath'];
691
        if ($path) {
692
            if (!file_exists($path)) {
693
                if (mkdir($path)) {
694
                    chmod($path, $this->options['tmbPathMode']);
695
                } else {
696
                    $path = '';
697
                }
698
            }
699
700
            if (is_dir($path) && is_readable($path)) {
701
                $this->tmbPath = $path;
702
                $this->tmbPathWritable = is_writable($path);
703
            }
704
        }
705
        // set resouce path
706
        if (! is_dir($this->options['resourcePath'])) {
707
            $this->options['resourcePath'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'resources';
708
        }
709
710
        // set image manipulation library
711
        $type = preg_match('/^(imagick|gd|convert|auto)$/i', $this->options['imgLib'])
712
            ? strtolower($this->options['imgLib'])
713
            : 'auto';
714
715
        $imgLibFallback = extension_loaded('imagick')? 'imagick' : (function_exists('gd_info')? 'gd' : '');
0 ignored issues
show
$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...
716
        if (($type === 'imagick' || $type === 'auto') && extension_loaded('imagick')) {
717
            $this->imgLib = 'imagick';
718
        } else if (($type === 'gd' || $type === 'auto') && function_exists('gd_info')) {
719
            $this->imgLib = 'gd';
720
        } else {
721
            $convertCache = 'imgLibConvert';
722
            if (($convertCmd = $this->session->get($convertCache, false)) !== false) {
723
                $this->imgLib = $convertCmd;
724
            } else {
725
                $this->imgLib = ($this->procExec('convert -version') === 0)? 'convert' : '';
726
                $this->session->set($convertCache, $this->imgLib);
727
            }
728
        }
729
        if ($type !== 'auto' && $this->imgLib === '') {
730
            // fallback
731
            $this->imgLib = extension_loaded('imagick')? 'imagick' : (function_exists('gd_info')? 'gd' : '');
732
        }
733
734
        // check video to img converter
735
        if (! empty($this->options['imgConverter']) && is_array($this->options['imgConverter'])) {
736
            foreach($this->options['imgConverter'] as $_type => $_converter) {
737
                if (isset($_converter['func'])) {
738
                    $this->imgConverter[strtolower($_type)] = $_converter;
739
                }
740
            }
741
        }
742
        if (! isset($this->imgConverter['video'])) {
743
            $videoLibCache = 'videoLib';
744
            if (($videoLibCmd = $this->session->get($videoLibCache, false)) === false) {
745
                $videoLibCmd = ($this->procExec('ffmpeg -version') === 0)? 'ffmpeg' : '';
746
                $this->session->set($videoLibCache, $videoLibCmd);
747
            }
748
            if ($videoLibCmd) {
749
                $this->imgConverter['video'] = array(
750
                    'func' => array($this, $videoLibCmd . 'ToImg'),
751
                    'maxlen' => $this->options['tmbVideoConvLen']
752
                );
753
            }
754
        }
755
756
        // check archivers
757
        if (empty($this->archivers['create'])) {
758
            $this->disabled[] ='archive';
759
        }
760
        if (empty($this->archivers['extract'])) {
761
            $this->disabled[] ='extract';
762
        }
763
        $_arc = $this->getArchivers();
764
        if (empty($_arc['create'])) {
765
            $this->disabled[] ='zipdl';
766
        }
767
768
        // check 'statOwner' for command `chmod`
769
        if (empty($this->options['statOwner'])) {
770
            $this->disabled[] ='chmod';
771
        }
772
773
        // check 'mimeMap'
774
        if (!is_array($this->options['mimeMap'])) {
775
            $this->options['mimeMap'] = array();
776
        }
777
    }
778
779
    /**
780
     * @deprecated
781
     */
782
    protected function sessionRestart() {
783
        $this->sessionCache = $this->session->start()->get($this->id, array());
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...
784
        return true;
785
    }
786
787
    /*********************************************************************/
788
    /*                              PUBLIC API                           */
789
    /*********************************************************************/
790
791
    /**
792
     * Return driver id. Used as a part of volume id.
793
     *
794
     * @return string
795
     * @author Dmitry (dio) Levashov
796
     **/
797
    public function driverId() {
798
        return $this->driverId;
799
    }
800
801
    /**
802
     * Return volume id
803
     *
804
     * @return string
805
     * @author Dmitry (dio) Levashov
806
     **/
807
    public function id() {
808
        return $this->id;
809
    }
810
811
    /**
812
     * Assign elFinder session wrapper object
813
     *
814
     * @param  $session  elFinderSessionInterface
815
     */
816
    public function setSession($session) {
817
        $this->session = $session;
818
    }
819
820
    /**
821
     * Return debug info for client
822
     *
823
     * @return array
824
     * @author Dmitry (dio) Levashov
825
     **/
826
    public function debug() {
827
        return array(
828
            'id'         => $this->id(),
829
            'name'       => strtolower(substr(get_class($this), strlen('elfinderdriver'))),
830
            'mimeDetect' => $this->mimeDetect,
831
            'imgLib'     => $this->imgLib
832
        );
833
    }
834
835
    /**
836
     * chmod a file or folder
837
     *
838
     * @param  string   $hash    file or folder hash to chmod
839
     * @param  string   $mode    octal string representing new permissions
840
     * @return array|false
841
     * @author David Bartle
842
     **/
843
    public function chmod($hash, $mode) {
844
        if ($this->commandDisabled('chmod')) {
845
            return $this->setError(elFinder::ERROR_PERM_DENIED);
846
        }
847
848
        if (!($file = $this->file($hash))) {
849
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
850
        }
851
852
        if (!$this->options['allowChmodReadOnly']) {
853
            if (!$this->attr($this->decode($hash), 'write', null, ($file['mime'] === 'directory'))) {
854
                return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
855
            }
856
        }
857
858
        $path = $this->decode($hash);
859
        $write = $file['write'];
860
861
        if ($this->convEncOut(!$this->_chmod($this->convEncIn($path), $mode))) {
862
            return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
863
        }
864
865
        $this->clearcache();
866
        if ($path == $this->root) {
867
            $this->rootModified = true;
868
        }
869
870
        if ($file = $this->stat($path)) {
871
            $files = array($file);
872
            if ($file['mime'] === 'directory' && $write !== $file['write']) {
873
                foreach ($this->getScandir($path) as $stat) {
874
                    if ($this->mimeAccepted($stat['mime'])) {
875
                        $files[] = $stat;
876
                    }
877
                }
878
            }
879
            return $files;
880
        } else {
881
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
882
        }
883
    }
884
885
    /**
886
     * stat a file or folder for elFinder cmd exec
887
     *
888
     * @param  string   $hash    file or folder hash to chmod
889
     * @return array
890
     * @author Naoki Sawada
891
     **/
892
    public function fstat($hash) {
893
        $path = $this->decode($hash);
894
        return $this->stat($path);
895
    }
896
897
898
    public function clearstatcache() {
899
        clearstatcache();
900
        $this->clearcache();
901
    }
902
903
    /**
904
     * "Mount" volume.
905
     * Return true if volume available for read or write,
906
     * false - otherwise
907
     *
908
     * @param array $opts
909
     * @return bool
910
     * @author Dmitry (dio) Levashov
911
     * @author Alexey Sukhotin
912
     */
913
    public function mount(array $opts) {
914
        $this->options = array_merge($this->options, $opts);
915
916
        if (!isset($this->options['path']) || $this->options['path'] === '') {
917
            return $this->setError('Path undefined.');
918
        }
919
920
        if (! $this->session) {
921
            return $this->setError('Session wrapper dose not set. Need to `$volume->setSession(elFinderSessionInterface);` before mount.');
922
        }
923
        if (! ($this->session instanceof elFinderSessionInterface)) {
924
            return $this->setError('Session wrapper instance must be "elFinderSessionInterface".');
925
        }
926
927
        $this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
928
        $this->root = $this->normpathCE($this->options['path']);
929
        $this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
930
        $this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator;
931
932
        // set server encoding
933
        if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') {
934
            $this->encoding = $this->options['encoding'];
935
        } else {
936
            $this->encoding = null;
937
        }
938
939
        $argInit = !empty($this->ARGS['init']);
940
941
        // session cache
942
        if ($argInit) {
943
            $this->session->set($this->id, array());
944
        }
945
        $this->sessionCache = $this->session->get($this->id, array());
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...
946
947
        // default file attribute
948
        $this->defaults = array(
949
            'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
950
            'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
951
            'locked'  => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false,
952
            'hidden'  => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false
953
        );
954
955
        // root attributes
956
        $this->attributes[] = array(
957
            'pattern' => '~^'.preg_quote($this->separator).'$~',
958
            'locked'  => true,
959
            'hidden'  => false
960
        );
961
        // set files attributes
962
        if (!empty($this->options['attributes']) && is_array($this->options['attributes'])) {
963
964
            foreach ($this->options['attributes'] as $a) {
965
                // attributes must contain pattern and at least one rule
966
                if (!empty($a['pattern']) || count($a) > 1) {
967
                    $this->attributes[] = $a;
968
                }
969
            }
970
        }
971
972
        if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
973
            $this->access = $this->options['accessControl'];
974
        }
975
976
        $this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
977
        $this->yesterday = $this->today-86400;
978
979
        // debug($this->attributes);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
980
        if (!$this->init()) {
981
            return false;
982
        }
983
984
        // check some options is arrays
985
        $this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
986
            ? $this->options['uploadAllow']
987
            : array();
988
989
        $this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
990
            ? $this->options['uploadDeny']
991
            : array();
992
993
        $this->options['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))
994
            ? $this->options['uiCmdMap']
995
            : array();
996
997
        if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
998
            $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
999
            $this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
1000
        } else { // telephat_mode off
1001
            $this->uploadOrder = ! empty($this->options['uploadOrder'])? $this->options['uploadOrder'] : array('deny', 'allow');
1002
        }
1003
1004
        if (!empty($this->options['uploadMaxSize'])) {
1005
            $size = ''.$this->options['uploadMaxSize'];
1006
            $unit = strtolower(substr($size, strlen($size) - 1));
1007
            $n = 1;
1008
            switch ($unit) {
1009
                case 'k':
1010
                    $n = 1024;
1011
                    break;
1012
                case 'm':
1013
                    $n = 1048576;
1014
                    break;
1015
                case 'g':
1016
                    $n = 1073741824;
1017
            }
1018
            $this->uploadMaxSize = intval($size)*$n;
1019
        }
1020
        // Set maximum to PHP_INT_MAX
1021
        if (!defined('PHP_INT_MAX')) {
1022
            define('PHP_INT_MAX', 2147483647);
1023
        }
1024
        if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) {
1025
            $this->uploadMaxSize = PHP_INT_MAX;
1026
        }
1027
1028
        $this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
1029
            ? array_values(array_diff($this->options['disabled'], array('open'))) // 'open' is required
1030
            : array();
1031
1032
        $this->cryptLib   = $this->options['cryptLib'];
1033
        $this->mimeDetect = $this->options['mimeDetect'];
1034
1035
        // find available mimetype detect method
1036
        $type = strtolower($this->options['mimeDetect']);
1037
        $type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
1038
        $regexp = '/text\/x\-(php|c\+\+)/';
1039
1040
        if (($type == 'finfo' || $type == 'auto')
1041
        && class_exists('finfo', false)) {
1042
            $tmpFileInfo = explode(';', finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
1043
        } else {
1044
            $tmpFileInfo = false;
1045
        }
1046
1047
        $type = 'internal';
1048
        if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
1049
            $type = 'finfo';
1050
            $this->finfo = finfo_open(FILEINFO_MIME);
1051
        } elseif (($type == 'mime_content_type' || $type == 'auto') && function_exists('mime_content_type')) {
1052
            $_mimetypes = explode(';', mime_content_type(__FILE__));
1053
            if (preg_match($regexp, array_shift($_mimetypes))) {
1054
                $type = 'mime_content_type';
1055
            }
1056
        }
1057
        $this->mimeDetect = $type;
1058
1059
        // load mimes from external file for mimeDetect == 'internal'
1060
        // based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
1061
        // file must be in file directory or in parent one
1062
        if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
1063
            self::$mimetypesLoaded = true;
1064
            $this->mimeDetect = 'internal';
1065
            $file = false;
1066
            if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
1067
                $file = $this->options['mimefile'];
1068
            } elseif (elFinder::$defaultMimefile && file_exists(elFinder::$defaultMimefile)) {
1069
                $file = elFinder::$defaultMimefile;
1070
            } elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
1071
                $file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
1072
            } elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
1073
                $file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
1074
            }
1075
1076
            if ($file && file_exists($file)) {
1077
                $mimecf = file($file);
1078
1079
                foreach ($mimecf as $line_num => $line) {
1080
                    if (!preg_match('/^\s*#/', $line)) {
1081
                        $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
1082
                        for ($i = 1, $size = count($mime); $i < $size ; $i++) {
1083
                            if (!isset(self::$mimetypes[$mime[$i]])) {
1084
                                self::$mimetypes[$mime[$i]] = $mime[0];
1085
                            }
1086
                        }
1087
                    }
1088
                }
1089
            }
1090
        }
1091
1092
        $this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias'];
1093
1094
        // This get's triggered if $this->root == '/' and alias is empty.
1095
        // Maybe modify _basename instead?
1096
        if ($this->rootName === '') $this->rootName = $this->separator;
1097
1098
        $root = $this->stat($this->root);
1099
1100
        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...
1101
            return $this->setError('Root folder does not exist.');
1102
        }
1103
        if (!$root['read'] && !$root['write']) {
1104
            return $this->setError('Root folder has not read and write permissions.');
1105
        }
1106
1107
        // debug($root);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1108
1109
        if ($root['read']) {
1110
            // check startPath - path to open by default instead of root
1111
            $startPath = $this->options['startPath']? $this->normpathCE($this->options['startPath']) : '';
1112
            if ($startPath) {
1113
                $start = $this->stat($startPath);
1114
                if (!empty($start)
1115
                && $start['mime'] == 'directory'
1116
                && $start['read']
1117
                && empty($start['hidden'])
1118
                && $this->inpathCE($startPath, $this->root)) {
1119
                    $this->startPath = $startPath;
1120
                    if (substr($this->startPath, -1, 1) == $this->options['separator']) {
1121
                        $this->startPath = substr($this->startPath, 0, -1);
1122
                    }
1123
                }
1124
            }
1125
        } else {
1126
            $this->options['URL']     = '';
1127
            $this->options['tmbURL']  = '';
1128
            $this->options['tmbPath'] = '';
1129
            // read only volume
1130
            array_unshift($this->attributes, array(
1131
                'pattern' => '/.*/',
1132
                'read'    => false
1133
            ));
1134
        }
1135
        $this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
1136
        $this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
1137
        $this->URL      = $this->options['URL'];
1138
        if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
1139
            $this->URL .= '/';
1140
        }
1141
        if (strtolower($this->options['dirUrlOwn']) === 'auto') {
1142
            $this->options['dirUrlOwn'] = $this->URL? false : true;
1143
        } else {
1144
            $this->options['dirUrlOwn'] = (bool)$this->options['dirUrlOwn'];
1145
        }
1146
1147
        $this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
1148
        if ($this->tmbURL && $this->tmbURL !== 'self' && preg_match("|[^/?&=]$|", $this->tmbURL)) {
1149
            $this->tmbURL .= '/';
1150
        }
1151
1152
        $this->nameValidator = !empty($this->options['acceptedName']) && (is_string($this->options['acceptedName']) || is_callable($this->options['acceptedName']))
1153
            ? $this->options['acceptedName']
1154
            : '';
1155
1156
        $this->_checkArchivers();
1157
        // manual control archive types to create
1158
        if (!empty($this->options['archiveMimes']) && is_array($this->options['archiveMimes'])) {
1159
            foreach ($this->archivers['create'] as $mime => $v) {
1160
                if (!in_array($mime, $this->options['archiveMimes'])) {
1161
                    unset($this->archivers['create'][$mime]);
1162
                }
1163
            }
1164
        }
1165
1166
        // manualy add archivers
1167
        if (!empty($this->options['archivers']['create']) && is_array($this->options['archivers']['create'])) {
1168
            foreach ($this->options['archivers']['create'] as $mime => $conf) {
1169
                if (strpos($mime, 'application/') === 0
1170
                && !empty($conf['cmd'])
1171
                && isset($conf['argc'])
1172
                && !empty($conf['ext'])
1173
                && !isset($this->archivers['create'][$mime])) {
1174
                    $this->archivers['create'][$mime] = $conf;
1175
                }
1176
            }
1177
        }
1178
1179
        if (!empty($this->options['archivers']['extract']) && is_array($this->options['archivers']['extract'])) {
1180
            foreach ($this->options['archivers']['extract'] as $mime => $conf) {
1181
                if (strpos($mime, 'application/') === 0
1182
                && !empty($conf['cmd'])
1183
                && isset($conf['argc'])
1184
                && !empty($conf['ext'])
1185
                && !isset($this->archivers['extract'][$mime])) {
1186
                    $this->archivers['extract'][$mime] = $conf;
1187
                }
1188
            }
1189
        }
1190
1191
        $this->configure();
1192
1193
        // Normarize disabled (array_merge`for type array of JSON)
1194
        $this->disabled = array_values(array_unique($this->disabled));
1195
1196
        // fix sync interval
1197
        if ($this->options['syncMinMs'] !== 0) {
1198
            $this->options['syncMinMs'] = max($this->options[$this->options['syncChkAsTs']? 'tsPlSleep' : 'lsPlSleep'] * 1000, intval($this->options['syncMinMs']));
1199
        }
1200
1201
        return $this->mounted = true;
1202
    }
1203
1204
    /**
1205
     * Some "unmount" stuffs - may be required by virtual fs
1206
     *
1207
     * @return void
1208
     * @author Dmitry (dio) Levashov
1209
     **/
1210
    public function umount() {
1211
    }
1212
1213
    /**
1214
     * Return error message from last failed action
1215
     *
1216
     * @return array
1217
     * @author Dmitry (dio) Levashov
1218
     **/
1219
    public function error() {
1220
        return $this->error;
1221
    }
1222
1223
    /**
1224
     * Return is uploadable that given file name
1225
     *
1226
     * @param  string  $name  file name
1227
     * @param  bool    $allowUnknown
1228
     * @return bool
1229
     * @author Naoki Sawada
1230
     **/
1231
    public function isUploadableByName($name, $allowUnknown = true) {
1232
        $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1233
        return (($allowUnknown && $mimeByName === 'unknown') || $this->allowPutMime($mimeByName));
1234
    }
1235
1236
    /**
1237
     * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes)
1238
     *
1239
     * @return array
1240
     * @author Naoki Sawada
1241
     */
1242
    public function getMimeTable() {
1243
        // load mime.types
1244
        ! elFinderVolumeDriver::$mimetypesLoaded && elFinderVolumeDriver::mimetypeInternalDetect();
1245
        return elFinderVolumeDriver::$mimetypes;
1246
    }
1247
1248
    /**
1249
     * Return file extention detected by MIME type
1250
     *
1251
     * @param  string  $mime    MIME type
1252
     * @param  string  $suffix  Additional suffix
1253
     * @return string
1254
     * @author Naoki Sawada
1255
     */
1256
    public function getExtentionByMime($mime, $suffix = '') {
1257
        static $extTable = null;
1258
1259
        if (is_null($extTable)) {
1260
            $extTable = array_flip(array_unique($this->getMimeTable()));
1261
            foreach(array_keys($this->options['mimeMap']) as $pair) {
1262
                list($ext, $_mime) = explode(':', $pair);
1263
                if (! isset($extTable[$_mime])) {
1264
                    $extTable[$_mime] = $ext;
1265
                }
1266
            }
1267
        }
1268
1269
        if ($mime && isset($extTable[$mime])) {
1270
            return $suffix? ($extTable[$mime] . $suffix) : $extTable[$mime];
1271
        }
1272
        return '';
1273
    }
1274
1275
    /**
1276
     * Set mimetypes allowed to display to client
1277
     *
1278
     * @param  array  $mimes
1279
     * @return void
1280
     * @author Dmitry (dio) Levashov
1281
     **/
1282
    public function setMimesFilter($mimes) {
1283
        if (is_array($mimes)) {
1284
            $this->onlyMimes = $mimes;
1285
        }
1286
    }
1287
1288
    /**
1289
     * Return root folder hash
1290
     *
1291
     * @return string
1292
     * @author Dmitry (dio) Levashov
1293
     **/
1294
    public function root() {
1295
        return $this->encode($this->root);
1296
    }
1297
1298
    /**
1299
     * Return target path hash
1300
     *
1301
     * @param  string $path
1302
     * @param  string $name
1303
     * @author Naoki Sawada
1304
     * @return string
1305
     */
1306
    public function getHash($path, $name = '') {
1307
        if ($name !== '') {
1308
            $path = $this->joinPathCE($path, $name);
1309
        }
1310
        return $this->encode($path);
1311
    }
1312
1313
    /**
1314
     * Return decoded path of target hash
1315
     * This method do not check the stat of target
1316
     * Use method `realpath()` to do check of the stat of target
1317
     *
1318
     * @param  string $hash
1319
     * @author Naoki Sawada
1320
     * @return string
1321
     */
1322
    public function getPath($hash) {
1323
        return $this->decode($hash);
1324
    }
1325
1326
    /**
1327
     * Return root or startPath hash
1328
     *
1329
     * @return string
1330
     * @author Dmitry (dio) Levashov
1331
     **/
1332
    public function defaultPath() {
1333
        return $this->encode($this->startPath ? $this->startPath : $this->root);
1334
    }
1335
1336
    /**
1337
     * Return volume options required by client:
1338
     *
1339
     * @param $hash
1340
     * @return array
1341
     * @author Dmitry (dio) Levashov
1342
     */
1343
    public function options($hash) {
1344
        $create = $createext = array();
1345
        if (isset($this->archivers['create']) && is_array($this->archivers['create'])) {
1346
            foreach($this->archivers['create'] as $m => $v) {
1347
                $create[] = $m;
1348
                $createext[$m] = $v['ext'];
1349
            }
1350
        }
1351
        $opts = array(
1352
            'path'            => $hash? $this->path($hash) : '',
1353
            'url'             => $this->URL,
1354
            'tmbUrl'          => (! $this->imgLib && $this->options['tmbFbSelf'])? 'self' : $this->tmbURL,
1355
            'disabled'        => $this->disabled,
1356
            'separator'       => $this->separator,
1357
            'copyOverwrite'   => intval($this->options['copyOverwrite']),
1358
            'uploadOverwrite' => intval($this->options['uploadOverwrite']),
1359
            'uploadMaxSize'   => intval($this->uploadMaxSize),
1360
            'uploadMaxConn'   => intval($this->options['uploadMaxConn']),
1361
            'uploadMime'      => array(
1362
                'firstOrder' => isset($this->uploadOrder[0])? $this->uploadOrder[0] : 'deny',
1363
                'allow'      => $this->uploadAllow,
1364
                'deny'       => $this->uploadDeny
1365
            ),
1366
            'dispInlineRegex' => $this->options['dispInlineRegex'],
1367
            'jpgQuality'      => intval($this->options['jpgQuality']),
1368
            'archivers'       => array(
1369
                'create'    => $create,
1370
                'extract'   => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(),
1371
                'createext' => $createext
1372
            ),
1373
            'uiCmdMap'        => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array(),
1374
            'syncChkAsTs'     => intval($this->options['syncChkAsTs']),
1375
            'syncMinMs'       => intval($this->options['syncMinMs']),
1376
            'i18nFolderName'  => intval($this->options['i18nFolderName'])
1377
        );
1378
        if ($hash === null) {
1379
            // call from getRootStatExtra()
1380
            if (! empty($this->options['icon'])) {
1381
                $opts['icon'] = $this->options['icon'];
1382
            }
1383
            if (! empty($this->options['rootCssClass'])) {
1384
                $opts['csscls'] = $this->options['rootCssClass'];
1385
            }
1386
            if (isset($this->options['netkey'])) {
1387
                $opts['netkey'] = $this->options['netkey'];
1388
            }
1389
        }
1390
        return $opts;
1391
    }
1392
1393
    /**
1394
     * Get option value of this volume
1395
     *
1396
     * @param string $name  target option name
1397
     * @return NULL|mixed   target option value
1398
     * @author Naoki Sawada
1399
     */
1400
    public function getOption($name) {
1401
        return isset($this->options[$name])? $this->options[$name] : null;
1402
    }
1403
1404
    /**
1405
     * Get plugin values of this options
1406
     *
1407
     * @param string $name  Plugin name
1408
     * @return NULL|array   Plugin values
1409
     * @author Naoki Sawada
1410
     */
1411
    public function getOptionsPlugin($name = '') {
1412
        if ($name) {
1413
            return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array();
1414
        } else {
1415
            return $this->options['plugin'];
1416
        }
1417
    }
1418
1419
    /**
1420
     * Return true if command disabled in options
1421
     *
1422
     * @param  string  $cmd  command name
1423
     * @return bool
1424
     * @author Dmitry (dio) Levashov
1425
     **/
1426
    public function commandDisabled($cmd) {
1427
        return in_array($cmd, $this->disabled);
1428
    }
1429
1430
    /**
1431
     * Return true if mime is required mimes list
1432
     *
1433
     * @param  string     $mime   mime type to check
1434
     * @param  array      $mimes  allowed mime types list or not set to use client mimes list
1435
     * @param  bool|null  $empty  what to return on empty list
1436
     * @return bool|null
1437
     * @author Dmitry (dio) Levashov
1438
     * @author Troex Nevelin
1439
     **/
1440
    public function mimeAccepted($mime, $mimes = null, $empty = true) {
1441
        $mimes = is_array($mimes) ? $mimes : $this->onlyMimes;
1442
        if (empty($mimes)) {
1443
            return $empty;
1444
        }
1445
        return $mime == 'directory'
1446
            || in_array('all', $mimes)
1447
            || in_array('All', $mimes)
1448
            || in_array($mime, $mimes)
1449
            || in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
1450
    }
1451
1452
    /**
1453
     * Return true if voume is readable.
1454
     *
1455
     * @return bool
1456
     * @author Dmitry (dio) Levashov
1457
     **/
1458
    public function isReadable() {
1459
        $stat = $this->stat($this->root);
1460
        return $stat['read'];
1461
    }
1462
1463
    /**
1464
     * Return true if copy from this volume allowed
1465
     *
1466
     * @return bool
1467
     * @author Dmitry (dio) Levashov
1468
     **/
1469
    public function copyFromAllowed() {
1470
        return !!$this->options['copyFrom'];
1471
    }
1472
1473
    /**
1474
     * Return file path related to root with convert encoging
1475
     *
1476
     * @param  string   $hash  file hash
1477
     * @return string
1478
     * @author Dmitry (dio) Levashov
1479
     **/
1480
    public function path($hash) {
1481
        return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash))));
1482
    }
1483
1484
    /**
1485
     * Return file real path if file exists
1486
     *
1487
     * @param  string  $hash  file hash
1488
     * @return string | false
1489
     * @author Dmitry (dio) Levashov
1490
     **/
1491
    public function realpath($hash) {
1492
        $path = $this->decode($hash);
1493
        return $this->stat($path) ? $path : false;
1494
    }
1495
1496
    /**
1497
     * Return list of moved/overwrited files
1498
     *
1499
     * @return array
1500
     * @author Dmitry (dio) Levashov
1501
     **/
1502
    public function removed() {
1503
        return $this->removed;
1504
    }
1505
1506
    /**
1507
     * Return list of added files
1508
     *
1509
     * @deprecated
1510
     * @return array
1511
     * @author Naoki Sawada
1512
     **/
1513
    public function added() {
1514
        return $this->added;
1515
    }
1516
1517
    /**
1518
     * Clean removed files list
1519
     *
1520
     * @return void
1521
     * @author Dmitry (dio) Levashov
1522
     **/
1523
    public function resetRemoved() {
1524
        $this->resetResultStat();
1525
    }
1526
1527
    /**
1528
     * Clean added/removed files list
1529
     *
1530
     * @return void
1531
     **/
1532
    public function resetResultStat() {
1533
        $this->removed = array();
1534
        $this->added = array();
1535
    }
1536
1537
    /**
1538
     * Return file/dir hash or first founded child hash with required attr == $val
1539
     *
1540
     * @param  string   $hash  file hash
1541
     * @param  string   $attr  attribute name
1542
     * @param  bool     $val   attribute value
1543
     * @return string|false
1544
     * @author Dmitry (dio) Levashov
1545
     **/
1546
    public function closest($hash, $attr, $val) {
1547
        return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1548
    }
1549
1550
    /**
1551
     * Return file info or false on error
1552
     *
1553
     * @param  string $hash file hash
1554
     * @return array|false
1555
     * @internal param bool $realpath add realpath field to file info
1556
     * @author Dmitry (dio) Levashov
1557
     */
1558
    public function file($hash) {
1559
        $path = $this->decode($hash);
1560
        $isRoot = ($path == $this->root);
1561
1562
        $file = $this->stat($path);
1563
1564
        if ($isRoot) {
1565
            $file = array_merge($file, $this->getRootStatExtra());
1566
        }
1567
1568
        return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1569
    }
1570
1571
    /**
1572
     * Return folder info
1573
     *
1574
     * @param  string $hash folder hash
1575
     * @param bool $resolveLink
1576
     * @return array|false
1577
     * @internal param bool $hidden return hidden file info
1578
     * @author Dmitry (dio) Levashov
1579
     */
1580
    public function dir($hash, $resolveLink=false) {
1581
        if (($dir = $this->file($hash)) == false) {
1582
            return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1583
        }
1584
1585
        if ($resolveLink && !empty($dir['thash'])) {
1586
            $dir = $this->file($dir['thash']);
1587
        }
1588
1589
        return $dir && $dir['mime'] == 'directory' && empty($dir['hidden'])
1590
            ? $dir
1591
            : $this->setError(elFinder::ERROR_NOT_DIR);
1592
    }
1593
1594
    /**
1595
     * Return directory content or false on error
1596
     *
1597
     * @param  string   $hash   file hash
1598
     * @return array|false
1599
     * @author Dmitry (dio) Levashov
1600
     **/
1601
    public function scandir($hash) {
1602
        if (($dir = $this->dir($hash)) == false) {
1603
            return false;
1604
        }
1605
1606
        return $dir['read']
1607
            ? $this->getScandir($this->decode($hash))
1608
            : $this->setError(elFinder::ERROR_PERM_DENIED);
1609
    }
1610
1611
    /**
1612
     * Return dir files names list
1613
     *
1614
     * @param  string $hash file hash
1615
     * @param null $intersect
1616
     * @return array
1617
     * @author Dmitry (dio) Levashov
1618
     */
1619
    public function ls($hash, $intersect = null) {
1620
        if (($dir = $this->dir($hash)) == false || !$dir['read']) {
1621
            return false;
1622
        }
1623
1624
        $list = array();
1625
        $path = $this->decode($hash);
1626
1627
        $check = array();
1628
        if ($intersect) {
1629
            $check = array_flip($intersect);
1630
        }
1631
1632
        foreach ($this->getScandir($path) as $stat) {
1633
            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...
1634
                $list[$stat['hash']] = $stat['name'];
1635
            }
1636
        }
1637
1638
        return $list;
1639
    }
1640
1641
    /**
1642
     * Return subfolders for required folder or false on error
1643
     *
1644
     * @param  string   $hash  folder hash or empty string to get tree from root folder
1645
     * @param  int      $deep  subdir deep
1646
     * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1647
     * @return array|false
1648
     * @author Dmitry (dio) Levashov
1649
     **/
1650
    public function tree($hash='', $deep=0, $exclude='') {
1651
        $path = $hash ? $this->decode($hash) : $this->root;
1652
1653
        if (($dir = $this->stat($path)) == false || $dir['mime'] != 'directory') {
1654
            return false;
1655
        }
1656
1657
        $dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1658
        array_unshift($dirs, $dir);
1659
        return $dirs;
1660
    }
1661
1662
    /**
1663
     * Return part of dirs tree from required dir up to root dir
1664
     *
1665
     * @param  string    $hash   directory hash
1666
     * @param  bool|null $lineal only lineal parents
1667
     * @return array
1668
     * @author Dmitry (dio) Levashov
1669
     **/
1670
    public function parents($hash, $lineal = false) {
1671
        if (($current = $this->dir($hash)) == false) {
1672
            return false;
1673
        }
1674
1675
        $path = $this->decode($hash);
1676
        $tree = array();
1677
1678
        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...
1679
            $path = $this->dirnameCE($path);
1680
            if (!($stat = $this->stat($path)) || !empty($stat['hidden']) || !$stat['read']) {
1681
                return false;
1682
            }
1683
1684
            array_unshift($tree, $stat);
1685
            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...
1686
                foreach ($this->gettree($path, 0) as $dir) {
1687
                    if (!in_array($dir, $tree)) {
1688
                        $tree[] = $dir;
1689
                    }
1690
                }
1691
            }
1692
        }
1693
1694
        return $tree ? $tree : array($current);
1695
    }
1696
1697
    /**
1698
     * Create thumbnail for required file and return its name of false on failed
1699
     *
1700
     * @param $hash
1701
     * @return false|string
1702
     * @author Dmitry (dio) Levashov
1703
     */
1704
    public function tmb($hash) {
1705
        $path = $this->decode($hash);
1706
        $stat = $this->stat($path);
1707
1708
        if (isset($stat['tmb'])) {
1709
            $res = $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1710
            if (! $res) {
1711
                list($type) = explode('/', $stat['mime']);
1712
                $fallback = $this->options['resourcePath'] . DIRECTORY_SEPARATOR . strtolower($type) . '.png';
1713
                if (is_file($fallback)) {
1714
                    $res = $this->tmbname($stat);
1715
                    if (! copy($fallback, $this->tmbPath . DIRECTORY_SEPARATOR . $res)) {
1716
                        $res = false;
1717
                    }
1718
                }
1719
            }
1720
            return $res;
1721
        }
1722
        return false;
1723
    }
1724
1725
    /**
1726
     * Return file size / total directory size
1727
     *
1728
     * @param  string   file hash
1729
     * @return int
1730
     * @author Dmitry (dio) Levashov
1731
     **/
1732
    public function size($hash) {
1733
        return $this->countSize($this->decode($hash));
1734
    }
1735
1736
    /**
1737
     * Open file for reading and return file pointer
1738
     *
1739
     * @param  string   file hash
1740
     * @return Resource
1741
     * @author Dmitry (dio) Levashov
1742
     **/
1743
    public function open($hash) {
1744
        if (($file = $this->file($hash)) == false
1745
        || $file['mime'] == 'directory') {
1746
            return false;
1747
        }
1748
1749
        return $this->fopenCE($this->decode($hash), 'rb');
1750
    }
1751
1752
    /**
1753
     * Close file pointer
1754
     *
1755
     * @param  Resource  $fp   file pointer
1756
     * @param  string    $hash file hash
1757
     * @return void
1758
     * @author Dmitry (dio) Levashov
1759
     **/
1760
    public function close($fp, $hash) {
1761
        $this->fcloseCE($fp, $this->decode($hash));
1762
    }
1763
1764
    /**
1765
     * Create directory and return dir info
1766
     *
1767
     * @param  string   $dsthash  destination directory hash
1768
     * @param  string   $name directory name
1769
     * @return array|false
1770
     * @author Dmitry (dio) Levashov
1771
     **/
1772
    public function mkdir($dsthash, $name) {
1773
        if ($this->commandDisabled('mkdir')) {
1774
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1775
        }
1776
1777
        if (!$this->nameAccepted($name)) {
1778
            return $this->setError(elFinder::ERROR_INVALID_NAME);
1779
        }
1780
1781
        if (($dir = $this->dir($dsthash)) == false) {
1782
            return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash);
1783
        }
1784
1785
        $path = $this->decode($dsthash);
1786
1787
        if (!$dir['write'] || !$this->allowCreate($path, $name, true)) {
1788
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1789
        }
1790
1791
        $dst  = $this->joinPathCE($path, $name);
1792
        $stat = $this->stat($dst);
1793
        if (!empty($stat)) {
1794
            return $this->setError(elFinder::ERROR_EXISTS, $name);
1795
        }
1796
        $this->clearcache();
1797
        return ($path = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1798
    }
1799
1800
    /**
1801
     * Create empty file and return its info
1802
     *
1803
     * @param  string   $dst  destination directory
1804
     * @param  string   $name file name
1805
     * @return array|false
1806
     * @author Dmitry (dio) Levashov
1807
     **/
1808
    public function mkfile($dst, $name) {
1809
        if ($this->commandDisabled('mkfile')) {
1810
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1811
        }
1812
1813
        if (!$this->nameAccepted($name)) {
1814
            return $this->setError(elFinder::ERROR_INVALID_NAME);
1815
        }
1816
1817
        $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1818
        if ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName)) {
1819
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name);
1820
        }
1821
1822
        if (($dir = $this->dir($dst)) == false) {
1823
            return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1824
        }
1825
1826
        $path = $this->decode($dst);
1827
1828
        if (!$dir['write'] || !$this->allowCreate($path, $name, false)) {
1829
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1830
        }
1831
1832
        if ($this->stat($this->joinPathCE($path, $name))) {
1833
            return $this->setError(elFinder::ERROR_EXISTS, $name);
1834
        }
1835
1836
        $this->clearcache();
1837
        return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1838
    }
1839
1840
    /**
1841
     * Rename file and return file info
1842
     *
1843
     * @param  string  $hash  file hash
1844
     * @param  string  $name  new file name
1845
     * @return array|false
1846
     * @author Dmitry (dio) Levashov
1847
     **/
1848
    public function rename($hash, $name) {
1849
        if ($this->commandDisabled('rename')) {
1850
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1851
        }
1852
1853
        if (!$this->nameAccepted($name)) {
1854
            return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1855
        }
1856
1857
        $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1858
        if ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName)) {
1859
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name);
1860
        }
1861
1862
        if (!($file = $this->file($hash))) {
1863
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1864
        }
1865
1866
        if ($name === $file['name']) {
1867
            return $file;
1868
        }
1869
1870
        if (!empty($file['locked'])) {
1871
            return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1872
        }
1873
1874
        $path = $this->decode($hash);
1875
        $dir  = $this->dirnameCE($path);
1876
        $stat = $this->stat($this->joinPathCE($dir, $name));
1877
        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...
1878
            return $this->setError(elFinder::ERROR_EXISTS, $name);
1879
        }
1880
1881
        if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) {
1882
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1883
        }
1884
1885
        $this->rmTmb($file); // remove old name tmbs, we cannot do this after dir move
0 ignored issues
show
$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...
1886
1887
1888
        if ($path = $this->convEncOut($this->_move($this->convEncIn($path), $this->convEncIn($dir), $this->convEncIn($name)))) {
1889
            $this->clearcache();
1890
            return $this->stat($path);
1891
        }
1892
        return false;
1893
    }
1894
1895
    /**
1896
     * Create file copy with suffix "copy number" and return its info
1897
     *
1898
     * @param  string   $hash    file hash
1899
     * @param  string   $suffix  suffix to add to file name
1900
     * @return array|false
1901
     * @author Dmitry (dio) Levashov
1902
     **/
1903
    public function duplicate($hash, $suffix='copy') {
1904
        if ($this->commandDisabled('duplicate')) {
1905
            return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1906
        }
1907
1908
        if (($file = $this->file($hash)) == false) {
1909
            return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1910
        }
1911
1912
        $path = $this->decode($hash);
1913
        $dir  = $this->dirnameCE($path);
1914
        $name = $this->uniqueName($dir, $file['name'], sprintf($this->options['duplicateSuffix'], $suffix));
1915
1916
        if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) {
1917
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1918
        }
1919
1920
        return ($path = $this->copy($path, $dir, $name)) == false
1921
            ? false
1922
            : $this->stat($path);
0 ignored issues
show
It seems like $path defined by $this->copy($path, $dir, $name) on line 1920 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...
1923
    }
1924
1925
    /**
1926
     * Save uploaded file.
1927
     * On success return array with new file stat and with removed file hash (if existed file was replaced)
1928
     *
1929
     * @param  Resource $fp file pointer
1930
     * @param  string $dst destination folder hash
1931
     * @param $name
1932
     * @param  string $tmpname file tmp name - required to detect mime type
1933
     * @param  array $hashes exists files hash array with filename as key
1934
     * @return array|false
1935
     * @internal param string $src file name
1936
     * @author Dmitry (dio) Levashov
1937
     */
1938
    public function upload($fp, $dst, $name, $tmpname, $hashes = array()) {
0 ignored issues
show
upload uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1939
        if ($this->commandDisabled('upload')) {
1940
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1941
        }
1942
1943
        if (($dir = $this->dir($dst)) == false) {
1944
            return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1945
        }
1946
1947
        if (!$dir['write']) {
1948
            return $this->setError(elFinder::ERROR_PERM_DENIED);
1949
        }
1950
1951
        if (!$this->nameAccepted($name)) {
1952
            return $this->setError(elFinder::ERROR_INVALID_NAME);
1953
        }
1954
1955
        $mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name);
1956
        $mimeByName = '';
1957
        if ($this->mimeDetect !== 'internal') {
1958
            $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1959
            if ($mime == 'unknown') {
1960
                $mime = $mimeByName;
1961
            }
1962
        }
1963
1964
        if (!$this->allowPutMime($mime) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
1965
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1966
        }
1967
1968
        $tmpsize = sprintf('%u', filesize($tmpname));
1969
        if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) {
1970
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1971
        }
1972
1973
        $dstpath = $this->decode($dst);
1974
        if (isset($hashes[$name])) {
1975
            $test = $this->decode($hashes[$name]);
1976
        } else {
1977
            $test = $this->joinPathCE($dstpath, $name);
1978
        }
1979
1980
        $file = $this->stat($test);
1981
        $this->clearcache();
1982
1983
        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...
1984
            // check POST data `overwrite` for 3rd party uploader
1985
            $overwrite = isset($_POST['overwrite'])? (bool)$_POST['overwrite'] : $this->options['uploadOverwrite'];
1986
            if ($overwrite) {
1987
                if (!$file['write']) {
1988
                    return $this->setError(elFinder::ERROR_PERM_DENIED);
1989
                } elseif ($file['mime'] == 'directory') {
1990
                    return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1991
                }
1992
                $this->remove($test);
1993
            } else {
1994
                $name = $this->uniqueName($dstpath, $name, '-', false);
1995
            }
1996
        }
1997
1998
        $stat = array(
1999
            'mime'   => $mime,
2000
            'width'  => 0,
2001
            'height' => 0,
2002
            'size'   => $tmpsize);
2003
2004
        // $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...
2005
        if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
2006
            $stat['width'] = $s[0];
2007
            $stat['height'] = $s[1];
2008
        }
2009
        // $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...
2010
        if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) {
2011
            return false;
2012
        }
2013
2014
        $stat = $this->stat($path);
0 ignored issues
show
It seems like $path defined by $this->saveCE($fp, $dstpath, $name, $stat) on line 2010 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...
2015
        // Try get URL
2016
        if (empty($stat['url']) && ($url = $this->getContentUrl($stat['hash']))) {
2017
            $stat['url'] = $url;
2018
        }
2019
2020
        return $stat;
2021
    }
2022
2023
    /**
2024
     * Paste files
2025
     *
2026
     * @param  Object $volume source volume
2027
     * @param $src
2028
     * @param  string $dst destination dir hash
2029
     * @param  bool $rmSrc remove source after copy?
2030
     * @param array $hashes
2031
     * @return array|false
2032
     * @internal param string $source file hash
2033
     * @author Dmitry (dio) Levashov
2034
     */
2035
    public function paste($volume, $src, $dst, $rmSrc = false, $hashes = array()) {
2036
        $err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
2037
2038
        if ($this->commandDisabled('paste')) {
2039
            return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
2040
        }
2041
2042
        if (($file = $volume->file($src, $rmSrc)) == false) {
2043
            return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
2044
        }
2045
2046
        $name = $file['name'];
2047
        $errpath = $volume->path($file['hash']);
2048
2049
        if (($dir = $this->dir($dst)) == false) {
2050
            return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
2051
        }
2052
2053
        if (!$dir['write'] || !$file['read']) {
2054
            return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
2055
        }
2056
2057
        $destination = $this->decode($dst);
2058
2059
        if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
2060
            return $rmSrc
2061
                ? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
2062
                : $this->setError($err, $errpath, !empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK);
2063
        }
2064
2065
        if (isset($hashes[$name])) {
2066
            $test = $this->decode($hashes[$name]);
2067
        } else {
2068
            $test = $this->joinPathCE($destination, $name);
2069
        }
2070
        $stat = $this->stat($test);
2071
        $this->clearcache();
2072
        $dstDirExists = false;
2073
        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...
2074
            if ($this->options['copyOverwrite']) {
2075
                // do not replace file with dir or dir with file
2076
                if (!$this->isSameType($file['mime'], $stat['mime'])) {
2077
                    return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash']));
2078
                }
2079
                // existed file is not writable
2080
                if (!$stat['write']) {
2081
                    return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
2082
                }
2083
                // existed file locked or has locked child
2084
                if (($locked = $this->closestByAttr($test, 'locked', true))) {
2085
                    $stat = $this->stat($locked);
2086
                    return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
2087
                }
2088
                // target is entity file of alias
2089
                if ($volume === $this && ((isset($file['target']) && $test == $file['target']) || $test == $this->decode($src))) {
2090
                    return $this->setError(elFinder::ERROR_REPLACE, $errpath);
2091
                }
2092
                // remove existed file
2093
                if (! $this->options['copyJoin'] || $stat['mime'] !== 'directory') {
2094
                    if (! $this->remove($test)) {
2095
                        return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash']));
2096
                    }
2097
                } else if ($stat['mime'] === 'directory') {
2098
                    $dstDirExists = true;
2099
                }
2100
            } else {
2101
                $name = $this->uniqueName($destination, $name, ' ', false);
2102
            }
2103
        }
2104
2105
        // copy/move inside current volume
2106
        if ($volume === $this) { //  changing == operand to === fixes issue #1285 - Paul Canning 24/03/2016
2107
            $source = $this->decode($src);
2108
            // do not copy into itself
2109
            if ($this->inpathCE($destination, $source)) {
2110
                return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
2111
            }
2112
            $rmDir = false;
2113
            if ($rmSrc) {
2114
                if ($dstDirExists) {
2115
                    $rmDir = true;
2116
                    $method = 'copy';
2117
                } else {
2118
                    $method = 'move';
2119
                }
2120
            } else {
2121
                $method = 'copy';
2122
            }
2123
            $this->clearcache();
2124
            if ($res = ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false) {
2125
                if ($rmDir) {
2126
                    $this->remove($source);
2127
                }
2128
            } else {
2129
                return false;
2130
            }
2131
        } else {
2132
            // copy/move from another volume
2133
            if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
2134
                return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
2135
            }
2136
2137
            if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
2138
                return false;
2139
            }
2140
2141
            if ($rmSrc) {
2142
                if (!$volume->rm($src)) {
2143
                    return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
2144
                }
2145
            }
2146
            $res = $this->stat($path);
0 ignored issues
show
It seems like $path defined by $this->copyFrom($volume,...c, $destination, $name) on line 2137 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...
2147
        }
2148
        return $res;
2149
    }
2150
2151
    /**
2152
     * Return path to archive of target items
2153
     *
2154
     * @param  array  $hashes
2155
     * @return string archive path
2156
     * @author Naoki Sawada
2157
     */
2158
    public function zipdl($hashes) {
2159
        if ($this->commandDisabled('zipdl')) {
2160
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2161
        }
2162
2163
        $archivers = $this->getArchivers();
2164
        $cmd = null;
2165
        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...
2166
            return false;
2167
        }
2168
        $archivers = $archivers['create'];
2169
        foreach(array('zip', 'tgz') as $ext) {
2170
            $mime = self::$mimetypes[$ext];
2171
            if (isset($archivers[$mime])) {
2172
                $cmd = $archivers[$mime];
2173
                break;
2174
            }
2175
        }
2176
        if (!$cmd) {
2177
            $cmd = $archivers[0];
2178
            $ext = $cmd['ext'];
2179
            $mime = elFinderVolumeDriver::mimetypeInternalDetect('file.'.$ext);
2180
        }
2181
        $res = false;
2182
        $mixed = false;
2183
        $hashes = array_values($hashes);
2184
        $dirname = dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[0])));
2185
        $cnt = count($hashes);
2186
        if ($cnt > 1) {
2187
            for($i = 1; $i < $cnt; $i++) {
2188
                if ($dirname !== dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[$i])))) {
2189
                    $mixed = true;
2190
                    break;
2191
                }
2192
            }
2193
        }
2194
        if ($mixed || $this->root == $this->dirnameCE($this->decode($hashes[0]))) {
2195
            $prefix = $this->rootName;
2196
        } else {
2197
            $prefix = basename($dirname);
2198
        }
2199
        if ($dir = $this->getItemsInHand($hashes)) {
2200
            $tmppre = (substr(PHP_OS, 0, 3) === 'WIN')? 'zdl' : 'elfzdl';
2201
            $pdir = dirname($dir);
2202
            // garbage collection
2203
            $ttl = 7200; // expire 2h
2204
            $time = time();
2205
            foreach(glob($pdir.DIRECTORY_SEPARATOR.$tmppre.'*') as $_file) {
2206
                if (filemtime($_file) + $ttl < $time) {
2207
                    unlink($_file);
2208
                }
2209
            }
2210
            $files = self::localScandir($dir);
2211
            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...
2212
                unlink($arc);
2213
                $arc = $arc.'.'.$ext;
0 ignored issues
show
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...
2214
                $name = basename($arc);
2215
                if ($arc = $this->makeArchive($dir, $files, $name, $cmd)) {
2216
                    $file = tempnam($pdir, $tmppre);
2217
                    unlink($file);
2218
                    $res = rename($arc, $file);
2219
                    $this->rmdirRecursive($dir);
2220
                }
2221
            }
2222
        }
2223
        return $res? array('path' => $file, 'ext' => $ext, 'mime' => $mime, 'prefix' => $prefix) : false;
0 ignored issues
show
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...
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...
2224
    }
2225
2226
    /**
2227
     * Return file contents
2228
     *
2229
     * @param  string  $hash  file hash
2230
     * @return string|false
2231
     * @author Dmitry (dio) Levashov
2232
     **/
2233
    public function getContents($hash) {
2234
        $file = $this->file($hash);
2235
2236
        if (!$file) {
2237
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2238
        }
2239
2240
        if ($file['mime'] == 'directory') {
2241
            return $this->setError(elFinder::ERROR_NOT_FILE);
2242
        }
2243
2244
        if (!$file['read']) {
2245
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2246
        }
2247
2248
        return $this->convEncOut($this->_getContents($this->convEncIn($this->decode($hash))));
2249
    }
2250
2251
    /**
2252
     * Put content in text file and return file info.
2253
     *
2254
     * @param  string  $hash     file hash
2255
     * @param  string  $content  new file content
2256
     * @return array
2257
     * @author Dmitry (dio) Levashov
2258
     **/
2259
    public function putContents($hash, $content) {
2260
        if ($this->commandDisabled('edit')) {
2261
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2262
        }
2263
2264
        $path = $this->decode($hash);
2265
2266
        if (!($file = $this->file($hash))) {
2267
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2268
        }
2269
2270
        if (!$file['write']) {
2271
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2272
        }
2273
2274
        // check MIME
2275
        $name = $this->basenameCE($path);
2276
        $mime = '';
2277
        $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
2278
        if ($this->mimeDetect !== 'internal') {
2279
            if ($tp = tmpfile()) {
2280
                fwrite($tp, $content);
2281
                $info = stream_get_meta_data($tp);
2282
                $filepath = $info['uri'];
2283
                $mime = $this->mimetype($filepath, $name);
2284
                fclose($tp);
2285
            }
2286
        }
2287
        if (!$this->allowPutMime($mimeByName) || ($mime && $mime !== 'unknown' && !$this->allowPutMime($mime))) {
2288
            return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
2289
        }
2290
2291
        $this->clearcache();
2292
        return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false;
2293
    }
2294
2295
    /**
2296
     * Extract files from archive
2297
     *
2298
     * @param  string $hash archive hash
2299
     * @param null $makedir
2300
     * @return array|bool
2301
     * @author Dmitry (dio) Levashov,
2302
     * @author Alexey Sukhotin
2303
     */
2304
    public function extract($hash, $makedir = null) {
2305
        if ($this->commandDisabled('extract')) {
2306
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2307
        }
2308
2309
        if (($file = $this->file($hash)) == false) {
2310
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2311
        }
2312
2313
        $archiver = isset($this->archivers['extract'][$file['mime']])
2314
            ? $this->archivers['extract'][$file['mime']]
2315
            : false;
2316
2317
        if (!$archiver) {
2318
            return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
2319
        }
2320
2321
        $path   = $this->decode($hash);
2322
        $parent = $this->stat($this->dirnameCE($path));
2323
2324
        if (!$file['read'] || !$parent['write']) {
2325
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2326
        }
2327
        $this->clearcache();
2328
        $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...
2329
2330
        if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) {
2331
            if (is_array($path)) {
2332
                foreach ($path as $_k => $_p) {
2333
                    $path[$_k] = $this->stat($_p);
2334
                }
2335
            } else {
2336
                $path = $this->stat($path);
2337
            }
2338
            return $path;
2339
        } else {
2340
            return false;
2341
        }
2342
    }
2343
2344
    /**
2345
     * Add files to archive
2346
     *
2347
     * @param $hashes
2348
     * @param $mime
2349
     * @param string $name
2350
     * @return array|bool
2351
     */
2352
    public function archive($hashes, $mime, $name = '') {
2353
        if ($this->commandDisabled('archive')) {
2354
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2355
        }
2356
2357
        if ($name !== '' && !$this->nameAccepted($name)) {
2358
            return $this->setError(elFinder::ERROR_INVALID_NAME);
2359
        }
2360
2361
        $archiver = isset($this->archivers['create'][$mime])
2362
            ? $this->archivers['create'][$mime]
2363
            : false;
2364
2365
        if (!$archiver) {
2366
            return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
2367
        }
2368
2369
        $files = array();
2370
2371
        foreach ($hashes as $hash) {
2372
            if (($file = $this->file($hash)) == false) {
2373
                return $this->setError(elFinder::ERROR_FILE_NOT_FOUND, '#'+$hash);
2374
            }
2375
            if (!$file['read']) {
2376
                return $this->setError(elFinder::ERROR_PERM_DENIED);
2377
            }
2378
            $path = $this->decode($hash);
2379
            if (!isset($dir)) {
2380
                $dir = $this->dirnameCE($path);
2381
                $stat = $this->stat($dir);
2382
                if (!$stat['write']) {
2383
                    return $this->setError(elFinder::ERROR_PERM_DENIED);
2384
                }
2385
            }
2386
2387
            $files[] = $this->basenameCE($path);
2388
        }
2389
2390
        if ($name === '') {
2391
            $name = count($files) == 1 ? $files[0] : 'Archive';
2392
        } else {
2393
            $name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name));
2394
        }
2395
        $name .='.' . $archiver['ext'];
2396
        $name = $this->uniqueName($dir, $name, '');
0 ignored issues
show
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...
2397
        $this->clearcache();
2398
        return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false;
2399
    }
2400
2401
    /**
2402
     * Resize image
2403
     *
2404
     * @param  string   $hash    image file
2405
     * @param  int      $width   new width
2406
     * @param  int      $height  new height
2407
     * @param  int      $x       X start poistion for crop
2408
     * @param  int      $y       Y start poistion for crop
2409
     * @param  string   $mode    action how to mainpulate image
2410
     * @param  string   $bg      background color
2411
     * @param  int      $degree  rotete degree
2412
     * @param  int      $jpgQuality  JEPG quality (1-100)
2413
     * @return array|false
2414
     * @author Dmitry (dio) Levashov
2415
     * @author Alexey Sukhotin
2416
     * @author nao-pon
2417
     * @author Troex Nevelin
2418
     **/
2419
    public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0, $jpgQuality = null) {
2420
        if ($this->commandDisabled('resize')) {
2421
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2422
        }
2423
2424
        if (($file = $this->file($hash)) == false) {
2425
            return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2426
        }
2427
2428
        if (!$file['write'] || !$file['read']) {
2429
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2430
        }
2431
2432
        $path = $this->decode($hash);
2433
2434
        $work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path);
2435
2436
        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...
2437
            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...
2438
                unlink($work_path);
2439
            }
2440
            return $this->setError(elFinder::ERROR_PERM_DENIED);
2441
        }
2442
2443
        if ($this->imgLib !== 'imagick' && $this->imgLib !== 'convert') {
2444
            if (elFinder::isAnimationGif($work_path)) {
2445
                return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
2446
            }
2447
        }
2448
2449
        switch($mode) {
2450
2451
            case 'propresize':
2452
                $result = $this->imgResize($work_path, $width, $height, true, true, null, $jpgQuality);
2453
                break;
2454
2455
            case 'crop':
2456
                $result = $this->imgCrop($work_path, $width, $height, $x, $y, null, $jpgQuality);
0 ignored issues
show
$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...
$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...
2457
                break;
2458
2459
            case 'fitsquare':
2460
                $result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality);
2461
                break;
2462
2463
            case 'rotate':
2464
                $result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['bgColorFb']), null, $jpgQuality);
2465
                break;
2466
2467
            default:
2468
                $result = $this->imgResize($work_path, $width, $height, false, true, null, $jpgQuality);
2469
                break;
2470
        }
2471
2472
        $ret = false;
2473
        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...
2474
            $stat = $this->stat($path);
2475
            clearstatcache();
2476
            $fstat = stat($work_path);
2477
            $stat['size'] = $fstat['size'];
2478
            $stat['ts'] = $fstat['mtime'];
2479
            if ($imgsize = getimagesize($work_path)) {
2480
                $stat['width'] = $imgsize[0];
2481
                $stat['height'] = $imgsize[1];
2482
                $stat['mime'] = $imgsize['mime'];
2483
            }
2484
            if ($path !== $work_path) {
2485
                if ($fp = fopen($work_path, 'rb')) {
2486
                    $ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat);
2487
                    fclose($fp);
2488
                }
2489
            } else {
2490
                $ret = true;
2491
            }
2492
            if ($ret) {
2493
                $this->rmTmb($file);
0 ignored issues
show
$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...
2494
                $this->clearcache();
2495
                $ret = $this->stat($path);
2496
                $ret['width'] = $stat['width'];
2497
                $ret['height'] = $stat['height'];
2498
            }
2499
        }
2500
        if ($path !== $work_path) {
2501
            is_file($work_path) && unlink($work_path);
2502
        }
2503
2504
        return $ret;
2505
    }
2506
2507
    /**
2508
     * Remove file/dir
2509
     *
2510
     * @param  string  $hash  file hash
2511
     * @return bool
2512
     * @author Dmitry (dio) Levashov
2513
     **/
2514
    public function rm($hash) {
2515
        return $this->commandDisabled('rm')
2516
            ? $this->setError(elFinder::ERROR_PERM_DENIED)
2517
            : $this->remove($this->decode($hash));
2518
    }
2519
2520
    /**
2521
     * Search files
2522
     *
2523
     * @param  string $q search string
2524
     * @param  array $mimes
2525
     * @param null $hash
2526
     * @return array
2527
     * @author Dmitry (dio) Levashov
2528
     */
2529
    public function search($q, $mimes, $hash = null) {
2530
        $dir = null;
2531
        if ($hash) {
2532
            $dir = $this->decode($hash);
2533
            $stat = $this->stat($dir);
2534
            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...
2535
                $q = '';
2536
            }
2537
        }
2538
        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...
2539
            $mimes = array_intersect($mimes, $this->onlyMimes);
2540
            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...
2541
                $q = '';
2542
            }
2543
        }
2544
        $this->searchStart = time();
2545
2546
        $qs = preg_split('/"([^"]+)"| +/', $q, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
2547
        $query = $excludes = array();
2548
        foreach($qs as $_q) {
2549
            $_q = trim($_q);
2550
            if ($_q !== '') {
2551
                if ($_q[0] === '-') {
2552
                    if (isset($_q[1])) {
2553
                        $excludes[] = substr($_q, 1);
2554
                    }
2555
                } else {
2556
                    $query[] = $_q;
2557
                }
2558
            }
2559
        }
2560
        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...
2561
            $q = '';
2562
        } else {
2563
            $q = join(' ', $query);
2564
            $this->doSearchCurrentQuery = array(
0 ignored issues
show
Documentation Bug introduced by
It seems like array('q' => $q, 'excludes' => $excludes) of type array<string,?,{"q":"?","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...
2565
                'q' => $q,
2566
                'excludes' => $excludes
2567
            );
2568
        }
2569
2570
        // 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...
2571
        if ($this->options['searchExDirReg']) {
2572
            if (false === preg_match($this->options['searchExDirReg'], '')) {
2573
                $this->options['searchExDirReg'] = '';
2574
            }
2575
        }
2576
2577
        return ($q === '' || $this->commandDisabled('search'))
2578
            ? array()
2579
            : $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes);
2580
    }
2581
2582
    /**
2583
     * Return image dimensions
2584
     *
2585
     * @param  string  $hash  file hash
2586
     * @return array
2587
     * @author Dmitry (dio) Levashov
2588
     **/
2589
    public function dimensions($hash) {
2590
        if (($file = $this->file($hash)) == false) {
2591
            return false;
2592
        }
2593
2594
        return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime']));
2595
    }
2596
2597
    /**
2598
     * Return content URL (for netmout volume driver)
2599
     * If file.url == 1 requests from JavaScript client with XHR
2600
     *
2601
     * @param string $hash  file hash
2602
     * @param array $options  options array
2603
     * @return boolean|string
2604
     * @author Naoki Sawada
2605
     */
2606
    public function getContentUrl($hash, $options = array()) {
0 ignored issues
show
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2607
        if (($file = $this->file($hash)) == false || (isset($file['url']) && $file['url'] == 1)) {
2608
            return false;
2609
        }
2610
        if (empty($file['url'])) {
2611
            if ($this->URL) {
2612
                $path = str_replace($this->separator, '/', substr($this->decode($hash), strlen($this->root) + 1));
2613
                if ($this->encoding) {
2614
                    $path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
2615
                }
2616
                return $this->URL . $path;
2617
            }
2618
            return false;
2619
        } else {
2620
            return $file['url'];
2621
        }
2622
    }
2623
2624
    /**
2625
     * Return temp path
2626
     *
2627
     * @return string
2628
     * @author Naoki Sawada
2629
     */
2630
    public function getTempPath() {
2631
        $tempPath = null;
2632
        if (isset($this->tmpPath) && $this->tmpPath && is_writable($this->tmpPath)) {
2633
            $tempPath = $this->tmpPath;
2634
        } else if (isset($this->tmp) && $this->tmp && is_writable($this->tmp)) {
2635
            $tempPath = $this->tmp;
2636
        } else if (function_exists('sys_get_temp_dir')) {
2637
            $tempPath = sys_get_temp_dir();
2638
        } else if (isset($this->tmbPath) && $this->tmbPath && is_writable($this->tmbPath)) {
2639
            $tempPath = $this->tmbPath;
2640
        }
2641
        if ($tempPath && DIRECTORY_SEPARATOR !== '/') {
2642
            $tempPath = str_replace('/', DIRECTORY_SEPARATOR, $tempPath);
2643
        }
2644
        return $tempPath;
2645
    }
2646
2647
    /**
2648
     * (Make &) Get upload taget dirctory hash
2649
     *
2650
     * @param string $baseTargetHash
2651
     * @param string $path
2652
     * @param array  $result
2653
     * @return boolean|string
2654
     * @author Naoki Sawada
2655
     */
2656
    public function getUploadTaget($baseTargetHash, $path, & $result) {
2657
        $base = $this->decode($baseTargetHash);
2658
        $targetHash = $baseTargetHash;
2659
        $path = ltrim($path, $this->separator);
2660
        $dirs = explode($this->separator, $path);
2661
        array_pop($dirs);
2662
        foreach($dirs as $dir) {
2663
            $targetPath = $this->joinPathCE($base, $dir);
2664
            if (! $_realpath = $this->realpath($this->encode($targetPath))) {
2665
                if ($stat = $this->mkdir($targetHash, $dir)) {
2666
                    $result['added'][] = $stat;
2667
                    $targetHash = $stat['hash'];
2668
                    $base = $this->decode($targetHash);
2669
                } else {
2670
                    return false;
2671
                }
2672
            } else {
2673
                $targetHash = $this->encode($_realpath);
2674
                if ($this->dir($targetHash)) {
2675
                    $base = $this->decode($targetHash);
2676
                } else {
2677
                    return false;
2678
                }
2679
            }
2680
        }
2681
        return $targetHash;
2682
    }
2683
2684
    /**
2685
     * Return this uploadMaxSize value
2686
     *
2687
     * @return integer
2688
     * @author Naoki Sawada
2689
     */
2690
    public function getUploadMaxSize() {
2691
        return $this->uploadMaxSize;
2692
    }
2693
2694
    /**
2695
     * Image file utility
2696
     *
2697
     * @param string $mode     'resize', 'rotate', 'propresize', 'crop', 'fitsquare'
2698
     * @param string $src      Image file local path
2699
     * @param array  $options  excute options
2700
     * @return bool
2701
     * @author Naoki Sawada
2702
     */
2703
    public function imageUtil($mode, $src, $options = array()) {
2704
        if (! isset($options['jpgQuality'])) {
2705
            $options['jpgQuality'] = intval($this->options['jpgQuality']);
2706
        }
2707
        if (! isset($options['bgcolor'])) {
2708
            $options['bgcolor'] = '#ffffff';
2709
        }
2710
        if (! isset($options['bgColorFb'])) {
2711
            $options['bgColorFb'] = $this->options['bgColorFb'];
2712
        }
2713
        switch($mode) {
2714
            case 'rotate':
2715
                if (empty($options['degree'])) {
2716
                    return true;
2717
                }
2718
                return (bool)$this->imgRotate($src, $options['degree'], $options['bgColorFb'], null, $options['jpgQuality']);
2719
2720
            case 'resize':
2721
            case 'propresize':
2722
            case 'crop':
2723
            case 'fitsquare':
0 ignored issues
show
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
2724
                if (empty($options['width']) || empty($options['height'])) {
2725
                    return false;
2726
                }
2727
2728
            case 'resize':
2729
                return (bool)$this->imgResize($src, $options['width'], $options['height'], false, true, null, $options['jpgQuality'], $options);
2730
2731
            case 'propresize':
2732
                return (bool)$this->imgResize($src, $options['width'], $options['height'], true, true, null, $options['jpgQuality'], $options);
2733
2734
            case 'crop':
0 ignored issues
show
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
2735
                if (isset($options['x']) && isset($options['y'])) {
2736
                    return (bool)$this->imgCrop($src, $options['width'], $options['height'], $options['x'], $options['y'], null, $options['jpgQuality']);
2737
                }
2738
2739
            case 'fitsquare':
2740
                return (bool)$this->imgSquareFit($src, $options['width'], $options['height'], 'center', 'middle', $options['bgcolor'], null, $options['jpgQuality']);
2741
2742
        }
2743
        return false;
2744
    }
2745
2746
    /**
2747
     * Convert Video To Image by ffmpeg
2748
     *
2749
     * @param  $file video source file path
2750
     * @param  $stat file stat array
2751
     * @return bool
2752
     * @author Naoki Sawada
2753
     */
2754
    public function ffmpegToImg($file, $stat) {
0 ignored issues
show
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...
2755
        $name = basename($file);
2756
        $path = dirname($file);
2757
        $tmp = $path . DIRECTORY_SEPARATOR . md5($name);
2758
        $GLOBALS['elFinderTempFiles'][] = $tmp; // regist to remove at the end
2759
        if (rename($file, $tmp)) {
2760
            // specific start time by file name (xxx^[sec].[extention] - video^3.mp4)
2761
            if (preg_match('/\^(\d+(?:\.\d+)?)\.[^.]+$/', $stat['name'], $_m)) {
2762
                $ss = $_m[1];
2763
            } else {
2764
                $ss = $this->options['tmbVideoConvSec'];
2765
            }
2766
            $cmd = sprintf('ffmpeg -ss 00:00:%.3f -vframes 1 -i %s -f image2 %s', $ss, escapeshellarg($tmp), escapeshellarg($file));
2767
            $r = $this->procExec($cmd);
2768
            unlink($tmp);
2769
            return ($r === 0);
2770
        }
2771
        return false;
2772
    }
2773
2774
    /**
2775
     * Save error message
2776
     *
2777
     * @param  array  error
2778
     * @return false
2779
     * @author Dmitry(dio) Levashov
2780
     **/
2781
    protected function setError($error) {
0 ignored issues
show
The parameter $error is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2782
2783
        $this->error = array();
2784
2785
        foreach (func_get_args() as $err) {
2786
            if (is_array($err)) {
2787
                $this->error = array_merge($this->error, $err);
2788
            } else {
2789
                $this->error[] = $err;
2790
            }
2791
        }
2792
2793
        // $this->error = is_array($error) ? $error : func_get_args();
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2794
        return false;
2795
    }
2796
2797
    /*********************************************************************/
2798
    /*                               FS API                              */
2799
    /*********************************************************************/
2800
2801
    /***************** server encoding support *******************/
2802
2803
    /**
2804
     * Return parent directory path (with convert encording)
2805
     *
2806
     * @param  string  $path  file path
2807
     * @return string
2808
     * @author Naoki Sawada
2809
     **/
2810
    protected function dirnameCE($path) {
2811
        return (!$this->encoding)? $this->_dirname($path) :    $this->convEncOut($this->_dirname($this->convEncIn($path)));
2812
    }
2813
2814
    /**
2815
     * Return file name (with convert encording)
2816
     *
2817
     * @param  string  $path  file path
2818
     * @return string
2819
     * @author Naoki Sawada
2820
     **/
2821
    protected function basenameCE($path) {
2822
        return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path)));
2823
    }
2824
2825
    /**
2826
     * Join dir name and file name and return full path. (with convert encording)
2827
     * Some drivers (db) use int as path - so we give to concat path to driver itself
2828
     *
2829
     * @param  string  $dir   dir path
2830
     * @param  string  $name  file name
2831
     * @return string
2832
     * @author Naoki Sawada
2833
     **/
2834
    protected function joinPathCE($dir, $name) {
2835
        return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name)));
2836
    }
2837
2838
    /**
2839
     * Return normalized path (with convert encording)
2840
     *
2841
     * @param  string  $path  file path
2842
     * @return string
2843
     * @author Naoki Sawada
2844
     **/
2845
    protected function normpathCE($path) {
2846
        return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path)));
2847
    }
2848
2849
    /**
2850
     * Return file path related to root dir (with convert encording)
2851
     *
2852
     * @param  string  $path  file path
2853
     * @return string
2854
     * @author Naoki Sawada
2855
     **/
2856
    protected function relpathCE($path) {
2857
        return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path)));
2858
    }
2859
2860
    /**
2861
     * Convert path related to root dir into real path (with convert encording)
2862
     *
2863
     * @param  string  $path  rel file path
2864
     * @return string
2865
     * @author Naoki Sawada
2866
     **/
2867
    protected function abspathCE($path) {
2868
        return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path)));
2869
    }
2870
2871
    /**
2872
     * Return true if $path is children of $parent (with convert encording)
2873
     *
2874
     * @param  string  $path    path to check
2875
     * @param  string  $parent  parent path
2876
     * @return bool
2877
     * @author Naoki Sawada
2878
     **/
2879
    protected function inpathCE($path, $parent) {
2880
        return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent)));
2881
    }
2882
2883
    /**
2884
     * Open file and return file pointer (with convert encording)
2885
     *
2886
     * @param  string $path file path
2887
     * @param string $mode
2888
     * @return false|resource
2889
     * @internal param bool $write open file for writing
2890
     * @author Naoki Sawada
2891
     */
2892
    protected function fopenCE($path, $mode='rb') {
2893
        return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode));
2894
    }
2895
2896
    /**
2897
     * Close opened file (with convert encording)
2898
     *
2899
     * @param  resource  $fp    file pointer
2900
     * @param  string    $path  file path
2901
     * @return bool
2902
     * @author Naoki Sawada
2903
     **/
2904
    protected function fcloseCE($fp, $path='') {
2905
        return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path)));
2906
    }
2907
2908
    /**
2909
     * Create new file and write into it from file pointer. (with convert encording)
2910
     * Return new file path or false on error.
2911
     *
2912
     * @param  resource  $fp   file pointer
2913
     * @param  string    $dir  target dir path
2914
     * @param  string    $name file name
2915
     * @param  array     $stat file stat (required by some virtual fs)
2916
     * @return bool|string
2917
     * @author Naoki Sawada
2918
     **/
2919
    protected function saveCE($fp, $dir, $name, $stat) {
2920
        return (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat)));
2921
    }
2922
2923
    /**
2924
     * Return true if path is dir and has at least one childs directory (with convert encording)
2925
     *
2926
     * @param  string  $path  dir path
2927
     * @return bool
2928
     * @author Naoki Sawada
2929
     **/
2930
    protected function subdirsCE($path) {
2931
        if (!isset($this->subdirsCache[$path])) {
2932
            $this->subdirsCache[$path] = (!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)));
2933
        }
2934
        return $this->subdirsCache[$path];
2935
    }
2936
2937
    /**
2938
     * Return files list in directory (with convert encording)
2939
     *
2940
     * @param  string  $path  dir path
2941
     * @return array
2942
     * @author Naoki Sawada
2943
     **/
2944
    protected function scandirCE($path) {
2945
        return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path)));
2946
    }
2947
2948
    /**
2949
     * Create symlink (with convert encording)
2950
     *
2951
     * @param  string  $source     file to link to
2952
     * @param  string  $targetDir  folder to create link in
2953
     * @param  string  $name       symlink name
2954
     * @return bool
2955
     * @author Naoki Sawada
2956
     **/
2957
    protected function symlinkCE($source, $targetDir, $name) {
2958
        return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name)));
2959
    }
2960
2961
    /***************** paths *******************/
2962
2963
    /**
2964
     * Encode path into hash
2965
     *
2966
     * @param  string  file path
2967
     * @return string
2968
     * @author Dmitry (dio) Levashov
2969
     * @author Troex Nevelin
2970
     **/
2971
    protected function encode($path) {
2972
        if ($path !== '') {
2973
2974
            // cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
2975
            $p = $this->relpathCE($path);
2976
            // if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
2977
            if ($p === '')    {
2978
                $p = $this->separator;
2979
            }
2980
2981
            // TODO crypt path and return hash
2982
            $hash = $this->crypt($p);
2983
            // hash is used as id in HTML that means it must contain vaild chars
2984
            // make base64 html safe and append prefix in begining
2985
            $hash = strtr(base64_encode($hash), '+/=', '-_.');
2986
            // remove dots '.' at the end, before it was '=' in base64
2987
            $hash = rtrim($hash, '.');
2988
            // append volume id to make hash unique
2989
            return $this->id.$hash;
2990
        }
2991
        //TODO: Add return statement here
2992
    }
2993
2994
    /**
2995
     * Decode path from hash
2996
     *
2997
     * @param  string  file hash
2998
     * @return string
2999
     * @author Dmitry (dio) Levashov
3000
     * @author Troex Nevelin
3001
     **/
3002
    protected function decode($hash) {
3003
        if (strpos($hash, $this->id) === 0) {
3004
            // cut volume id after it was prepended in encode
3005
            $h = substr($hash, strlen($this->id));
3006
            // replace HTML safe base64 to normal
3007
            $h = base64_decode(strtr($h, '-_.', '+/='));
3008
            // TODO uncrypt hash and return path
3009
            $path = $this->uncrypt($h);
3010
            // append ROOT to path after it was cut in encode
3011
            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...
3012
        }
3013
        //TODO: Add return statement here
3014
    }
3015
3016
    /**
3017
     * Return crypted path
3018
     * Not implemented
3019
     *
3020
     * @param  string  path
3021
     * @return mixed
3022
     * @author Dmitry (dio) Levashov
3023
     **/
3024
    protected function crypt($path) {
3025
        return $path;
3026
    }
3027
3028
    /**
3029
     * Return uncrypted path
3030
     * Not implemented
3031
     *
3032
     * @param  mixed  hash
3033
     * @return mixed
3034
     * @author Dmitry (dio) Levashov
3035
     **/
3036
    protected function uncrypt($hash) {
3037
        return $hash;
3038
    }
3039
3040
    /**
3041
     * Validate file name based on $this->options['acceptedName'] regexp or function
3042
     *
3043
     * @param  string  $name  file name
3044
     * @return bool
3045
     * @author Dmitry (dio) Levashov
3046
     **/
3047
    protected function nameAccepted($name) {
3048
        if (json_encode($name)===false) {
3049
            return false;
3050
        }
3051
        if ($this->nameValidator) {
3052
            if (is_callable($this->nameValidator)) {
3053
                $res = call_user_func($this->nameValidator, $name);
3054
                return $res;
3055
            }
3056
            if (preg_match($this->nameValidator, '') !== false) {
3057
                return preg_match($this->nameValidator, $name);
3058
            }
3059
        }
3060
        return true;
3061
    }
3062
3063
    /**
3064
     * Return new unique name based on file name and suffix
3065
     *
3066
     * @param $dir
3067
     * @param $name
3068
     * @param  string $suffix suffix append to name
3069
     * @param bool $checkNum
3070
     * @param int $start
3071
     * @return string
3072
     * @internal param string $path file path
3073
     * @author Dmitry (dio) Levashov
3074
     */
3075
    public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) {
3076
        $ext  = '';
3077
3078
        if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
3079
            $ext  = '.'.$m[1];
3080
            $name = substr($name, 0,  strlen($name)-strlen($m[0]));
3081
        }
3082
3083
        if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) {
3084
            $i    = (int)$m[2];
3085
            $name = substr($name, 0, strlen($name)-strlen($m[2]));
3086
        } else {
3087
            $i     = $start;
3088
            $name .= $suffix;
3089
        }
3090
        $max = $i+100000;
3091
3092
        while ($i <= $max) {
3093
            $n = $name.($i > 0 ? sprintf($this->options['uniqueNumFormat'], $i) : '').$ext;
3094
3095
            if (!$this->stat($this->joinPathCE($dir, $n))) {
3096
                $this->clearcache();
3097
                return $n;
3098
            }
3099
            $i++;
3100
        }
3101
        return $name.md5($dir).$ext;
3102
    }
3103
3104
    /**
3105
     * Converts character encoding from UTF-8 to server's one
3106
     *
3107
     * @param  mixed  $var           target string or array var
3108
     * @param  bool   $restoreLocale do retore global locale, default is false
3109
     * @param  string $unknown       replaces character for unknown
3110
     * @return mixed
3111
     * @author Naoki Sawada
3112
     */
3113
    public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') {
3114
        return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown);
3115
    }
3116
3117
    /**
3118
     * Converts character encoding from server's one to UTF-8
3119
     *
3120
     * @param  mixed  $var           target string or array var
3121
     * @param  bool   $restoreLocale do retore global locale, default is true
3122
     * @param  string $unknown       replaces character for unknown
3123
     * @return mixed
3124
     * @author Naoki Sawada
3125
     */
3126
    public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') {
3127
        return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown);
3128
    }
3129
3130
    /**
3131
     * Converts character encoding (base function)
3132
     *
3133
     * @param  mixed $var target string or array var
3134
     * @param  string $from from character encoding
3135
     * @param  string $to to character encoding
3136
     * @param  string $locale local locale
3137
     * @param $restoreLocale
3138
     * @param  string $unknown replaces character for unknown
3139
     * @return mixed
3140
     */
3141
    protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') {
3142
        if (strtoupper($from) !== strtoupper($to)) {
3143
            if ($locale) {
3144
                setlocale(LC_ALL, $locale);
3145
            }
3146
            if (is_array($var)) {
3147
                $_ret = array();
3148
                foreach($var as $_k => $_v) {
3149
                    $_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_');
3150
                }
3151
                $var = $_ret;
3152
            } else {
3153
                $_var = false;
3154
                if (is_string($var)) {
3155
                    $_var = $var;
3156
                    if (false !== ($_var = iconv($from, $to.'//TRANSLIT', $_var))) {
3157
                        $_var = str_replace('?', $unknown, $_var);
3158
                    }
3159
                }
3160
                if  ($_var !== false) {
3161
                    $var = $_var;
3162
                }
3163
            }
3164
            if ($restoreLocale) {
3165
                setlocale(LC_ALL, elFinder::$locale);
3166
            }
3167
        }
3168
        return $var;
3169
    }
3170
3171
    /*********************** util mainly for inheritance class *********************/
3172
3173
    /**
3174
     * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called.
3175
     * When needing the unique file to a path, give $path to parameter.
3176
     *
3177
     * @param  string       $path for get unique file to a path
3178
     * @return string|false
3179
     * @author Naoki Sawada
3180
     */
3181
    protected function getTempFile($path = '') {
3182
        static $cache = array();
3183
        static $rmfunc;
3184
3185
        $key = '';
3186
        if ($path !== '') {
3187
            $key = $this->id . '#' . $path;
3188
            if (isset($cache[$key])) {
3189
                return $cache[$key];
3190
            }
3191
        }
3192
3193
        if ($tmpdir = $this->getTempPath()) {
3194
            if (!$rmfunc) {
3195
                $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...
3196
            }
3197
            $name = tempnam($tmpdir, 'ELF');
3198
            if ($key) {
3199
                $cache[$key] = $name;
3200
            }
3201
            register_shutdown_function($rmfunc, $name);
3202
            return $name;
3203
        }
3204
3205
        return false;
3206
    }
3207
3208
    /**
3209
     * File path of local server side work file path
3210
     *
3211
     * @param  string $path path need convert encoding to server encoding
3212
     * @return string
3213
     * @author Naoki Sawada
3214
     */
3215
    protected function getWorkFile($path) {
3216
        if ($work = $this->getTempFile()) {
3217
            if ($wfp = fopen($work, 'wb')) {
3218
                if ($fp = $this->_fopen($path)) {
3219
                    while(!feof($fp)) {
3220
                        fwrite($wfp, fread($fp, 8192));
3221
                    }
3222
                    $this->_fclose($fp, $path);
3223
                    fclose($wfp);
3224
                    return $work;
3225
                }
3226
            }
3227
        }
3228
        return false;
3229
    }
3230
3231
    /**
3232
     * Get image size array with `dimensions`
3233
     *
3234
     * @param string $path path need convert encoding to server encoding
3235
     * @param string $mime file mime type
3236
     * @return array|false
3237
     */
3238
    public function getImageSize($path, $mime = '') {
3239
        $size = false;
3240
        if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') {
3241
            if ($work = $this->getWorkFile($path)) {
3242
                if ($size = getimagesize($work)) {
3243
                    $size['dimensions'] = $size[0].'x'.$size[1];
3244
                }
3245
            }
3246
            is_file($work) && unlink($work);
3247
        }
3248
        return $size;
3249
    }
3250
3251
    /**
3252
     * Delete dirctory trees
3253
     *
3254
     * @param string $localpath path need convert encoding to server encoding
3255
     * @return boolean
3256
     * @author Naoki Sawada
3257
     */
3258
    protected function delTree($localpath) {
3259
        foreach ($this->_scandir($localpath) as $p) {
3260
            elFinder::extendTimeLimit();
3261
            $stat = $this->stat($this->convEncOut($p));
3262
            $this->convEncIn();
3263
            ($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p);
3264
        }
3265
        return $this->_rmdir($localpath);
3266
    }
3267
3268
    /**
3269
     * Copy items to a new temporary directory on the local server
3270
     *
3271
     * @param  array  $hashes  target hashes
3272
     * @param  string $dir     destination directory (for recurcive)
3273
     * @return string|false    saved path name
3274
     * @author Naoki Sawada
3275
     */
3276
    protected function getItemsInHand($hashes, $dir = null) {
3277
        static $totalSize = 0;
3278
        if (is_null($dir)) {
3279
            $totalSize = 0;
3280
            if (! $tmpDir = $this->getTempPath()) {
3281
                return false;
3282
            }
3283
            $dir = tempnam($tmpDir, 'elf');
3284
            if (!unlink($dir) || !mkdir($dir, 0700, true)) {
3285
                return false;
3286
            }
3287
            register_shutdown_function(array($this, 'rmdirRecursive'), $dir);
3288
        }
3289
        $res = true;
3290
        $files = array();
3291
        foreach ($hashes as $hash) {
3292
            if (($file = $this->file($hash)) == false) {
3293
                continue;
3294
            }
3295
            if (!$file['read']) {
3296
                continue;
3297
            }
3298
3299
            $name = $file['name'];
3300
            // for call from search results
3301
            if (isset($files[$name])) {
3302
                $name = preg_replace('/^(.*?)(\..*)?$/', '$1_'.$files[$name]++.'$2', $name);
3303
            } else {
3304
                $files[$name] = 1;
3305
            }
3306
            $target = $dir.DIRECTORY_SEPARATOR.$name;
3307
3308
            if ($file['mime'] === 'directory') {
3309
                $chashes = array();
3310
                $_files = $this->scandir($hash);
3311
                foreach($_files as $_file) {
0 ignored issues
show
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...
3312
                    if ($file['read']) {
3313
                        $chashes[] = $_file['hash'];
3314
                    }
3315
                }
3316
                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...
3317
                    $res = $this->getItemsInHand($chashes, $target);
3318
                }
3319
                if (!$res) {
3320
                    break;
3321
                }
3322
                !empty($file['ts']) && touch($target, $file['ts']);
3323
            } else {
3324
                $path = $this->decode($hash);
3325
                if ($fp = $this->fopenCE($path)) {
3326
                    if ($tfp = fopen($target, 'wb')) {
3327
                        $totalSize += stream_copy_to_stream($fp, $tfp);
3328
                        fclose($tfp);
3329
                    }
3330
                    !empty($file['ts']) && touch($target, $file['ts']);
3331
                    $this->fcloseCE($fp, $path);
3332
                }
3333
                if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $totalSize) {
3334
                    $res = $this->setError(elFinder::ERROR_ARC_MAXSIZE);
3335
                }
3336
            }
3337
        }
3338
        return $res? $dir : false;
3339
    }
3340
3341
    /*********************** file stat *********************/
3342
3343
    /**
3344
     * Check file attribute
3345
     *
3346
     * @param  string  $path  file path
3347
     * @param  string  $name  attribute name (read|write|locked|hidden)
3348
     * @param  bool    $val   attribute value returned by file system
3349
     * @param  bool    $isDir path is directory (true: directory, false: file)
3350
     * @return bool
3351
     * @author Dmitry (dio) Levashov
3352
     **/
3353
    protected function attr($path, $name, $val=null, $isDir=null) {
3354
        if (!isset($this->defaults[$name])) {
3355
            return false;
3356
        }
3357
3358
3359
        $perm = null;
3360
3361
        if ($this->access) {
3362
            $perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir);
3363
3364
            if ($perm !== null) {
3365
                return !!$perm;
3366
            }
3367
        }
3368
3369
        if ($this->separator != '/') {
3370
            $path = str_replace($this->separator, '/', $this->relpathCE($path));
3371
        } else {
3372
            $path = $this->relpathCE($path);
3373
        }
3374
3375
        $path = '/'.$path;
3376
3377
        for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
3378
            $attrs = $this->attributes[$i];
3379
3380
            if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
3381
                $perm = $attrs[$name];
3382
            }
3383
        }
3384
3385
        return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
3386
    }
3387
3388
    /**
3389
     * Return true if file with given name can be created in given folder.
3390
     *
3391
     * @param string $dir parent dir path
3392
     * @param string $name new file name
3393
     * @param null $isDir
3394
     * @return bool
3395
     * @author Dmitry (dio) Levashov
3396
     */
3397
    protected function allowCreate($dir, $name, $isDir = null) {
3398
        $path = $this->joinPathCE($dir, $name);
3399
        $perm = null;
3400
3401
        if ($this->access) {
3402
            $perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this, $isDir);
3403
            if ($perm !== null) {
3404
                return !!$perm;
3405
            }
3406
        }
3407
3408
        $testPath = $this->separator.$this->relpathCE($path);
3409
3410
        for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
3411
            $attrs = $this->attributes[$i];
3412
3413
            if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
3414
                $perm = $attrs['write'];
3415
            }
3416
        }
3417
3418
        return $perm === null ? true : $perm;
3419
    }
3420
3421
    /**
3422
     * Return true if file MIME type can save with check uploadOrder config.
3423
     *
3424
     * @param string $mime
3425
     * @return boolean
3426
     */
3427
    protected function allowPutMime($mime) {
3428
        // logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
3429
        $allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
3430
        $deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
3431
        $res = true; // default to allow
0 ignored issues
show
$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...
3432
        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...
3433
            $res = false; // default is deny
3434
            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...
3435
                $res = true;
3436
            }// else (both match | no match | match only deny) { deny }
3437
        } else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
3438
            $res = true; // default is allow
3439
            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...
3440
                $res = false;
3441
            } // else (both match | no match | match only allow) { allow }
3442
        }
3443
        return $res;
3444
    }
3445
3446
    /**
3447
     * Return fileinfo
3448
     *
3449
     * @param  string  $path  file cache
3450
     * @return array
3451
     * @author Dmitry (dio) Levashov
3452
     **/
3453
    protected function stat($path) {
3454
        if ($path === false || is_null($path)) {
3455
            return false;
3456
        }
3457
        $is_root = ($path == $this->root);
3458
        if ($is_root) {
3459
            $rootKey = md5($path.(isset($this->options['alias'])? $this->options['alias'] : ''));
3460
            if (!isset($this->sessionCache['rootstat'])) {
3461
                $this->sessionCache['rootstat'] = array();
3462
            }
3463
            if (! $this->isMyReload()) {
3464
                // need $path as key for netmount/netunmount
3465
                if (isset($this->sessionCache['rootstat'][$rootKey])) {
3466
                    if ($ret = $this->sessionCache['rootstat'][$rootKey]) {
3467
                        if ($this->options['rootRev'] === $ret['rootRev']) {
3468
                            if (isset($this->options['phash'])) {
3469
                                $ret['isroot'] = 1;
3470
                                $ret['phash'] = $this->options['phash'];
3471
                            }
3472
                            return $ret;
3473
                        }
3474
                    }
3475
                }
3476
            }
3477
        }
3478
        $ret = isset($this->cache[$path])
3479
            ? $this->cache[$path]
3480
            : $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
3481
        if ($is_root) {
3482
            $this->rootModified = false;
3483
            $this->sessionCache['rootstat'][$rootKey] = $ret;
0 ignored issues
show
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...
3484
            $this->session->set($this->id, $this->sessionCache);
3485
            if (isset($this->options['phash'])) {
3486
                $ret['isroot'] = 1;
3487
                $ret['phash'] = $this->options['phash'];
3488
            }
3489
        }
3490
        return $ret;
3491
    }
3492
3493
    /**
3494
     * Get root stat extra key values
3495
     *
3496
     * @return array stat extras
3497
     * @author Naoki Sawada
3498
     */
3499
    protected function getRootStatExtra() {
3500
        $stat = array();
3501
        if ($this->rootName) {
3502
            $stat['name'] = $this->rootName;
3503
        }
3504
        $stat['rootRev'] = $this->options['rootRev'];
3505
        $stat['options'] = $this->options(null);
3506
        return $stat;
3507
    }
3508
3509
    /**
3510
     * Put file stat in cache and return it
3511
     *
3512
     * @param  string  $path   file path
3513
     * @param  array   $stat   file stat
3514
     * @return array
3515
     * @author Dmitry (dio) Levashov
3516
     **/
3517
    protected function updateCache($path, $stat) {
3518
        if (empty($stat) || !is_array($stat)) {
3519
            return $this->cache[$path] = array();
3520
        }
3521
3522
        $stat['hash'] = $this->encode($path);
3523
3524
        $root = $path == $this->root;
3525
        $parent = '';
3526
3527
        if ($root) {
3528
            $stat = array_merge($stat, $this->getRootStatExtra());
3529
        } else {
3530
            if (!isset($stat['name']) || $stat['name'] === '') {
3531
                $stat['name'] = $this->basenameCE($path);
3532
            }
3533
            if (empty($stat['phash'])) {
3534
                $parent = $this->dirnameCE($path);
3535
                $stat['phash'] = $this->encode($parent);
3536
            } else {
3537
                $parent = $this->getPath($stat['phash']);
3538
            }
3539
        }
3540
3541
        // name check
3542
        if (!$jeName = json_encode($stat['name'])) {
3543
            return $this->cache[$path] = array();
3544
        }
3545
        // fix name if required
3546
        if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
3547
            $stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], $jeName));
3548
        }
3549
3550
3551
        if (empty($stat['mime'])) {
3552
            $stat['mime'] = $this->mimetype($stat['name']);
3553
        }
3554
3555
        // @todo move dateformat to client
3556
        // $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...
3557
        //     ? $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...
3558
        //     : 'unknown';
3559
3560
        if (!isset($stat['size'])) {
3561
            $stat['size'] = 'unknown';
3562
        }
3563
3564
        if ($isDir = ($stat['mime'] === 'directory')) {
3565
            $stat['volumeid'] = $this->id;
3566
        }
3567
3568
        $stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir));
3569
        $stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir));
3570
        if ($root) {
3571
            $stat['locked'] = 1;
3572
            if ($this->options['type'] !== '') {
3573
                $stat['type'] = $this->options['type'];
3574
            }
3575
        } else {
3576
            // lock when parent directory is not writable
3577
            if (!isset($stat['locked'])) {
3578
                $pstat = $this->stat($parent);
3579
                if (isset($pstat['write']) && !$pstat['write']) {
3580
                    $stat['locked'] = true;
3581
                }
3582
            }
3583
            if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) {
3584
                $stat['locked'] = 1;
3585
            } else {
3586
                unset($stat['locked']);
3587
            }
3588
        }
3589
3590
        if ($root) {
3591
            unset($stat['hidden']);
3592
        } elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir)
3593
        || !$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...
3594
            $stat['hidden'] = 1;
3595
        } else {
3596
            unset($stat['hidden']);
3597
        }
3598
3599
        if ($stat['read'] && empty($stat['hidden'])) {
3600
3601
            if ($isDir) {
3602
                // caching parent's subdirs
3603
                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...
3604
                    $this->subdirsCache[$parent] = true;
3605
                }
3606
                // for dir - check for subdirs
3607
                if ($this->options['checkSubfolders']) {
3608
                    if (isset($stat['dirs'])) {
3609
                        if ($stat['dirs']) {
3610
                            $stat['dirs'] = 1;
3611
                        } else {
3612
                            unset($stat['dirs']);
3613
                        }
3614
                    } elseif (!empty($stat['alias']) && !empty($stat['target'])) {
3615
                        $stat['dirs'] = isset($this->cache[$stat['target']])
3616
                            ? intval(isset($this->cache[$stat['target']]['dirs']))
3617
                            : $this->subdirsCE($stat['target']);
3618
3619
                    } elseif ($this->subdirsCE($path)) {
3620
                        $stat['dirs'] = 1;
3621
                    }
3622
                } else {
3623
                    $stat['dirs'] = 1;
3624
                }
3625
                if ($this->options['dirUrlOwn'] === true) {
3626
                    $stat['url'] = '#elf_' . $stat['hash'];
3627
                }
3628
            } else {
3629
                // for files - check for thumbnails
3630
                $p = isset($stat['target']) ? $stat['target'] : $path;
3631
                if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
3632
                    $tmb = $this->gettmb($p, $stat);
3633
                    $stat['tmb'] = $tmb ? $tmb : 1;
3634
                }
3635
3636
            }
3637
            if (!isset($stat['url']) && $this->URL && $this->encoding) {
3638
                $_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
3639
                $stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN')? $_path : $this->convEncIn($_path, true)));
3640
            }
3641
        } else {
3642
            if ($isDir) {
3643
                unset($stat['dirs']);
3644
            }
3645
        }
3646
3647
        if (!empty($stat['alias']) && !empty($stat['target'])) {
3648
            $stat['thash'] = $this->encode($stat['target']);
3649
            //$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...
3650
            unset($stat['target']);
3651
        }
3652
3653
        return $this->cache[$path] = $stat;
3654
    }
3655
3656
    /**
3657
     * Get stat for folder content and put in cache
3658
     *
3659
     * @param  string  $path
3660
     * @return void
3661
     * @author Dmitry (dio) Levashov
3662
     **/
3663
    protected function cacheDir($path) {
3664
        $this->dirsCache[$path] = array();
3665
        $this->subdirsCache[$path] = false;
3666
3667
        foreach ($this->scandirCE($path) as $p) {
3668
            if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3669
                if ($stat['mime'] === 'directory') {
3670
                    $this->subdirsCache[$path] = true;
3671
                }
3672
                $this->dirsCache[$path][] = $p;
3673
            }
3674
        }
3675
    }
3676
3677
    /**
3678
     * Clean cache
3679
     *
3680
     * @return void
3681
     * @author Dmitry (dio) Levashov
3682
     **/
3683
    protected function clearcache() {
3684
        $this->cache = $this->dirsCache = $this->subdirsCache = array();
3685
    }
3686
3687
    /**
3688
     * Return file mimetype
3689
     *
3690
     * @param  string $path file path
3691
     * @param string $name
3692
     * @return string
3693
     * @author Dmitry (dio) Levashov
3694
     */
3695
    protected function mimetype($path, $name = '') {
3696
        $type = '';
3697
3698
        if ($name === '') {
3699
            $name = $path;
3700
        }
3701
        $ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
3702
        if (is_readable($path)) {
3703
            if ($this->mimeDetect == 'finfo') {
3704
                if ($type = finfo_file($this->finfo, $path)) {
3705
                    if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
3706
                        if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
3707
                    } else if ($ext === 'js' && preg_match('~^text/~', $type)) {
3708
                        $type = 'text/javascript';
3709
                    }
3710
                } else {
3711
                    $type = 'unknown';
3712
                }
3713
            } else if ($this->mimeDetect == 'mime_content_type') {
3714
                $type = mime_content_type($path);
3715
            }
3716
        }
3717
        if (! $type) {
3718
            $type = elFinderVolumeDriver::mimetypeInternalDetect($path);
3719
        }
3720
3721
        $type = explode(';', $type);
3722
        $type = trim($type[0]);
3723
3724
        if (in_array($type, array('application/x-empty', 'inode/x-empty'))) {
3725
            // finfo return this mime for empty files
3726
            $type = 'text/plain';
3727
        } elseif ($type == 'application/x-zip') {
3728
            // http://elrte.org/redmine/issues/163
3729
            $type = 'application/zip';
3730
        }
3731
3732
        // mime type normalization
3733
        $_checkKey = strtolower($ext.':'.$type);
3734
        if (isset($this->options['mimeMap'][$_checkKey])) {
3735
            $type = $this->options['mimeMap'][$_checkKey];
3736
        }
3737
3738
        return $type == 'unknown' && $this->mimeDetect != 'internal'
3739
            ? elFinderVolumeDriver::mimetypeInternalDetect($path)
3740
            : $type;
3741
3742
    }
3743
3744
    /**
3745
     * Detect file mimetype using "internal" method or Loading mime.types with $path = ''
3746
     *
3747
     * @param  string  $path  file path
3748
     * @return string
3749
     * @author Dmitry (dio) Levashov
3750
     **/
3751
    static protected function mimetypeInternalDetect($path = '') {
0 ignored issues
show
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
3752
        // load default MIME table file "mime.types"
3753
        if (!elFinderVolumeDriver::$mimetypesLoaded) {
3754
            elFinderVolumeDriver::$mimetypesLoaded = true;
3755
            $file = elFinder::$defaultMimefile;
3756
            if ($file === '' || ! is_readable($file)) {
3757
                $file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
3758
            }
3759
            if (is_readable($file)) {
3760
                $mimecf = file($file);
3761
                foreach ($mimecf as $line_num => $line) {
3762
                    if (!preg_match('/^\s*#/', $line)) {
3763
                        $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
3764
                        for ($i = 1, $size = count($mime); $i < $size ; $i++) {
3765
                            if (!isset(elFinderVolumeDriver::$mimetypes[$mime[$i]])) {
3766
                                elFinderVolumeDriver::$mimetypes[$mime[$i]] = $mime[0];
3767
                            }
3768
                        }
3769
                    }
3770
                }
3771
            }
3772
        }
3773
        $ext = '';
3774
        if ($path) {
3775
            $pinfo = pathinfo($path);
3776
            $ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
3777
        }
3778
        return ($ext && isset(elFinderVolumeDriver::$mimetypes[$ext])) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
3779
    }
3780
3781
    /**
3782
     * Return file/total directory size
3783
     *
3784
     * @param  string  $path  file path
3785
     * @return int
3786
     * @author Dmitry (dio) Levashov
3787
     **/
3788
    protected function countSize($path) {
3789
3790
        elFinder::extendTimeLimit();
3791
3792
        $stat = $this->stat($path);
3793
3794
        if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3795
            return 'unknown';
3796
        }
3797
3798
        if ($stat['mime'] != 'directory') {
3799
            return $stat['size'];
3800
        }
3801
3802
        $subdirs = $this->options['checkSubfolders'];
3803
        $this->options['checkSubfolders'] = true;
3804
        $result = 0;
3805
        foreach ($this->getScandir($path) as $stat) {
3806
            $size = $stat['mime'] == 'directory' && $stat['read']
3807
                ? $this->countSize($this->joinPathCE($path, $stat['name']))
3808
                : (isset($stat['size']) ? intval($stat['size']) : 0);
3809
            if ($size > 0) {
3810
                $result += $size;
3811
            }
3812
        }
3813
        $this->options['checkSubfolders'] = $subdirs;
3814
        return $result;
3815
    }
3816
3817
    /**
3818
     * Return true if all mimes is directory or files
3819
     *
3820
     * @param  string  $mime1  mimetype
3821
     * @param  string  $mime2  mimetype
3822
     * @return bool
3823
     * @author Dmitry (dio) Levashov
3824
     **/
3825
    protected function isSameType($mime1, $mime2) {
3826
        return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3827
    }
3828
3829
    /**
3830
     * If file has required attr == $val - return file path,
3831
     * If dir has child with has required attr == $val - return child path
3832
     *
3833
     * @param  string   $path  file path
3834
     * @param  string   $attr  attribute name
3835
     * @param  bool     $val   attribute value
3836
     * @return string|false
3837
     * @author Dmitry (dio) Levashov
3838
     **/
3839
    protected function closestByAttr($path, $attr, $val) {
3840
        $stat = $this->stat($path);
3841
3842
        if (empty($stat)) {
3843
            return false;
3844
        }
3845
3846
        $v = isset($stat[$attr]) ? $stat[$attr] : false;
3847
3848
        if ($v == $val) {
3849
            return $path;
3850
        }
3851
3852
        return $stat['mime'] == 'directory'
3853
            ? $this->childsByAttr($path, $attr, $val)
3854
            : false;
3855
    }
3856
3857
    /**
3858
     * Return first found children with required attr == $val
3859
     *
3860
     * @param  string   $path  file path
3861
     * @param  string   $attr  attribute name
3862
     * @param  bool     $val   attribute value
3863
     * @return string|false
3864
     * @author Dmitry (dio) Levashov
3865
     **/
3866
    protected function childsByAttr($path, $attr, $val) {
3867
        foreach ($this->scandirCE($path) as $p) {
3868
            if (($_p = $this->closestByAttr($p, $attr, $val)) != false) {
0 ignored issues
show
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...
3869
                return $_p;
3870
            }
3871
        }
3872
        return false;
3873
    }
3874
3875
    protected function isMyReload($target = '', $ARGtarget = '') {
3876
        if ($this->rootModified || (! empty($this->ARGS['cmd']) && $this->ARGS['cmd'] === 'parents')) {
3877
            return true;
3878
        }
3879
        if (! empty($this->ARGS['reload'])) {
3880
            if ($ARGtarget === '') {
3881
                $ARGtarget = isset($this->ARGS['target'])? $this->ARGS['target']
3882
                    : ((isset($this->ARGS['targets']) && is_array($this->ARGS['targets']) && count($this->ARGS['targets']) === 1)?
3883
                        $this->ARGS['targets'][0] : '');
3884
            }
3885
            if ($ARGtarget !== '') {
3886
                $ARGtarget = strval($ARGtarget);
3887
                if ($target === '') {
3888
                    return (strpos($ARGtarget, $this->id) === 0);
3889
                } else {
3890
                    $target = strval($target);
3891
                    return ($target === $ARGtarget);
3892
                }
3893
            }
3894
        }
3895
        return false;
3896
    }
3897
3898
    /*****************  get content *******************/
3899
3900
    /**
3901
     * Return required dir's files info.
3902
     * If onlyMimes is set - return only dirs and files of required mimes
3903
     *
3904
     * @param  string  $path  dir path
3905
     * @return array
3906
     * @author Dmitry (dio) Levashov
3907
     **/
3908
    protected function getScandir($path) {
3909
        $files = array();
3910
3911
        !isset($this->dirsCache[$path]) && $this->cacheDir($path);
3912
3913
        foreach ($this->dirsCache[$path] as $p) {
3914
            if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3915
                $files[] = $stat;
3916
            }
3917
        }
3918
3919
        return $files;
3920
    }
3921
3922
3923
    /**
3924
     * Return subdirs tree
3925
     *
3926
     * @param  string $path parent dir path
3927
     * @param  int $deep tree deep
3928
     * @param string $exclude
3929
     * @return array
3930
     * @author Dmitry (dio) Levashov
3931
     */
3932
    protected function gettree($path, $deep, $exclude='') {
3933
        $dirs = array();
3934
3935
        !isset($this->dirsCache[$path]) && $this->cacheDir($path);
3936
3937
        foreach ($this->dirsCache[$path] as $p) {
3938
            $stat = $this->stat($p);
3939
3940
            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...
3941
                $dirs[] = $stat;
3942
                if ($deep > 0 && !empty($stat['dirs'])) {
3943
                    $dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3944
                }
3945
            }
3946
        }
3947
3948
        return $dirs;
3949
    }
3950
3951
    /**
3952
     * Recursive files search
3953
     *
3954
     * @param  string  $path   dir path
3955
     * @param  string  $q      search string
3956
     * @param  array   $mimes
3957
     * @return array
3958
     * @author Dmitry (dio) Levashov
3959
     **/
3960
    protected function doSearch($path, $q, $mimes) {
3961
        $result = array();
3962
3963
        $timeout = $this->options['searchTimeout']? $this->searchStart + $this->options['searchTimeout'] : 0;
3964
        if ($timeout && $timeout < time()) {
3965
            $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
3966
            return $result;
3967
        }
3968
3969
        foreach($this->scandirCE($path) as $p) {
3970
            elFinder::extendTimeLimit($this->options['searchTimeout'] + 30);
3971
3972
            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...
3973
                !$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...
3974
                break;
3975
            }
3976
3977
3978
            $stat = $this->stat($p);
3979
3980
            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...
3981
                continue;
3982
            }
3983
3984
            if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3985
                continue;
3986
            }
3987
3988
            $name = $stat['name'];
3989
3990
            if ($this->doSearchCurrentQuery['excludes']) {
3991
                foreach($this->doSearchCurrentQuery['excludes'] as $exclude) {
0 ignored issues
show
The expression $this->doSearchCurrentQuery['excludes'] of type string is not traversable.
Loading history...
3992
                    if ($this->stripos($name, $exclude) !== false) {
3993
                        continue 2;
3994
                    }
3995
                }
3996
            }
3997
3998
            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...
3999
                $stat['path'] = $this->path($stat['hash']);
4000
                if ($this->URL && !isset($stat['url'])) {
4001
                    $path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
4002
                    if ($this->encoding) {
4003
                        $path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
4004
                    }
4005
                    $stat['url'] = $this->URL . $path;
4006
                }
4007
4008
                $result[] = $stat;
4009
            }
4010
            if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) {
4011
                if (! $this->options['searchExDirReg'] || ! preg_match($this->options['searchExDirReg'], $p)) {
4012
                    $result = array_merge($result, $this->doSearch($p, $q, $mimes));
4013
                }
4014
            }
4015
        }
4016
4017
        return $result;
4018
    }
4019
4020
    /**********************  manuipulations  ******************/
4021
4022
    /**
4023
     * Copy file/recursive copy dir only in current volume.
4024
     * Return new file path or false.
4025
     *
4026
     * @param  string  $src   source path
4027
     * @param  string  $dst   destination dir path
4028
     * @param  string  $name  new file name (optionaly)
4029
     * @return string|false
4030
     * @author Dmitry (dio) Levashov
4031
     **/
4032
    protected function copy($src, $dst, $name) {
4033
4034
        elFinder::extendTimeLimit();
4035
4036
        $srcStat = $this->stat($src);
4037
        $this->clearcache();
4038
4039
        if (!empty($srcStat['thash'])) {
4040
            $target = $this->decode($srcStat['thash']);
4041
            if (!$this->inpathCE($target, $this->root)) {
4042
                return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
4043
            }
4044
            $stat   = $this->stat($target);
4045
            $this->clearcache();
4046
            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...
4047
                ? $this->joinPathCE($dst, $name)
4048
                : $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
4049
        }
4050
4051
        if ($srcStat['mime'] === 'directory') {
4052
            $testStat = $this->stat($this->joinPathCE($dst, $name));
4053
            $this->clearcache();
4054
4055
            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...
4056
                return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
4057
            }
4058
4059
            $dst = $this->decode($testStat['hash']);
4060
4061
            foreach ($this->getScandir($src) as $stat) {
4062
                if (empty($stat['hidden'])) {
4063
                    $name = $stat['name'];
4064
                    $_src = $this->decode($stat['hash']);
4065
                    if (! $this->copy($_src, $dst, $name)) {
4066
                        $this->remove($dst, true); // fall back
4067
                        return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
4068
                    }
4069
                }
4070
            }
4071
4072
            $this->added[] = $testStat;
4073
4074
            return $dst;
4075
        }
4076
4077
        if ($this->options['copyJoin']) {
4078
            $test = $this->joinPathCE($dst, $name);
4079
            if ($testStat = $this->stat($test)) {
0 ignored issues
show
$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...
4080
                $this->remove($test);
4081
            }
4082
        } else {
4083
            $testStat = false;
0 ignored issues
show
$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...
4084
        }
4085
        if ($res = $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) {
4086
            $path = is_string($res)? $res : $this->joinPathCE($dst, $name);
4087
            $this->clearcache();
4088
            $this->added[] = $this->stat($path);
4089
            return $path;
4090
        }
4091
4092
        return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
4093
    }
4094
4095
    /**
4096
     * Move file
4097
     * Return new file path or false.
4098
     *
4099
     * @param  string  $src   source path
4100
     * @param  string  $dst   destination dir path
4101
     * @param  string  $name  new file name
4102
     * @return string|false
4103
     * @author Dmitry (dio) Levashov
4104
     **/
4105
    protected function move($src, $dst, $name) {
4106
        $stat = $this->stat($src);
4107
        $stat['realpath'] = $src;
4108
        $this->rmTmb($stat); // can not do rmTmb() after _move()
0 ignored issues
show
$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...
4109
        $this->clearcache();
4110
4111
        if ($res = $this->convEncOut($this->_move($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) {
4112
            $this->removed[] = $stat;
4113
            return is_string($res)? $res : $this->joinPathCE($dst, $name);
4114
        }
4115
4116
        return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
4117
    }
4118
4119
    /**
4120
     * Copy file from another volume.
4121
     * Return new file path or false.
4122
     *
4123
     * @param  Object  $volume       source volume
4124
     * @param  string  $src          source file hash
4125
     * @param  string  $destination  destination dir path
4126
     * @param  string  $name         file name
4127
     * @return string|false
4128
     * @author Dmitry (dio) Levashov
4129
     **/
4130
    protected function copyFrom($volume, $src, $destination, $name) {
4131
4132
        elFinder::extendTimeLimit();
4133
4134
        if (($source = $volume->file($src)) == false) {
4135
            return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
4136
        }
4137
4138
        $errpath = $volume->path($source['hash']);
4139
4140
        if (!$this->nameAccepted($source['name'])) {
4141
            return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
4142
        }
4143
4144
        if (!$source['read']) {
4145
            return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
4146
        }
4147
4148
        if ($source['mime'] == 'directory') {
4149
            $test = $this->stat($this->joinPathCE($destination, $name));
4150
            $this->clearcache();
4151
4152
            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...
4153
                return $this->setError(elFinder::ERROR_COPY, $errpath);
4154
            }
4155
4156
            $path = $this->joinPathCE($destination, $name);
0 ignored issues
show
$path 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...
4157
            $path = $this->decode($test['hash']);
4158
4159
            foreach ($volume->scandir($src) as $entr) {
4160
                if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
4161
                    $this->remove($path, true); // fall back
4162
                    return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
4163
                }
4164
            }
4165
4166
            $this->added[] = $test;
4167
        } else {
4168
            // $mime = $source['mime'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
4169
            // $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...
4170
            if (($dim = $volume->dimensions($src))) {
4171
                $s = explode('x', $dim);
4172
                $source['width']  = $s[0];
4173
                $source['height'] = $s[1];
4174
            }
4175
4176
            if (($fp = $volume->open($src)) == false
4177
            || ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
4178
                $fp && $volume->close($fp, $src);
4179
                return $this->setError(elFinder::ERROR_COPY, $errpath);
4180
            }
4181
            $volume->close($fp, $src);
4182
4183
            // MIME check
4184
            $stat = $this->stat($path);
0 ignored issues
show
It seems like $path defined by $this->saveCE($fp, $destination, $name, $source) on line 4177 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...
4185
            $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($stat['name']);
4186
            if ($stat['mime'] === $mimeByName) {
4187
                $mimeByName = '';
4188
            }
4189
            if (!$this->allowPutMime($stat['mime']) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
4190
                $this->remove($path, true);
0 ignored issues
show
It seems like $path defined by $this->saveCE($fp, $destination, $name, $source) on line 4177 can also be of type boolean; however, elFinderVolumeDriver::remove() does only seem to accept string, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
4191
                return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
4192
            }
4193
4194
            $this->added[] = $stat;
4195
        }
4196
4197
        return $path;
4198
    }
4199
4200
    /**
4201
     * Remove file/ recursive remove dir
4202
     *
4203
     * @param  string  $path   file path
4204
     * @param  bool    $force  try to remove even if file locked
4205
     * @return bool
4206
     * @author Dmitry (dio) Levashov
4207
     **/
4208
    protected function remove($path, $force = false) {
4209
        $stat = $this->stat($path);
4210
4211
        if (empty($stat)) {
4212
            return $this->setError(elFinder::ERROR_RM, $path, elFinder::ERROR_FILE_NOT_FOUND);
4213
        }
4214
4215
        $stat['realpath'] = $path;
4216
        $this->rmTmb($stat);
0 ignored issues
show
$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...
4217
        $this->clearcache();
4218
4219
        if (!$force && !empty($stat['locked'])) {
4220
            return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
4221
        }
4222
4223
        if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
4224
            $ret = $this->delTree($this->convEncIn($path));
4225
            $this->convEncOut();
4226
            if (!$ret) {
4227
                return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
4228
            }
4229
        } else {
4230
            if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
4231
                return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
4232
            }
4233
        }
4234
4235
        $this->removed[] = $stat;
4236
        return true;
4237
    }
4238
4239
4240
    /************************* thumbnails **************************/
4241
4242
    /**
4243
     * Return thumbnail file name for required file
4244
     *
4245
     * @param  array  $stat  file stat
4246
     * @return string
4247
     * @author Dmitry (dio) Levashov
4248
     **/
4249
    protected function tmbname($stat) {
4250
        return $stat['hash'].$stat['ts'].'.png';
4251
    }
4252
4253
    /**
4254
     * Return thumnbnail name if exists
4255
     *
4256
     * @param  string  $path file path
4257
     * @param  array   $stat file stat
4258
     * @return string|false
4259
     * @author Dmitry (dio) Levashov
4260
     **/
4261
    protected function gettmb($path, $stat) {
4262
        if ($this->tmbURL && $this->tmbPath) {
4263
            // file itself thumnbnail
4264
            if (strpos($path, $this->tmbPath) === 0) {
4265
                return basename($path);
4266
            }
4267
4268
            $name = $this->tmbname($stat);
4269
            if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
4270
                return $name;
4271
            }
4272
        }
4273
        return false;
4274
    }
4275
4276
    /**
4277
     * Return true if thumnbnail for required file can be created
4278
     *
4279
     * @param  string  $path  thumnbnail path
4280
     * @param  array   $stat  file stat
4281
     * @param  bool    $checkTmbPath
4282
     * @return string|bool
4283
     * @author Dmitry (dio) Levashov
4284
     **/
4285
    protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
4286
        if ((! $checkTmbPath || $this->tmbPathWritable)
4287
            && (! $this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
4288
        ) {
4289
            $mime = strtolower($stat['mime']);
4290
            list($type) = explode('/', $mime);
4291
            if (! empty($this->imgConverter)) {
4292
                if (isset($this->imgConverter[$mime])) {
4293
                    return true;
4294
                }
4295
                if (isset($this->imgConverter[$type])) {
4296
                    return true;
4297
                }
4298
            }
4299
            return $this->imgLib
4300
                && ($type === 'image')
4301
                && ($this->imgLib == 'gd' ? in_array($stat['mime'], array('image/jpeg', 'image/png', 'image/gif', 'image/x-ms-bmp')) : true);
4302
        }
4303
        return false;
4304
    }
4305
4306
    /**
4307
     * Return true if required file can be resized.
4308
     * By default - the same as canCreateTmb
4309
     *
4310
     * @param  string  $path  thumnbnail path
4311
     * @param  array   $stat  file stat
4312
     * @return string|bool
4313
     * @author Dmitry (dio) Levashov
4314
     **/
4315
    protected function canResize($path, $stat) {
4316
        return $this->canCreateTmb($path, $stat, false);
4317
    }
4318
4319
    /**
4320
     * Create thumnbnail and return it's URL on success
4321
     *
4322
     * @param  string $path file path
4323
     * @param $stat
4324
     * @return false|string
4325
     * @internal param string $mime file mime type
4326
     * @author Dmitry (dio) Levashov
4327
     */
4328
    protected function createTmb($path, $stat) {
4329
        if (!$stat || !$this->canCreateTmb($path, $stat)) {
4330
            return false;
4331
        }
4332
4333
        $name = $this->tmbname($stat);
4334
        $tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
4335
4336
        $maxlength = -1;
4337
        $imgConverter = null;
4338
4339
        // check imgConverter
4340
        $mime = strtolower($stat['mime']);
4341
        list($type) = explode('/', $mime);
4342
        if (isset($this->imgConverter[$mime])) {
4343
            $imgConverter = $this->imgConverter[$mime]['func'];
4344
            if (! empty($this->imgConverter[$mime]['maxlen'])) {
4345
                $maxlength = intval($this->imgConverter[$mime]['maxlen']);
4346
            }
4347
        } else if (isset($this->imgConverter[$type])) {
4348
            $imgConverter = $this->imgConverter[$type]['func'];
4349
            if (! empty($this->imgConverter[$type]['maxlen'])) {
4350
                $maxlength = intval($this->imgConverter[$type]['maxlen']);
4351
            }
4352
        }
4353
        if ($imgConverter && ! is_callable($imgConverter)) {
4354
            return false;
4355
        }
4356
4357
        // copy image into tmbPath so some drivers does not store files on local fs
4358
        if (($src = $this->fopenCE($path, 'rb')) == false) {
4359
            return false;
4360
        }
4361
4362
        if (($trg = fopen($tmb, 'wb')) == false) {
4363
            $this->fcloseCE($src, $path);
4364
            return false;
4365
        }
4366
4367
        stream_copy_to_stream($src, $trg, $maxlength);
4368
4369
        $this->fcloseCE($src, $path);
4370
        fclose($trg);
4371
4372
        // call imgConverter
4373
        if ($imgConverter) {
4374
            if (! call_user_func_array($imgConverter, array($tmb, $stat, $this))) {
4375
                file_exists($tmb) && unlink($tmb);
4376
                return false;
4377
            }
4378
        }
4379
4380
        $result = false;
4381
4382
        $tmbSize = $this->tmbSize;
4383
4384
        if ($this->imgLib === 'imagick') {
4385
            try {
4386
                $imagickTest = new imagick($tmb);
4387
                $imagickTest->clear();
4388
                $imagickTest = true;
4389
            } catch (Exception $e) {
4390
                $imagickTest = false;
4391
            }
4392
        }
4393
4394
        if (($this->imgLib === 'imagick' && ! $imagickTest) || ($s = getimagesize($tmb)) === false) {
0 ignored issues
show
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...
4395
            if ($this->imgLib === 'imagick') {
4396
                $bgcolor = $this->options['tmbBgColor'];
4397
                if ($bgcolor === 'transparent') {
4398
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
4399
                }
4400
                try {
4401
                    $imagick = new imagick();
4402
                    $imagick->setBackgroundColor(new ImagickPixel($bgcolor));
4403
                    $imagick->readImage($this->getExtentionByMime($stat['mime'], ':') . $tmb);
4404
                    $imagick->setImageFormat('png');
4405
                    $imagick->writeImage($tmb);
4406
                    $imagick->clear();
4407
                    if (($s = getimagesize($tmb)) !== false) {
4408
                        $result = true;
4409
                    }
4410
                } catch (Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
4411
            }
4412
            if (! $result) {
4413
                file_exists($tmb) && unlink($tmb);
4414
                return false;
4415
            }
4416
            $result = false;
0 ignored issues
show
$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...
4417
        }
4418
4419
        /* If image smaller or equal thumbnail size - just fitting to thumbnail square */
4420
        if ($s[0] <= $tmbSize && $s[1]    <= $tmbSize) {
4421
            $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
4422
        } else {
4423
4424
            if ($this->options['tmbCrop']) {
4425
4426
                $result = $tmb;
4427
                /* Resize and crop if image bigger than thumbnail */
4428
                if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
0 ignored issues
show
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...
4429
                    $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
4430
                }
4431
4432
                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...
4433
                    $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
4434
                    $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
4435
                    $result = $this->imgCrop($result, $tmbSize, $tmbSize, $x, $y, 'png');
0 ignored issues
show
$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...
$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...
4436
                } else {
4437
                    $result = false;
4438
                }
4439
4440
            } else {
4441
                $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
4442
            }
4443
4444
            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...
4445
                if ($s = getimagesize($tmb)) {
4446
                    if ($s[0] !== $tmbSize || $s[1] !== $tmbSize) {
4447
                        $result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
4448
                    }
4449
                }
4450
            }
4451
        }
4452
4453
        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...
4454
            unlink($tmb);
4455
            return false;
4456
        }
4457
4458
        return $name;
4459
    }
4460
4461
    /**
4462
     * Resize image
4463
     *
4464
     * @param  string   $path               image file
4465
     * @param  int      $width              new width
4466
     * @param  int      $height             new height
4467
     * @param  bool        $keepProportions    crop image
4468
     * @param  bool        $resizeByBiggerSide resize image based on bigger side if true
4469
     * @param  string   $destformat         image destination format
4470
     * @param  int      $jpgQuality         JEPG quality (1-100)
4471
     * @param  array    $options            Other extra options
4472
     * @return string|false
4473
     * @author Dmitry (dio) Levashov
4474
     * @author Alexey Sukhotin
4475
     **/
4476
    protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null, $jpgQuality = null, $options = array()) {
4477
        if (($s = getimagesize($path)) == false) {
4478
            return false;
4479
        }
4480
4481
        $result = false;
0 ignored issues
show
$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...
4482
4483
        list($size_w, $size_h) = array($width, $height);
4484
4485
        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...
4486
            $jpgQuality = $this->options['jpgQuality'];
4487
        }
4488
4489
        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...
4490
4491
            list($orig_w, $orig_h) = array($s[0], $s[1]);
4492
4493
            /* Resizing by biggest side */
4494
            if ($resizeByBiggerSide) {
4495
                if ($orig_w > $orig_h) {
4496
                    $size_h = round($orig_h * $width / $orig_w);
4497
                    $size_w = $width;
4498
                } else {
4499
                    $size_w = round($orig_w * $height / $orig_h);
4500
                    $size_h = $height;
4501
                }
4502
            } else {
4503
                if ($orig_w > $orig_h) {
4504
                    $size_w = round($orig_w * $height / $orig_h);
4505
                    $size_h = $height;
4506
                } else {
4507
                    $size_h = round($orig_h * $width / $orig_w);
4508
                    $size_w = $width;
4509
                }
4510
            }
4511
        }
4512
4513
        elFinder::extendTimeLimit(300);
4514
        switch ($this->imgLib) {
4515
            case 'imagick':
0 ignored issues
show
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...
4516
4517
                try {
4518
                    $img = new imagick($path);
4519
                } catch (Exception $e) {
4520
                    return false;
4521
                }
4522
4523
                // Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
4524
                // resize bench: http://app-mgng.rhcloud.com/9
4525
                // resize sample: http://www.dylanbeattie.net/magick/filters/result.html
4526
                $filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
4527
4528
                $ani = ($img->getNumberImages() > 1);
4529
                if ($ani && is_null($destformat)) {
4530
                    $img = $img->coalesceImages();
4531
                    do {
4532
                        $img->resizeImage($size_w, $size_h, $filter, 1);
4533
                    } while ($img->nextImage());
4534
                    $img = $img->optimizeImageLayers();
4535
                    $result = $img->writeImages($path, true);
0 ignored issues
show
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...
4536
                } else {
4537
                    if ($ani) {
4538
                        $img->setFirstIterator();
4539
                    }
4540
                    if (strtoupper($img->getImageFormat()) === 'JPEG') {
4541
                        $img->setImageCompression(imagick::COMPRESSION_JPEG);
4542
                        $img->setImageCompressionQuality($jpgQuality);
4543
                        if (isset($options['preserveExif']) && ! $options['preserveExif']) {
4544
                            try {
4545
                                $orientation = $img->getImageOrientation();
4546
                            } catch (ImagickException $e) {
4547
                                $orientation = 0;
4548
                            }
4549
                            $img->stripImage();
4550
                            if ($orientation) {
4551
                                $img->setImageOrientation($orientation);
4552
                            }
4553
                        }
4554
                    }
4555
                    $img->resizeImage($size_w, $size_h, $filter, true);
4556
                    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...
4557
                        $result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
$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...
4558
                    } else {
4559
                        $result = $img->writeImage($path);
4560
                    }
4561
                }
4562
4563
                $img->clear();
4564
4565
                return $result ? $path : false;
4566
4567
                break;
0 ignored issues
show
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...
4568
4569
            case 'convert':
4570
                extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s));
0 ignored issues
show
$this->imageMagickConver...ormat, $jpgQuality, $s) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
4571
                $filter = ($destformat === 'png' /* createTmb */)? '-filter Box' : '-filter Lanczos';
4572
                $strip = (isset($options['preserveExif']) && ! $options['preserveExif'])? ' -strip' : '';
0 ignored issues
show
$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...
4573
                //$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...
4574
                $cmd = sprintf('convert %s%s%s %s -geometry %dx%d! %s %s', $quotedPath, $coalesce, $jpgQuality, $filter, $size_w, $size_h, $deconstruct, $quotedDstPath);
4575
4576
                $result = false;
4577
                if ($this->procExec($cmd) === 0) {
4578
                    $result = true;
4579
                }
4580
                return $result ? $path : false;
4581
4582
                break;
0 ignored issues
show
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...
4583
4584
            case 'gd':
4585
                $img = $this->gdImageCreate($path,$s['mime']);
4586
4587
                if ($img && false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
4588
4589
                    $bgNum = false;
4590
                    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...
4591
                        $bgIdx = imagecolortransparent($img);
4592
                        if ($bgIdx !== -1) {
4593
                            $c = imagecolorsforindex($img, $bgIdx);
4594
                            $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']);
4595
                            imagefill($tmp, 0, 0, $bgNum);
4596
                            imagecolortransparent($tmp, $bgNum);
4597
                        }
4598
                    }
4599
                    if ($bgNum === false) {
4600
                        $this->gdImageBackground($tmp, 'transparent');
4601
                    }
4602
4603
                    if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
4604
                        return false;
4605
                    }
4606
4607
                    $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
4608
4609
                    imagedestroy($img);
4610
                    imagedestroy($tmp);
4611
4612
                    return $result ? $path : false;
4613
4614
                }
4615
                break;
4616
        }
4617
4618
        return false;
4619
      }
4620
4621
    /**
4622
     * Crop image
4623
     *
4624
     * @param  string   $path               image file
4625
     * @param  int      $width              crop width
4626
     * @param  int      $height             crop height
4627
     * @param  bool        $x                  crop left offset
4628
     * @param  bool        $y                  crop top offset
4629
     * @param  string   $destformat         image destination format
4630
     * @param  int      $jpgQuality         JEPG quality (1-100)
4631
     * @return string|false
4632
     * @author Dmitry (dio) Levashov
4633
     * @author Alexey Sukhotin
4634
     **/
4635
      protected function imgCrop($path, $width, $height, $x, $y, $destformat = null, $jpgQuality = null) {
4636
        if (($s = getimagesize($path)) == false) {
4637
            return false;
4638
        }
4639
4640
        $result = false;
0 ignored issues
show
$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...
4641
4642
        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...
4643
            $jpgQuality = $this->options['jpgQuality'];
4644
        }
4645
4646
        elFinder::extendTimeLimit(300);
4647
        switch ($this->imgLib) {
4648
            case 'imagick':
0 ignored issues
show
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...
4649
4650
                try {
4651
                    $img = new imagick($path);
4652
                } catch (Exception $e) {
4653
                    return false;
4654
                }
4655
4656
                $ani = ($img->getNumberImages() > 1);
4657
                if ($ani && is_null($destformat)) {
4658
                    $img = $img->coalesceImages();
4659
                    do {
4660
                        $img->setImagePage($s[0], $s[1], 0, 0);
4661
                        $img->cropImage($width, $height, $x, $y);
4662
                        $img->setImagePage($width, $height, 0, 0);
4663
                    } while ($img->nextImage());
4664
                    $img = $img->optimizeImageLayers();
4665
                    $result = $img->writeImages($path, true);
0 ignored issues
show
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...
4666
                } else {
4667
                    if ($ani) {
4668
                        $img->setFirstIterator();
4669
                    }
4670
                    $img->setImagePage($s[0], $s[1], 0, 0);
4671
                    $img->cropImage($width, $height, $x, $y);
4672
                    $img->setImagePage($width, $height, 0, 0);
4673
                    $result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
$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...
4674
                }
4675
4676
                $img->clear();
4677
4678
                return $result ? $path : false;
4679
4680
                break;
0 ignored issues
show
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...
4681
4682
            case 'convert':
4683
                extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s));
0 ignored issues
show
$this->imageMagickConver...ormat, $jpgQuality, $s) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
4684
                $cmd = sprintf('convert %s%s%s -crop %dx%d+%d+%d%s %s', $quotedPath, $coalesce, $jpgQuality, $width, $height, $x, $y, $deconstruct, $quotedDstPath);
4685
4686
                $result = false;
4687
                if ($this->procExec($cmd) === 0) {
4688
                    $result = true;
4689
                }
4690
                return $result ? $path : false;
4691
4692
                break;
0 ignored issues
show
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...
4693
4694
            case 'gd':
4695
                $img = $this->gdImageCreate($path,$s['mime']);
4696
4697
                if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) {
4698
4699
                    $bgNum = false;
4700
                    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...
4701
                        $bgIdx = imagecolortransparent($img);
4702
                        if ($bgIdx !== -1) {
4703
                            $c = imagecolorsforindex($img, $bgIdx);
4704
                            $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']);
4705
                            imagefill($tmp, 0, 0, $bgNum);
4706
                            imagecolortransparent($tmp, $bgNum);
4707
                        }
4708
                    }
4709
                    if ($bgNum === false) {
4710
                        $this->gdImageBackground($tmp, 'transparent');
4711
                    }
4712
4713
                    $size_w = $width;
4714
                    $size_h = $height;
4715
4716
                    if ($s[0] < $width || $s[1] < $height) {
4717
                        $size_w = $s[0];
4718
                        $size_h = $s[1];
4719
                    }
4720
4721
                    if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
4722
                        return false;
4723
                    }
4724
4725
                    $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
4726
4727
                    imagedestroy($img);
4728
                    imagedestroy($tmp);
4729
4730
                    return $result ? $path : false;
4731
4732
                }
4733
                break;
4734
        }
4735
4736
        return false;
4737
    }
4738
4739
    /**
4740
     * Put image to square
4741
     *
4742
     * @param  string $path image file
4743
     * @param  int $width square width
4744
     * @param  int $height square height
4745
     * @param int|string $align reserved
4746
     * @param int|string $valign reserved
4747
     * @param  string $bgcolor square background color in #rrggbb format
4748
     * @param  string $destformat image destination format
4749
     * @param  int $jpgQuality JEPG quality (1-100)
4750
     * @return false|string
4751
     * @author Dmitry (dio) Levashov
4752
     * @author Alexey Sukhotin
4753
     */
4754
    protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null, $jpgQuality = null) {
0 ignored issues
show
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...
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...
4755
        if (($s = getimagesize($path)) == false) {
4756
            return false;
4757
        }
4758
4759
        $result = false;
0 ignored issues
show
$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...
4760
4761
        /* Coordinates for image over square aligning */
4762
        $y = ceil(abs($height - $s[1]) / 2);
4763
        $x = ceil(abs($width - $s[0]) / 2);
4764
4765
        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...
4766
            $jpgQuality = $this->options['jpgQuality'];
4767
        }
4768
4769
        elFinder::extendTimeLimit(300);
4770
        switch ($this->imgLib) {
4771
            case 'imagick':
4772
                try {
4773
                    $img = new imagick($path);
4774
                } catch (Exception $e) {
4775
                    return false;
4776
                }
4777
4778
                if ($bgcolor === 'transparent') {
4779
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
4780
                }
4781
                $ani = ($img->getNumberImages() > 1);
4782
                if ($ani && is_null($destformat)) {
4783
                    $img1 = new Imagick();
4784
                    $img1->setFormat('gif');
4785
                    $img = $img->coalesceImages();
4786
                    do {
4787
                        $gif = new Imagick();
4788
                        $gif->newImage($width, $height, new ImagickPixel($bgcolor));
4789
                        $gif->setImageColorspace($img->getImageColorspace());
4790
                        $gif->setImageFormat('gif');
4791
                        $gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
4792
                        $gif->setImageDelay($img->getImageDelay());
4793
                        $gif->setImageIterations($img->getImageIterations());
4794
                        $img1->addImage($gif);
4795
                        $gif->clear();
4796
                    } while ($img->nextImage());
4797
                    $img1 = $img1->optimizeImageLayers();
4798
                    $result = $img1->writeImages($path, true);
0 ignored issues
show
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...
4799
                } else {
4800
                    if ($ani) {
4801
                        $img->setFirstIterator();
4802
                    }
4803
                    $img1 = new Imagick();
4804
                    $img1->newImage($width, $height, new ImagickPixel($bgcolor));
4805
                    $img1->setImageColorspace($img->getImageColorspace());
4806
                    $img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
4807
                    $result = $this->imagickImage($img1, $path, $destformat, $jpgQuality);
0 ignored issues
show
$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...
4808
                }
4809
4810
                $img1->clear();
4811
                $img->clear();
4812
                return $result ? $path : false;
4813
4814
                break;
0 ignored issues
show
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...
4815
4816
            case 'convert':
4817
                extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s));
0 ignored issues
show
$this->imageMagickConver...ormat, $jpgQuality, $s) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
4818
                if ($bgcolor === 'transparent') {
4819
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
4820
                }
4821
                $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);
4822
4823
                $result = false;
4824
                if ($this->procExec($cmd) === 0) {
4825
                    $result = true;
4826
                }
4827
                return $result ? $path : false;
4828
4829
                break;
0 ignored issues
show
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...
4830
4831
            case 'gd':
4832
                $img = $this->gdImageCreate($path,$s['mime']);
4833
4834
                if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
4835
4836
                    $this->gdImageBackground($tmp, $bgcolor);
4837
                    if ($bgcolor === 'transparent' && ($destformat === 'png' || $s[2] === IMAGETYPE_PNG)) {
4838
                        $bgNum = imagecolorallocatealpha($tmp, 255, 255, 255, 127);
4839
                        imagefill($tmp, 0, 0, $bgNum);
4840
                    }
4841
4842
                    if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
4843
                        return false;
4844
                    }
4845
4846
                    $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
4847
4848
                    imagedestroy($img);
4849
                    imagedestroy($tmp);
4850
4851
                    return $result ? $path : false;
4852
                }
4853
                break;
4854
        }
4855
4856
        return false;
4857
    }
4858
4859
    /**
4860
     * Rotate image
4861
     *
4862
     * @param  string   $path               image file
4863
     * @param  int      $degree             rotete degrees
4864
     * @param  string   $bgcolor            square background color in #rrggbb format
4865
     * @param  string   $destformat         image destination format
4866
     * @param  int      $jpgQuality         JEPG quality (1-100)
4867
     * @return string|false
4868
     * @author nao-pon
4869
     * @author Troex Nevelin
4870
     **/
4871
    protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null, $jpgQuality = null) {
4872
        if (($s = getimagesize($path)) == false || $degree % 360 === 0) {
4873
            return false;
4874
        }
4875
4876
        $result = false;
4877
4878
        // try lossless rotate
4879
        if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
4880
            $count = ($degree / 90) % 4;
4881
            $exiftran = array(
4882
                1 => '-9',
4883
                2 => '-1',
4884
                3 => '-2'
4885
            );
4886
            $jpegtran = array(
4887
                1 => '90',
4888
                2 => '180',
4889
                3 => '270'
4890
            );
4891
            $quotedPath = escapeshellarg($path);
4892
            $cmds = array(
4893
                'exiftran -i '.$exiftran[$count].' '.$path,
4894
                'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
4895
            );
4896
            foreach($cmds as $cmd) {
4897
                if ($this->procExec($cmd) === 0) {
4898
                    $result = true;
4899
                    break;
4900
                }
4901
            }
4902
            if ($result) {
4903
                return $path;
4904
            }
4905
        }
4906
4907
        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...
4908
            $jpgQuality = $this->options['jpgQuality'];
4909
        }
4910
4911
        elFinder::extendTimeLimit(300);
4912
        switch ($this->imgLib) {
4913
            case 'imagick':
4914
                try {
4915
                    $img = new imagick($path);
4916
                } catch (Exception $e) {
4917
                    return false;
4918
                }
4919
4920
                if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) {
4921
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
4922
                }
4923
                if ($img->getNumberImages() > 1) {
4924
                    $img = $img->coalesceImages();
4925
                    do {
4926
                        $img->rotateImage(new ImagickPixel($bgcolor), $degree);
4927
                    } while ($img->nextImage());
4928
                    $img = $img->optimizeImageLayers();
4929
                    $result = $img->writeImages($path, true);
0 ignored issues
show
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...
4930
                } else {
4931
                    $img->rotateImage(new ImagickPixel($bgcolor), $degree);
4932
                    $result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
$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...
4933
                }
4934
                $img->clear();
4935
                return $result ? $path : false;
4936
4937
                break;
0 ignored issues
show
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...
4938
4939
            case 'convert':
4940
                extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s));
0 ignored issues
show
$this->imageMagickConver...ormat, $jpgQuality, $s) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
4941
                if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) {
4942
                    $bgcolor = 'rgba(255, 255, 255, 0.0)';
4943
                }
4944
                $cmd = sprintf('convert %s%s%s -background "%s" -rotate %d%s %s', $quotedPath, $coalesce, $jpgQuality, $bgcolor, $degree, $deconstruct, $quotedDstPath);
4945
4946
                $result = false;
4947
                if ($this->procExec($cmd) === 0) {
4948
                    $result = true;
4949
                }
4950
                return $result ? $path : false;
4951
4952
                break;
0 ignored issues
show
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...
4953
4954
            case 'gd':
4955
                $img = $this->gdImageCreate($path,$s['mime']);
4956
4957
                $degree = 360 - $degree;
4958
4959
                $bgNum = -1;
4960
                $bgIdx = false;
4961
                if ($s[2] === IMAGETYPE_GIF) {
4962
                    $bgIdx = imagecolortransparent($img);
4963
                    if ($bgIdx !== -1) {
4964
                        $c = imagecolorsforindex($img, $bgIdx);
4965
                        $w = imagesx($img);
4966
                        $h = imagesy($img);
4967
                        $newImg = imagecreatetruecolor($w, $h);
4968
                        imagepalettecopy($newImg, $img);
4969
                        $bgNum = imagecolorallocate($newImg, $c['red'], $c['green'], $c['blue']);
4970
                        imagefill($newImg, 0, 0, $bgNum);
4971
                        imagecolortransparent($newImg, $bgNum);
4972
                        imagecopy($newImg, $img, 0, 0, 0, 0, $w, $h);
4973
                        imagedestroy($img);
4974
                        $img = $newImg;
4975
                        $newImg = null;
0 ignored issues
show
$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...
4976
                    }
4977
                } else if ($s[2] === IMAGETYPE_PNG) {
4978
                    $bgNum = imagecolorallocatealpha($img, 255, 255, 255, 127);
4979
                }
4980
                if ($bgNum === -1) {
4981
                    list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4982
                    $bgNum = imagecolorallocate($img, $r, $g, $b);
4983
                }
4984
4985
                $tmp = imageRotate($img, $degree, $bgNum);
4986
                if ($bgIdx !== -1) {
4987
                    imagecolortransparent($tmp, $bgNum);
4988
                }
4989
4990
                $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
4991
4992
                imageDestroy($img);
4993
                imageDestroy($tmp);
4994
4995
                return $result ? $path : false;
4996
4997
                break;
0 ignored issues
show
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...
4998
        }
4999
5000
        return false;
5001
    }
5002
5003
    /**
5004
     * Execute shell command
5005
     *
5006
     * @param  string $command command line
5007
     * @param  array $output stdout strings
5008
     * @param array|int $return_var process exit code
5009
     * @param  array $error_output stderr strings
5010
     * @return int exit code
5011
     * @author Alexey Sukhotin
5012
     */
5013
    protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
5014
5015
        static $allowed = null;
5016
5017
        if ($allowed === null) {
5018
            if ($allowed = function_exists('proc_open')) {
5019
                if ($disabled = ini_get('disable_functions')) {
5020
                    $funcs = array_map('trim', explode(',', $disabled));
5021
                    $allowed = ! in_array('proc_open', $funcs);
5022
                }
5023
            }
5024
        }
5025
5026
        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...
5027
            $return_var = -1;
5028
            return $return_var;
5029
        }
5030
5031
        if (! $command) {
5032
            $return_var = 0;
5033
            return $return_var;
5034
        }
5035
5036
        $descriptorspec = array(
5037
            0 => array("pipe", "r"),  // stdin
5038
            1 => array("pipe", "w"),  // stdout
5039
            2 => array("pipe", "w")   // stderr
5040
        );
5041
5042
        $process = proc_open($command, $descriptorspec, $pipes, null, null);
5043
5044
        if (is_resource($process)) {
5045
5046
            fclose($pipes[0]);
5047
5048
            $tmpout = '';
0 ignored issues
show
$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...
5049
            $tmperr = '';
0 ignored issues
show
$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...
5050
5051
            $output = stream_get_contents($pipes[1]);
5052
            $error_output = stream_get_contents($pipes[2]);
5053
5054
            fclose($pipes[1]);
5055
            fclose($pipes[2]);
5056
            $return_var = proc_close($process);
5057
5058
        } else {
5059
            $return_var = -1;
5060
        }
5061
5062
        return $return_var;
5063
5064
    }
5065
5066
    /**
5067
     * Remove thumbnail, also remove recursively if stat is directory
5068
     *
5069
     * @param  string  $stat  file stat
5070
     * @return void
5071
     * @author Dmitry (dio) Levashov
5072
     * @author Naoki Sawada
5073
     * @author Troex Nevelin
5074
     **/
5075
    protected function rmTmb($stat) {
5076
        if ($stat['mime'] === 'directory') {
5077
            foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
5078
                elFinder::extendTimeLimit(30);
5079
                $name = $this->basenameCE($p);
5080
                $name != '.' && $name != '..' && $this->rmTmb($this->stat($p));
0 ignored issues
show
$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...
5081
            }
5082
        } else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
5083
            $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
5084
            file_exists($tmb) && unlink($tmb);
5085
            clearstatcache();
5086
        }
5087
    }
5088
5089
    /**
5090
     * Create an gd image according to the specified mime type
5091
     *
5092
     * @param string $path image file
5093
     * @param string $mime
5094
     * @return gd image resource identifier
5095
     */
5096
    protected function gdImageCreate($path,$mime){
5097
        switch($mime){
5098
            case 'image/jpeg':
5099
            return imagecreatefromjpeg($path);
5100
5101
            case 'image/png':
5102
            return imagecreatefrompng($path);
5103
5104
            case 'image/gif':
5105
            return imagecreatefromgif($path);
5106
5107
            case 'image/x-ms-bmp':
5108
            if (!function_exists('imagecreatefrombmp')) {
5109
                include_once dirname(__FILE__).'/libs/GdBmp.php';
5110
            }
5111
            return imagecreatefrombmp($path);
5112
5113
            case 'image/xbm':
5114
            return imagecreatefromxbm($path);
5115
5116
            case 'image/xpm':
5117
            return imagecreatefromxpm($path);
5118
        }
5119
        return false;
5120
    }
5121
5122
    /**
5123
     * Output gd image to file
5124
     *
5125
     * @param resource $image gd image resource
5126
     * @param string $filename The path to save the file to.
5127
     * @param string $destformat The Image type to use for $filename
5128
     * @param string $mime The original image mime type
5129
     * @param int $jpgQuality JEPG quality (1-100)
5130
     * @return bool
5131
     */
5132
    protected function gdImage($image, $filename, $destformat, $mime, $jpgQuality = null ){
5133
5134
        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...
5135
            $jpgQuality = $this->options['jpgQuality'];
5136
        }
5137
        if ($destformat) {
5138
            switch ($destformat) {
5139
                case 'jpg':
5140
                    $mime = 'image/jpeg';
5141
                    break;
5142
                case 'gif':
5143
                    $mime = 'image/gif';
5144
                    break;
5145
                case 'png':
5146
                default:
5147
                    $mime = 'image/png';
5148
                    break;
5149
            }
5150
        }
5151
        switch ($mime) {
5152
            case 'image/gif':
5153
                return imagegif($image, $filename);
5154
            case 'image/jpeg':
5155
                return imagejpeg($image, $filename, $jpgQuality);
5156
            case 'image/wbmp':
5157
                return imagewbmp($image, $filename);
5158
            case 'image/png':
5159
            default:
5160
                return imagepng($image, $filename);
5161
        }
5162
    }
5163
5164
    /**
5165
     * Output imagick image to file
5166
     *
5167
     * @param resource $img imagick image resource
5168
     * @param string $filename The path to save the file to.
5169
     * @param string $destformat The Image type to use for $filename
5170
     * @param int $jpgQuality JEPG quality (1-100)
5171
     * @return bool
5172
     */
5173
    protected function imagickImage($img, $filename, $destformat, $jpgQuality = null ){
5174
5175
        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...
5176
            $jpgQuality = $this->options['jpgQuality'];
5177
        }
5178
5179
        try {
5180
            if ($destformat) {
5181
                if ($destformat === 'gif') {
5182
                    $img->setImageFormat('gif');
0 ignored issues
show
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...
5183
                } else if ($destformat === 'png') {
5184
                    $img->setImageFormat('png');
0 ignored issues
show
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...
5185
                } else if ($destformat === 'jpg') {
5186
                    $img->setImageFormat('jpeg');
0 ignored issues
show
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...
5187
                }
5188
            }
5189
            if (strtoupper($img->getImageFormat()) === 'JPEG') {
0 ignored issues
show
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...
5190
                $img->setImageCompression(imagick::COMPRESSION_JPEG);
0 ignored issues
show
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...
5191
                $img->setImageCompressionQuality($jpgQuality);
0 ignored issues
show
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...
5192
                try {
5193
                    $orientation = $img->getImageOrientation();
0 ignored issues
show
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...
5194
                } catch (ImagickException $e) {
5195
                    $orientation = 0;
5196
                }
5197
                $img->stripImage();
0 ignored issues
show
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...
5198
                if ($orientation) {
5199
                    $img->setImageOrientation($orientation);
0 ignored issues
show
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...
5200
                }
5201
            }
5202
            $result = $img->writeImage($filename);
0 ignored issues
show
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...
5203
        } catch (Exception $e) {
5204
            $result = false;
5205
        }
5206
5207
        return $result;
5208
    }
5209
5210
    /**
5211
     * Assign the proper background to a gd image
5212
     *
5213
     * @param resource $image gd image resource
5214
     * @param string $bgcolor background color in #rrggbb format
5215
     */
5216
    protected function gdImageBackground($image, $bgcolor){
5217
5218
        if ($bgcolor === 'transparent'){
5219
            imagealphablending($image, false);
5220
            imagesavealpha($image, true);
5221
        } else {
5222
            list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
5223
            $bgcolor1 = imagecolorallocate($image, $r, $g, $b);
5224
            imagefill($image, 0, 0, $bgcolor1);
5225
        }
5226
    }
5227
5228
    /**
5229
     * Prepare variables for exec convert of ImageMagick
5230
     *
5231
     * @param  string  $path
5232
     * @param  string  $destformat
5233
     * @param  int     $jpgQuality
5234
     * @param  array   $imageSize
5235
     * @return array
5236
     */
5237
    protected function imageMagickConvertPrepare($path, $destformat, $jpgQuality, $imageSize = null) {
5238
        if (is_null($imageSize)) {
5239
            $imageSize = getimagesize($path);
5240
        }
5241
        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...
5242
            return array();
5243
        }
5244
        $srcType = $this->getExtentionByMime($imageSize['mime'], ':');
5245
        $ani = false;
5246
        $cmd = 'identify ' . escapeshellarg($srcType . $path);
5247
        if ($this->procExec($cmd, $o) === 0) {
5248
            $ani = preg_split('/(?:\r\n|\n|\r)/', trim($o));
5249
            if (count($ani) < 2) {
5250
                $ani = false;
5251
            }
5252
        }
5253
        $coalesce = $index = '';
5254
        $deconstruct = ' +repage';
5255
        if ($ani) {
5256
            if (is_null($destformat)) {
5257
                $coalesce = ' -coalesce -repage 0x0';
5258
                $deconstruct = ' +repage -deconstruct -layers optimize';
5259
            } else {
5260
                $index = '[0]';
5261
                if ($srcType === 'ico:') {
5262
                    foreach($ani as $_i => $_info) {
5263
                        if (preg_match('/ (\d+)x(\d+) /', $_info, $m)) {
5264
                            if ($m[1] == $imageSize[0] && $m[2] == $imageSize[1]) {
5265
                                $index = '[' . $_i . ']';
5266
                                break;
5267
                            }
5268
                        }
5269
                    }
5270
                }
5271
            }
5272
        }
5273
        if ($imageSize[2] === IMAGETYPE_JPEG || $imageSize[2] === IMAGETYPE_JPEG2000) {
5274
            $jpgQuality = ' -quality ' . $jpgQuality;
5275
        } else {
5276
            $jpgQuality = '';
5277
        }
5278
        $quotedPath = escapeshellarg($srcType . $path . $index);
5279
        $quotedDstPath = escapeshellarg(($destformat? ($destformat . ':') : $srcType) . $path);
5280
        return compact('ani', 'index', 'coalesce', 'deconstruct', 'jpgQuality', 'quotedPath', 'quotedDstPath');
5281
    }
5282
5283
    /*********************** misc *************************/
5284
5285
    /**
5286
     * Return smart formatted date
5287
     *
5288
     * @param  int     $ts  file timestamp
5289
     * @return string
5290
     * @author Dmitry (dio) Levashov
5291
     **/
5292
    // 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...
5293
    //     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...
5294
    //         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...
5295
    //     }
5296
    //
5297
    //     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...
5298
    //         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...
5299
    //     }
5300
    //
5301
    //     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...
5302
    // }
5303
5304
    /**
5305
    * Find position of first occurrence of string in a string with multibyte support
5306
    *
5307
    * @param  string  $haystack  The string being checked.
5308
    * @param  string  $needle    The string to find in haystack.
5309
    * @param  int     $offset    The search offset. If it is not specified, 0 is used.
5310
    * @return int|bool
5311
    * @author Alexey Sukhotin
5312
    **/
5313
    protected function stripos($haystack , $needle , $offset = 0) {
5314
        if (function_exists('mb_stripos')) {
5315
            return mb_stripos($haystack , $needle , $offset, 'UTF-8');
5316
        } else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
5317
            return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset);
5318
        }
5319
        return stripos($haystack , $needle , $offset);
5320
    }
5321
5322
    /**
5323
     * Get server side available archivers
5324
     *
5325
     * @param bool $use_cache
5326
     * @return array
5327
     */
5328
    protected function getArchivers($use_cache = true) {
5329
5330
        $sessionKey = 'ARCHIVERS_CACHE';
5331
        if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
5332
            return $this->sessionCache[$sessionKey];
5333
        }
5334
5335
        $arcs = array(
5336
            'create'  => array(),
5337
            'extract' => array()
5338
        );
5339
5340
        if ($this->procExec('') === 0) {
5341
5342
            $this->procExec('tar --version', $o, $ctar);
5343
5344
            if ($ctar == 0) {
5345
                $arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
5346
                $arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
5347
                unset($o);
5348
                $test = $this->procExec('gzip --version', $o, $c);
0 ignored issues
show
$test is not used, you could remove the assignment.

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

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

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

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

Loading history...
5349
                if ($c == 0) {
5350
                    $arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
5351
                    $arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
5352
                }
5353
                unset($o);
5354
                $test = $this->procExec('bzip2 --version', $o, $c);
0 ignored issues
show
$test is not used, you could remove the assignment.

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

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

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

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

Loading history...
5355
                if ($c == 0) {
5356
                    $arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
5357
                    $arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
5358
                }
5359
                unset($o);
5360
                $test = $this->procExec('xz --version', $o, $c);
0 ignored issues
show
$test is not used, you could remove the assignment.

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

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

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

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

Loading history...
5361
                if ($c == 0) {
5362
                    $arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
5363
                    $arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
5364
                }
5365
            }
5366
            unset($o);
5367
            $this->procExec('zip -v', $o, $c);
5368
            if ($c == 0) {
5369
                $arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
5370
            }
5371
            unset($o);
5372
            $this->procExec('unzip --help', $o, $c);
5373
            if ($c == 0) {
5374
                $arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
5375
            }
5376
            unset($o);
5377
            $this->procExec('rar --version', $o, $c);
5378
            if ($c == 0 || $c == 7) {
5379
                $arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
5380
                $arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
5381
            } else {
5382
                unset($o);
5383
                $test = $this->procExec('unrar', $o, $c);
0 ignored issues
show
$test is not used, you could remove the assignment.

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

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

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

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

Loading history...
5384
                if ($c==0 || $c == 7) {
5385
                    $arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
5386
                }
5387
            }
5388
            unset($o);
5389
            $this->procExec('7za --help', $o, $c);
5390
            if ($c == 0) {
5391
                $arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
5392
                $arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
5393
5394
                if (empty($arcs['create']['application/zip'])) {
5395
                    $arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
5396
                }
5397
                if (empty($arcs['extract']['application/zip'])) {
5398
                    $arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
5399
                }
5400
                if (empty($arcs['create']['application/x-tar'])) {
5401
                    $arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
5402
                }
5403
                if (empty($arcs['extract']['application/x-tar'])) {
5404
                    $arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
5405
                }
5406
            } else if (substr(PHP_OS,0,3) === 'WIN') {
5407
                // check `7z` for Windows server.
5408
                unset($o);
5409
                $this->procExec('7z', $o, $c);
5410
                if ($c == 0) {
5411
                    $arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
5412
                    $arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
5413
5414
                    if (empty($arcs['create']['application/zip'])) {
5415
                        $arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
5416
                    }
5417
                    if (empty($arcs['extract']['application/zip'])) {
5418
                        $arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
5419
                    }
5420
                    if (empty($arcs['create']['application/x-tar'])) {
5421
                        $arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
5422
                    }
5423
                    if (empty($arcs['extract']['application/x-tar'])) {
5424
                        $arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
5425
                    }
5426
                }
5427
            }
5428
5429
        }
5430
5431
        // Use PHP ZipArchive Class
5432
        if (class_exists('ZipArchive', false)) {
5433
            if (empty($arcs['create']['application/zip'])) {
5434
                $arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => array('self', 'zipArchiveZip'), 'ext' => 'zip');
5435
            }
5436
            if (empty($arcs['extract']['application/zip'])) {
5437
                $arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => array('self', 'zipArchiveUnzip'), 'ext' => 'zip');
5438
            }
5439
        }
5440
5441
        $this->sessionCache[$sessionKey] = $arcs;
5442
        $this->session->set($this->id, $this->sessionCache);
5443
        return $arcs;
5444
    }
5445
5446
    /**
5447
     * Resolve relative / (Unix-like)absolute path
5448
     *
5449
     * @param string $path  target path
5450
     * @param string $base  base path
5451
     * @return string
5452
     */
5453
    protected function getFullPath($path, $base) {
5454
        $separator = $this->separator;
5455
        $systemroot = $this->systemRoot;
5456
5457
        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...
5458
            $base = $systemroot . substr($base, 1);
5459
        }
5460
5461
        // 'Here'
5462
        if ($path === '' || $path === '.' . $separator) return $base;
5463
5464
        $sepquoted = preg_quote($separator, '#');
5465
5466
        if (substr($path, 0, 3) === '..' . $separator) {
5467
            $path = $base . $separator . $path;
5468
        }
5469
        // normalize `/../`
5470
        $normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#'; // '#(/)[^\/]+/\.\./#'
5471
        while(preg_match($normreg, $path)) {
5472
            $path = preg_replace($normreg, '$1', $path, 1);
5473
        }
5474
5475
        // Absolute path
5476
        if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
5477
            return $path;
5478
        }
5479
5480
        $preg_separator = '#' . $sepquoted . '#';
5481
5482
        // Relative path from 'Here'
5483
        if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.') {
5484
            $arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
5485
            if ($arrn[0] !== '.') {
5486
                array_unshift($arrn, '.');
5487
            }
5488
            $arrn[0] = $base;
5489
            return join($separator, $arrn);
5490
        }
5491
5492
        return $path;
5493
    }
5494
5495
    /**
5496
     * Remove directory recursive on local file system
5497
     *
5498
     * @param string $dir Target dirctory path
5499
     * @return boolean
5500
     * @author Naoki Sawada
5501
     */
5502
    public function rmdirRecursive($dir) {
5503
        if (!is_link($dir) && is_dir($dir)) {
5504
            chmod($dir, 0777);
5505
            if ($handle = opendir($dir)) {
5506
                while (false !== ($file = readdir($handle))) {
5507
                    if ($file === '.' || $file === '..') {
5508
                        continue;
5509
                    }
5510
                    elFinder::extendTimeLimit(30);
5511
                    $path = $dir . DIRECTORY_SEPARATOR . $file;
5512
                    if (!is_link($dir) && is_dir($path)) {
5513
                        $this->rmdirRecursive($path);
5514
                    } else {
5515
                        chmod($path, 0666);
5516
                        unlink($path);
5517
                    }
5518
                }
5519
                closedir($handle);
5520
            }
5521
            return rmdir($dir);
5522
        } elseif (is_file($dir) || is_link($dir)) {
5523
            chmod($dir, 0666);
5524
            return unlink($dir);
5525
        }
5526
        return false;
5527
    }
5528
5529
    /**
5530
     * Create archive and return its path
5531
     *
5532
     * @param  string  $dir    target dir
5533
     * @param  array   $files  files names list
5534
     * @param  string  $name   archive name
5535
     * @param  array   $arc    archiver options
5536
     * @return string|bool
5537
     * @author Dmitry (dio) Levashov,
5538
     * @author Alexey Sukhotin
5539
     * @author Naoki Sawada
5540
     **/
5541
    protected function makeArchive($dir, $files, $name, $arc) {
5542
        if ($arc['cmd'] === 'phpfunction') {
5543
            if (is_callable($arc['argc'])) {
5544
                call_user_func_array($arc['argc'], array($dir, $files, $name));
5545
            }
5546
        } else {
5547
            $cwd = getcwd();
5548
            chdir($dir);
5549
5550
            foreach($files as $i => $file) {
5551
                $files[$i] = '.'.DIRECTORY_SEPARATOR.$file;
5552
            }
5553
            $files = array_map('escapeshellarg', $files);
5554
5555
            $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
5556
            $this->procExec($cmd, $o, $c);
5557
            chdir($cwd);
5558
        }
5559
        $path = $dir.DIRECTORY_SEPARATOR.$name;
5560
        return file_exists($path) ? $path : false;
5561
    }
5562
5563
    /**
5564
     * Unpack archive
5565
     *
5566
     * @param  string  $path   archive path
5567
     * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
5568
     * @param  bool    $remove remove archive ( unlink($path) )
5569
     * @return void
5570
     * @author Dmitry (dio) Levashov
5571
     * @author Alexey Sukhotin
5572
     * @author Naoki Sawada
5573
     **/
5574
    protected function unpackArchive($path, $arc, $remove = true) {
5575
        $dir = dirname($path);
5576
        if ($arc['cmd'] === 'phpfunction') {
5577
            if (is_callable($arc['argc'])) {
5578
                call_user_func_array($arc['argc'], array($path, $dir));
5579
            }
5580
        } else {
5581
            $cwd = getcwd();
5582
            chdir($dir);
5583
            $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
5584
            $this->procExec($cmd, $o, $c);
5585
            chdir($cwd);
5586
        }
5587
        $remove && unlink($path);
5588
    }
5589
5590
    /**
5591
     * Return files of target directory that is dotfiles excludes.
5592
     *
5593
     * @param  string $dir target directory path
5594
     * @return array
5595
     * @throws Exception
5596
     * @author Naoki Sawada
5597
     */
5598
    protected static function localScandir($dir) {
5599
        // PHP function scandir() is not work well in specific environment. I dont know why.
5600
        // ref. https://github.com/Studio-42/elFinder/issues/1248
5601
        $files = array();
5602
        if ($dh = opendir($dir)) {
5603
            while (false !== ($file = readdir($dh))) {
5604
                if ($file !== '.' && $file !== '..') {
5605
                    $files[] = $file;
5606
                }
5607
            }
5608
            closedir($dh);
5609
        } else {
5610
            throw new Exception('Can not open local directory.');
5611
        }
5612
        return $files;
5613
    }
5614
5615
    /**
5616
     * Create Zip archive using PHP class ZipArchive
5617
     *
5618
     * @param  string        $dir      target dir
5619
     * @param  array         $files    files names list
5620
     * @param  string|object $zipPath  Zip archive name
5621
     * @return bool
5622
     * @author Naoki Sawada
5623
     */
5624
    protected static function zipArchiveZip($dir, $files, $zipPath) {
5625
        try {
5626
            if ($start = is_string($zipPath)) {
5627
                $zip = new ZipArchive();
5628
                if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
5629
                    $zip = false;
5630
                }
5631
            } else {
5632
                $zip = $zipPath;
5633
            }
5634
            if ($zip) {
5635
                foreach($files as $file) {
5636
                    $path = $dir . DIRECTORY_SEPARATOR . $file;
5637
                    if (is_dir($path)) {
5638
                        $zip->addEmptyDir($file);
5639
                        $_files = array();
5640
                        if ($handle = opendir($path)) {
5641
                            while (false !== ($entry = readdir($handle))) {
5642
                                if ($entry !== "." && $entry !== "..") {
5643
                                    $_files[] = $file . DIRECTORY_SEPARATOR . $entry;
5644
                                }
5645
                            }
5646
                            closedir($handle);
5647
                        }
5648
                        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...
5649
                            self::zipArchiveZip($dir, $_files, $zip);
5650
                        }
5651
                    } else {
5652
                        $zip->addFile($path, $file);
5653
                    }
5654
                }
5655
                $start && $zip->close();
5656
            }
5657
        } catch (Exception $e) {
5658
            return false;
5659
        }
5660
        return true;
5661
    }
5662
5663
    /**
5664
     * Unpack Zip archive using PHP class ZipArchive
5665
     *
5666
     * @param  string $zipPath  Zip archive name
5667
     * @param  string $toDir    Extract to path
5668
     * @return bool
5669
     * @author Naoki Sawada
5670
     */
5671
    protected static function zipArchiveUnzip($zipPath, $toDir) {
5672
        try {
5673
            $zip = new ZipArchive();
5674
            if ($zip->open($zipPath) === true) {
5675
                $zip->extractTo($toDir);
5676
                $zip->close();
5677
            }
5678
        } catch (Exception $e) {
5679
            return false;
5680
        }
5681
        return true;
5682
    }
5683
5684
    /**==================================* abstract methods *====================================**/
5685
5686
    /*********************** paths/urls *************************/
5687
5688
    /**
5689
     * Return parent directory path
5690
     *
5691
     * @param  string  $path  file path
5692
     * @return string
5693
     * @author Dmitry (dio) Levashov
5694
     **/
5695
    abstract protected function _dirname($path);
5696
5697
    /**
5698
     * Return file name
5699
     *
5700
     * @param  string  $path  file path
5701
     * @return string
5702
     * @author Dmitry (dio) Levashov
5703
     **/
5704
    abstract protected function _basename($path);
5705
5706
    /**
5707
     * Join dir name and file name and return full path.
5708
     * Some drivers (db) use int as path - so we give to concat path to driver itself
5709
     *
5710
     * @param  string  $dir   dir path
5711
     * @param  string  $name  file name
5712
     * @return string
5713
     * @author Dmitry (dio) Levashov
5714
     **/
5715
    abstract protected function _joinPath($dir, $name);
5716
5717
    /**
5718
     * Return normalized path
5719
     *
5720
     * @param  string  $path  file path
5721
     * @return string
5722
     * @author Dmitry (dio) Levashov
5723
     **/
5724
    abstract protected function _normpath($path);
5725
5726
    /**
5727
     * Return file path related to root dir
5728
     *
5729
     * @param  string  $path  file path
5730
     * @return string
5731
     * @author Dmitry (dio) Levashov
5732
     **/
5733
    abstract protected function _relpath($path);
5734
5735
    /**
5736
     * Convert path related to root dir into real path
5737
     *
5738
     * @param  string  $path  rel file path
5739
     * @return string
5740
     * @author Dmitry (dio) Levashov
5741
     **/
5742
    abstract protected function _abspath($path);
5743
5744
    /**
5745
     * Return fake path started from root dir.
5746
     * Required to show path on client side.
5747
     *
5748
     * @param  string  $path  file path
5749
     * @return string
5750
     * @author Dmitry (dio) Levashov
5751
     **/
5752
    abstract protected function _path($path);
5753
5754
    /**
5755
     * Return true if $path is children of $parent
5756
     *
5757
     * @param  string  $path    path to check
5758
     * @param  string  $parent  parent path
5759
     * @return bool
5760
     * @author Dmitry (dio) Levashov
5761
     **/
5762
    abstract protected function _inpath($path, $parent);
5763
5764
    /**
5765
     * Return stat for given path.
5766
     * Stat contains following fields:
5767
     * - (int)    size    file size in b. required
5768
     * - (int)    ts      file modification time in unix time. required
5769
     * - (string) mime    mimetype. required for folders, others - optionally
5770
     * - (bool)   read    read permissions. required
5771
     * - (bool)   write   write permissions. required
5772
     * - (bool)   locked  is object locked. optionally
5773
     * - (bool)   hidden  is object hidden. optionally
5774
     * - (string) alias   for symlinks - link target path relative to root path. optionally
5775
     * - (string) target  for symlinks - link target path. optionally
5776
     *
5777
     * If file does not exists - returns empty array or false.
5778
     *
5779
     * @param  string  $path    file path
5780
     * @return array|false
5781
     * @author Dmitry (dio) Levashov
5782
     **/
5783
    abstract protected function _stat($path);
5784
5785
5786
    /***************** file stat ********************/
5787
5788
5789
    /**
5790
     * Return true if path is dir and has at least one childs directory
5791
     *
5792
     * @param  string  $path  dir path
5793
     * @return bool
5794
     * @author Dmitry (dio) Levashov
5795
     **/
5796
    abstract protected function _subdirs($path);
5797
5798
    /**
5799
     * Return object width and height
5800
     * Ususaly used for images, but can be realize for video etc...
5801
     *
5802
     * @param  string  $path  file path
5803
     * @param  string  $mime  file mime type
5804
     * @return string
5805
     * @author Dmitry (dio) Levashov
5806
     **/
5807
    abstract protected function _dimensions($path, $mime);
5808
5809
    /******************** file/dir content *********************/
5810
5811
    /**
5812
     * Return files list in directory
5813
     *
5814
     * @param  string  $path  dir path
5815
     * @return array
5816
     * @author Dmitry (dio) Levashov
5817
     **/
5818
    abstract protected function _scandir($path);
5819
5820
    /**
5821
     * Open file and return file pointer
5822
     *
5823
     * @param  string $path file path
5824
     * @param  string $mode open mode
5825
     * @return resource|false
5826
     * @author Dmitry (dio) Levashov
5827
     **/
5828
    abstract protected function _fopen($path, $mode="rb");
5829
5830
    /**
5831
     * Close opened file
5832
     *
5833
     * @param  resource  $fp    file pointer
5834
     * @param  string    $path  file path
5835
     * @return bool
5836
     * @author Dmitry (dio) Levashov
5837
     **/
5838
    abstract protected function _fclose($fp, $path='');
5839
5840
    /********************  file/dir manipulations *************************/
5841
5842
    /**
5843
     * Create dir and return created dir path or false on failed
5844
     *
5845
     * @param  string  $path  parent dir path
5846
     * @param string  $name  new directory name
5847
     * @return string|bool
5848
     * @author Dmitry (dio) Levashov
5849
     **/
5850
    abstract protected function _mkdir($path, $name);
5851
5852
    /**
5853
     * Create file and return it's path or false on failed
5854
     *
5855
     * @param  string  $path  parent dir path
5856
     * @param string  $name  new file name
5857
     * @return string|bool
5858
     * @author Dmitry (dio) Levashov
5859
     **/
5860
    abstract protected function _mkfile($path, $name);
5861
5862
    /**
5863
     * Create symlink
5864
     *
5865
     * @param  string  $source     file to link to
5866
     * @param  string  $targetDir  folder to create link in
5867
     * @param  string  $name       symlink name
5868
     * @return bool
5869
     * @author Dmitry (dio) Levashov
5870
     **/
5871
    abstract protected function _symlink($source, $targetDir, $name);
5872
5873
    /**
5874
     * Copy file into another file (only inside one volume)
5875
     *
5876
     * @param  string $source source file path
5877
     * @param $targetDir
5878
     * @param  string $name file name
5879
     * @return bool|string
5880
     * @internal param string $target target dir path
5881
     * @author Dmitry (dio) Levashov
5882
     */
5883
    abstract protected function _copy($source, $targetDir, $name);
5884
5885
    /**
5886
     * Move file into another parent dir.
5887
     * Return new file path or false.
5888
     *
5889
     * @param  string $source source file path
5890
     * @param $targetDir
5891
     * @param  string $name file name
5892
     * @return bool|string
5893
     * @internal param string $target target dir path
5894
     * @author Dmitry (dio) Levashov
5895
     */
5896
    abstract protected function _move($source, $targetDir, $name);
5897
5898
    /**
5899
     * Remove file
5900
     *
5901
     * @param  string  $path  file path
5902
     * @return bool
5903
     * @author Dmitry (dio) Levashov
5904
     **/
5905
    abstract protected function _unlink($path);
5906
5907
    /**
5908
     * Remove dir
5909
     *
5910
     * @param  string  $path  dir path
5911
     * @return bool
5912
     * @author Dmitry (dio) Levashov
5913
     **/
5914
    abstract protected function _rmdir($path);
5915
5916
    /**
5917
     * Create new file and write into it from file pointer.
5918
     * Return new file path or false on error.
5919
     *
5920
     * @param  resource  $fp   file pointer
5921
     * @param  string    $dir  target dir path
5922
     * @param  string    $name file name
5923
     * @param  array     $stat file stat (required by some virtual fs)
5924
     * @return bool|string
5925
     * @author Dmitry (dio) Levashov
5926
     **/
5927
    abstract protected function _save($fp, $dir, $name, $stat);
5928
5929
    /**
5930
     * Get file contents
5931
     *
5932
     * @param  string  $path  file path
5933
     * @return string|false
5934
     * @author Dmitry (dio) Levashov
5935
     **/
5936
    abstract protected function _getContents($path);
5937
5938
    /**
5939
     * Write a string to a file
5940
     *
5941
     * @param  string  $path     file path
5942
     * @param  string  $content  new file content
5943
     * @return bool
5944
     * @author Dmitry (dio) Levashov
5945
     **/
5946
    abstract protected function _filePutContents($path, $content);
5947
5948
    /**
5949
     * Extract files from archive
5950
     *
5951
     * @param  string  $path file path
5952
     * @param  array   $arc  archiver options
5953
     * @return bool
5954
     * @author Dmitry (dio) Levashov,
5955
     * @author Alexey Sukhotin
5956
     **/
5957
    abstract protected function _extract($path, $arc);
5958
5959
    /**
5960
     * Create archive and return its path
5961
     *
5962
     * @param  string  $dir    target dir
5963
     * @param  array   $files  files names list
5964
     * @param  string  $name   archive name
5965
     * @param  array   $arc    archiver options
5966
     * @return string|bool
5967
     * @author Dmitry (dio) Levashov,
5968
     * @author Alexey Sukhotin
5969
     **/
5970
    abstract protected function _archive($dir, $files, $name, $arc);
5971
5972
    /**
5973
     * Detect available archivers
5974
     *
5975
     * @return void
5976
     * @author Dmitry (dio) Levashov,
5977
     * @author Alexey Sukhotin
5978
     **/
5979
    abstract protected function _checkArchivers();
5980
5981
    /**
5982
     * Change file mode (chmod)
5983
     *
5984
     * @param  string  $path  file path
5985
     * @param  string  $mode  octal string such as '0755'
5986
     * @return bool
5987
     * @author David Bartle,
5988
     **/
5989
    abstract protected function _chmod($path, $mode);
5990
5991
5992
} // END class
5993