Completed
Push — 2.x ( 5243ac...ccb6e2 )
by Naoki
03:52
created

elFinderVolumeDriver::stat()   D

Complexity

Conditions 10
Paths 31

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 28
rs 4.8197
cc 10
eloc 18
nc 31
nop 1

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

namespace YourVendor;

class YourClass { }

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

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

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

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

// Better
class Router
{
    private $host;

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

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

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

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

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

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

// Better
class Router
{
    private $host;

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

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

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

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

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
595
		// set ARGS
596
		$this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST'? $_POST : $_GET;
597
		// set thumbnails path
598
		$path = $this->options['tmbPath'];
599
		if ($path) {
600 View Code Duplication
			if (!file_exists($path)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
601
				if (@mkdir($path)) {
602
					chmod($path, $this->options['tmbPathMode']);
603
				} else {
604
					$path = '';
605
				}
606
			} 
607
			
608
			if (is_dir($path) && is_readable($path)) {
609
				$this->tmbPath = $path;
610
				$this->tmbPathWritable = is_writable($path);
611
			}
612
		}
613
614
		// set image manipulation library
615
		$type = preg_match('/^(imagick|gd|auto)$/i', $this->options['imgLib'])
616
			? strtolower($this->options['imgLib'])
617
			: 'auto';
618
619
		if (($type == 'imagick' || $type == 'auto') && extension_loaded('imagick')) {
620
			$this->imgLib = 'imagick';
621
		} else {
622
			$this->imgLib = function_exists('gd_info') ? 'gd' : '';
623
		}
624
		
625
		// check 'statOwner' for command `chmod`
626
		if (empty($this->options['statOwner'])) {
627
			$this->disabled[] ='chmod';
628
		}
629
		
630
		// check 'mimeMap'
631
		if (!is_array($this->options['mimeMap'])) {
632
			$this->options['mimeMap'] = array();
633
		}
634
	}
635
	
636
	
637
	/*********************************************************************/
638
	/*                              PUBLIC API                           */
639
	/*********************************************************************/
640
	
641
	/**
642
	 * Return driver id. Used as a part of volume id.
643
	 *
644
	 * @return string
645
	 * @author Dmitry (dio) Levashov
646
	 **/
647
	public function driverId() {
648
		return $this->driverId;
649
	}
650
	
651
	/**
652
	 * Return volume id
653
	 *
654
	 * @return string
655
	 * @author Dmitry (dio) Levashov
656
	 **/
657
	public function id() {
658
		return $this->id;
659
	}
660
		
661
	/**
662
	 * Return debug info for client
663
	 *
664
	 * @return array
665
	 * @author Dmitry (dio) Levashov
666
	 **/
667
	public function debug() {
668
		return array(
669
			'id'         => $this->id(),
670
			'name'       => strtolower(substr(get_class($this), strlen('elfinderdriver'))),
671
			'mimeDetect' => $this->mimeDetect,
672
			'imgLib'     => $this->imgLib
673
		);
674
	}
675
676
	/**
677
	 * chmod a file or folder
678
	 *
679
	 * @param  string   $hash    file or folder hash to chmod
680
	 * @param  string   $mode    octal string representing new permissions
681
	 * @return array|false
682
	 * @author David Bartle
683
	 **/
684
	public function chmod($hash, $mode) {
685
		if ($this->commandDisabled('chmod')) {
686
			return $this->setError(elFinder::ERROR_PERM_DENIED);
687
		}
688
689
		if (!($file = $this->file($hash))) {
690
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
691
		}
692
693
		if (!$this->options['allowChmodReadOnly']) {
694 View Code Duplication
			if (!$this->attr($this->decode($hash), 'write', null, ($file['mime'] === 'directory'))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
695
				return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
696
			}
697
		}
698
699
		$path = $this->decode($hash);
700
701 View Code Duplication
		if ($this->convEncOut(!$this->_chmod($this->convEncIn($path), $mode))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
702
			return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']);
703
		}
704
705
		$this->clearcache();
706
707
		if ($file = $this->stat($path)) {
708
			$files = array($file);
709
			if ($file['mime'] === 'directory' && $write !== $file['write']) {
0 ignored issues
show
Bug introduced by
The variable $write does not exist. Did you forget to declare it?

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

Loading history...
710
				foreach ($this->getScandir($path) as $stat) {
711
					if ($this->mimeAccepted($stat['mime'])) {
712
						$files[] = $stat;
713
					}
714
				}
715
			}
716
			return $files;
717
		} else {
718
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
719
		}
720
	}
721
	
722
	/**
723
	 * stat a file or folder for elFinder cmd exec
724
	 *
725
	 * @param  string   $hash    file or folder hash to chmod
726
	 * @return array
727
	 * @author Naoki Sawada
728
	 **/
729
	public function fstat($hash) {
730
		$path = $this->decode($hash);
731
		return $this->stat($path);
732
	}
733
	
734
	
735
	public function clearstatcache() {
736
		clearstatcache();
737
		$this->cache = $this->dirsCache = array();
738
	}
739
	
740
	/**
741
	 * "Mount" volume.
742
	 * Return true if volume available for read or write, 
743
	 * false - otherwise
744
	 *
745
	 * @return bool
746
	 * @author Dmitry (dio) Levashov
747
	 * @author Alexey Sukhotin
748
	 **/
749
	public function mount(array $opts) {
0 ignored issues
show
Coding Style introduced by
mount uses the super-global variable $_SESSION which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
750
		if (!isset($opts['path']) || $opts['path'] === '') {
751
			return $this->setError('Path undefined.');;
752
		}
753
		
754
		$this->options = array_merge($this->options, $opts);
755
		$this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
756
		$this->root = $this->normpathCE($this->options['path']);
757
		$this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
758
		$this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator;
759
		
760
		// set server encoding
761
		if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') {
762
			$this->encoding = $this->options['encoding'];
763
		} else {
764
			$this->encoding = null;
765
		}
766
		
767
		$argInit = !empty($this->ARGS['init']);
768
		
769
		// session cache
770
		if ($argInit || ! isset($_SESSION[elFinder::$sessionCacheKey][$this->id])) {
771
			$_SESSION[elFinder::$sessionCacheKey][$this->id] = array();
772
		}
773
		$this->sessionCache = &$_SESSION[elFinder::$sessionCacheKey][$this->id];
774
		
775
		// default file attribute
776
		$this->defaults = array(
777
			'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
778
			'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
779
			'locked'  => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false,
780
			'hidden'  => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false
781
		);
782
783
		// root attributes
784
		$this->attributes[] = array(
785
			'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
786
			'locked'  => true,
787
			'hidden'  => false
788
		);
789
		// set files attributes
790 View Code Duplication
		if (!empty($this->options['attributes']) && is_array($this->options['attributes'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
791
			
792
			foreach ($this->options['attributes'] as $a) {
793
				// attributes must contain pattern and at least one rule
794
				if (!empty($a['pattern']) || count($a) > 1) {
795
					$this->attributes[] = $a;
796
				}
797
			}
798
		}
799
800
		if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
801
			$this->access = $this->options['accessControl'];
802
		}
803
		
804
		$this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
805
		$this->yesterday = $this->today-86400;
806
		
807
		// 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...
808
		if (!$this->init()) {
809
			return false;
810
		}
811
		
812
		// check some options is arrays
813
		$this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
814
			? $this->options['uploadAllow']
815
			: array();
816
			
817
		$this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
818
			? $this->options['uploadDeny']
819
			: array();
820
821
		if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
822
			$parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
823
			$this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
824
		} else { // telephat_mode off
825
			$this->uploadOrder = $this->options['uploadOrder'];
826
		}
827
			
828
		if (!empty($this->options['uploadMaxSize'])) {
829
			$size = ''.$this->options['uploadMaxSize'];
830
			$unit = strtolower(substr($size, strlen($size) - 1));
831
			$n = 1;
832
			switch ($unit) {
833
				case 'k':
834
					$n = 1024;
835
					break;
836
				case 'm':
837
					$n = 1048576;
838
					break;
839
				case 'g':
840
					$n = 1073741824;
841
			}
842
			$this->uploadMaxSize = intval($size)*$n;
843
		}
844
		// Set maximum to PHP_INT_MAX
845
		if (!defined('PHP_INT_MAX')) {
846
			define('PHP_INT_MAX', 2147483647);
847
		}
848
		if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) {
849
			$this->uploadMaxSize = PHP_INT_MAX;
850
		}
851
		
852
		$this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
853
			? $this->options['disabled']
854
			: array();
855
		
856
		$this->cryptLib   = $this->options['cryptLib'];
857
		$this->mimeDetect = $this->options['mimeDetect'];
858
859
		// find available mimetype detect method
860
		$type = strtolower($this->options['mimeDetect']);
861
		$type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
862
		$regexp = '/text\/x\-(php|c\+\+)/';
863
	
864
		if (($type == 'finfo' || $type == 'auto') 
865
		&& class_exists('finfo', false)) {
866
			$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
867
		} else {
868
			$tmpFileInfo = false;
869
		}
870
	
871
		if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
872
			$type = 'finfo';
873
			$this->finfo = finfo_open(FILEINFO_MIME);
874
		} elseif (($type == 'mime_content_type' || $type == 'auto') 
875
		&& function_exists('mime_content_type')
876
		&& preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) {
0 ignored issues
show
Bug introduced by
explode(';', mime_content_type(__FILE__)) cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
877
			$type = 'mime_content_type';
878
		} else {
879
			$type = 'internal';
880
		}
881
		$this->mimeDetect = $type;
882
883
		// load mimes from external file for mimeDetect == 'internal'
884
		// based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
885
		// file must be in file directory or in parent one 
886
		if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
887
			self::$mimetypesLoaded = true;
888
			$this->mimeDetect = 'internal';
889
			$file = false;
890
			if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
891
				$file = $this->options['mimefile'];
892
			} elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
893
				$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
894
			} elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
895
				$file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
896
			}
897
898 View Code Duplication
			if ($file && file_exists($file)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
899
				$mimecf = file($file);
900
901
				foreach ($mimecf as $line_num => $line) {
902
					if (!preg_match('/^\s*#/', $line)) {
903
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
904
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
905
							if (!isset(self::$mimetypes[$mime[$i]])) {
906
								self::$mimetypes[$mime[$i]] = $mime[0];
907
							}
908
						}
909
					}
910
				}
911
			}
912
		}
913
914
		$this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias'];
915
916
		// This get's triggered if $this->root == '/' and alias is empty.
917
		// Maybe modify _basename instead?
918
		if ($this->rootName === '') $this->rootName = $this->separator;
919
920
		$root = $this->stat($this->root);
921
		
922
		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...
923
			return $this->setError('Root folder does not exists.');
924
		}
925
		if (!$root['read'] && !$root['write']) {
926
			return $this->setError('Root folder has not read and write permissions.');
927
		}
928
		
929
		// 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...
930
		
931
		if ($root['read']) {
932
			// check startPath - path to open by default instead of root
933
			$startPath = $this->options['startPath']? $this->normpathCE($this->options['startPath']) : '';
934
			if ($startPath) {
935
				$start = $this->stat($startPath);
936
				if (!empty($start)
937
				&& $start['mime'] == 'directory'
938
				&& $start['read']
939
				&& empty($start['hidden'])
940
				&& $this->inpathCE($startPath, $this->root)) {
941
					$this->startPath = $startPath;
942
					if (substr($this->startPath, -1, 1) == $this->options['separator']) {
943
						$this->startPath = substr($this->startPath, 0, -1);
944
					}
945
				}
946
			}
947
		} else {
948
			$this->options['URL']     = '';
949
			$this->options['tmbURL']  = '';
950
			$this->options['tmbPath'] = '';
951
			// read only volume
952
			array_unshift($this->attributes, array(
953
				'pattern' => '/.*/',
954
				'read'    => false
955
			));
956
		}
957
		$this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
958
		$this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
959
		$this->URL      = $this->options['URL'];
960
		if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
961
			$this->URL .= '/';
962
		}
963
964
		$this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
965
		if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
966
			$this->tmbURL .= '/';
967
		}
968
		
969
		$this->nameValidator = !empty($this->options['acceptedName']) && (is_string($this->options['acceptedName']) || is_callable($this->options['acceptedName']))
0 ignored issues
show
Bug introduced by
The property nameValidator does not exist. Did you maybe forget to declare it?

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
970
			? $this->options['acceptedName']
971
			: '';
972
973
		$this->_checkArchivers();
974
		// manual control archive types to create
975 View Code Duplication
		if (!empty($this->options['archiveMimes']) && is_array($this->options['archiveMimes'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
976
			foreach ($this->archivers['create'] as $mime => $v) {
977
				if (!in_array($mime, $this->options['archiveMimes'])) {
978
					unset($this->archivers['create'][$mime]);
979
				}
980
			}
981
		}
982
		
983
		// manualy add archivers
984 View Code Duplication
		if (!empty($this->options['archivers']['create']) && is_array($this->options['archivers']['create'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
985
			foreach ($this->options['archivers']['create'] as $mime => $conf) {
986
				if (strpos($mime, 'application/') === 0 
987
				&& !empty($conf['cmd']) 
988
				&& isset($conf['argc']) 
989
				&& !empty($conf['ext'])
990
				&& !isset($this->archivers['create'][$mime])) {
991
					$this->archivers['create'][$mime] = $conf;
992
				}
993
			}
994
		}
995
		
996 View Code Duplication
		if (!empty($this->options['archivers']['extract']) && is_array($this->options['archivers']['extract'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
997
			foreach ($this->options['archivers']['extract'] as $mime => $conf) {
998
				if (strpos($mime, 'application/') === 0
999
				&& !empty($conf['cmd']) 
1000
				&& isset($conf['argc']) 
1001
				&& !empty($conf['ext'])
1002
				&& !isset($this->archivers['extract'][$mime])) {
1003
					$this->archivers['extract'][$mime] = $conf;
1004
				}
1005
			}
1006
		}
1007
1008
		$this->configure();
1009
		
1010
		// fix sync interval
1011
		$options['syncMinMs'] = max($options[$this->options['syncChkAsTs']? 'tsPlSleep' : 'lsPlSleep'] * 1000, intval($options['syncMinMs']));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
Bug introduced by
The variable $options seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

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

Let’s take a look at a simple example:

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

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

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

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

Loading history...
1012
1013
		return $this->mounted = true;
1014
	}
1015
	
1016
	/**
1017
	 * Some "unmount" stuffs - may be required by virtual fs
1018
	 *
1019
	 * @return void
1020
	 * @author Dmitry (dio) Levashov
1021
	 **/
1022
	public function umount() {
1023
	}
1024
	
1025
	/**
1026
	 * Return error message from last failed action
1027
	 *
1028
	 * @return array
1029
	 * @author Dmitry (dio) Levashov
1030
	 **/
1031
	public function error() {
1032
		return $this->error;
1033
	}
1034
	
1035
	/**
1036
	 * Return is uploadable that given file name 
1037
	 *
1038
	 * @param  string  $name  file name
1039
	 * @param  bool    $allowUnknown
1040
	 * @return bool
1041
	 * @author Naoki Sawada
1042
	 **/
1043
	public function isUploadableByName($name, $allowUnknown = true) {
1044
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1045
		return (($allowUnknown && $mimeByName === 'unknown') || $this->allowPutMime($mimeByName));
1046
	}
1047
	
1048
	/**
1049
	 * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes)
1050
	 * 
1051
	 * @return array
1052
	 * @author Naoki Sawada
1053
	 */
1054
	public function getMimeTable() {
1055
		return elFinderVolumeDriver::$mimetypes;
1056
	}
1057
	
1058
	/**
1059
	 * Set mimetypes allowed to display to client
1060
	 *
1061
	 * @param  array  $mimes
1062
	 * @return void
1063
	 * @author Dmitry (dio) Levashov
1064
	 **/
1065
	public function setMimesFilter($mimes) {
1066
		if (is_array($mimes)) {
1067
			$this->onlyMimes = $mimes;
1068
		}
1069
	}
1070
	
1071
	/**
1072
	 * Return root folder hash
1073
	 *
1074
	 * @return string
1075
	 * @author Dmitry (dio) Levashov
1076
	 **/
1077
	public function root() {
1078
		return $this->encode($this->root);
1079
	}
1080
	
1081
	/**
1082
	 * Return target path hash
1083
	 * 
1084
	 * @param  string $path
1085
	 * @param  string $name
1086
	 * @author Naoki Sawada
1087
	 */
1088
	public function getHash($path, $name = '') {
1089
		if ($name !== '') {
1090
			$path = $this->joinPathCE($path, $name);
1091
		}
1092
		return $this->encode($path);
1093
	}
1094
	
1095
	/**
1096
	 * Return root or startPath hash
1097
	 *
1098
	 * @return string
1099
	 * @author Dmitry (dio) Levashov
1100
	 **/
1101
	public function defaultPath() {
1102
		return $this->encode($this->startPath ? $this->startPath : $this->root);
1103
	}
1104
		
1105
	/**
1106
	 * Return volume options required by client:
1107
	 *
1108
	 * @return array
1109
	 * @author Dmitry (dio) Levashov
1110
	 **/
1111
	public function options($hash) {
1112
		$create = $createext = array();
1113
		if (isset($this->archivers['create']) && is_array($this->archivers['create'])) {
1114
			foreach($this->archivers['create'] as $m => $v) {
1115
				$create[] = $m;
1116
				$createext[$m] = $v['ext'];
1117
			}
1118
		}
1119
		return array(
1120
			'path'            => $this->path($hash),
1121
			'url'             => $this->URL,
1122
			'tmbUrl'          => $this->tmbURL,
1123
			'disabled'        => array_merge(array_unique($this->disabled)), // `array_merge` for type array of JSON
1124
			'separator'       => $this->separator,
1125
			'copyOverwrite'   => intval($this->options['copyOverwrite']),
1126
			'uploadOverwrite' => intval($this->options['uploadOverwrite']),
1127
			'uploadMaxSize'   => intval($this->uploadMaxSize),
1128
			'dispInlineRegex' => $this->options['dispInlineRegex'],
1129
			'jpgQuality'      => intval($this->options['jpgQuality']),
1130
			'archivers'       => array(
1131
				'create'    => $create,
1132
				'extract'   => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(),
1133
				'createext' => $createext
1134
			),
1135
			'uiCmdMap'        => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array(),
1136
			'syncChkAsTs'     => intval($this->options['syncChkAsTs']),
1137
			'syncMinMs'       => intval($this->options['syncMinMs'])
1138
		);
1139
	}
1140
	
1141
	/**
1142
	 * Get option value of this volume
1143
	 * 
1144
	 * @param string $name  target option name
1145
	 * @return NULL|mixed   target option value
1146
	 * @author Naoki Sawada
1147
	 */
1148
	public function getOption($name) {
1149
		return isset($this->options[$name])? $this->options[$name] : null;
1150
	}
1151
	
1152
	/**
1153
	 * Get plugin values of this options
1154
	 * 
1155
	 * @param string $name  Plugin name
1156
	 * @return NULL|array   Plugin values
1157
	 * @author Naoki Sawada
1158
	 */
1159
	public function getOptionsPlugin($name = '') {
1160
		if ($name) {
1161
			return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array();
1162
		} else {
1163
			return $this->options['plugin'];
1164
		}
1165
	}
1166
	
1167
	/**
1168
	 * Return true if command disabled in options
1169
	 *
1170
	 * @param  string  $cmd  command name
1171
	 * @return bool
1172
	 * @author Dmitry (dio) Levashov
1173
	 **/
1174
	public function commandDisabled($cmd) {
1175
		return in_array($cmd, $this->disabled);
1176
	}
1177
	
1178
	/**
1179
	 * Return true if mime is required mimes list
1180
	 *
1181
	 * @param  string     $mime   mime type to check
1182
	 * @param  array      $mimes  allowed mime types list or not set to use client mimes list
1183
	 * @param  bool|null  $empty  what to return on empty list
1184
	 * @return bool|null
1185
	 * @author Dmitry (dio) Levashov
1186
	 * @author Troex Nevelin
1187
	 **/
1188
	public function mimeAccepted($mime, $mimes = null, $empty = true) {
1189
		$mimes = is_array($mimes) ? $mimes : $this->onlyMimes;
1190
		if (empty($mimes)) {
1191
			return $empty;
1192
		}
1193
		return $mime == 'directory'
1194
			|| in_array('all', $mimes)
1195
			|| in_array('All', $mimes)
1196
			|| in_array($mime, $mimes)
1197
			|| in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
1198
	}
1199
	
1200
	/**
1201
	 * Return true if voume is readable.
1202
	 *
1203
	 * @return bool
1204
	 * @author Dmitry (dio) Levashov
1205
	 **/
1206
	public function isReadable() {
1207
		$stat = $this->stat($this->root);
1208
		return $stat['read'];
1209
	}
1210
	
1211
	/**
1212
	 * Return true if copy from this volume allowed
1213
	 *
1214
	 * @return bool
1215
	 * @author Dmitry (dio) Levashov
1216
	 **/
1217
	public function copyFromAllowed() {
1218
		return !!$this->options['copyFrom'];
1219
	}
1220
	
1221
	/**
1222
	 * Return file path related to root with convert encoging
1223
	 *
1224
	 * @param  string   $hash  file hash
1225
	 * @return string
1226
	 * @author Dmitry (dio) Levashov
1227
	 **/
1228
	public function path($hash) {
1229
		return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash))));
1230
	}
1231
	
1232
	/**
1233
	 * Return file real path if file exists
1234
	 *
1235
	 * @param  string  $hash  file hash
1236
	 * @return string
1237
	 * @author Dmitry (dio) Levashov
1238
	 **/
1239
	public function realpath($hash) {
1240
		$path = $this->decode($hash);
1241
		return $this->stat($path) ? $path : false;
1242
	}
1243
	
1244
	/**
1245
	 * Return list of moved/overwrited files
1246
	 *
1247
	 * @return array
1248
	 * @author Dmitry (dio) Levashov
1249
	 **/
1250
	public function removed() {
1251
		return $this->removed;
1252
	}
1253
	
1254
	/**
1255
	 * Clean removed files list
1256
	 *
1257
	 * @return void
1258
	 * @author Dmitry (dio) Levashov
1259
	 **/
1260
	public function resetRemoved() {
1261
		$this->removed = array();
1262
	}
1263
	
1264
	/**
1265
	 * Return file/dir hash or first founded child hash with required attr == $val
1266
	 *
1267
	 * @param  string   $hash  file hash
1268
	 * @param  string   $attr  attribute name
1269
	 * @param  bool     $val   attribute value
1270
	 * @return string|false
1271
	 * @author Dmitry (dio) Levashov
1272
	 **/
1273
	public function closest($hash, $attr, $val) {
1274
		return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1275
	}
1276
	
1277
	/**
1278
	 * Return file info or false on error
1279
	 *
1280
	 * @param  string   $hash      file hash
1281
	 * @param  bool     $realpath  add realpath field to file info
0 ignored issues
show
Bug introduced by
There is no parameter named $realpath. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1282
	 * @return array|false
1283
	 * @author Dmitry (dio) Levashov
1284
	 **/
1285
	public function file($hash) {
1286
		$path = $this->decode($hash);
1287
		$isRoot = ($path === $this->root);
1288
		
1289
		// for stat() cache contorol
1290
		//$isRoot && $this->ARGS['target'] = $hash;
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...
1291
		
1292
		$file = $this->stat($path);
1293
		
1294
		if ($isRoot) {
1295
			$file['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array();
1296
			$file['disabled'] = array_merge(array_unique($this->disabled)); // `array_merge` for type array of JSON
1297
		}
1298
		
1299
		return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1300
	}
1301
	
1302
	/**
1303
	 * Return folder info
1304
	 *
1305
	 * @param  string   $hash  folder hash
1306
	 * @param  bool     $hidden  return hidden file info
0 ignored issues
show
Bug introduced by
There is no parameter named $hidden. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1307
	 * @return array|false
1308
	 * @author Dmitry (dio) Levashov
1309
	 **/
1310
	public function dir($hash, $resolveLink=false) {
1311
		if (($dir = $this->file($hash)) == false) {
1312
			return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1313
		}
1314
1315
		if ($resolveLink && !empty($dir['thash'])) {
1316
			$dir = $this->file($dir['thash']);
1317
		}
1318
		
1319
		return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) 
1320
			? $dir 
1321
			: $this->setError(elFinder::ERROR_NOT_DIR);
1322
	}
1323
	
1324
	/**
1325
	 * Return directory content or false on error
1326
	 *
1327
	 * @param  string   $hash   file hash
1328
	 * @return array|false
1329
	 * @author Dmitry (dio) Levashov
1330
	 **/
1331
	public function scandir($hash) {
1332
		if (($dir = $this->dir($hash)) == false) {
1333
			return false;
1334
		}
1335
		
1336
		return $dir['read']
1337
			? $this->getScandir($this->decode($hash))
1338
			: $this->setError(elFinder::ERROR_PERM_DENIED);
1339
	}
1340
1341
	/**
1342
	 * Return dir files names list
1343
	 * 
1344
	 * @param  string  $hash   file hash
1345
	 * @return array
1346
	 * @author Dmitry (dio) Levashov
1347
	 **/
1348
	public function ls($hash) {
1349 View Code Duplication
		if (($dir = $this->dir($hash)) == false || !$dir['read']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1350
			return false;
1351
		}
1352
		
1353
		$list = array();
1354
		$path = $this->decode($hash);
1355
		
1356
		foreach ($this->getScandir($path) as $stat) {
1357
			if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) {
1358
				$list[] = $stat['name'];
1359
			}
1360
		}
1361
1362
		return $list;
1363
	}
1364
1365
	/**
1366
	 * Return subfolders for required folder or false on error
1367
	 *
1368
	 * @param  string   $hash  folder hash or empty string to get tree from root folder
1369
	 * @param  int      $deep  subdir deep
1370
	 * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1371
	 * @return array|false
1372
	 * @author Dmitry (dio) Levashov
1373
	 **/
1374
	public function tree($hash='', $deep=0, $exclude='') {
1375
		$path = $hash ? $this->decode($hash) : $this->root;
1376
		
1377 View Code Duplication
		if (($dir = $this->stat($path)) == false || $dir['mime'] != 'directory') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1378
			return false;
1379
		}
1380
		
1381
		$dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1382
		array_unshift($dirs, $dir);
1383
		return $dirs;
1384
	}
1385
	
1386
	/**
1387
	 * Return part of dirs tree from required dir up to root dir
1388
	 *
1389
	 * @param  string    $hash   directory hash
1390
	 * @param  bool|null $lineal only lineal parents
1391
	 * @return array
1392
	 * @author Dmitry (dio) Levashov
1393
	 **/
1394
	public function parents($hash, $lineal = false) {
1395
		if (($current = $this->dir($hash)) == false) {
1396
			return false;
1397
		}
1398
1399
		$path = $this->decode($hash);
1400
		$tree = array();
1401
		
1402
		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...
1403
			$path = $this->dirnameCE($path);
1404
			$stat = $this->stat($path);
1405
			if (!empty($stat['hidden']) || !$stat['read']) {
1406
				return false;
1407
			}
1408
			
1409
			array_unshift($tree, $stat);
1410
			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...
1411
				foreach ($this->gettree($path, 0) as $dir) {
1412
					if (!in_array($dir, $tree)) {
1413
						$tree[] = $dir;
1414
					}
1415
				}
1416
			}
1417
		}
1418
1419
		return $tree ? $tree : array($current);
1420
	}
1421
	
1422
	/**
1423
	 * Create thumbnail for required file and return its name of false on failed
1424
	 *
1425
	 * @return string|false
1426
	 * @author Dmitry (dio) Levashov
1427
	 **/
1428
	public function tmb($hash) {
1429
		$path = $this->decode($hash);
1430
		$stat = $this->stat($path);
1431
		
1432
		if (isset($stat['tmb'])) {
1433
			return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1434
		}
1435
		return false;
1436
	}
1437
	
1438
	/**
1439
	 * Return file size / total directory size
1440
	 *
1441
	 * @param  string   file hash
1442
	 * @return int
1443
	 * @author Dmitry (dio) Levashov
1444
	 **/
1445
	public function size($hash) {
1446
		return $this->countSize($this->decode($hash));
1447
	}
1448
	
1449
	/**
1450
	 * Open file for reading and return file pointer
1451
	 *
1452
	 * @param  string   file hash
1453
	 * @return Resource
1454
	 * @author Dmitry (dio) Levashov
1455
	 **/
1456
	public function open($hash) {
1457 View Code Duplication
		if (($file = $this->file($hash)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1458
		|| $file['mime'] == 'directory') {
1459
			return false;
1460
		}
1461
		
1462
		return $this->fopenCE($this->decode($hash), 'rb');
1463
	}
1464
	
1465
	/**
1466
	 * Close file pointer
1467
	 *
1468
	 * @param  Resource  $fp   file pointer
1469
	 * @param  string    $hash file hash
1470
	 * @return void
1471
	 * @author Dmitry (dio) Levashov
1472
	 **/
1473
	public function close($fp, $hash) {
1474
		$this->fcloseCE($fp, $this->decode($hash));
1475
	}
1476
	
1477
	/**
1478
	 * Create directory and return dir info
1479
	 *
1480
	 * @param  string   $dsthash  destination directory hash
1481
	 * @param  string   $name directory name
1482
	 * @return array|false
1483
	 * @author Dmitry (dio) Levashov
1484
	 **/
1485
	public function mkdir($dsthash, $name) {
1486
		if ($this->commandDisabled('mkdir')) {
1487
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1488
		}
1489
		
1490
		if (!$this->nameAccepted($name)) {
1491
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1492
		}
1493
		
1494
		if (($dir = $this->dir($dsthash)) == false) {
1495
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash);
1496
		}
1497
		
1498
		$path = $this->decode($dsthash);
1499
		
1500
		if (!$dir['write'] || !$this->allowCreate($path, $name, true)) {
1501
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1502
		}
1503
		
1504
		$dst  = $this->joinPathCE($path, $name);
1505
		$stat = $this->stat($dst); 
1506
		if (!empty($stat)) { 
1507
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1508
		}
1509
		$this->clearcache();
1510
		return ($path = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1511
	}
1512
	
1513
	/**
1514
	 * Create empty file and return its info
1515
	 *
1516
	 * @param  string   $dst  destination directory
1517
	 * @param  string   $name file name
1518
	 * @return array|false
1519
	 * @author Dmitry (dio) Levashov
1520
	 **/
1521
	public function mkfile($dst, $name) {
1522
		if ($this->commandDisabled('mkfile')) {
1523
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1524
		}
1525
		
1526
		if (!$this->nameAccepted($name)) {
1527
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1528
		}
1529
		
1530 View Code Duplication
		if (($dir = $this->dir($dst)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1531
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1532
		}
1533
		
1534
		$path = $this->decode($dst);
1535
		
1536
		if (!$dir['write'] || !$this->allowCreate($path, $name, false)) {
1537
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1538
		}
1539
		
1540
		if ($this->stat($this->joinPathCE($path, $name))) {
1541
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1542
		}
1543
		
1544
		$this->clearcache();
1545
		return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false;
1546
	}
1547
	
1548
	/**
1549
	 * Rename file and return file info
1550
	 *
1551
	 * @param  string  $hash  file hash
1552
	 * @param  string  $name  new file name
1553
	 * @return array|false
1554
	 * @author Dmitry (dio) Levashov
1555
	 **/
1556
	public function rename($hash, $name) {
1557
		if ($this->commandDisabled('rename')) {
1558
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1559
		}
1560
		
1561
		if (!$this->nameAccepted($name)) {
1562
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1563
		}
1564
		
1565
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1566 View Code Duplication
		if ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1567
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1568
		}
1569
		
1570
		if (!($file = $this->file($hash))) {
1571
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1572
		}
1573
		
1574
		if ($name == $file['name']) {
1575
			return $file;
1576
		}
1577
		
1578
		if (!empty($file['locked'])) {
1579
			return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1580
		}
1581
		
1582
		$path = $this->decode($hash);
1583
		$dir  = $this->dirnameCE($path);
1584
		$stat = $this->stat($this->joinPathCE($dir, $name));
1585
		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...
1586
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1587
		}
1588
		
1589 View Code Duplication
		if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1590
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1591
		}
1592
1593
		$this->rmTmb($file); // remove old name tmbs, we cannot do this after dir move
0 ignored issues
show
Documentation introduced by
$file is of type array|boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1594
1595
1596 View Code Duplication
		if ($path = $this->convEncOut($this->_move($this->convEncIn($path), $this->convEncIn($dir), $this->convEncIn($name)))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1597
			$this->clearcache();
1598
			return $this->stat($path);
1599
		}
1600
		return false;
1601
	}
1602
	
1603
	/**
1604
	 * Create file copy with suffix "copy number" and return its info
1605
	 *
1606
	 * @param  string   $hash    file hash
1607
	 * @param  string   $suffix  suffix to add to file name
1608
	 * @return array|false
1609
	 * @author Dmitry (dio) Levashov
1610
	 **/
1611
	public function duplicate($hash, $suffix='copy') {
1612
		if ($this->commandDisabled('duplicate')) {
1613
			return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1614
		}
1615
		
1616
		if (($file = $this->file($hash)) == false) {
1617
			return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1618
		}
1619
1620
		$path = $this->decode($hash);
1621
		$dir  = $this->dirnameCE($path);
1622
		$name = $this->uniqueName($dir, $this->basenameCE($path), ' '.$suffix.' ');
1623
1624 View Code Duplication
		if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1625
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1626
		}
1627
1628
		return ($path = $this->copy($path, $dir, $name)) == false
1629
			? false
1630
			: $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copy($path, $dir, $name) on line 1628 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...
1631
	}
1632
	
1633
	/**
1634
	 * Save uploaded file. 
1635
	 * On success return array with new file stat and with removed file hash (if existed file was replaced)
1636
	 *
1637
	 * @param  Resource $fp      file pointer
1638
	 * @param  string   $dst     destination folder hash
1639
	 * @param  string   $src     file name
0 ignored issues
show
Bug introduced by
There is no parameter named $src. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1640
	 * @param  string   $tmpname file tmp name - required to detect mime type
1641
	 * @return array|false
1642
	 * @author Dmitry (dio) Levashov
1643
	 **/
1644
	public function upload($fp, $dst, $name, $tmpname) {
0 ignored issues
show
Coding Style introduced by
upload uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1645
		if ($this->commandDisabled('upload')) {
1646
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1647
		}
1648
		
1649 View Code Duplication
		if (($dir = $this->dir($dst)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1650
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1651
		}
1652
1653
		if (!$dir['write']) {
1654
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1655
		}
1656
		
1657
		if (!$this->nameAccepted($name)) {
1658
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1659
		}
1660
		
1661
		$mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name);
1662
		$mimeByName = '';
1663
		if ($this->mimeDetect !== 'internal') {
1664
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1665
			if ($mime == 'unknown') {
1666
				$mime = $mimeByName;
1667
			}
1668
		}
1669
1670 View Code Duplication
		if (!$this->allowPutMime($mime) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1671
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1672
		}
1673
1674
		$tmpsize = sprintf('%u', filesize($tmpname));
1675
		if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) {
1676
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1677
		}
1678
1679
		$dstpath = $this->decode($dst);
1680
		$test    = $this->joinPathCE($dstpath, $name);
1681
		
1682
		$file = $this->stat($test);
1683
		$this->clearcache();
1684
		
1685
		if ($file) { // file exists
0 ignored issues
show
Bug Best Practice introduced by
The expression $file of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1686
			// check POST data `overwrite` for 3rd party uploader
1687
			$overwrite = isset($_POST['overwrite'])? (bool)$_POST['overwrite'] : $this->options['uploadOverwrite'];
1688
			if ($overwrite) {
1689 View Code Duplication
				if (!$file['write']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1690
					return $this->setError(elFinder::ERROR_PERM_DENIED);
1691
				} elseif ($file['mime'] == 'directory') {
1692
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1693
				} 
1694
				$this->remove($test);
1695
			} else {
1696
				$name = $this->uniqueName($dstpath, $name, '-', false);
1697
			}
1698
		}
1699
		
1700
		$stat = array(
1701
			'mime'   => $mime, 
1702
			'width'  => 0, 
1703
			'height' => 0, 
1704
			'size'   => $tmpsize);
1705
		
1706
		// $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...
1707
		if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
1708
			$stat['width'] = $s[0];
1709
			$stat['height'] = $s[1];
1710
		}
1711
		// $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...
1712
		if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) {
1713
			return false;
1714
		}
1715
		
1716
		
1717
1718
		return $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->saveCE($fp, $dstpath, $name, $stat) on line 1712 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...
1719
	}
1720
	
1721
	/**
1722
	 * Paste files
1723
	 *
1724
	 * @param  Object  $volume  source volume
1725
	 * @param  string  $source  file hash
0 ignored issues
show
Bug introduced by
There is no parameter named $source. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1726
	 * @param  string  $dst     destination dir hash
1727
	 * @param  bool    $rmSrc   remove source after copy?
1728
	 * @return array|false
1729
	 * @author Dmitry (dio) Levashov
1730
	 **/
1731
	public function paste($volume, $src, $dst, $rmSrc = false) {
1732
		$err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
1733
		
1734
		if ($this->commandDisabled('paste')) {
1735
			return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
1736
		}
1737
1738
		if (($file = $volume->file($src, $rmSrc)) == false) {
1739
			return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
1740
		}
1741
1742
		$name = $file['name'];
1743
		$errpath = $volume->path($file['hash']);
1744
		
1745 View Code Duplication
		if (($dir = $this->dir($dst)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1746
			return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1747
		}
1748
		
1749
		if (!$dir['write'] || !$file['read']) {
1750
			return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1751
		}
1752
1753
		$destination = $this->decode($dst);
1754
1755
		if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
1756
			return $rmSrc
1757
				? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
1758
				: $this->setError($err, $errpath, !empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK);
1759
		}
1760
1761
		$test = $this->joinPathCE($destination, $name);
1762
		$stat = $this->stat($test);
1763
		$this->clearcache();
1764
		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...
1765
			if ($this->options['copyOverwrite']) {
1766
				// do not replace file with dir or dir with file
1767
				if (!$this->isSameType($file['mime'], $stat['mime'])) {
1768
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash']));
1769
				}
1770
				// existed file is not writable
1771
				if (!$stat['write']) {
1772
					return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1773
				}
1774
				// existed file locked or has locked child
1775
				if (($locked = $this->closestByAttr($test, 'locked', true))) {
1776
					$stat = $this->stat($locked);
1777
					return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
1778
				}
1779
				// target is entity file of alias
1780
				if ($volume == $this && ($test == @$file['target'] || $test == $this->decode($src))) {
1781
					return $this->setError(elFinder::ERROR_REPLACE, $errpath);
1782
				}
1783
				// remove existed file
1784
				if (!$this->remove($test)) {
1785
					return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash']));
1786
				}
1787
			} else {
1788
				$name = $this->uniqueName($destination, $name, ' ', false);
1789
			}
1790
		}
1791
		
1792
		// copy/move inside current volume
1793
		if ($volume == $this) {
1794
			$source = $this->decode($src);
1795
			// do not copy into itself
1796
			if ($this->inpathCE($destination, $source)) {
1797
				return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
1798
			}
1799
			$method = $rmSrc ? 'move' : 'copy';
1800
			return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false;
1801
		}
1802
		
1803
		// copy/move from another volume
1804
		if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
1805
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
1806
		}
1807
		
1808
		if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
1809
			return false;
1810
		}
1811
		
1812
		if ($rmSrc) {
1813
			if (!$volume->rm($src)) {
1814
				return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
1815
			}
1816
		}
1817
		return $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->copyFrom($volume,...c, $destination, $name) on line 1808 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...
1818
	}
1819
	
1820
	/**
1821
	 * Return file contents
1822
	 *
1823
	 * @param  string  $hash  file hash
1824
	 * @return string|false
1825
	 * @author Dmitry (dio) Levashov
1826
	 **/
1827
	public function getContents($hash) {
1828
		$file = $this->file($hash);
1829
		
1830
		if (!$file) {
1831
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->setError(\...:ERROR_FILE_NOT_FOUND); (boolean) is incompatible with the return type documented by elFinderVolumeDriver::getContents of type string|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1832
		}
1833
		
1834
		if ($file['mime'] == 'directory') {
1835
			return $this->setError(elFinder::ERROR_NOT_FILE);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->setError(\...inder::ERROR_NOT_FILE); (boolean) is incompatible with the return type documented by elFinderVolumeDriver::getContents of type string|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1836
		}
1837
		
1838
		if (!$file['read']) {
1839
			return $this->setError(elFinder::ERROR_PERM_DENIED);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->setError(\...er::ERROR_PERM_DENIED); (boolean) is incompatible with the return type documented by elFinderVolumeDriver::getContents of type string|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1840
		}
1841
		
1842
		return $this->convEncOut($this->_getContents($this->convEncIn($this->decode($hash))));
1843
	}
1844
	
1845
	/**
1846
	 * Put content in text file and return file info.
1847
	 *
1848
	 * @param  string  $hash     file hash
1849
	 * @param  string  $content  new file content
1850
	 * @return array
1851
	 * @author Dmitry (dio) Levashov
1852
	 **/
1853
	public function putContents($hash, $content) {
1854
		if ($this->commandDisabled('edit')) {
1855
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1856
		}
1857
		
1858
		$path = $this->decode($hash);
1859
		
1860
		if (!($file = $this->file($hash))) {
1861
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1862
		}
1863
		
1864
		if (!$file['write']) {
1865
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1866
		}
1867
		
1868
		// check MIME
1869
		$name = $this->basenameCE($path);
1870
		$mime = '';
1871
		$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name);
1872
		if ($this->mimeDetect !== 'internal') {
1873
			if ($tp = tmpfile()) {
1874
				fwrite($tp, $content);
1875
				$info = stream_get_meta_data($tp);
1876
				$filepath = $info['uri'];
1877
				$mime = $this->mimetype($filepath, $name);
1878
				fclose($tp);
1879
			}
1880
		}
1881 View Code Duplication
		if (!$this->allowPutMime($mimeByName) || ($mime && $mime !== 'unknown' && !$this->allowPutMime($mime))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1882
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1883
		}
1884
		
1885
		$this->clearcache();
1886
		return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false;
1887
	}
1888
	
1889
	/**
1890
	 * Extract files from archive
1891
	 *
1892
	 * @param  string  $hash  archive hash
1893
	 * @return array|bool
1894
	 * @author Dmitry (dio) Levashov, 
1895
	 * @author Alexey Sukhotin
1896
	 **/
1897
	public function extract($hash, $makedir = null) {
1898
		if ($this->commandDisabled('extract')) {
1899
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1900
		}
1901
		
1902
		if (($file = $this->file($hash)) == false) {
1903
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1904
		}
1905
		
1906
		$archiver = isset($this->archivers['extract'][$file['mime']])
1907
			? $this->archivers['extract'][$file['mime']]
1908
			: false;
1909
			
1910
		if (!$archiver) {
1911
			return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
1912
		}
1913
		
1914
		$path   = $this->decode($hash);
1915
		$parent = $this->stat($this->dirnameCE($path));
1916
1917
		if (!$file['read'] || !$parent['write']) {
1918
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1919
		}
1920
		$this->clearcache();
1921
		$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...
1922
		
1923
		if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) {
1924
			if (is_array($path)) {
1925
				foreach ($path as $_k => $_p) {
1926
					$path[$_k] = $this->stat($_p);
1927
				}
1928
			} else {
1929
				$path = $this->stat($path);
1930
			}
1931
			return $path;
1932
		} else {
1933
			return false;
1934
		}
1935
	}
1936
1937
	/**
1938
	 * Add files to archive
1939
	 *
1940
	 * @return void
1941
	 **/
1942
	public function archive($hashes, $mime, $name = '') {
1943
		if ($this->commandDisabled('archive')) {
1944
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1945
		}
1946
1947
		$archiver = isset($this->archivers['create'][$mime])
1948
			? $this->archivers['create'][$mime]
1949
			: false;
1950
			
1951
		if (!$archiver) {
1952
			return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
1953
		}
1954
		
1955
		$files = array();
1956
		
1957
		foreach ($hashes as $hash) {
1958
			if (($file = $this->file($hash)) == false) {
1959
				return $this->error(elFinder::ERROR_FILE_NOT_FOUND, '#'+$hash);
0 ignored issues
show
Unused Code introduced by
The call to elFinderVolumeDriver::error() has too many arguments starting with \elFinder::ERROR_FILE_NOT_FOUND.

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

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

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

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

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

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

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

Loading history...
1963
			}
1964
			$path = $this->decode($hash);
1965
			if (!isset($dir)) {
1966
				$dir = $this->dirnameCE($path);
1967
				$stat = $this->stat($dir);
1968
				if (!$stat['write']) {
1969
					return $this->error(elFinder::ERROR_PERM_DENIED);
0 ignored issues
show
Unused Code introduced by
The call to elFinderVolumeDriver::error() has too many arguments starting with \elFinder::ERROR_PERM_DENIED.

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

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

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

Loading history...
1970
				}
1971
			}
1972
			
1973
			$files[] = $this->basenameCE($path);
1974
		}
1975
		
1976
		if ($name === '') {
1977
			$name = count($files) == 1 ? $files[0] : 'Archive';
1978
		} else {
1979
			$name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name));
1980
		}
1981
		$name .='.' . $archiver['ext'];
1982
		$name = $this->uniqueName($dir, $name, '');
0 ignored issues
show
Bug introduced by
The variable $dir does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1983
		$this->clearcache();
1984
		return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false;
1985
	}
1986
	
1987
	/**
1988
	 * Resize image
1989
	 *
1990
	 * @param  string   $hash    image file
1991
	 * @param  int      $width   new width
1992
	 * @param  int      $height  new height
1993
	 * @param  int      $x       X start poistion for crop
1994
	 * @param  int      $y       Y start poistion for crop
1995
	 * @param  string   $mode    action how to mainpulate image
1996
	 * @param  string   $bg      background color
1997
	 * @param  int      $degree  rotete degree
1998
	 * @param  int      $jpgQuality  JEPG quality (1-100)
1999
	 * @return array|false
2000
	 * @author Dmitry (dio) Levashov
2001
	 * @author Alexey Sukhotin
2002
	 * @author nao-pon
2003
	 * @author Troex Nevelin
2004
	 **/
2005
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0, $jpgQuality = null) {
2006
		if ($this->commandDisabled('resize')) {
2007
			return $this->setError(elFinder::ERROR_PERM_DENIED);
2008
		}
2009
		
2010
		if (($file = $this->file($hash)) == false) {
2011
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
2012
		}
2013
		
2014
		if (!$file['write'] || !$file['read']) {
2015
			return $this->setError(elFinder::ERROR_PERM_DENIED);
2016
		}
2017
		
2018
		$path = $this->decode($hash);
2019
		
2020
		$work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path);
2021
2022
		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...
2023
			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...
2024
				@unlink($work_path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2025
			}
2026
			return $this->setError(elFinder::ERROR_PERM_DENIED);
2027
		}
2028
2029
		if ($this->imgLib != 'imagick') {
2030
			if (elFinder::isAnimationGif($work_path)) {
2031
				return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
2032
			}
2033
		}
2034
2035
		switch($mode) {
2036
			
2037 View Code Duplication
			case 'propresize':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2038
				$result = $this->imgResize($work_path, $width, $height, true, true, null, $jpgQuality);
2039
				break;
2040
2041
			case 'crop':
2042
				$result = $this->imgCrop($work_path, $width, $height, $x, $y, null, $jpgQuality);
0 ignored issues
show
Documentation introduced by
$x is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$y is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2043
				break;
2044
2045
			case 'fitsquare':
2046
				$result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality);
2047
				break;
2048
2049
			case 'rotate':
2050
				$result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality);
2051
				break;
2052
2053 View Code Duplication
			default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2054
				$result = $this->imgResize($work_path, $width, $height, false, true, null, $jpgQuality);
2055
				break;
2056
		}
2057
		
2058
		$ret = false;
2059
		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...
2060
			$stat = $this->stat($path);
2061
			clearstatcache();
2062
			$fstat = stat($work_path);
2063
			$stat['size'] = $fstat['size'];
2064
			$stat['ts'] = $fstat['mtime'];
2065
			if ($imgsize = @getimagesize($work_path)) {
2066
				$stat['width'] = $imgsize[0];
2067
				$stat['height'] = $imgsize[1];
2068
				$stat['mime'] = $imgsize['mime'];
2069
			}
2070
			if ($path !== $work_path) {
2071
				if ($fp = @fopen($work_path, 'rb')) {
2072
					$ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat);
2073
					@fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2074
				}
2075
			} else {
2076
				$ret = true;
2077
			}
2078
			if ($ret) {
2079
				$this->rmTmb($file);
0 ignored issues
show
Documentation introduced by
$file is of type array|boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2080
				$this->clearcache();
2081
				$ret = $this->stat($path);
2082
				$ret['width'] = $stat['width'];
2083
				$ret['height'] = $stat['height'];
2084
			}
2085
		}
2086
		if ($path !== $work_path) {
2087
			is_file($work_path) && @unlink($work_path);
2088
		}
2089
		
2090
		return $ret;
2091
	}
2092
	
2093
	/**
2094
	 * Remove file/dir
2095
	 *
2096
	 * @param  string  $hash  file hash
2097
	 * @return bool
2098
	 * @author Dmitry (dio) Levashov
2099
	 **/
2100
	public function rm($hash) {
2101
		return $this->commandDisabled('rm')
2102
			? $this->setError(elFinder::ERROR_PERM_DENIED)
2103
			: $this->remove($this->decode($hash));
2104
	}
2105
	
2106
	/**
2107
	 * Search files
2108
	 *
2109
	 * @param  string  $q  search string
2110
	 * @param  array   $mimes
2111
	 * @return array
2112
	 * @author Dmitry (dio) Levashov
2113
	 **/
2114
	public function search($q, $mimes, $hash = null) {
2115
		$dir = null;
2116
		if ($hash) {
2117
			$dir = $this->decode($hash);
2118
			$stat = $this->stat($dir);
2119
			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...
2120
				$q = '';
2121
			}
2122
		}
2123
		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...
2124
			$mimes = array_intersect($mimes, $this->onlyMimes);
2125
			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...
2126
				$q = '';
2127
			}
2128
		}
2129
		return ($q === '' || $this->commandDisabled('search'))
2130
			? array()
2131
			: $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes);
2132
	}
2133
	
2134
	/**
2135
	 * Return image dimensions
2136
	 *
2137
	 * @param  string  $hash  file hash
2138
	 * @return array
2139
	 * @author Dmitry (dio) Levashov
2140
	 **/
2141
	public function dimensions($hash) {
2142
		if (($file = $this->file($hash)) == false) {
2143
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by elFinderVolumeDriver::dimensions of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2144
		}
2145
		
2146
		return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime']));
2147
	}
2148
	
2149
	/**
2150
	 * Return content URL (for netmout volume driver)
2151
	 * If file.url == 1 requests from JavaScript client with XHR
2152
	 * 
2153
	 * @param string $hash  file hash
2154
	 * @param array $options  options array
2155
	 * @return boolean|string
2156
	 * @author Naoki Sawada
2157
	 */
2158
	public function getContentUrl($hash, $options = array()) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

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

Loading history...
2159 View Code Duplication
		if (($file = $this->file($hash)) == false || !$file['url'] || $file['url'] == 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2160
			return false;
2161
		}
2162
		return $file['url'];
2163
	}
2164
	
2165
	/**
2166
	 * Return temp path
2167
	 * 
2168
	 * @return string
2169
	 * @author Naoki Sawada
2170
	 */
2171
	public function getTempPath() {
2172
		$tempPath = null;
2173
		if (isset($this->tmpPath) && $this->tmpPath && is_writable($this->tmpPath)) {
2174
			$tempPath = $this->tmpPath;
0 ignored issues
show
Bug introduced by
The property tmpPath does not exist. Did you maybe forget to declare it?

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
2175
		} else if (isset($this->tmp) && $this->tmp && is_writable($this->tmp)) {
2176
			$tempPath = $this->tmp;
0 ignored issues
show
Bug introduced by
The property tmp does not exist. Did you maybe forget to declare it?

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
2177
		} else if (function_exists('sys_get_temp_dir')) {
2178
			$tempPath = sys_get_temp_dir();
2179
		} else if (isset($this->tmbPath) && $this->tmbPath && is_writable($this->tmbPath)) {
2180
			$tempPath = $this->tmbPath;
2181
		}
2182
		if ($tempPath && DIRECTORY_SEPARATOR !== '/') {
2183
			$tempPath = str_replace('/', DIRECTORY_SEPARATOR, $tempPath);
2184
		}
2185
		return $tempPath;
2186
	}
2187
	
2188
	/**
2189
	 * (Make &) Get upload taget dirctory hash
2190
	 * 
2191
	 * @param string $baseTargetHash
2192
	 * @param string $path
2193
	 * @param array  $result
2194
	 * @return boolean|string
2195
	 * @author Naoki Sawada
2196
	 */
2197
	public function getUploadTaget($baseTargetHash, $path, & $result) {
2198
		$base = $this->decode($baseTargetHash);
2199
		$targetHash = $baseTargetHash;
2200
		$path = ltrim($path, $this->separator);
2201
		$dirs = explode($this->separator, $path);
2202
		array_pop($dirs);
2203
		foreach($dirs as $dir) {
2204
			$targetPath = $this->joinPathCE($base, $dir);
2205
			if (! $_realpath = $this->realpath($this->encode($targetPath))) {
2206
				if ($stat = $this->mkdir($targetHash, $dir)) {
2207
					$result['added'][] = $stat;
2208
					$targetHash = $stat['hash'];
2209
					$base = $this->decode($targetHash);
2210
				} else {
2211
					return false;
2212
				}
2213
			} else {
2214
				$targetHash = $this->encode($_realpath);
2215
				if ($this->dir($targetHash)) {
2216
					$base = $this->decode($targetHash);
2217
				} else {
2218
					return false;
2219
				}
2220
			}
2221
		}
2222
		return $targetHash;
2223
	}
2224
	
2225
	/**
2226
	 * Return this uploadMaxSize value
2227
	 * 
2228
	 * @return integer
2229
	 * @author Naoki Sawada
2230
	 */
2231
	public function getUploadMaxSize() {
2232
		return $this->uploadMaxSize;
2233
	}
2234
	
2235
	/**
2236
	 * Save error message
2237
	 *
2238
	 * @param  array  error 
2239
	 * @return false
2240
	 * @author Dmitry(dio) Levashov
2241
	 **/
2242
	protected function setError($error) {
0 ignored issues
show
Unused Code introduced by
The parameter $error is not used and could be removed.

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

Loading history...
2243
		
2244
		$this->error = array();
2245
		
2246
		foreach (func_get_args() as $err) {
2247
			if (is_array($err)) {
2248
				$this->error = array_merge($this->error, $err);
2249
			} else {
2250
				$this->error[] = $err;
2251
			}
2252
		}
2253
		
2254
		// $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...
2255
		return false;
2256
	}
2257
	
2258
	/*********************************************************************/
2259
	/*                               FS API                              */
2260
	/*********************************************************************/
2261
	
2262
	/***************** server encoding support *******************/
2263
	
2264
	/**
2265
	 * Return parent directory path (with convert encording)
2266
	 *
2267
	 * @param  string  $path  file path
2268
	 * @return string
2269
	 * @author Naoki Sawada
2270
	 **/
2271
	protected function dirnameCE($path) {
2272
		return (!$this->encoding)? $this->_dirname($path) :	$this->convEncOut($this->_dirname($this->convEncIn($path)));
2273
	}
2274
	
2275
	/**
2276
	 * Return file name (with convert encording)
2277
	 *
2278
	 * @param  string  $path  file path
2279
	 * @return string
2280
	 * @author Naoki Sawada
2281
	 **/
2282
	protected function basenameCE($path) {
2283
		return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path)));
2284
	}
2285
	
2286
	/**
2287
	 * Join dir name and file name and return full path. (with convert encording)
2288
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
2289
	 *
2290
	 * @param  string  $dir   dir path
2291
	 * @param  string  $name  file name
2292
	 * @return string
2293
	 * @author Naoki Sawada
2294
	 **/
2295
	protected function joinPathCE($dir, $name) {
2296
		return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name)));
2297
	}
2298
	
2299
	/**
2300
	 * Return normalized path (with convert encording)
2301
	 *
2302
	 * @param  string  $path  file path
2303
	 * @return string
2304
	 * @author Naoki Sawada
2305
	 **/
2306
	protected function normpathCE($path) {
2307
		return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path)));
2308
	}
2309
	
2310
	/**
2311
	 * Return file path related to root dir (with convert encording)
2312
	 *
2313
	 * @param  string  $path  file path
2314
	 * @return string
2315
	 * @author Naoki Sawada
2316
	 **/
2317
	protected function relpathCE($path) {
2318
		return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path)));
2319
	}
2320
	
2321
	/**
2322
	 * Convert path related to root dir into real path (with convert encording)
2323
	 *
2324
	 * @param  string  $path  rel file path
2325
	 * @return string
2326
	 * @author Naoki Sawada
2327
	 **/
2328
	protected function abspathCE($path) {
2329
		return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path)));
2330
	}
2331
	
2332
	/**
2333
	 * Return true if $path is children of $parent (with convert encording)
2334
	 *
2335
	 * @param  string  $path    path to check
2336
	 * @param  string  $parent  parent path
2337
	 * @return bool
2338
	 * @author Naoki Sawada
2339
	 **/
2340
	protected function inpathCE($path, $parent) {
2341
		return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent)));
2342
	}
2343
	
2344
	/**
2345
	 * Open file and return file pointer (with convert encording)
2346
	 *
2347
	 * @param  string  $path  file path
2348
	 * @param  bool    $write open file for writing
0 ignored issues
show
Bug introduced by
There is no parameter named $write. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
2349
	 * @return resource|false
2350
	 * @author Naoki Sawada
2351
	 **/
2352
	protected function fopenCE($path, $mode='rb') {
2353
		return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode));
2354
	}
2355
	
2356
	/**
2357
	 * Close opened file (with convert encording)
2358
	 * 
2359
	 * @param  resource  $fp    file pointer
2360
	 * @param  string    $path  file path
2361
	 * @return bool
2362
	 * @author Naoki Sawada
2363
	 **/
2364
	protected function fcloseCE($fp, $path='') {
2365
		return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path)));
2366
	}
2367
	
2368
	/**
2369
	 * Create new file and write into it from file pointer. (with convert encording)
2370
	 * Return new file path or false on error.
2371
	 *
2372
	 * @param  resource  $fp   file pointer
2373
	 * @param  string    $dir  target dir path
2374
	 * @param  string    $name file name
2375
	 * @param  array     $stat file stat (required by some virtual fs)
2376
	 * @return bool|string
2377
	 * @author Naoki Sawada
2378
	 **/
2379
	protected function saveCE($fp, $dir, $name, $stat) {
2380
		return (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat)));
2381
	}
2382
	
2383
	/**
2384
	 * Return true if path is dir and has at least one childs directory (with convert encording)
2385
	 *
2386
	 * @param  string  $path  dir path
2387
	 * @return bool
2388
	 * @author Naoki Sawada
2389
	 **/
2390
	protected function subdirsCE($path) {
2391
		if (!isset($this->subdirsCache[$path])) {
2392
			$this->subdirsCache[$path] = (!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)));
2393
		}
2394
		return $this->subdirsCache[$path];
2395
	}
2396
	
2397
	/**
2398
	 * Return files list in directory (with convert encording)
2399
	 *
2400
	 * @param  string  $path  dir path
2401
	 * @return array
2402
	 * @author Naoki Sawada
2403
	 **/
2404
	protected function scandirCE($path) {
2405
		return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path)));
2406
	}
2407
	
2408
	/**
2409
	 * Create symlink (with convert encording)
2410
	 *
2411
	 * @param  string  $source     file to link to
2412
	 * @param  string  $targetDir  folder to create link in
2413
	 * @param  string  $name       symlink name
2414
	 * @return bool
2415
	 * @author Naoki Sawada
2416
	 **/
2417
	protected function symlinkCE($source, $targetDir, $name) {
2418
		return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name)));
2419
	}
2420
	
2421
	/***************** paths *******************/
2422
	
2423
	/**
2424
	 * Encode path into hash
2425
	 *
2426
	 * @param  string  file path
2427
	 * @return string
2428
	 * @author Dmitry (dio) Levashov
2429
	 * @author Troex Nevelin
2430
	 **/
2431
	protected function encode($path) {
2432
		if ($path !== '') {
2433
2434
			// cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
2435
			$p = $this->relpathCE($path);
2436
			// if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
2437
			if ($p === '')	{
2438
				$p = DIRECTORY_SEPARATOR;
2439
			}
2440
2441
			// TODO crypt path and return hash
2442
			$hash = $this->crypt($p);
2443
			// hash is used as id in HTML that means it must contain vaild chars
2444
			// make base64 html safe and append prefix in begining
2445
			$hash = strtr(base64_encode($hash), '+/=', '-_.');
2446
			// remove dots '.' at the end, before it was '=' in base64
2447
			$hash = rtrim($hash, '.'); 
2448
			// append volume id to make hash unique
2449
			return $this->id.$hash;
2450
		}
2451
	}
2452
	
2453
	/**
2454
	 * Decode path from hash
2455
	 *
2456
	 * @param  string  file hash
2457
	 * @return string
2458
	 * @author Dmitry (dio) Levashov
2459
	 * @author Troex Nevelin
2460
	 **/
2461
	protected function decode($hash) {
2462
		if (strpos($hash, $this->id) === 0) {
2463
			// cut volume id after it was prepended in encode
2464
			$h = substr($hash, strlen($this->id));
2465
			// replace HTML safe base64 to normal
2466
			$h = base64_decode(strtr($h, '-_.', '+/='));
2467
			// TODO uncrypt hash and return path
2468
			$path = $this->uncrypt($h); 
2469
			// append ROOT to path after it was cut in encode
2470
			return $this->abspathCE($path);//$this->root.($path == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR.$path); 
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2471
		}
2472
	}
2473
	
2474
	/**
2475
	 * Return crypted path 
2476
	 * Not implemented
2477
	 *
2478
	 * @param  string  path
2479
	 * @return mixed
2480
	 * @author Dmitry (dio) Levashov
2481
	 **/
2482
	protected function crypt($path) {
2483
		return $path;
2484
	}
2485
	
2486
	/**
2487
	 * Return uncrypted path 
2488
	 * Not implemented
2489
	 *
2490
	 * @param  mixed  hash
2491
	 * @return mixed
2492
	 * @author Dmitry (dio) Levashov
2493
	 **/
2494
	protected function uncrypt($hash) {
2495
		return $hash;
2496
	}
2497
	
2498
	/**
2499
	 * Validate file name based on $this->options['acceptedName'] regexp or function
2500
	 *
2501
	 * @param  string  $name  file name
2502
	 * @return bool
2503
	 * @author Dmitry (dio) Levashov
2504
	 **/
2505
	protected function nameAccepted($name) {
2506
		if (!json_encode($name)) {
2507
			return false;
2508
		}
2509
		if ($this->nameValidator) {
2510
			if (is_callable($this->nameValidator)) {
2511
				$res = call_user_func($this->nameValidator, $name);
2512
				return $res;
2513
			}
2514
			if (preg_match($this->nameValidator, '') !== false) {
2515
				return preg_match($this->nameValidator, $name);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return preg_match($this->nameValidator, $name); (integer) is incompatible with the return type documented by elFinderVolumeDriver::nameAccepted of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2516
			}
2517
		}
2518
		return true;
2519
	}
2520
	
2521
	/**
2522
	 * Return new unique name based on file name and suffix
2523
	 *
2524
	 * @param  string  $path    file path
0 ignored issues
show
Bug introduced by
There is no parameter named $path. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
2525
	 * @param  string  $suffix  suffix append to name
2526
	 * @return string
2527
	 * @author Dmitry (dio) Levashov
2528
	 **/
2529
	public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) {
2530
		$ext  = '';
2531
2532
		if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
2533
			$ext  = '.'.$m[1];
2534
			$name = substr($name, 0,  strlen($name)-strlen($m[0]));
2535
		} 
2536
		
2537
		if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) {
2538
			$i    = (int)$m[2];
2539
			$name = substr($name, 0, strlen($name)-strlen($m[2]));
2540
		} else {
2541
			$i     = $start;
2542
			$name .= $suffix;
2543
		}
2544
		$max = $i+100000;
2545
2546
		while ($i <= $max) {
2547
			$n = $name.($i > 0 ? $i : '').$ext;
2548
2549
			if (!$this->stat($this->joinPathCE($dir, $n))) {
2550
				$this->clearcache();
2551
				return $n;
2552
			}
2553
			$i++;
2554
		}
2555
		return $name.md5($dir).$ext;
2556
	}
2557
	
2558
	/**
2559
	 * Converts character encoding from UTF-8 to server's one
2560
	 * 
2561
	 * @param  mixed  $var           target string or array var
2562
	 * @param  bool   $restoreLocale do retore global locale, default is false
2563
	 * @param  string $unknown       replaces character for unknown
2564
	 * @return mixed
2565
	 * @author Naoki Sawada
2566
	 */
2567
	public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') {
2568
		return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown);
2569
	}
2570
	
2571
	/**
2572
	 * Converts character encoding from server's one to UTF-8
2573
	 * 
2574
	 * @param  mixed  $var           target string or array var
2575
	 * @param  bool   $restoreLocale do retore global locale, default is true
2576
	 * @param  string $unknown       replaces character for unknown
2577
	 * @return mixed
2578
	 * @author Naoki Sawada
2579
	 */
2580
	public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') {
2581
		return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown);
2582
	}
2583
	
2584
	/**
2585
	 * Converts character encoding (base function)
2586
	 * 
2587
	 * @param  mixed  $var     target string or array var
2588
	 * @param  string $from    from character encoding
2589
	 * @param  string $to      to character encoding
2590
	 * @param  string $locale  local locale
2591
	 * @param  string $unknown replaces character for unknown
2592
	 * @return mixed
2593
	 */
2594
	protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') {
2595
		if (strtoupper($from) !== strtoupper($to)) {
2596
			if ($locale) {
2597
				@setlocale(LC_ALL, $locale);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2598
			}
2599
			if (is_array($var)) {
2600
				$_ret = array();
2601
				foreach($var as $_k => $_v) {
2602
					$_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_');
2603
				}
2604
				$var = $_ret;
2605
			} else {
2606
				$_var = false;
2607
				if (is_string($var)) {
2608
					$_var = $var;
2609
					if (false !== ($_var = @iconv($from, $to.'//TRANSLIT', $_var))) {
2610
						$_var = str_replace('?', $unknown, $_var);
2611
					}
2612
				}
2613
				if  ($_var !== false) {
2614
					$var = $_var;
2615
				}
2616
			}
2617
			if ($restoreLocale) {
2618
				setlocale(LC_ALL, elFinder::$locale);
2619
			}
2620
		}
2621
		return $var;
2622
	}
2623
	
2624
	/*********************** util mainly for inheritance class *********************/
2625
	
2626
	/**
2627
	 * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called.
2628
	 * When needing the unique file to a path, give $path to parameter.
2629
	 * 
2630
	 * @param  string       $path for get unique file to a path
2631
	 * @return string|false
2632
	 * @author Naoki Sawada
2633
	 */
2634
	protected function getTempFile($path = '') {
2635
		static $cache = array();
2636
		static $rmfunc;
2637
		
2638
		$key = '';
2639
		if ($path !== '') {
2640
			$key = $this->id . '#' . $path;
2641
			if (isset($cache[$key])) {
2642
				return $cache[$key];
2643
			}
2644
		}
2645
		
2646
		if ($tmpdir = $this->getTempPath()) {
2647
			if (!$rmfunc) {
2648
				$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...
2649
			}
2650
			$name = tempnam($tmpdir, 'ELF');
2651
			if ($key) {
2652
				$cache[$key] = $name;
2653
			}
2654
			register_shutdown_function($rmfunc, $name);
2655
			return $name;
2656
		}
2657
		
2658
		return false;
2659
	}
2660
	
2661
	/**
2662
	 * File path of local server side work file path
2663
	 * 
2664
	 * @param  string $path path need convert encoding to server encoding
2665
	 * @return string
2666
	 * @author Naoki Sawada
2667
	 */
2668
	protected function getWorkFile($path) {
2669
		if ($work = $this->getTempFile()) {
2670
			if ($wfp = fopen($work, 'wb')) {
2671
				if ($fp = $this->_fopen($path)) {
2672
					while(!feof($fp)) {
2673
						fwrite($wfp, fread($fp, 8192));
2674
					}
2675
					$this->_fclose($fp, $path);
2676
					fclose($wfp);
2677
					return $work;
2678
				}
2679
			}
2680
		}
2681
		return false;
2682
	}
2683
	
2684
	/**
2685
	 * Get image size array with `dimensions`
2686
	 *
2687
	 * @param string $path path need convert encoding to server encoding
2688
	 * @param string $mime file mime type
2689
	 * @return array|false
2690
	 */
2691
	public function getImageSize($path, $mime = '') {
2692
		$size = false;
2693
		if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') {
2694
			if ($work = $this->getWorkFile($path)) {
2695
				if ($size = @getimagesize($work)) {
2696
					$size['dimensions'] = $size[0].'x'.$size[1];
2697
				}
2698
			}
2699
			is_file($work) && @unlink($work);
2700
		}
2701
		return $size;
2702
	}
2703
	
2704
	/**
2705
	 * Delete dirctory trees
2706
	 *
2707
	 * @param string $localpath path need convert encoding to server encoding
2708
	 * @return boolean
2709
	 * @author Naoki Sawada
2710
	 */
2711
	protected function delTree($localpath) {
2712
		foreach ($this->_scandir($localpath) as $p) {
2713
			@set_time_limit(30);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2714
			$stat = $this->stat($this->convEncOut($p));
2715
			$this->convEncIn();
2716
			($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p);
2717
		}
2718
		return $this->_rmdir($localpath);
2719
	}
2720
	
2721
	/*********************** file stat *********************/
2722
	
2723
	/**
2724
	 * Check file attribute
2725
	 *
2726
	 * @param  string  $path  file path
2727
	 * @param  string  $name  attribute name (read|write|locked|hidden)
2728
	 * @param  bool    $val   attribute value returned by file system
2729
	 * @param  bool    $isDir path is directory (true: directory, false: file)
2730
	 * @return bool
2731
	 * @author Dmitry (dio) Levashov
2732
	 **/
2733
	protected function attr($path, $name, $val=null, $isDir=null) {
2734
		if (!isset($this->defaults[$name])) {
2735
			return false;
2736
		}
2737
		
2738
		
2739
		$perm = null;
2740
		
2741 View Code Duplication
		if ($this->access) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2742
			$perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir);
2743
2744
			if ($perm !== null) {
2745
				return !!$perm;
2746
			}
2747
		}
2748
		
2749
		if ($this->separator != '/') {
2750
			$path = str_replace($this->separator, '/', $this->relpathCE($path));
2751
		} else {
2752
			$path = $this->relpathCE($path);
2753
		}
2754
2755
		$path = '/'.$path;
2756
2757 View Code Duplication
		for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2758
			$attrs = $this->attributes[$i];
2759
			
2760
			if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
2761
				$perm = $attrs[$name];
2762
			} 
2763
		}
2764
		
2765
		return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
2766
	}
2767
	
2768
	/**
2769
	 * Return true if file with given name can be created in given folder.
2770
	 *
2771
	 * @param string $dir  parent dir path
2772
	 * @param string $name new file name
2773
	 * @return bool
2774
	 * @author Dmitry (dio) Levashov
2775
	 **/
2776
	protected function allowCreate($dir, $name, $isDir = null) {
2777
		$path = $this->joinPathCE($dir, $name);
2778
		$perm = null;
2779
		
2780 View Code Duplication
		if ($this->access) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2781
			$perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this, $isDir);			
2782
			if ($perm !== null) {
2783
				return !!$perm;
2784
			}
2785
		}
2786
		
2787
		$testPath = $this->separator.$this->relpathCE($path);
2788
		
2789 View Code Duplication
		for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2790
			$attrs = $this->attributes[$i];
2791
			
2792
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2793
				$perm = $attrs['write'];
2794
			} 
2795
		}
2796
		
2797
		return $perm === null ? true : $perm;
2798
	}
2799
	
2800
	/**
2801
	 * Return true if file MIME type can save with check uploadOrder config.
2802
	 * 
2803
	 * @param string $mime
2804
	 * @return boolean
2805
	 */
2806
	protected function allowPutMime($mime) {
2807
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
2808
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
2809
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
2810
		$res = true; // default to allow
0 ignored issues
show
Unused Code introduced by
$res is not used, you could remove the assignment.

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

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

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

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

Loading history...
2811
		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...
2812
			$res = false; // default is deny
2813
			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...
2814
				$res = true;
2815
			}// else (both match | no match | match only deny) { deny }
2816
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
2817
			$res = true; // default is allow
2818
			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...
2819
				$res = false;
2820
			} // else (both match | no match | match only allow) { allow }
2821
		}
2822
		return $res;
2823
	}
2824
	
2825
	/**
2826
	 * Return fileinfo 
2827
	 *
2828
	 * @param  string  $path  file cache
2829
	 * @return array
2830
	 * @author Dmitry (dio) Levashov
2831
	 **/
2832
	protected function stat($path) {
2833
		if ($path === false || is_null($path)) {
2834
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by elFinderVolumeDriver::stat of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2835
		}
2836
		$is_root = ($path == $this->root);
2837
		if ($is_root) {
2838
			$rootKey = md5($path);
2839
			if (!isset($this->sessionCache['rootstat'])) {
2840
				$this->sessionCache['rootstat'] = array();
2841
			}
2842
			//if (empty($this->ARGS['reload']) || empty($this->ARGS['target']) || strpos($this->ARGS['target'], $this->id) !== 0) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2843
			if (! $this->isMyReload()) {
2844
				// need $path as key for netmount/netunmount
2845
				if (isset($this->sessionCache['rootstat'][$rootKey])) {
2846
					if ($ret = elFinder::sessionDataDecode($this->sessionCache['rootstat'][$rootKey], 'array')) {
0 ignored issues
show
Documentation introduced by
'array' is of type string, but the function expects a boolean|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2847
						return $ret;
2848
					}
2849
				}
2850
			}
2851
		}
2852
		$ret = isset($this->cache[$path])
2853
			? $this->cache[$path]
2854
			: $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path))));
2855
		if ($is_root) {
2856
			$this->sessionCache['rootstat'][$rootKey] = elFinder::sessionDataEncode($ret);
0 ignored issues
show
Bug introduced by
The variable $rootKey does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2857
		}
2858
		return $ret;
2859
	}
2860
	
2861
	/**
2862
	 * Put file stat in cache and return it
2863
	 *
2864
	 * @param  string  $path   file path
2865
	 * @param  array   $stat   file stat
2866
	 * @return array
2867
	 * @author Dmitry (dio) Levashov
2868
	 **/
2869
	protected function updateCache($path, $stat) {
2870
		if (empty($stat) || !is_array($stat)) {
2871
			return $this->cache[$path] = array();
2872
		}
2873
2874
		$stat['hash'] = $this->encode($path);
2875
2876
		$root = $path == $this->root;
2877
		$parent = '';
2878
		
2879
		if ($root) {
2880
			if ($this->rootName) {
2881
				$stat['name'] = $this->rootName;
2882
			}
2883
			if (! empty($this->options['icon'])) {
2884
				$stat['icon'] = $this->options['icon'];
2885
			}
2886
			if (! empty($this->options['rootCssClass'])) {
2887
				$stat['csscls'] = $this->options['rootCssClass'];
2888
			}
2889
		} else {
2890
			if (!isset($stat['name']) || $stat['name'] === '') {
2891
				$stat['name'] = $this->basenameCE($path);
2892
			}
2893
			if (empty($stat['phash'])) {
2894
				$parent = $this->dirnameCE($path);
2895
				$stat['phash'] = $this->encode($parent);
2896
			}
2897
		}
2898
		
2899
		// name check
2900
		if (!$jeName = json_encode($stat['name'])) {
2901
			return $this->cache[$path] = array();
2902
		}
2903
		// fix name if required
2904
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2905
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], $jeName));
2906
		}
2907
		
2908
		
2909
		if (empty($stat['mime'])) {
2910
			$stat['mime'] = $this->mimetype($stat['name']);
2911
		}
2912
		
2913
		// @todo move dateformat to client
2914
		// $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...
2915
		// 	? $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...
2916
		// 	: 'unknown';
2917
			
2918
		if (!isset($stat['size'])) {
2919
			$stat['size'] = 'unknown';
2920
		}	
2921
2922
		if ($isDir = ($stat['mime'] === 'directory')) {
2923
			$stat['volumeid'] = $this->id;
2924
		}
2925
		
2926
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir));
2927
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir));
2928
		if ($root) {
2929
			$stat['locked'] = 1;
2930
		} else {
2931
			// lock when parent directory is not writable
2932
			if (!isset($stat['locked'])) {
2933
				$parent = $this->dirnameCE($path);
2934
				$pstat = isset($this->cache[$parent])? $this->cache[$parent] : array();
2935
				if (isset($pstat['write']) && !$pstat['write']) {
2936
					$stat['locked'] = true;
2937
				}
2938
			}
2939
			if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) {
2940
				$stat['locked'] = 1;
2941
			} else {
2942
				unset($stat['locked']);
2943
			}
2944
		}
2945
2946
		if ($root) {
2947
			unset($stat['hidden']);
2948
		} elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) 
2949
		|| !$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...
2950
			$stat['hidden'] = 1;
2951
		} else {
2952
			unset($stat['hidden']);
2953
		}
2954
		
2955
		if ($stat['read'] && empty($stat['hidden'])) {
2956
			
2957
			if ($isDir) {
2958
				// caching parent's subdirs
2959
				if ($parent) {
2960
					$this->subdirsCache[$parent] = true;
2961
				}
2962
				// for dir - check for subdirs
2963
				if ($this->options['checkSubfolders']) {
2964
					if (isset($stat['dirs'])) {
2965
						if ($stat['dirs']) {
2966
							$stat['dirs'] = 1;
2967
						} else {
2968
							unset($stat['dirs']);
2969
						}
2970
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2971
						$stat['dirs'] = isset($this->cache[$stat['target']])
2972
							? intval(isset($this->cache[$stat['target']]['dirs']))
2973
							: $this->subdirsCE($stat['target']);
2974
						
2975
					} elseif ($this->subdirsCE($path)) {
2976
						$stat['dirs'] = 1;
2977
					}
2978
				} else {
2979
					$stat['dirs'] = 1;
2980
				}
2981
			} else {
2982
				// for files - check for thumbnails
2983
				$p = isset($stat['target']) ? $stat['target'] : $path;
2984
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
2985
					$tmb = $this->gettmb($p, $stat);
2986
					$stat['tmb'] = $tmb ? $tmb : 1;
2987
				}
2988
				
2989
			}
2990
			if (!isset($stat['url']) && $this->URL && $this->encoding) {
2991
				$_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1));
2992
				$stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN')? $_path : $this->convEncIn($_path, true)));
2993
			}
2994
		} else {
2995
			if ($isDir) {
2996
				unset($stat['dirs']);
2997
			}
2998
		}
2999
		
3000
		if (!empty($stat['alias']) && !empty($stat['target'])) {
3001
			$stat['thash'] = $this->encode($stat['target']);
3002
			//$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...
3003
			unset($stat['target']);
3004
		}
3005
		
3006
		if (isset($this->options['netkey']) && $path === $this->root) {
3007
			$stat['netkey'] = $this->options['netkey'];
3008
		}
3009
		
3010
		return $this->cache[$path] = $stat;
3011
	}
3012
	
3013
	/**
3014
	 * Get stat for folder content and put in cache
3015
	 *
3016
	 * @param  string  $path
3017
	 * @return void
3018
	 * @author Dmitry (dio) Levashov
3019
	 **/
3020
	protected function cacheDir($path) {
3021
		$this->dirsCache[$path] = array();
3022
		$this->subdirsCache[$path] = false;
3023
3024
		foreach ($this->scandirCE($path) as $p) {
3025 View Code Duplication
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3026
				if ($stat['mime'] === 'directory') {
3027
					$this->subdirsCache[$path] = true;
3028
				}
3029
				$this->dirsCache[$path][] = $p;
3030
			}
3031
		}
3032
	}
3033
	
3034
	/**
3035
	 * Clean cache
3036
	 *
3037
	 * @return void
3038
	 * @author Dmitry (dio) Levashov
3039
	 **/
3040
	protected function clearcache() {
3041
		$this->cache = $this->dirsCache = array();
3042
		unset($this->sessionCache['rootstat'][md5($this->root)]);
3043
	}
3044
	
3045
	/**
3046
	 * Return file mimetype
3047
	 *
3048
	 * @param  string  $path  file path
3049
	 * @return string
3050
	 * @author Dmitry (dio) Levashov
3051
	 **/
3052
	protected function mimetype($path, $name = '') {
3053
		$type = '';
0 ignored issues
show
Unused Code introduced by
$type is not used, you could remove the assignment.

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

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

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

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

Loading history...
3054
		
3055
		if ($name === '') {
3056
			$name = $path;
3057
		}
3058
		$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
3059
		if ($this->mimeDetect == 'finfo') {
3060
			if ($type = @finfo_file($this->finfo, $path)) {
3061
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
3062
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
3063
				} else if ($ext === 'js' && preg_match('~^text/~', $type)) {
3064
					$type = 'text/javascript';
3065
				}
3066
			} else {
3067
				$type = 'unknown';
3068
			}
3069
		} elseif ($this->mimeDetect == 'mime_content_type') {
3070
			$type = mime_content_type($path);
3071
		} else {
3072
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
3073
		}
3074
		
3075
		$type = explode(';', $type);
3076
		$type = trim($type[0]);
3077
3078 View Code Duplication
		if (in_array($type, array('application/x-empty', 'inode/x-empty'))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3079
			// finfo return this mime for empty files
3080
			$type = 'text/plain';
3081
		} elseif ($type == 'application/x-zip') {
3082
			// http://elrte.org/redmine/issues/163
3083
			$type = 'application/zip';
3084
		}
3085
		
3086
		// mime type normalization
3087
		$_checkKey = strtolower($ext.':'.$type);
3088
		if (isset($this->options['mimeMap'][$_checkKey])) {
3089
			$type = $this->options['mimeMap'][$_checkKey];
3090
		}
3091
		
3092
		return $type == 'unknown' && $this->mimeDetect != 'internal'
3093
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
3094
			: $type;
3095
		
3096
	}
3097
	
3098
	/**
3099
	 * Detect file mimetype using "internal" method
3100
	 *
3101
	 * @param  string  $path  file path
3102
	 * @return string
3103
	 * @author Dmitry (dio) Levashov
3104
	 **/
3105
	static protected function mimetypeInternalDetect($path) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
3106
		// load default MIME table file "mime.types"
3107
		if (!elFinderVolumeDriver::$mimetypesLoaded) {
3108
			elFinderVolumeDriver::$mimetypesLoaded = true;
3109
			$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
3110 View Code Duplication
			if (is_readable($file)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3111
				$mimecf = file($file);
3112
				foreach ($mimecf as $line_num => $line) {
3113
					if (!preg_match('/^\s*#/', $line)) {
3114
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
3115
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
3116
							if (!isset(elFinderVolumeDriver::$mimetypes[$mime[$i]])) {
3117
								elFinderVolumeDriver::$mimetypes[$mime[$i]] = $mime[0];
3118
							}
3119
						}
3120
					}
3121
				}
3122
			}
3123
		}
3124
		$pinfo = pathinfo($path); 
3125
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
3126
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
3127
	}
3128
	
3129
	/**
3130
	 * Return file/total directory size
3131
	 *
3132
	 * @param  string  $path  file path
3133
	 * @return int
3134
	 * @author Dmitry (dio) Levashov
3135
	 **/
3136
	protected function countSize($path) {
3137
		$stat = $this->stat($path);
3138
3139
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
3140
			return 'unknown';
3141
		}
3142
		
3143
		if ($stat['mime'] != 'directory') {
3144
			return $stat['size'];
3145
		}
3146
		
3147
		$subdirs = $this->options['checkSubfolders'];
3148
		$this->options['checkSubfolders'] = true;
3149
		$result = 0;
3150
		foreach ($this->getScandir($path) as $stat) {
3151
			$size = $stat['mime'] == 'directory' && $stat['read'] 
3152
				? $this->countSize($this->joinPathCE($path, $stat['name'])) 
3153
				: (isset($stat['size']) ? intval($stat['size']) : 0);
3154
			if ($size > 0) {
3155
				$result += $size;
3156
			}
3157
		}
3158
		$this->options['checkSubfolders'] = $subdirs;
3159
		return $result;
3160
	}
3161
	
3162
	/**
3163
	 * Return true if all mimes is directory or files
3164
	 *
3165
	 * @param  string  $mime1  mimetype
3166
	 * @param  string  $mime2  mimetype
3167
	 * @return bool
3168
	 * @author Dmitry (dio) Levashov
3169
	 **/
3170
	protected function isSameType($mime1, $mime2) {
3171
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
3172
	}
3173
	
3174
	/**
3175
	 * If file has required attr == $val - return file path,
3176
	 * If dir has child with has required attr == $val - return child path
3177
	 *
3178
	 * @param  string   $path  file path
3179
	 * @param  string   $attr  attribute name
3180
	 * @param  bool     $val   attribute value
3181
	 * @return string|false
3182
	 * @author Dmitry (dio) Levashov
3183
	 **/
3184
	protected function closestByAttr($path, $attr, $val) {
3185
		$stat = $this->stat($path);
3186
		
3187
		if (empty($stat)) {
3188
			return false;
3189
		}
3190
		
3191
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
3192
		
3193
		if ($v == $val) {
3194
			return $path;
3195
		}
3196
3197
		return $stat['mime'] == 'directory'
3198
			? $this->childsByAttr($path, $attr, $val) 
3199
			: false;
3200
	}
3201
	
3202
	/**
3203
	 * Return first found children with required attr == $val
3204
	 *
3205
	 * @param  string   $path  file path
3206
	 * @param  string   $attr  attribute name
3207
	 * @param  bool     $val   attribute value
3208
	 * @return string|false
3209
	 * @author Dmitry (dio) Levashov
3210
	 **/
3211
	protected function childsByAttr($path, $attr, $val) {
3212
		foreach ($this->scandirCE($path) as $p) {
3213
			if (($_p = $this->closestByAttr($p, $attr, $val)) != false) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $_p = $this->closestByAttr($p, $attr, $val) of type string|false against false; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
3214
				return $_p;
3215
			}
3216
		}
3217
		return false;
3218
	}
3219
	
3220
	protected function isMyReload($target = '', $ARGtarget = '') {
3221
		if (! empty($this->ARGS['reload'])) {
3222
			if ($ARGtarget === '') {
3223
				$ARGtarget = isset($this->ARGS['target'])? $this->ARGS['target']
3224
					: ((isset($this->ARGS['targets']) && is_array($this->ARGS['targets']) && count($this->ARGS['targets']) === 1)?
3225
						$this->ARGS['targets'][0] : '');
3226
			}
3227
			if ($ARGtarget !== '') {
3228
				$ARGtarget = strval($ARGtarget);
3229
				if ($target === '') {
3230
					return (strpos($ARGtarget, $this->id) === 0);
3231
				} else {
3232
					$target = strval($target);
3233
					return ($target === $ARGtarget);
3234
				}
3235
			}
3236
		}
3237
		return false;
3238
	}
3239
	
3240
	/*****************  get content *******************/
3241
	
3242
	/**
3243
	 * Return required dir's files info.
3244
	 * If onlyMimes is set - return only dirs and files of required mimes
3245
	 *
3246
	 * @param  string  $path  dir path
3247
	 * @return array
3248
	 * @author Dmitry (dio) Levashov
3249
	 **/
3250
	protected function getScandir($path) {
3251
		$files = array();
3252
		
3253
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3254
3255
		foreach ($this->dirsCache[$path] as $p) {
3256
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
3257
				$files[] = $stat;
3258
			}
3259
		}
3260
3261
		return $files;
3262
	}
3263
	
3264
	
3265
	/**
3266
	 * Return subdirs tree
3267
	 *
3268
	 * @param  string  $path  parent dir path
3269
	 * @param  int     $deep  tree deep
3270
	 * @return array
3271
	 * @author Dmitry (dio) Levashov
3272
	 **/
3273
	protected function gettree($path, $deep, $exclude='') {
3274
		$dirs = array();
3275
		
3276
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
3277
3278
		foreach ($this->dirsCache[$path] as $p) {
3279
			$stat = $this->stat($p);
3280
			
3281
			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...
3282
				$dirs[] = $stat;
3283
				if ($deep > 0 && !empty($stat['dirs'])) {
3284
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
3285
				}
3286
			}
3287
		}
3288
3289
		return $dirs;
3290
	}	
3291
		
3292
	/**
3293
	 * Recursive files search
3294
	 *
3295
	 * @param  string  $path   dir path
3296
	 * @param  string  $q      search string
3297
	 * @param  array   $mimes
3298
	 * @return array
3299
	 * @author Dmitry (dio) Levashov
3300
	 **/
3301
	protected function doSearch($path, $q, $mimes) {
3302
		$result = array();
3303
3304
		foreach($this->scandirCE($path) as $p) {
3305
			@set_time_limit(30);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
3306
			$stat = $this->stat($p);
3307
3308
			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...
3309
				continue;
3310
			}
3311
3312
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
3313
				continue;
3314
			}
3315
			
3316
			$name = $stat['name'];
3317
3318
			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...
3319
				$stat['path'] = $this->path($stat['hash']);
3320
				if ($this->URL && !isset($stat['url'])) {
3321
					$path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
3322
					if ($this->encoding) {
3323
						$path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true)));
3324
					}
3325
					$stat['url'] = $this->URL . $path;
3326
				}
3327
				
3328
				$result[] = $stat;
3329
			}
3330 View Code Duplication
			if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3331
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
3332
			}
3333
		}
3334
		
3335
		return $result;
3336
	}
3337
		
3338
	/**********************  manuipulations  ******************/
3339
		
3340
	/**
3341
	 * Copy file/recursive copy dir only in current volume.
3342
	 * Return new file path or false.
3343
	 *
3344
	 * @param  string  $src   source path
3345
	 * @param  string  $dst   destination dir path
3346
	 * @param  string  $name  new file name (optionaly)
3347
	 * @return string|false
3348
	 * @author Dmitry (dio) Levashov
3349
	 **/
3350
	protected function copy($src, $dst, $name) {
3351
		$srcStat = $this->stat($src);
3352
		$this->clearcache();
3353
		
3354
		if (!empty($srcStat['thash'])) {
3355
			$target = $this->decode($srcStat['thash']);
3356
			if (!$this->inpathCE($target, $this->root)) {
3357
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK);
3358
			}
3359
			$stat   = $this->stat($target);
3360
			$this->clearcache();
3361
			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...
3362
				? $this->joinPathCE($dst, $name)
3363
				: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3364
		} 
3365
		
3366
		if ($srcStat['mime'] == 'directory') {
3367
			$test = $this->stat($this->joinPathCE($dst, $name));
3368
			
3369 View Code Duplication
			if (($test && $test['mime'] != 'directory') || $this->convEncOut(!$this->_mkdir($this->convEncIn($dst), $this->convEncIn($name)))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $test of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

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

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

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

Loading history...
3370
				return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3371
			}
3372
			
3373
			$dst = $this->joinPathCE($dst, $name);
3374
			
3375
			foreach ($this->getScandir($src) as $stat) {
3376
				if (empty($stat['hidden'])) {
3377
					$name = $stat['name'];
3378
					if (!$this->copy($this->joinPathCE($src, $name), $dst, $name)) {
3379
						$this->remove($dst, true); // fall back
3380
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
3381
					}
3382
				}
3383
			}
3384
			$this->clearcache();
3385
			return $dst;
3386
		} 
3387
3388
		return $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))
3389
			? $this->joinPathCE($dst, $name) 
3390
			: $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']));
3391
	}
3392
3393
	/**
3394
	 * Move file
3395
	 * Return new file path or false.
3396
	 *
3397
	 * @param  string  $src   source path
3398
	 * @param  string  $dst   destination dir path
3399
	 * @param  string  $name  new file name 
3400
	 * @return string|false
3401
	 * @author Dmitry (dio) Levashov
3402
	 **/
3403
	protected function move($src, $dst, $name) {
3404
		$stat = $this->stat($src);
3405
		$stat['realpath'] = $src;
3406
		$this->rmTmb($stat); // can not do rmTmb() after _move()
0 ignored issues
show
Documentation introduced by
$stat is of type array<string,string,{"realpath":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3407
		$this->clearcache();
3408
		
3409 View Code Duplication
		if ($this->convEncOut($this->_move($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3410
			$this->removed[] = $stat;
3411
3412
			return $this->joinPathCE($dst, $name);
3413
		}
3414
3415
		return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash']));
3416
	}
3417
3418
	/**
3419
	 * Copy file from another volume.
3420
	 * Return new file path or false.
3421
	 *
3422
	 * @param  Object  $volume       source volume
3423
	 * @param  string  $src          source file hash
3424
	 * @param  string  $destination  destination dir path
3425
	 * @param  string  $name         file name
3426
	 * @return string|false
3427
	 * @author Dmitry (dio) Levashov
3428
	 **/
3429
	protected function copyFrom($volume, $src, $destination, $name) {
3430
		
3431
		if (($source = $volume->file($src)) == false) {
3432
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
3433
		}
3434
		
3435
		$errpath = $volume->path($source['hash']);
3436
		
3437
		if (!$this->nameAccepted($source['name'])) {
3438
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
3439
		}
3440
				
3441
		if (!$source['read']) {
3442
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
3443
		}
3444
		
3445
		if ($source['mime'] == 'directory') {
3446
			$stat = $this->stat($this->joinPathCE($destination, $name));
3447
			$this->clearcache();
3448 View Code Duplication
			if ((!$stat || $stat['mime'] != 'directory') && $this->convEncOut(!$this->_mkdir($this->convEncIn($destination), $this->convEncIn($name)))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

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

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

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

Loading history...
3449
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3450
			}
3451
			
3452
			$path = $this->joinPathCE($destination, $name);
3453
			
3454
			foreach ($volume->scandir($src) as $entr) {
3455
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
3456
					$this->remove($path, true); // fall back
3457
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
3458
				}
3459
			}
3460
			
3461
		} else {
3462
			// $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...
3463
			// $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...
3464
			if (($dim = $volume->dimensions($src))) {
3465
				$s = explode('x', $dim);
3466
				$source['width']  = $s[0];
3467
				$source['height'] = $s[1];
3468
			}
3469
			
3470
			if (($fp = $volume->open($src)) == false
3471
			|| ($path = $this->saveCE($fp, $destination, $name, $source)) == false) {
3472
				$fp && $volume->close($fp, $src);
3473
				return $this->setError(elFinder::ERROR_COPY, $errpath);
3474
			}
3475
			$volume->close($fp, $src);
3476
			
3477
			// MIME check
3478
			$stat = $this->stat($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->saveCE($fp, $destination, $name, $source) on line 3471 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...
3479
			$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($stat['name']);
3480
			if ($stat['mime'] === $mimeByName) {
3481
				$mimeByName = '';
3482
			}
3483
			if (!$this->allowPutMime($stat['mime']) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
3484
				$this->remove($path, true);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $this->saveCE($fp, $destination, $name, $source) on line 3471 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...
3485
				return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath);
3486
			}
3487
		}
3488
		
3489
		return $path;
3490
	}
3491
		
3492
	/**
3493
	 * Remove file/ recursive remove dir
3494
	 *
3495
	 * @param  string  $path   file path
3496
	 * @param  bool    $force  try to remove even if file locked
3497
	 * @return bool
3498
	 * @author Dmitry (dio) Levashov
3499
	 **/
3500
	protected function remove($path, $force = false) {
3501
		$stat = $this->stat($path);
3502
		
3503
		if (empty($stat)) {
3504
			return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']), elFinder::ERROR_FILE_NOT_FOUND);
3505
		}
3506
		
3507
		$stat['realpath'] = $path;
3508
		$this->rmTmb($stat);
0 ignored issues
show
Documentation introduced by
$stat is of type array<string,string,{"realpath":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3509
		$this->clearcache();
3510
		
3511 View Code Duplication
		if (!$force && !empty($stat['locked'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3512
			return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash']));
3513
		}
3514
		
3515
		if ($stat['mime'] == 'directory' && empty($stat['thash'])) {
3516
			$ret = $this->delTree($this->convEncIn($path));
3517
			$this->convEncOut();
3518
			if (!$ret) {
3519
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3520
			}
3521 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3522
			if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) {
3523
				return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash']));
3524
			}
3525
		}
3526
3527
		$this->removed[] = $stat;
3528
		return true;
3529
	}
3530
	
3531
3532
	/************************* thumbnails **************************/
3533
		
3534
	/**
3535
	 * Return thumbnail file name for required file
3536
	 *
3537
	 * @param  array  $stat  file stat
3538
	 * @return string
3539
	 * @author Dmitry (dio) Levashov
3540
	 **/
3541
	protected function tmbname($stat) {
3542
		return $stat['hash'].$stat['ts'].'.png';
3543
	}
3544
	
3545
	/**
3546
	 * Return thumnbnail name if exists
3547
	 *
3548
	 * @param  string  $path file path
3549
	 * @param  array   $stat file stat
3550
	 * @return string|false
3551
	 * @author Dmitry (dio) Levashov
3552
	 **/
3553
	protected function gettmb($path, $stat) {
3554
		if ($this->tmbURL && $this->tmbPath) {
3555
			// file itself thumnbnail
3556
			if (strpos($path, $this->tmbPath) === 0) {
3557
				return basename($path);
3558
			}
3559
3560
			$name = $this->tmbname($stat);
3561
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
3562
				return $name;
3563
			}
3564
		}
3565
		return false;
3566
	}
3567
	
3568
	/**
3569
	 * Return true if thumnbnail for required file can be created
3570
	 *
3571
	 * @param  string  $path  thumnbnail path 
3572
	 * @param  array   $stat  file stat
3573
	 * @param  bool    $checkTmbPath
3574
	 * @return string|bool
3575
	 * @author Dmitry (dio) Levashov
3576
	 **/
3577
	protected function canCreateTmb($path, $stat, $checkTmbPath = true) {
3578
		return (!$checkTmbPath || $this->tmbPathWritable) 
3579
			&& (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail
3580
			&& $this->imgLib 
3581
			&& strpos($stat['mime'], 'image') === 0 
3582
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
3583
	}
3584
	
3585
	/**
3586
	 * Return true if required file can be resized.
3587
	 * By default - the same as canCreateTmb
3588
	 *
3589
	 * @param  string  $path  thumnbnail path 
3590
	 * @param  array   $stat  file stat
3591
	 * @return string|bool
3592
	 * @author Dmitry (dio) Levashov
3593
	 **/
3594
	protected function canResize($path, $stat) {
3595
		return $this->canCreateTmb($path, $stat, false);
3596
	}
3597
	
3598
	/**
3599
	 * Create thumnbnail and return it's URL on success
3600
	 *
3601
	 * @param  string  $path  file path
3602
	 * @param  string  $mime  file mime type
0 ignored issues
show
Bug introduced by
There is no parameter named $mime. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
3603
	 * @return string|false
3604
	 * @author Dmitry (dio) Levashov
3605
	 **/
3606
	protected function createTmb($path, $stat) {
3607
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
3608
			return false;
3609
		}
3610
3611
		$name = $this->tmbname($stat);
3612
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
3613
3614
		// copy image into tmbPath so some drivers does not store files on local fs
3615
		if (($src = $this->fopenCE($path, 'rb')) == false) {
3616
			return false;
3617
		}
3618
3619
		if (($trg = fopen($tmb, 'wb')) == false) {
3620
			$this->fcloseCE($src, $path);
3621
			return false;
3622
		}
3623
3624
		while (!feof($src)) {
3625
			fwrite($trg, fread($src, 8192));
3626
		}
3627
3628
		$this->fcloseCE($src, $path);
3629
		fclose($trg);
3630
3631
		$result = false;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

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

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

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

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

Loading history...
3632
		
3633
		$tmbSize = $this->tmbSize;
3634
		
3635
		if (($s = getimagesize($tmb)) == false) {
3636
			return false;
3637
		}
3638
3639
		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
3640
		if ($s[0] <= $tmbSize && $s[1]	<= $tmbSize) {
3641
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3642
		} else {
3643
		
3644
			if ($this->options['tmbCrop']) {
3645
		
3646
				$result = $tmb;
3647
				/* Resize and crop if image bigger than thumbnail */
3648 View Code Duplication
				if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3649
					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
3650
				}
3651
		
3652 View Code Duplication
				if ($result && ($s = getimagesize($tmb)) != false) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3653
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
3654
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
3655
					$result = $this->imgCrop($result, $tmbSize, $tmbSize, $x, $y, 'png');
0 ignored issues
show
Documentation introduced by
$x is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$y is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3656
				} else {
3657
					$result = false;
3658
				}
3659
		
3660
			} else {
3661
				$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
3662
			}
3663
		
3664
			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...
3665
				$result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
3666
			}
3667
		}
3668
		
3669
		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...
3670
			unlink($tmb);
3671
			return false;
3672
		}
3673
		
3674
		return $name;
3675
	}
3676
3677
	/**
3678
	 * Resize image
3679
	 *
3680
	 * @param  string   $path               image file
3681
	 * @param  int      $width              new width
3682
	 * @param  int      $height             new height
3683
	 * @param  bool	    $keepProportions    crop image
3684
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
3685
	 * @param  string   $destformat         image destination format
3686
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3687
	 * @return string|false
3688
	 * @author Dmitry (dio) Levashov
3689
	 * @author Alexey Sukhotin
3690
	 **/
3691
	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null, $jpgQuality = null) {
3692
		if (($s = @getimagesize($path)) == false) {
3693
			return false;
3694
		}
3695
3696
		$result = false;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

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

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

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

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

Loading history...
3697
		
3698
		list($size_w, $size_h) = array($width, $height);
3699
	
3700
		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...
3701
			$jpgQuality = $this->options['jpgQuality'];
3702
		}
3703
		
3704
		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...
3705
		
3706
			list($orig_w, $orig_h) = array($s[0], $s[1]);
3707
		
3708
			/* Resizing by biggest side */
3709
			if ($resizeByBiggerSide) {
3710 View Code Duplication
				if ($orig_w > $orig_h) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3711
					$size_h = round($orig_h * $width / $orig_w);
3712
					$size_w = $width;
3713
				} else {
3714
					$size_w = round($orig_w * $height / $orig_h);
3715
					$size_h = $height;
3716
				}
3717 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3718
				if ($orig_w > $orig_h) {
3719
					$size_w = round($orig_w * $height / $orig_h);
3720
					$size_h = $height;
3721
				} else {
3722
					$size_h = round($orig_h * $width / $orig_w);
3723
					$size_w = $width;
3724
				}
3725
			}
3726
		}
3727
3728
		switch ($this->imgLib) {
3729
			case 'imagick':
3730
				
3731
				try {
3732
					$img = new imagick($path);
3733
				} catch (Exception $e) {
3734
					return false;
3735
				}
3736
3737
				// Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb
3738
				// resize bench: http://app-mgng.rhcloud.com/9
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

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

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

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

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

Loading history...
3740
				$filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS;
3741
				
3742
				$ani = ($img->getNumberImages() > 1);
3743
				if ($ani && is_null($destformat)) {
3744
					$img = $img->coalesceImages();
3745
					do {
3746
						$img->resizeImage($size_w, $size_h, $filter, 1);
3747
					} while ($img->nextImage());
3748
					$img = $img->optimizeImageLayers();
3749
					$result = $img->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img (of type boolean).

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

Loading history...
3750
				} else {
3751
					if ($ani) {
3752
						$img->setFirstIterator();
3753
					}
3754
					$img->resizeImage($size_w, $size_h, $filter, 1);
3755
					$result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
Documentation introduced by
$img is of type object<Imagick>, but the function expects a resource.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3756
				}
3757
				
3758
				$img->destroy();
3759
3760
				return $result ? $path : false;
3761
3762
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
3763
3764
			case 'gd':
3765
				$img = $this->gdImageCreate($path,$s['mime']);
3766
3767
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
3768
				
3769
					$this->gdImageBackground($tmp,$this->options['tmbBgColor']);
3770
					
3771
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
3772
						return false;
3773
					}
3774
		
3775
					$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
3776
3777
					imagedestroy($img);
3778
					imagedestroy($tmp);
3779
3780
					return $result ? $path : false;
3781
3782
				}
3783
				break;
3784
		}
3785
		
3786
		return false;
3787
  	}
3788
  
3789
	/**
3790
	 * Crop image
3791
	 *
3792
	 * @param  string   $path               image file
3793
	 * @param  int      $width              crop width
3794
	 * @param  int      $height             crop height
3795
	 * @param  bool	    $x                  crop left offset
3796
	 * @param  bool	    $y                  crop top offset
3797
	 * @param  string   $destformat         image destination format
3798
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3799
	 * @return string|false
3800
	 * @author Dmitry (dio) Levashov
3801
	 * @author Alexey Sukhotin
3802
	 **/
3803
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null, $jpgQuality = null) {
3804
		if (($s = @getimagesize($path)) == false) {
3805
			return false;
3806
		}
3807
3808
		$result = false;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

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

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

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

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

Loading history...
3809
		
3810
		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...
3811
			$jpgQuality = $this->options['jpgQuality'];
3812
		}
3813
3814
		switch ($this->imgLib) {
3815
			case 'imagick':
3816
				
3817
				try {
3818
					$img = new imagick($path);
3819
				} catch (Exception $e) {
3820
					return false;
3821
				}
3822
				
3823
				$ani = ($img->getNumberImages() > 1);
3824
				if ($ani && is_null($destformat)) {
3825
					$img = $img->coalesceImages();
3826
					do {
3827
						$img->setImagePage($s[0], $s[1], 0, 0);
3828
						$img->cropImage($width, $height, $x, $y);
3829
						$img->setImagePage($width, $height, 0, 0);
3830
					} while ($img->nextImage());
3831
					$img = $img->optimizeImageLayers();
3832
					$result = $img->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img (of type boolean).

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

Loading history...
3833
				} else {
3834
					if ($ani) {
3835
						$img->setFirstIterator();
3836
					}
3837
					$img->setImagePage($s[0], $s[1], 0, 0);
3838
					$img->cropImage($width, $height, $x, $y);
3839
					$img->setImagePage($width, $height, 0, 0);
3840
					$result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
Documentation introduced by
$img is of type object<Imagick>, but the function expects a resource.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3841
				}
3842
				
3843
				$img->destroy();
3844
3845
				return $result ? $path : false;
3846
3847
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
3848
3849
			case 'gd':
3850
				$img = $this->gdImageCreate($path,$s['mime']);
3851
3852
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3853
					
3854
					$this->gdImageBackground($tmp,$this->options['tmbBgColor']);
3855
3856
					$size_w = $width;
3857
					$size_h = $height;
3858
3859
					if ($s[0] < $width || $s[1] < $height) {
3860
						$size_w = $s[0];
3861
						$size_h = $s[1];
3862
					}
3863
3864
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
3865
						return false;
3866
					}
3867
					
3868
					$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
3869
3870
					imagedestroy($img);
3871
					imagedestroy($tmp);
3872
3873
					return $result ? $path : false;
3874
3875
				}
3876
				break;
3877
		}
3878
3879
		return false;
3880
	}
3881
3882
	/**
3883
	 * Put image to square
3884
	 *
3885
	 * @param  string   $path               image file
3886
	 * @param  int      $width              square width
3887
	 * @param  int      $height             square height
3888
	 * @param  int	    $align              reserved
3889
	 * @param  int 	    $valign             reserved
3890
	 * @param  string   $bgcolor            square background color in #rrggbb format
3891
	 * @param  string   $destformat         image destination format
3892
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3893
	 * @return string|false
3894
	 * @author Dmitry (dio) Levashov
3895
	 * @author Alexey Sukhotin
3896
	 **/
3897
	protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null, $jpgQuality = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $align is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $valign is not used and could be removed.

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

Loading history...
3898
		if (($s = @getimagesize($path)) == false) {
3899
			return false;
3900
		}
3901
3902
		$result = false;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

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

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

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

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

Loading history...
3903
3904
		/* Coordinates for image over square aligning */
3905
		$y = ceil(abs($height - $s[1]) / 2); 
3906
		$x = ceil(abs($width - $s[0]) / 2);
3907
3908
		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...
3909
			$jpgQuality = $this->options['jpgQuality'];
3910
		}
3911
3912
		switch ($this->imgLib) {
3913
			case 'imagick':
3914
				try {
3915
					$img = new imagick($path);
3916
				} catch (Exception $e) {
3917
					return false;
3918
				}
3919
				
3920
				$ani = ($img->getNumberImages() > 1);
3921
				if ($ani && is_null($destformat)) {
3922
					$img1 = new Imagick();
3923
					$img1->setFormat('gif');
3924
					$img = $img->coalesceImages();
3925
					do {
3926
						$gif = new Imagick();
3927
						$gif->newImage($width, $height, new ImagickPixel($bgcolor));
3928
						$gif->setImageColorspace($img->getImageColorspace());
3929
						$gif->setImageFormat('gif');
3930
						$gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3931
						$gif->setImageDelay($img->getImageDelay());
3932
						$gif->setImageIterations($img->getImageIterations());
3933
						$img1->addImage($gif);
3934
						$gif->destroy();
3935
					} while ($img->nextImage());
3936
					$img1 = $img1->optimizeImageLayers();
3937
					$result = $img1->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img1 (of type boolean).

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

Loading history...
3938
				} else {
3939
					if ($ani) {
3940
						$img->setFirstIterator();
3941
					}
3942
					$img1 = new Imagick();
3943
					$img1->newImage($width, $height, new ImagickPixel($bgcolor));
3944
					$img1->setImageColorspace($img->getImageColorspace());
3945
					$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
3946
					$result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
Documentation introduced by
$img is of type object<Imagick>, but the function expects a resource.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3947
				}
3948
				
3949
				$img1->destroy();
3950
				$img->destroy();
3951
				return $result ? $path : false;
3952
3953
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
3954
3955
			case 'gd':
3956
				$img = $this->gdImageCreate($path,$s['mime']);
3957
3958
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
3959
3960
					$this->gdImageBackground($tmp,$bgcolor);
3961
3962
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
3963
						return false;
3964
					}
3965
3966
					$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
3967
3968
					imagedestroy($img);
3969
					imagedestroy($tmp);
3970
3971
					return $result ? $path : false;
3972
				}
3973
				break;
3974
		}
3975
3976
		return false;
3977
	}
3978
3979
	/**
3980
	 * Rotate image
3981
	 *
3982
	 * @param  string   $path               image file
3983
	 * @param  int      $degree             rotete degrees
3984
	 * @param  string   $bgcolor            square background color in #rrggbb format
3985
	 * @param  string   $destformat         image destination format
3986
	 * @param  int      $jpgQuality         JEPG quality (1-100)
3987
	 * @return string|false
3988
	 * @author nao-pon
3989
	 * @author Troex Nevelin
3990
	 **/
3991
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null, $jpgQuality = null) {
3992
		if (($s = @getimagesize($path)) == false || $degree % 360 === 0) {
3993
			return false;
3994
		}
3995
3996
		$result = false;
3997
3998
		// try lossless rotate
3999
		if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) {
4000
			$count = ($degree / 90) % 4;
4001
			$exiftran = array(
4002
				1 => '-9',
4003
				2 => '-1',
4004
				3 => '-2'
4005
			);
4006
			$jpegtran = array(
4007
				1 => '90',
4008
				2 => '180',
4009
				3 => '270'
4010
			);
4011
			$quotedPath = escapeshellarg($path);
4012
			$cmds = array(
4013
				'exiftran -i '.$exiftran[$count].' '.$path,
4014
				'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath
4015
			);
4016
			foreach($cmds as $cmd) {
4017
				if ($this->procExec($cmd) === 0) {
4018
					$result = true;
4019
					break;
4020
				}
4021
			}
4022
			if ($result) {
4023
				return $path;
4024
			}
4025
		}
4026
4027
		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...
4028
			$jpgQuality = $this->options['jpgQuality'];
4029
		}
4030
4031
		switch ($this->imgLib) {
4032
			case 'imagick':
4033
				try {
4034
					$img = new imagick($path);
4035
				} catch (Exception $e) {
4036
					return false;
4037
				}
4038
4039
				if ($img->getNumberImages() > 1) {
4040
					$img = $img->coalesceImages();
4041
					do {
4042
						$img->rotateImage(new ImagickPixel($bgcolor), $degree);
4043
					} while ($img->nextImage());
4044
					$img = $img->optimizeImageLayers();
4045
					$result = $img->writeImages($path, true);
0 ignored issues
show
Bug introduced by
The method writeImages cannot be called on $img (of type boolean).

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

Loading history...
4046
				} else {
4047
					$img->rotateImage(new ImagickPixel($bgcolor), $degree);
4048
					$result = $this->imagickImage($img, $path, $destformat, $jpgQuality);
0 ignored issues
show
Documentation introduced by
$img is of type object<Imagick>, but the function expects a resource.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
4049
				}
4050
				$img->destroy();
4051
				return $result ? $path : false;
4052
4053
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
4054
4055
			case 'gd':
4056
				$img = $this->gdImageCreate($path,$s['mime']);
4057
4058
				$degree = 360 - $degree;
4059
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4060
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
4061
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
4062
4063
				$result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality);
4064
4065
				imageDestroy($img);
4066
				imageDestroy($tmp);
4067
4068
				return $result ? $path : false;
4069
4070
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
4071
		}
4072
4073
		return false;
4074
	}
4075
4076
	/**
4077
	 * Execute shell command
4078
	 *
4079
	 * @param  string  $command       command line
4080
	 * @param  array   $output        stdout strings
4081
	 * @param  array   $return_var    process exit code
4082
	 * @param  array   $error_output  stderr strings
4083
	 * @return int     exit code
4084
	 * @author Alexey Sukhotin
4085
	 **/
4086
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
4087
4088
		$descriptorspec = array(
4089
			0 => array("pipe", "r"),  // stdin
4090
			1 => array("pipe", "w"),  // stdout
4091
			2 => array("pipe", "w")   // stderr
4092
		);
4093
4094
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
4095
4096
		if (is_resource($process)) {
4097
4098
			fclose($pipes[0]);
4099
4100
			$tmpout = '';
0 ignored issues
show
Unused Code introduced by
$tmpout is not used, you could remove the assignment.

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

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

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

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

Loading history...
4101
			$tmperr = '';
0 ignored issues
show
Unused Code introduced by
$tmperr is not used, you could remove the assignment.

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

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

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

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

Loading history...
4102
4103
			$output = stream_get_contents($pipes[1]);
4104
			$error_output = stream_get_contents($pipes[2]);
4105
4106
			fclose($pipes[1]);
4107
			fclose($pipes[2]);
4108
			$return_var = proc_close($process);
4109
4110
4111
		}
4112
		
4113
		return $return_var;
4114
		
4115
	}
4116
4117
	/**
4118
	 * Remove thumbnail, also remove recursively if stat is directory
4119
	 *
4120
	 * @param  string  $stat  file stat
4121
	 * @return void
4122
	 * @author Dmitry (dio) Levashov
4123
	 * @author Naoki Sawada
4124
	 * @author Troex Nevelin
4125
	 **/
4126
	protected function rmTmb($stat) {
4127
		if ($stat['mime'] === 'directory') {
4128
			foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) {
4129
				@set_time_limit(30);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4130
				$name = $this->basenameCE($p);
4131
				$name != '.' && $name != '..' && $this->rmTmb($this->stat($p));
0 ignored issues
show
Documentation introduced by
$this->stat($p) is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
4132
			}
4133
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
4134
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
4135
			file_exists($tmb) && @unlink($tmb);
4136
			clearstatcache();
4137
		}
4138
	}
4139
	
4140
	/**
4141
	 * Create an gd image according to the specified mime type
4142
	 *
4143
	 * @param string $path image file
4144
	 * @param string $mime
4145
	 * @return gd image resource identifier
4146
	 */
4147
	protected function gdImageCreate($path,$mime){
4148
		switch($mime){
4149
			case 'image/jpeg':
4150
			return imagecreatefromjpeg($path);
4151
4152
			case 'image/png':
4153
			return imagecreatefrompng($path);
4154
4155
			case 'image/gif':
4156
			return imagecreatefromgif($path);
4157
4158
			case 'image/xbm':
4159
			return imagecreatefromxbm($path);
4160
		}
4161
		return false;
4162
	}
4163
4164
	/**
4165
	 * Output gd image to file
4166
	 *
4167
	 * @param resource $image gd image resource
4168
	 * @param string $filename The path to save the file to.
4169
	 * @param string $destformat The Image type to use for $filename
4170
	 * @param string $mime The original image mime type
4171
	 * @param int    $jpgQuality  JEPG quality (1-100)
4172
	 */
4173
	protected function gdImage($image, $filename, $destformat, $mime, $jpgQuality = null ){
4174
4175
		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...
4176
			$jpgQuality = $this->options['jpgQuality'];
4177
		}
4178 View Code Duplication
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4179
			return imagejpeg($image, $filename, $jpgQuality);
4180
		}
4181
4182 View Code Duplication
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4183
			return imagegif($image, $filename, 7);
4184
		}
4185
4186
		return imagepng($image, $filename, 7);
4187
	}
4188
4189
	/**
4190
	 * Output imagick image to file
4191
	 *
4192
	 * @param resource $img imagick image resource
4193
	 * @param string $filename The path to save the file to.
4194
	 * @param string $destformat The Image type to use for $filename
4195
	 * @param int    $jpgQuality  JEPG quality (1-100)
4196
	 */
4197
	protected function imagickImage($img, $filename, $destformat, $jpgQuality = null ){
4198
4199
		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...
4200
			$jpgQuality = $this->options['jpgQuality'];
4201
		}
4202
		
4203
		try {
4204
			if ($destformat) {
4205
				if ($destformat === 'gif') {
4206
					$img->setImageFormat('gif');
0 ignored issues
show
Bug introduced by
The method setImageFormat cannot be called on $img (of type resource).

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

Loading history...
4207
				} else if ($destformat === 'png') {
4208
					$img->setImageFormat('png');
0 ignored issues
show
Bug introduced by
The method setImageFormat cannot be called on $img (of type resource).

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

Loading history...
4209
				} else if ($destformat === 'jpg') {
4210
					$img->setImageFormat('jpeg');
0 ignored issues
show
Bug introduced by
The method setImageFormat cannot be called on $img (of type resource).

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

Loading history...
4211
				}
4212
			}
4213 View Code Duplication
			if (strtoupper($img->getImageFormat()) === 'JPEG') {
0 ignored issues
show
Bug introduced by
The method getImageFormat cannot be called on $img (of type resource).

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

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

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

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

Loading history...
4214
				$img->setImageCompression(imagick::COMPRESSION_JPEG);
0 ignored issues
show
Bug introduced by
The method setImageCompression cannot be called on $img (of type resource).

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

Loading history...
4215
				$img->setImageCompressionQuality($jpgQuality);
0 ignored issues
show
Bug introduced by
The method setImageCompressionQuality cannot be called on $img (of type resource).

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

Loading history...
4216
				try {
4217
					$orientation = $img->getImageOrientation();
0 ignored issues
show
Bug introduced by
The method getImageOrientation cannot be called on $img (of type resource).

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

Loading history...
4218
				} catch (ImagickException $e) {
4219
					$orientation = 0;
4220
				}
4221
				$img->stripImage();
0 ignored issues
show
Bug introduced by
The method stripImage cannot be called on $img (of type resource).

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

Loading history...
4222
				if ($orientation) {
4223
					$img->setImageOrientation($orientation);
0 ignored issues
show
Bug introduced by
The method setImageOrientation cannot be called on $img (of type resource).

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

Loading history...
4224
				}
4225
			}
4226
			$result = $img->writeImage($filename);
0 ignored issues
show
Bug introduced by
The method writeImage cannot be called on $img (of type resource).

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

Loading history...
4227
		} catch (Exception $e) {
4228
			$result = false;
4229
		}
4230
		
4231
		return $result;
4232
		
4233
		
4234
		
4235 View Code Duplication
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
0 ignored issues
show
Unused Code introduced by
if ($destformat == 'jpg'...lename, $jpgQuality); } does not seem to be reachable.

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

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

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

    return false;
}

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

Loading history...
Bug introduced by
The variable $destformat seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

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

Let’s take a look at a simple example:

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

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

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

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

Loading history...
Bug introduced by
The variable $mime does not exist. Did you forget to declare it?

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

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

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

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

Loading history...
4236
			return imagejpeg($image, $filename, $jpgQuality);
4237
		}
4238
4239 View Code Duplication
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
0 ignored issues
show
Bug introduced by
The variable $destformat seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

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

Let’s take a look at a simple example:

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

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

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

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

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

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

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

Loading history...
4240
			return imagegif($image, $filename, 7);
4241
		}
4242
4243
		return imagepng($image, $filename, 7);
4244
	}
4245
4246
	/**
4247
	 * Assign the proper background to a gd image
4248
	 *
4249
	 * @param resource $image gd image resource
4250
	 * @param string $bgcolor background color in #rrggbb format
4251
	 */
4252
	protected function gdImageBackground($image, $bgcolor){
4253
4254
		if( $bgcolor == 'transparent' ){
4255
			imagesavealpha($image,true);
4256
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
4257
4258
		}else{
4259
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
4260
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
4261
		}
4262
4263
		imagefill($image, 0, 0, $bgcolor1);
4264
	}
4265
4266
	/*********************** misc *************************/
4267
	
4268
	/**
4269
	 * Return smart formatted date
4270
	 *
4271
	 * @param  int     $ts  file timestamp
4272
	 * @return string
4273
	 * @author Dmitry (dio) Levashov
4274
	 **/
4275
	// 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...
4276
	// 	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...
4277
	// 		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...
4278
	// 	}
4279
	// 	
0 ignored issues
show
Coding Style introduced by
There is some trailing whitespace on this line which should be avoided as per coding-style.
Loading history...
4280
	// 	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...
4281
	// 		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...
4282
	// 	} 
4283
	// 	
0 ignored issues
show
Coding Style introduced by
There is some trailing whitespace on this line which should be avoided as per coding-style.
Loading history...
4284
	// 	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...
4285
	// }
4286
4287
	/**
4288
	* Find position of first occurrence of string in a string with multibyte support
4289
	*
4290
	* @param  string  $haystack  The string being checked.
4291
	* @param  string  $needle    The string to find in haystack.
4292
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
4293
	* @return int|bool
4294
	* @author Alexey Sukhotin
4295
	**/
4296
	protected function stripos($haystack , $needle , $offset = 0) {
4297
		if (function_exists('mb_stripos')) {
4298
			return mb_stripos($haystack , $needle , $offset, 'UTF-8');
4299
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
4300
			return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset);
4301
		} 
4302
		return stripos($haystack , $needle , $offset);
4303
	}
4304
4305
	/**
4306
	 * Get server side available archivers
4307
	 * 
4308
	 * @param bool $use_cache
4309
	 * @return array
4310
	 */
4311
	protected function getArchivers($use_cache = true) {
4312
4313
		$sessionKey = 'ARCHIVERS_CACHE';
4314
		if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) {
4315
			return $this->sessionCache[$sessionKey];
4316
		}
4317
		
4318
		$arcs = array(
4319
			'create'  => array(),
4320
			'extract' => array()
4321
		);
4322
		
4323
		if (function_exists('proc_open')) {
4324
		
4325
			$this->procExec('tar --version', $o, $ctar);
4326
			
4327
			if ($ctar == 0) {
4328
				$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
4329
				$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
4330
				unset($o);
4331
				$test = $this->procExec('gzip --version', $o, $c);
0 ignored issues
show
Unused Code introduced by
$test is not used, you could remove the assignment.

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

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

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

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

Loading history...
4332 View Code Duplication
				if ($c == 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4333
					$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
4334
					$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
4335
				}
4336
				unset($o);
4337
				$test = $this->procExec('bzip2 --version', $o, $c);
0 ignored issues
show
Unused Code introduced by
$test is not used, you could remove the assignment.

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

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

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

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

Loading history...
4338 View Code Duplication
				if ($c == 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4339
					$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
4340
					$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
4341
				}
4342
				unset($o);
4343
				$test = $this->procExec('xz --version', $o, $c);
0 ignored issues
show
Unused Code introduced by
$test is not used, you could remove the assignment.

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

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

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

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

Loading history...
4344 View Code Duplication
				if ($c == 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4345
					$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
4346
					$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
4347
				}
4348
			}
4349
			unset($o);
4350
			$this->procExec('zip -v', $o, $c);
4351
			if ($c == 0) {
4352
				$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
4353
			}
4354
			unset($o);
4355
			$this->procExec('unzip --help', $o, $c);
4356
			if ($c == 0) {
4357
				$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
4358
			}
4359
			unset($o);
4360
			$this->procExec('rar --version', $o, $c);
4361
			if ($c == 0 || $c == 7) {
4362
				$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
4363
				$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
4364
			} else {
4365
				unset($o);
4366
				$test = $this->procExec('unrar', $o, $c);
0 ignored issues
show
Unused Code introduced by
$test is not used, you could remove the assignment.

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

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

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

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

Loading history...
4367
				if ($c==0 || $c == 7) {
4368
					$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
4369
				}
4370
			}
4371
			unset($o);
4372
			$this->procExec('7za --help', $o, $c);
4373
			if ($c == 0) {
4374
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
4375
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
4376
				
4377
				if (empty($arcs['create']['application/zip'])) {
4378
					$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
4379
				}
4380 View Code Duplication
				if (empty($arcs['extract']['application/zip'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4381
					$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4382
				}
4383
				if (empty($arcs['create']['application/x-tar'])) {
4384
					$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
4385
				}
4386 View Code Duplication
				if (empty($arcs['extract']['application/x-tar'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4387
					$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4388
				}
4389
			} else if (substr(PHP_OS,0,3) === 'WIN') {
4390
				// check `7z` for Windows server.
4391
				unset($o);
4392
				$this->procExec('7z', $o, $c);
4393
				if ($c == 0) {
4394
					$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
4395
					$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
4396
					
4397
					if (empty($arcs['create']['application/zip'])) {
4398
						$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
4399
					}
4400
					if (empty($arcs['extract']['application/zip'])) {
4401
						$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
4402
					}
4403
					if (empty($arcs['create']['application/x-tar'])) {
4404
						$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
4405
					}
4406
					if (empty($arcs['extract']['application/x-tar'])) {
4407
						$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
4408
					}
4409
				}
4410
			}
4411
		
4412
		}
4413
		
4414
		// Use PHP ZipArchive Class
4415
		if (class_exists('ZipArchive', false)) {
4416
			if (empty($arcs['create']['application/zip'])) {
4417
				$arcs['create']['application/zip']  = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveZip', 'ext' => 'zip');
4418
			}
4419 View Code Duplication
			if (empty($arcs['extract']['application/zip'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4420
				$arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => 'self::zipArchiveUnzip', 'ext' => 'zip');
4421
			}
4422
		}
4423
		
4424
		$this->sessionCache[$sessionKey] = $arcs;
4425
		return $arcs;
4426
	}
4427
4428
	/**
4429
	 * Resolve relative / (Unix-like)absolute path
4430
	 * 
4431
	 * @param string $path  target path
4432
	 * @param string $base  base path
4433
	 * @return string
4434
	 */
4435
	protected function getFullPath($path, $base) {
4436
		$separator = $this->separator;
4437
		$systemroot = $this->systemRoot;
4438
4439
		$sepquoted = preg_quote($separator, '#');
4440
4441
		// normalize `/../`
4442
		$normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#';
4443
		while(preg_match($normreg, $path)) {
4444
			$path = preg_replace($normreg, '$1', $path);
4445
		}
4446
		
4447
		// 'Here'
4448
		if ($path === '' || $path === '.' . $separator) return $base;
4449
		
4450
		// Absolute path
4451
		if ($path[0] === $separator || strpos($path, $systemroot) === 0) {
4452
			return $path;
4453
		}
4454
		
4455
		$preg_separator = '#' . $sepquoted . '#';
4456
		
4457
		// Relative path from 'Here'
4458
		if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.' || substr($path, 0, 3) !== '..' . $separator) {
4459
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4460
			if ($arrn[0] !== '.') {
4461
				array_unshift($arrn, '.');
4462
			}
4463
			$arrn[0] = $base;
4464
			return join($separator, $arrn);
4465
		}
4466
		
4467
		// Relative path from dirname()
4468
		if (substr($path, 0, 3) === '../') {
4469
			$arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY);
4470
			$arrp = preg_split($preg_separator, $base, -1, PREG_SPLIT_NO_EMPTY);
4471
		
4472
			while (! empty($arrn) && $arrn[0] === '..') {
4473
				array_shift($arrn);
4474
				array_pop($arrp);
4475
			}
4476
			$path = ! empty($arrp) ? $systemroot . join($separator, array_merge($arrp, $arrn)) :
4477
				(! empty($arrn) ? $systemroot . join($separator, $arrn) : $systemroot);
4478
		}
4479
		
4480
		return $path;
4481
	}
4482
4483
	/**
4484
	 * Remove directory recursive on local file system
4485
	 *
4486
	 * @param string $dir Target dirctory path
4487
	 * @return boolean
4488
	 * @author Naoki Sawada
4489
	 */
4490
	public function rmdirRecursive($dir) {
4491
		if (!is_link($dir) && is_dir($dir)) {
4492
			@chmod($dir, 0777);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4506
			return @unlink($dir);
4507
		}
4508
		return false;
4509
	}
4510
4511
	/**
4512
	 * Create archive and return its path
4513
	 *
4514
	 * @param  string  $dir    target dir
4515
	 * @param  array   $files  files names list
4516
	 * @param  string  $name   archive name
4517
	 * @param  array   $arc    archiver options
4518
	 * @return string|bool
4519
	 * @author Dmitry (dio) Levashov, 
4520
	 * @author Alexey Sukhotin
4521
	 * @author Naoki Sawada
4522
	 **/
4523
	protected function makeArchive($dir, $files, $name, $arc) {
4524
		if ($arc['cmd'] === 'phpfunction') {
4525 View Code Duplication
			if (is_callable($arc['argc'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4526
				call_user_func_array($arc['argc'], array($dir, $files, $name));
4527
			}
4528
		} else {
4529
			$cwd = getcwd();
4530
			chdir($dir);
4531
			
4532
			$files = array_map('escapeshellarg', $files);
4533
			
4534
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files);
4535
			$this->procExec($cmd, $o, $c);
4536
			chdir($cwd);
4537
		}
4538
		$path = $dir.DIRECTORY_SEPARATOR.$name;
4539
		return file_exists($path) ? $path : false;
4540
	}
4541
	
4542
	/**
4543
	 * Unpack archive
4544
	 *
4545
	 * @param  string  $path   archive path
4546
	 * @param  array   $arc    archiver command and arguments (same as in $this->archivers)
4547
	 * @param  bool    $remove remove archive ( unlink($path) )
4548
	 * @return void
4549
	 * @author Dmitry (dio) Levashov
4550
	 * @author Alexey Sukhotin
4551
	 * @author Naoki Sawada
4552
	 **/
4553
	protected function unpackArchive($path, $arc, $remove = true) {
4554
		$dir = dirname($path);
4555
		if ($arc['cmd'] === 'phpfunction') {
4556 View Code Duplication
			if (is_callable($arc['argc'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4557
				call_user_func_array($arc['argc'], array($path, $dir));
4558
			}
4559
		} else {
4560
			$cwd = getcwd();
4561
			chdir($dir);
4562
			$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path));
4563
			$this->procExec($cmd, $o, $c);
4564
			chdir($cwd);
4565
		}
4566
		$remove && unlink($path);
4567
	}
4568
	
4569
	/**
4570
	 * Create Zip archive using PHP class ZipArchive
4571
	 * 
4572
	 * @param  string        $dir      target dir
4573
	 * @param  array         $files    files names list
4574
	 * @param  string|object $zipPath  Zip archive name
4575
	 * @return void
4576
	 * @author Naoki Sawada
4577
	 */
4578
	protected static function zipArchiveZip($dir, $files, $zipPath) {
4579
		try {
4580
			if ($start = is_string($zipPath)) {
4581
				$zip = new ZipArchive();
4582
				if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) {
4583
					$zip = false;
4584
				}
4585
			} else {
4586
				$zip = $zipPath;
4587
			}
4588
			if ($zip) {
4589
				foreach($files as $file) {
4590
					$path = $dir . DIRECTORY_SEPARATOR . $file;
4591
					if (is_dir($path)) {
4592
						$zip->addEmptyDir($file);
4593
						$_files = array();
4594
						if ($handle = opendir($path)) {
4595
							while (false !== ($entry = readdir($handle))) {
4596
								if ($entry !== "." && $entry !== "..") {
4597
									$_files[] = $file . DIRECTORY_SEPARATOR . $entry;
4598
								}
4599
							}
4600
							closedir($handle);
4601
						}
4602
						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...
4603
							self::zipArchiveZip($dir, $_files, $zip);
4604
						}
4605
					} else {
4606
						$zip->addFile($path, $file);
4607
					}
4608
				}
4609
				$start && $zip->close();
4610
			}
4611
		} catch (Exception $e) {
4612
			return false;
4613
		}
4614
		return true;
4615
	}
4616
	
4617
	/**
4618
	 * Unpack Zip archive using PHP class ZipArchive
4619
	 * 
4620
	 * @param  string $zipPath  Zip archive name
4621
	 * @param  string $toDir    Extract to path
4622
	 * @return bool
4623
	 * @author Naoki Sawada
4624
	 */
4625
	protected static function zipArchiveUnzip($zipPath, $toDir) {
4626
		try {
4627
			$zip = new ZipArchive();
4628
			if ($zip->open($zipPath) === true) {
4629
				$zip->extractTo($toDir);
4630
				$zip->close();
4631
			}
4632
		} catch (Exception $e) {
4633
			return false;
4634
		}
4635
		return true;
4636
	}
4637
	
4638
	/**==================================* abstract methods *====================================**/
4639
	
4640
	/*********************** paths/urls *************************/
4641
	
4642
	/**
4643
	 * Return parent directory path
4644
	 *
4645
	 * @param  string  $path  file path
4646
	 * @return string
4647
	 * @author Dmitry (dio) Levashov
4648
	 **/
4649
	abstract protected function _dirname($path);
4650
4651
	/**
4652
	 * Return file name
4653
	 *
4654
	 * @param  string  $path  file path
4655
	 * @return string
4656
	 * @author Dmitry (dio) Levashov
4657
	 **/
4658
	abstract protected function _basename($path);
4659
4660
	/**
4661
	 * Join dir name and file name and return full path.
4662
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
4663
	 *
4664
	 * @param  string  $dir   dir path
4665
	 * @param  string  $name  file name
4666
	 * @return string
4667
	 * @author Dmitry (dio) Levashov
4668
	 **/
4669
	abstract protected function _joinPath($dir, $name);
4670
4671
	/**
4672
	 * Return normalized path 
4673
	 *
4674
	 * @param  string  $path  file path
4675
	 * @return string
4676
	 * @author Dmitry (dio) Levashov
4677
	 **/
4678
	abstract protected function _normpath($path);
4679
4680
	/**
4681
	 * Return file path related to root dir
4682
	 *
4683
	 * @param  string  $path  file path
4684
	 * @return string
4685
	 * @author Dmitry (dio) Levashov
4686
	 **/
4687
	abstract protected function _relpath($path);
4688
	
4689
	/**
4690
	 * Convert path related to root dir into real path
4691
	 *
4692
	 * @param  string  $path  rel file path
4693
	 * @return string
4694
	 * @author Dmitry (dio) Levashov
4695
	 **/
4696
	abstract protected function _abspath($path);
4697
	
4698
	/**
4699
	 * Return fake path started from root dir.
4700
	 * Required to show path on client side.
4701
	 *
4702
	 * @param  string  $path  file path
4703
	 * @return string
4704
	 * @author Dmitry (dio) Levashov
4705
	 **/
4706
	abstract protected function _path($path);
4707
	
4708
	/**
4709
	 * Return true if $path is children of $parent
4710
	 *
4711
	 * @param  string  $path    path to check
4712
	 * @param  string  $parent  parent path
4713
	 * @return bool
4714
	 * @author Dmitry (dio) Levashov
4715
	 **/
4716
	abstract protected function _inpath($path, $parent);
4717
	
4718
	/**
4719
	 * Return stat for given path.
4720
	 * Stat contains following fields:
4721
	 * - (int)    size    file size in b. required
4722
	 * - (int)    ts      file modification time in unix time. required
4723
	 * - (string) mime    mimetype. required for folders, others - optionally
4724
	 * - (bool)   read    read permissions. required
4725
	 * - (bool)   write   write permissions. required
4726
	 * - (bool)   locked  is object locked. optionally
4727
	 * - (bool)   hidden  is object hidden. optionally
4728
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
4729
	 * - (string) target  for symlinks - link target path. optionally
4730
	 *
4731
	 * If file does not exists - returns empty array or false.
4732
	 *
4733
	 * @param  string  $path    file path 
4734
	 * @return array|false
4735
	 * @author Dmitry (dio) Levashov
4736
	 **/
4737
	abstract protected function _stat($path);
4738
	
4739
4740
	/***************** file stat ********************/
4741
4742
		
4743
	/**
4744
	 * Return true if path is dir and has at least one childs directory
4745
	 *
4746
	 * @param  string  $path  dir path
4747
	 * @return bool
4748
	 * @author Dmitry (dio) Levashov
4749
	 **/
4750
	abstract protected function _subdirs($path);
4751
	
4752
	/**
4753
	 * Return object width and height
4754
	 * Ususaly used for images, but can be realize for video etc...
4755
	 *
4756
	 * @param  string  $path  file path
4757
	 * @param  string  $mime  file mime type
4758
	 * @return string
4759
	 * @author Dmitry (dio) Levashov
4760
	 **/
4761
	abstract protected function _dimensions($path, $mime);
4762
	
4763
	/******************** file/dir content *********************/
4764
4765
	/**
4766
	 * Return files list in directory
4767
	 *
4768
	 * @param  string  $path  dir path
4769
	 * @return array
4770
	 * @author Dmitry (dio) Levashov
4771
	 **/
4772
	abstract protected function _scandir($path);
4773
	
4774
	/**
4775
	 * Open file and return file pointer
4776
	 *
4777
	 * @param  string $path file path
4778
	 * @param  string $mode open mode
4779
	 * @return resource|false
4780
	 * @author Dmitry (dio) Levashov
4781
	 **/
4782
	abstract protected function _fopen($path, $mode="rb");
4783
	
4784
	/**
4785
	 * Close opened file
4786
	 * 
4787
	 * @param  resource  $fp    file pointer
4788
	 * @param  string    $path  file path
4789
	 * @return bool
4790
	 * @author Dmitry (dio) Levashov
4791
	 **/
4792
	abstract protected function _fclose($fp, $path='');
4793
	
4794
	/********************  file/dir manipulations *************************/
4795
	
4796
	/**
4797
	 * Create dir and return created dir path or false on failed
4798
	 *
4799
	 * @param  string  $path  parent dir path
4800
	 * @param string  $name  new directory name
4801
	 * @return string|bool
4802
	 * @author Dmitry (dio) Levashov
4803
	 **/
4804
	abstract protected function _mkdir($path, $name);
4805
	
4806
	/**
4807
	 * Create file and return it's path or false on failed
4808
	 *
4809
	 * @param  string  $path  parent dir path
4810
	 * @param string  $name  new file name
4811
	 * @return string|bool
4812
	 * @author Dmitry (dio) Levashov
4813
	 **/
4814
	abstract protected function _mkfile($path, $name);
4815
	
4816
	/**
4817
	 * Create symlink
4818
	 *
4819
	 * @param  string  $source     file to link to
4820
	 * @param  string  $targetDir  folder to create link in
4821
	 * @param  string  $name       symlink name
4822
	 * @return bool
4823
	 * @author Dmitry (dio) Levashov
4824
	 **/
4825
	abstract protected function _symlink($source, $targetDir, $name);
4826
	
4827
	/**
4828
	 * Copy file into another file (only inside one volume)
4829
	 *
4830
	 * @param  string  $source  source file path
4831
	 * @param  string  $target  target dir path
0 ignored issues
show
Documentation introduced by
There is no parameter named $target. Did you maybe mean $targetDir?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
4832
	 * @param  string  $name    file name
4833
	 * @return bool
4834
	 * @author Dmitry (dio) Levashov
4835
	 **/
4836
	abstract protected function _copy($source, $targetDir, $name);
4837
	
4838
	/**
4839
	 * Move file into another parent dir.
4840
	 * Return new file path or false.
4841
	 *
4842
	 * @param  string  $source  source file path
4843
	 * @param  string  $target  target dir path
0 ignored issues
show
Documentation introduced by
There is no parameter named $target. Did you maybe mean $targetDir?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
4844
	 * @param  string  $name    file name
4845
	 * @return string|bool
4846
	 * @author Dmitry (dio) Levashov
4847
	 **/
4848
	abstract protected function _move($source, $targetDir, $name);
4849
	
4850
	/**
4851
	 * Remove file
4852
	 *
4853
	 * @param  string  $path  file path
4854
	 * @return bool
4855
	 * @author Dmitry (dio) Levashov
4856
	 **/
4857
	abstract protected function _unlink($path);
4858
4859
	/**
4860
	 * Remove dir
4861
	 *
4862
	 * @param  string  $path  dir path
4863
	 * @return bool
4864
	 * @author Dmitry (dio) Levashov
4865
	 **/
4866
	abstract protected function _rmdir($path);
4867
4868
	/**
4869
	 * Create new file and write into it from file pointer.
4870
	 * Return new file path or false on error.
4871
	 *
4872
	 * @param  resource  $fp   file pointer
4873
	 * @param  string    $dir  target dir path
4874
	 * @param  string    $name file name
4875
	 * @param  array     $stat file stat (required by some virtual fs)
4876
	 * @return bool|string
4877
	 * @author Dmitry (dio) Levashov
4878
	 **/
4879
	abstract protected function _save($fp, $dir, $name, $stat);
4880
	
4881
	/**
4882
	 * Get file contents
4883
	 *
4884
	 * @param  string  $path  file path
4885
	 * @return string|false
4886
	 * @author Dmitry (dio) Levashov
4887
	 **/
4888
	abstract protected function _getContents($path);
4889
	
4890
	/**
4891
	 * Write a string to a file
4892
	 *
4893
	 * @param  string  $path     file path
4894
	 * @param  string  $content  new file content
4895
	 * @return bool
4896
	 * @author Dmitry (dio) Levashov
4897
	 **/
4898
	abstract protected function _filePutContents($path, $content);
4899
4900
	/**
4901
	 * Extract files from archive
4902
	 *
4903
	 * @param  string  $path file path
4904
	 * @param  array   $arc  archiver options
4905
	 * @return bool
4906
	 * @author Dmitry (dio) Levashov, 
4907
	 * @author Alexey Sukhotin
4908
	 **/
4909
	abstract protected function _extract($path, $arc);
4910
4911
	/**
4912
	 * Create archive and return its path
4913
	 *
4914
	 * @param  string  $dir    target dir
4915
	 * @param  array   $files  files names list
4916
	 * @param  string  $name   archive name
4917
	 * @param  array   $arc    archiver options
4918
	 * @return string|bool
4919
	 * @author Dmitry (dio) Levashov, 
4920
	 * @author Alexey Sukhotin
4921
	 **/
4922
	abstract protected function _archive($dir, $files, $name, $arc);
4923
4924
	/**
4925
	 * Detect available archivers
4926
	 *
4927
	 * @return void
4928
	 * @author Dmitry (dio) Levashov, 
4929
	 * @author Alexey Sukhotin
4930
	 **/
4931
	abstract protected function _checkArchivers();
4932
4933
	/**
4934
	 * Change file mode (chmod)
4935
	 *
4936
	 * @param  string  $path  file path
4937
	 * @param  string  $mode  octal string such as '0755'
4938
	 * @return bool
4939
	 * @author David Bartle,
4940
	 **/
4941
	abstract protected function _chmod($path, $mode);
4942
4943
	
4944
} // END class
4945